Skip to content

feat: packing with rollup #44

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Aug 9, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .babelrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"presets": ["@babel/preset-env"]
}
5 changes: 2 additions & 3 deletions .github/workflows/nodejs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ name: Node Test
on:
push:
branches:
- master
- "*"
pull_request:
branches:
- master
Expand Down Expand Up @@ -40,5 +40,4 @@ jobs:
run: yarn
- name: Test
run: |
yarn test
yarn test-with-coverage
yarn test
6 changes: 5 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -9,4 +9,8 @@ yarn.lock
package-lock.json

# test
.nyc_output
.nyc_output
.test_output

# build
dist
6 changes: 3 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# string-multiple-replace

> Replace multiple substrings in a string in turn.
> Replace multiple substrings in a string sequentially.

[![LICENSE](https://img.shields.io/badge/license-MIT-blue)](./LICENSE)
[![npm-version](https://img.shields.io/npm/v/string-multiple-replace)](https://www.npmjs.com/package/string-multiple-replace)
Expand Down Expand Up @@ -71,7 +71,7 @@ multiReplace(input, matcherObj, keys => keys);

## API

> multiReplace(input, matcherObj[,sequencer])
> multiReplace(input, matcherObj [,sequencer])

The original string is replaced in turn according to the `matcherObj`, where `sequencer` determines the replacement order, and the existence state of `sequencer` determines whether the last operation overwrites the previous operation.

Expand Down Expand Up @@ -99,4 +99,4 @@ Type: `function`, `array`

A `function` that takes the keys of `matcherObj`, and return an suquence array.

>Upgrade Instruction: the existence state of `sequencer` determines whether the last operation overwrites the previous operation.
> Upgrade Instruction: the existence state of `sequencer` determines whether the last operation overwrites the previous operation.
File renamed without changes.
43 changes: 30 additions & 13 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,25 +1,42 @@
{
"name": "string-multiple-replace",
"version": "1.0.5",
"description": "Replace multiple substrings in a string in turn",
"version": "1.1.0",
"description": "Replace multiple substrings in a string sequentially",
"author": "iChengbo",
"scripts": {
"build": "rollup -c",
"test": "mocha",
"test-with-coverage": "nyc --reporter=text mocha",
"test:coverage": "nyc mocha test/**/*.js",
"prepare": "husky install"
},
"main": "index.js",
"directories": {
"lib": "lib",
"test": "test"
"type": "module",
"main": "dist/index.cjs.js",
"module": "dist/index.esm.js",
"exports": {
".": {
"require": "./dist/index.cjs.js",
"import": "./dist/index.esm.js"
}
},
"files": [
"dist"
],
"devDependencies": {
"@commitlint/cli": "^17.1.2",
"@commitlint/config-conventional": "^17.1.0",
"chai": "^4.3.4",
"husky": "^8.0.3",
"mocha": "^10.2.0",
"nyc": "^15.1.0"
"@babel/core": "^7.24.8",
"@babel/preset-env": "^7.24.8",
"@babel/register": "^7.24.6",
"@commitlint/cli": "^19.3.0",
"@commitlint/config-conventional": "^19.2.2",
"@rollup/plugin-commonjs": "^26.0.1",
"@rollup/plugin-node-resolve": "^15.2.3",
"@rollup/plugin-terser": "^0.4.4",
"babel-preset-env": "^1.7.0",
"babel-register": "^6.26.0",
"chai": "^5.1.1",
"husky": "^9.0.11",
"mocha": "^10.6.0",
"nyc": "^17.0.0",
"rollup": "^4.18.1"
},
"keywords": [
"replace",
Expand Down
27 changes: 27 additions & 0 deletions rollup.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import { nodeResolve } from "@rollup/plugin-node-resolve";
import commonjs from "@rollup/plugin-commonjs";
import terser from "@rollup/plugin-terser";

export default {
input: "src/index.js",
output: [
{
file: "dist/index.esm.js",
format: "es",
},
{
file: "dist/index.cjs.js",
format: "cjs",
},
{
file: "dist/index.umd.js",
format: "umd",
name: 'StringMultipleReplace'
}
],
plugins: [
nodeResolve(),
commonjs(),
terser()
],
};
20 changes: 20 additions & 0 deletions src/core/replace-with-cover.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
const replaceAll = (replaceThis, withThis, inThis) => inThis.split(replaceThis).join(withThis);

const isObject = (value) => value !== null && typeof value === "object" && !Array.isArray(value);

const stringifyValue = (value) => isObject(value) ? JSON.stringify(value) : String(value);

const getSequence = (keys, matcherObj) => typeof keys === "function" ? keys(Object.keys(matcherObj)) : keys;

const replaceWithCover = (input, matcherObj, sequence) => {
let output = input;
const sequencer = getSequence(sequence, matcherObj);
for (let i = 0; i < sequencer.length; i++) {
const key = sequencer[i];
const withThis = matcherObj[key] !== undefined ? stringifyValue(matcherObj[key]) : key;
output = replaceAll(key, withThis, output);
}
return output;
};

export default replaceWithCover;
45 changes: 45 additions & 0 deletions src/core/replace-without-cover.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
const replaceRange = (input, items) => {
items.sort((a, b) => a[0] - b[0]);

let result = "";
let index = 0;
items.forEach((item) => {
const [start, end, callback] = item;
result += input.substring(index, start);
result += callback(input.substring(start, end));
index = end;
});

result += input.substring(index);

return result;
};

const generateItemByTarget = (input, target, replaced) => {
const items = [];
let index = 0;
while (true) {
const idx = input.indexOf(target, index);
if (idx === -1) {
break;
}
items.push([idx, idx + target.length, () => replaced]);
index = idx + 1;
}
return items;
};

const generateItems = (input, matcherObj) => {
return Object.keys(matcherObj).reduce((items, key) => {
const generatedItems = generateItemByTarget(input, key, matcherObj[key]);
return items.concat(generatedItems);
}, []);
};

// this replacement operation will not be affected next time.
const replaceWithoutCover = (input, matcherObj) => {
const items = generateItems(input, matcherObj);
return replaceRange(input, items);
};

export default replaceWithoutCover;
55 changes: 55 additions & 0 deletions src/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
import replaceWithCover from "./core/replace-with-cover.js";
import replaceWithoutCover from "./core/replace-without-cover.js";

const isSubArray = (arr, subArr) => subArr.every((s) => arr.includes(s));

/**
* @param {String} input A string to be processed.
* @param {Object} matcherObj An object that represents a string replacement mapping.
* @param {Array|Function} sequencer A `function` that takes the keys of `matcherObj`, and return an suquence array.
*/
const multiReplace = (...args) => {
const [input, matcherObj, sequencer] = args
if (args.length !== 2 && args.length !== 3) {
throw new TypeError("The number of parameters is incorrect.");
}

if (typeof input !== "string") {
throw new TypeError(`Expected input to be a string, got ${typeof input}`);
}

if (typeof matcherObj !== "object") {
throw new TypeError(
`Expected matcherObj to be a object, got ${typeof matcherObj}`
);
}

if (Object.keys(matcherObj).length === 0) {
return input;
}

if (sequencer) {
if (typeof sequencer !== "function" && !Array.isArray(sequencer)) {
throw new TypeError(
`Expected sequencer to be a callback or array, got ${Object.prototype.toString.call(
sequencer
)}`
);
}

const keys = Object.keys(matcherObj);
const sequence = Array.isArray(sequencer) ? sequencer : sequencer(keys);

if (!isSubArray(Object.keys(matcherObj), sequence)) {
throw new TypeError(
`Expected sequence is the subset of Object.keys(matcherObj), got: ${sequence}`
);
}

return replaceWithCover(input, matcherObj, sequence);
} else {
return replaceWithoutCover(input, matcherObj);
}
};

export default multiReplace;
30 changes: 15 additions & 15 deletions test/index.js
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
const should = require('chai').should();
const expect = require('chai').expect;
const multiReplace = require('../index');
import { expect } from 'chai';
// import { default as multiReplace } from '../dist/index.esm.js'
import multiReplace from '../src/index.js'

describe('String-multiple-replace', function () {
describe('String-Multiple-Replace', function () {

describe("need cover previous replacement", function () {
it('Replace brave & trouble from a text with cowardly & escape, and sequencer is a array that decide the order of replacement.', function () {
Expand All @@ -13,8 +13,8 @@ describe('String-multiple-replace', function () {
}
const sequencer = ["brave", "trouble"];

const resultStr = multiReplace(input, matcherObj, sequencer);;
resultStr.should.equal("I'm only cowardly when I have to be. Being cowardly doesn't mean you go looking for escape.");
const resultStr = multiReplace(input, matcherObj, sequencer);
expect(resultStr).to.equal("I'm only cowardly when I have to be. Being cowardly doesn't mean you go looking for escape.");
});

it('Replace brave & trouble from a text with cowardly & escape, and sequencer is a function that will return a array', function () {
Expand All @@ -27,7 +27,7 @@ describe('String-multiple-replace', function () {
const resultStr = multiReplace(input, matcherObj, function (keys) {
return keys; //or keys.sort(callback)
});
resultStr.should.equal("I'm only cowardly when I have to be. Being cowardly doesn't mean you go looking for escape.");
expect(resultStr).to.equal("I'm only cowardly when I have to be. Being cowardly doesn't mean you go looking for escape.");
});

it("Replace 'My friend' with 'I', but then 'I' will be overwritten with 'My friend'", function () {
Expand All @@ -40,12 +40,12 @@ describe('String-multiple-replace', function () {
const sequencer = ["My friend", "has", "I"];

const resultStr = multiReplace(input, matcherObj, sequencer);;
resultStr.should.equal("My friend have a dog. My friend want a dog too!");
expect(resultStr).to.equal("My friend have a dog. My friend want a dog too!");
});
});

describe("needn't cover previous replacement", function () {
it("Replace 'My firend' with 'I', and then 'I' will not be overwritten with 'My friend'", function () {
it("Replace 'My friend' with 'I', and then 'I' will not be overwritten with 'My friend'", function () {
const input = "My friend has a dog. I want a dog too!";
const matcherObj = {
"My friend": "I",
Expand All @@ -54,7 +54,7 @@ describe('String-multiple-replace', function () {
}

const resultStr = multiReplace(input, matcherObj);;
resultStr.should.equal("I have a dog. My friend want a dog too!");
expect(resultStr).to.equal("I have a dog. My friend want a dog too!");
});

it("Replace all 'a' with 'b', and replace all 'b' with 'a'", function () {
Expand All @@ -65,12 +65,12 @@ describe('String-multiple-replace', function () {
}

const resultStr = multiReplace(input, matcherObj);
resultStr.should.equal("bacd, bacd, b");
expect(resultStr).to.equal("bacd, bacd, b");
});
});

describe("Some special cases", function () {
it('Replace "My firend" with "{"Name":"Tom"}"', function () {
it('Replace "My friend" with "{"Name":"Tom"}"', function () {
const input = 'My friend has a dog. I want a dog too!';
const matcherObj = {
'My friend': {
Expand All @@ -79,15 +79,15 @@ describe('String-multiple-replace', function () {
}
const sequencer = ["My friend"];

const resultStr = multiReplace(input, matcherObj, sequencer);;
resultStr.should.equal('{"Name":"Tom"} has a dog. I want a dog too!');
const resultStr = multiReplace(input, matcherObj, sequencer);
expect(resultStr).to.equal('{"Name":"Tom"} has a dog. I want a dog too!');
});
it("should return 'input'", function () {
const input = 'My friend has a dog. I want a dog too!';
const matcherObj = {}

const resultStr = multiReplace(input, matcherObj);;
resultStr.should.equal('My friend has a dog. I want a dog too!');
expect(resultStr).to.equal('My friend has a dog. I want a dog too!');
});
});

Expand Down
Loading