json-placeholder-replacer
Advanced tools
Comparing version 1.0.37 to 2.0.0
@@ -1,2 +0,2 @@ | ||
export type DelimiterTag = { | ||
export interface DelimiterTag { | ||
begin: string; | ||
@@ -6,14 +6,21 @@ end: string; | ||
escapedEnding?: string; | ||
}; | ||
} | ||
export interface Configuration { | ||
delimiterTags: DelimiterTag[]; | ||
defaultValueSeparator: string; | ||
} | ||
export declare class JsonPlaceholderReplacer { | ||
private variablesMap; | ||
private readonly delimiterTags; | ||
constructor(...delimiterTags: DelimiterTag[]); | ||
addVariableMap(variableMap: object | string): JsonPlaceholderReplacer; | ||
replace(json: object): {}; | ||
private readonly variablesMap; | ||
private readonly configuration; | ||
private readonly delimiterTagsRegex; | ||
constructor(options?: Partial<Configuration>); | ||
addVariableMap(variableMap: Record<string, unknown> | string): JsonPlaceholderReplacer; | ||
replace(json: object): object; | ||
private initializeOptions; | ||
private replaceChildren; | ||
private replaceValue; | ||
private replacer; | ||
private parseTag; | ||
private checkInEveryMap; | ||
private navigateThroughMap; | ||
} |
@@ -6,21 +6,24 @@ "use strict"; | ||
{ | ||
begin: '{{', | ||
end: '}}' | ||
begin: "{{", | ||
end: "}}", | ||
}, | ||
{ | ||
begin: '<<', | ||
end: '>>' | ||
} | ||
begin: "<<", | ||
end: ">>", | ||
}, | ||
]; | ||
const defaultSeparator = ":"; | ||
class JsonPlaceholderReplacer { | ||
constructor(...delimiterTags) { | ||
constructor(options) { | ||
this.variablesMap = []; | ||
if (delimiterTags.length === 0) { | ||
delimiterTags = defaultDelimiterTags; | ||
} | ||
const escapeRegExp = (text) => text.replace(/[-[\]{}()*+?.,\\^$|#\s]/g, '\\$&'); | ||
this.delimiterTags = delimiterTags.map(tag => (Object.assign(Object.assign({}, tag), { escapedBeginning: escapeRegExp(tag.begin), escapedEnding: escapeRegExp(tag.end) }))); | ||
this.configuration = this.initializeOptions(options); | ||
const escapeRegExp = (text) => text.replace(/[-[\]{}()*+?.,\\^$|#\s]/g, "\\$&"); | ||
this.configuration.delimiterTags = this.configuration.delimiterTags.map((tag) => (Object.assign(Object.assign({}, tag), { escapedBeginning: escapeRegExp(tag.begin), escapedEnding: escapeRegExp(tag.end) }))); | ||
const delimiterTagsRegexes = this.configuration.delimiterTags | ||
.map((delimiterTag) => `^${delimiterTag.begin}[^${delimiterTag.end}]+${delimiterTag.end}$`) | ||
.join("|"); | ||
this.delimiterTagsRegex = new RegExp(delimiterTagsRegexes); | ||
} | ||
addVariableMap(variableMap) { | ||
if (typeof variableMap == 'string') { | ||
if (typeof variableMap === "string") { | ||
this.variablesMap.push(JSON.parse(variableMap)); | ||
@@ -36,6 +39,20 @@ } | ||
} | ||
initializeOptions(options) { | ||
let delimiterTags = defaultDelimiterTags; | ||
let defaultValueSeparator = defaultSeparator; | ||
if (options !== undefined) { | ||
if (options.delimiterTags !== undefined && | ||
options.delimiterTags.length > 0) { | ||
delimiterTags = options.delimiterTags; | ||
} | ||
if (options.defaultValueSeparator !== undefined) { | ||
defaultValueSeparator = options.defaultValueSeparator; | ||
} | ||
} | ||
return { defaultValueSeparator, delimiterTags }; | ||
} | ||
replaceChildren(node) { | ||
for (const key in node) { | ||
const attribute = node[key]; | ||
if (typeof attribute == 'object') { | ||
if (typeof attribute === "object") { | ||
node[key] = this.replaceChildren(attribute); | ||
@@ -50,9 +67,5 @@ } | ||
replaceValue(node) { | ||
const delimiterTagRegex = this.delimiterTags | ||
.map(delimiterTag => `^${delimiterTag.begin}[^${delimiterTag.end}]+${delimiterTag.end}$`).join('|'); | ||
const regExp = new RegExp(delimiterTagRegex); | ||
const placeHolderIsInsideStringContext = !regExp.test(node); | ||
const output = this.delimiterTags | ||
.reduce((acc, delimiterTag) => { | ||
const regex = new RegExp(`(${delimiterTag.escapedBeginning}[^${delimiterTag.escapedEnding}]+${delimiterTag.escapedEnding})`, 'g'); | ||
const placeHolderIsInsideStringContext = !this.delimiterTagsRegex.test(node); | ||
const output = this.configuration.delimiterTags.reduce((acc, delimiterTag) => { | ||
const regex = new RegExp(`(${delimiterTag.escapedBeginning}[^${delimiterTag.escapedEnding}]+${delimiterTag.escapedEnding})`, "g"); | ||
return acc.replace(regex, this.replacer(placeHolderIsInsideStringContext)(delimiterTag)); | ||
@@ -69,5 +82,8 @@ }, node); | ||
return (delimiterTag) => (placeHolder) => { | ||
const path = placeHolder.substr(delimiterTag.begin.length, placeHolder.length - (delimiterTag.begin.length + delimiterTag.end.length)); | ||
const mapCheckResult = this.checkInEveryMap(path); | ||
const { tag, defaultValue } = this.parseTag(placeHolder, delimiterTag); | ||
const mapCheckResult = this.checkInEveryMap(tag); | ||
if (mapCheckResult === undefined) { | ||
if (defaultValue !== undefined) { | ||
return defaultValue; | ||
} | ||
return placeHolder; | ||
@@ -79,3 +95,3 @@ } | ||
const parsed = JSON.parse(mapCheckResult); | ||
if (typeof parsed === 'object') { | ||
if (typeof parsed === "object") { | ||
return JSON.stringify(parsed); | ||
@@ -86,5 +102,16 @@ } | ||
} | ||
parseTag(placeHolder, delimiterTag) { | ||
const path = placeHolder.substring(delimiterTag.begin.length, placeHolder.length - delimiterTag.begin.length); | ||
let tag = path; | ||
let defaultValue; | ||
const defaultValueSeparatorIndex = path.indexOf(this.configuration.defaultValueSeparator); | ||
if (defaultValueSeparatorIndex > 0) { | ||
tag = path.substring(0, defaultValueSeparatorIndex); | ||
defaultValue = path.substring(defaultValueSeparatorIndex + 1); | ||
} | ||
return { tag, defaultValue }; | ||
} | ||
checkInEveryMap(path) { | ||
let result = undefined; | ||
this.variablesMap.forEach(map => result = this.navigateThroughMap(map, path)); | ||
let result; | ||
this.variablesMap.forEach((map) => (result = this.navigateThroughMap(map, path))); | ||
return result; | ||
@@ -100,8 +127,8 @@ } | ||
} | ||
let keys = path.split('.'); | ||
const keys = path.split("."); | ||
const key = keys[0]; | ||
keys.shift(); | ||
return this.navigateThroughMap(map[key], keys.join('.')); | ||
return this.navigateThroughMap(map[key], keys.join(".")); | ||
} | ||
} | ||
exports.JsonPlaceholderReplacer = JsonPlaceholderReplacer; |
{ | ||
"name": "json-placeholder-replacer", | ||
"version": "1.0.37", | ||
"version": "2.0.0", | ||
"description": "Javascript/Typescript library/cli to replace placeholders in an javascript object", | ||
@@ -10,5 +10,6 @@ "main": "dist/index.js", | ||
"codeCoverage": "node_modules/.bin/jest --silent --coverage", | ||
"lint": "node_modules/.bin/tslint --project tsconfig.json", | ||
"build": "tsc", | ||
"all": "npm run lint && npm run build && npm run codeCoverage && npm run test" | ||
"lint": "node_modules/.bin/tslint --project tsconfig.json --force", | ||
"build": "tsc --project tsconfig.build.json", | ||
"all": "npm run lint && npm run build && npm run codeCoverage && npm run test", | ||
"prepare": "husky install" | ||
}, | ||
@@ -37,3 +38,3 @@ "repository": { | ||
"*": "prettier --write", | ||
"*.ts": "eslint --fix" | ||
"*.ts": "npm run lint --fix" | ||
}, | ||
@@ -70,10 +71,8 @@ "homepage": "https://github.com/virgs/jsonPlaceholderReplacer#readme", | ||
"devDependencies": { | ||
"@types/jest": "^29.5.4", | ||
"@types/node": "^20.5.7", | ||
"husky": "^8.0.3", | ||
"jest": "^29.6.4", | ||
"lint-staged": "^13.2.2", | ||
"prettier": "^2.8.8", | ||
"eslint-config-prettier": "^8.8.0", | ||
"eslint-plugin-prettier": "^4.2.1", | ||
"@types/jest": "^29.5.4", | ||
"@types/node": "^20.5.7", | ||
"jest": "^29.6.4", | ||
"ts-jest": "^29.1.1", | ||
@@ -80,0 +79,0 @@ "ts-node": "^10.9.1", |
209
README.md
# jsonPlaceholderReplacer | ||
[![npm version](https://badge.fury.io/js/json-placeholder-replacer.svg)](https://badge.fury.io/js/json-placeholder-replacer) | ||
[![npm version](https://badge.fury.io/js/json-placeholder-replacer.svg)](https://badge.fury.io/js/json-placeholder-replacer) | ||
[![build status](https://circleci.com/gh/virgs/jsonPlaceholderReplacer.svg?style=shield)](https://app.circleci.com/pipelines/github/virgs/jsonPlaceholderReplacer) | ||
[![Maintainability](https://api.codeclimate.com/v1/badges/6e586ff6eb12a67da08e/maintainability)](https://codeclimate.com/github/lopidio/jsonPlaceholderReplacer/maintainability) | ||
[![Maintainability](https://api.codeclimate.com/v1/badges/6e586ff6eb12a67da08e/maintainability)](https://codeclimate.com/github/lopidio/jsonPlaceholderReplacer/maintainability) | ||
[![Test Coverage](https://api.codeclimate.com/v1/badges/6e586ff6eb12a67da08e/test_coverage)](https://codeclimate.com/github/lopidio/jsonPlaceholderReplacer/test_coverage) | ||
[![Known Vulnerabilities](https://snyk.io/test/github/virgs/jsonPlaceholderReplacer/badge.svg)](https://app.snyk.io/) | ||
Lightweight yet really powerful typescript library/cli to replace placeholders in an javascript object/JSON. | ||
By default, all you have to do is to use double curly brackets **{{**placeholderKey**}}** or angle brackets **<<**placeholderKey**>>**, interchangeably, to identify the placeholder. | ||
Don't worry, if you don't like these default placeholders you can create your own. | ||
Don't worry, if you don't like these default placeholders you can create your own. | ||
## CLI usage | ||
```shell | ||
json-placeholder-replacer replaceableFilename [...variableMaps] | ||
``` | ||
$ json-placeholder-replacer replaceableFilename [...variableMaps] | ||
### Example | ||
```shell | ||
json-placeholder-replacer [replaceable.json](/rep) [variable.map](/map) | ||
``` | ||
Example: | ||
```$ json-placeholder-replacer ```[replaceable.json](/rep) [variable.map](/map) | ||
Would result: | ||
### Would result | ||
```shell | ||
cat replaceable.json | ||
# { | ||
# "curly": "{{key}}", | ||
# "angle": "<<key>>" | ||
# } | ||
cat variable.map: | ||
# { | ||
# "key": 10, | ||
# "not-mapped": 20 | ||
# } | ||
json-placeholder-replacer replaceable.json variable.map | ||
# { | ||
# "curly": 10, | ||
# "angle": 10, | ||
# "not-mapped": 20 | ||
# } | ||
``` | ||
replaceable.json: | ||
{ | ||
"curly": "{{key}}", | ||
"angle": "<<key>>" | ||
} | ||
variable.map: | ||
{ | ||
"key": 10 | ||
} | ||
result: | ||
{ | ||
"curly": 10, | ||
"angle": 10 | ||
} | ||
``` | ||
## Library usage | ||
## Library usage: | ||
As simples as: | ||
``` | ||
import {JsonPlaceholderReplacer} from "json-placeholder-replacer"; | ||
```typescript | ||
import { JsonPlaceholderReplacer } from "json-placeholder-replacer"; | ||
const placeHolderReplacer = new JsonPlaceholderReplacer(); | ||
placeHolderReplacer.addVariableMap({ | ||
key: 100, | ||
otherKey: 200 | ||
key: 100, | ||
otherKey: 200, | ||
}); | ||
const afterReplace = placeHolderReplacer.replace({ | ||
replaceable: "{{key}}", | ||
otherReplaceableWithSameKey: "<<key>>", | ||
otherReplaceable: "{{otherKey}}" | ||
}) | ||
replaceable: "{{key}}", | ||
otherReplaceableWithSameKey: "<<key>>", | ||
otherReplaceable: "{{otherKey}}", | ||
}); | ||
@@ -66,11 +71,14 @@ // afterReplace = { | ||
It's possible to replace the default placeholders with some as cool as you want. | ||
``` | ||
const placeHolderReplacer = new JsonPlaceholderReplacer({begin: '@{{-', end: '-}}@'}); | ||
### You can replace the default placeholders with some as cool as you want | ||
```typescript | ||
const placeHolderReplacer = new JsonPlaceholderReplacer({ | ||
delimiterTags: [{ begin: "@{{-", end: "-}}@" }], | ||
}); | ||
placeHolderReplacer.addVariableMap({ | ||
key: "nice" | ||
key: "nice", | ||
}); | ||
const afterReplace = placeHolderReplacer.replace({ | ||
replaceable: "@{{-key-}}@", | ||
}) | ||
replaceable: "@{{-key-}}@", | ||
}); | ||
@@ -82,14 +90,15 @@ // afterReplace = { | ||
It's possible to add more than one variables map. | ||
``` | ||
### It's also possible to add more than one variables map | ||
```typescript | ||
placeHolderReplacer.addVariableMap({ | ||
firstMapKey: "1" | ||
firstMapKey: "1", | ||
}); | ||
placeHolderReplacer.addVariableMap({ | ||
secondMapKey: 2 | ||
secondMapKey: 2, | ||
}); | ||
const afterReplace = placeHolderReplacer.replace({ | ||
replaceable: "{{firstMapKey}}", | ||
otherReplaceable: "<<secondMapKey>>" | ||
}) | ||
replaceable: "{{firstMapKey}}", | ||
otherReplaceable: "<<secondMapKey>>", | ||
}); | ||
@@ -102,13 +111,14 @@ // afterReplace = { | ||
And the last added maps have higher priority, so: | ||
``` | ||
### And the last added maps have higher priority, so | ||
```typescript | ||
placeHolderReplacer.addVariableMap({ | ||
id: "lowerPriority" | ||
id: "lowerPriority", | ||
}); | ||
placeHolderReplacer.addVariableMap({ | ||
id: "higherPriority" | ||
id: "higherPriority", | ||
}); | ||
const afterReplace = placeHolderReplacer.replace({ | ||
replaceable: "{{id}}" | ||
}) | ||
replaceable: "{{id}}", | ||
}); | ||
@@ -119,18 +129,20 @@ // afterReplace = { | ||
``` | ||
It keeps original variable types. So, if, in the map, a variable is boolean/string/number/object when it's replaced, it remains as boolean/string/number/object: | ||
``` | ||
### It keeps original variable types. So, if, in the map, a variable is boolean/string/number/object when it's replaced, it remains as boolean/string/number/object | ||
```typescript | ||
placeHolderReplacer.addVariableMap({ | ||
booleanKey: true, | ||
stringKey: "string", | ||
numberKey: 10, | ||
objectKey: { | ||
inner: "inner" | ||
} | ||
booleanKey: true, | ||
stringKey: "string", | ||
numberKey: 10, | ||
objectKey: { | ||
inner: "inner", | ||
}, | ||
}); | ||
const afterReplace = placeHolderReplacer.replace({ | ||
booleanReplaceable: "{{booleanKey}}", | ||
stringReplaceable: "{{stringKey}}", | ||
numberReplaceable: "{{numberKey}}", | ||
objectReplaceable: "{{objectKey}}" | ||
}) | ||
booleanReplaceable: "{{booleanKey}}", | ||
stringReplaceable: "{{stringKey}}", | ||
numberReplaceable: "{{numberKey}}", | ||
objectReplaceable: "{{objectKey}}", | ||
}); | ||
@@ -145,13 +157,13 @@ // afterReplace = { | ||
// } | ||
``` | ||
Just to make it clearer, it does not replace the placeholder Key: | ||
``` | ||
### Just to make it clearer, it does not replace the placeholder Key | ||
```typescript | ||
placeHolderReplacer.addVariableMap({ | ||
key: "someValue" | ||
key: "someValue", | ||
}); | ||
const afterReplace = placeHolderReplacer.replace({ | ||
"{{key}}": "value" | ||
}) | ||
"{{key}}": "value", | ||
}); | ||
// afterReplace = { | ||
@@ -162,4 +174,5 @@ // "{{key}}": "value" | ||
And, of course, it handles array substitution as well: | ||
``` | ||
### And, of course, it handles array substitution as well | ||
```typescript | ||
placeHolderReplacer.addVariableMap({ | ||
@@ -182,11 +195,12 @@ key: 987, | ||
Want to get nested elements? Go for it! | ||
``` | ||
### Want to get nested elements? Go for it | ||
```typescript | ||
placeHolderReplacer.addVariableMap({ | ||
key: { | ||
nested: "value" | ||
} | ||
key: { | ||
nested: "value", | ||
}, | ||
}); | ||
const afterReplace: any = placeHolderReplacer.replace({ | ||
replaceable: "<<key.nested>>" | ||
replaceable: "<<key.nested>>", | ||
}); | ||
@@ -197,3 +211,36 @@ | ||
// } | ||
``` | ||
### This feature allows you to have default values in case you don't have them mapped | ||
```typescript | ||
placeHolderReplacer.addVariableMap({ | ||
key: "value", | ||
}); | ||
const afterReplace: any = placeHolderReplacer.replace({ | ||
replaceable: "<<not-found-key:default-value>>", | ||
}); | ||
// afterReplace = { | ||
// replaceable: "default-value" | ||
// } | ||
``` | ||
### Of course, you can also change what is the default value separator (defaults to ':') | ||
```typescript | ||
const placeHolderReplacer = new JsonPlaceholderReplacer({ | ||
defaultValueSeparator: "=", | ||
}); | ||
placeHolderReplacer.addVariableMap({ | ||
key: "value", | ||
}); | ||
const afterReplace: any = placeHolderReplacer.replace({ | ||
replaceable: "<<not-found-key=default-value>>", // Note the '=' character here | ||
}); | ||
// afterReplace = { | ||
// replaceable: "default-value" | ||
// } | ||
``` |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
13026502
10
16
200
239