├── .github └── workflows │ ├── ci.yml │ └── release.yml ├── .gitignore ├── .prettierrc ├── .releaserc ├── CHANGELOG.md ├── CODE_OF_CONDUCT.md ├── LICENSE ├── README.md ├── __mocks__ └── jsonfile.js ├── assets └── logo.png ├── bin └── replace-json-property.bin.js ├── package-lock.json ├── package.json └── src ├── log.js ├── replace-json-property.js └── replace-json-property.spec.js /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: feature-branch 2 | on: 3 | push: 4 | branches: 5 | - '*' 6 | - '*/*' 7 | - '**' 8 | - '!main' 9 | jobs: 10 | CI: 11 | runs-on: ubuntu-latest 12 | steps: 13 | - name: Checkout 14 | uses: actions/checkout@v3 15 | - name: Install Node & NPM 16 | uses: actions/setup-node@v3 17 | - name: Install node modules 18 | run: npm install 19 | - name: Test 20 | run: npm run test 21 | -------------------------------------------------------------------------------- /.github/workflows/release.yml: -------------------------------------------------------------------------------- 1 | name: release 2 | on: 3 | push: 4 | branches: 5 | - 'main' 6 | jobs: 7 | release: 8 | runs-on: ubuntu-latest 9 | steps: 10 | - name: Checkout 11 | uses: actions/checkout@v3 12 | with: 13 | persist-credentials: false 14 | - name: Install Node & NPM 15 | uses: actions/setup-node@v3 16 | - name: Install node_modules 17 | run: npm install 18 | - name: Test 19 | run: npm run test:coverage 20 | - name: Report coverage 21 | uses: codecov/codecov-action@v3.1.4 22 | with: 23 | token: ${{ secrets.CODECOV_TOKEN }} 24 | - name: Build 25 | run: npm run build 26 | - name: Release 27 | uses: cycjimmy/semantic-release-action@v3 28 | env: 29 | GITHUB_TOKEN: ${{ secrets.GH_TOKEN }} 30 | NPM_TOKEN: ${{ secrets.NPM_TOKEN }} 31 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | 8 | # Runtime data 9 | pids 10 | *.pid 11 | *.seed 12 | *.pid.lock 13 | 14 | # Directory for instrumented libs generated by jscoverage/JSCover 15 | lib-cov 16 | 17 | # Coverage directory used by tools like istanbul 18 | coverage 19 | 20 | # nyc test coverage 21 | .nyc_output 22 | 23 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 24 | .grunt 25 | 26 | # Bower dependency directory (https://bower.io/) 27 | bower_components 28 | 29 | # node-waf configuration 30 | .lock-wscript 31 | 32 | # Compiled binary addons (https://nodejs.org/api/addons.html) 33 | build/Release 34 | 35 | # Dependency directories 36 | node_modules/ 37 | jspm_packages/ 38 | 39 | # TypeScript v1 declaration files 40 | typings/ 41 | 42 | # Optional npm cache directory 43 | .npm 44 | 45 | # Optional eslint cache 46 | .eslintcache 47 | 48 | # Optional REPL history 49 | .node_repl_history 50 | 51 | # Output of 'npm pack' 52 | *.tgz 53 | 54 | # Yarn Integrity file 55 | .yarn-integrity 56 | 57 | # dotenv environment variables file 58 | .env 59 | 60 | # next.js build output 61 | .next 62 | 63 | .idea 64 | dist/ 65 | -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "trailingComma": "none", 3 | "tabWidth": 4, 4 | "semi": true, 5 | "singleQuote": true, 6 | "arrowParens": "avoid" 7 | } -------------------------------------------------------------------------------- /.releaserc: -------------------------------------------------------------------------------- 1 | { 2 | "pkgRoot": "dist", 3 | "plugins": [ 4 | "@semantic-release/commit-analyzer", 5 | "@semantic-release/release-notes-generator", 6 | "@semantic-release/changelog", 7 | "@semantic-release/npm", 8 | ["@semantic-release/git", { 9 | "assets": ["package.json", "CHANGELOG.md"], 10 | "message": "chore(release): ${nextRelease.version} [skip ci]\n\n${nextRelease.notes}" 11 | }], 12 | "@semantic-release/github" 13 | ] 14 | } 15 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # [1.8.0](https://github.com/kreuzerk/replace-json-property/compare/v1.7.1...v1.8.0) (2021-10-07) 2 | 3 | 4 | ### Features 5 | 6 | * **limiter:** add ability to limit number of value replacements ([42bae44](https://github.com/kreuzerk/replace-json-property/commit/42bae44c734df498d500081dec57b9bacf18a6d8)) 7 | 8 | ## [1.7.1](https://github.com/kreuzerk/replace-json-property/compare/v1.7.0...v1.7.1) (2021-09-06) 9 | 10 | 11 | ### Bug Fixes 12 | 13 | * 🐛 (silent) document silent flag ([4e59fdb](https://github.com/kreuzerk/replace-json-property/commit/4e59fdb4d4bf012a08221cd7aabd924aa3d6329c)) 14 | 15 | # [1.7.0](https://github.com/kreuzerk/replace-json-property/compare/v1.6.3...v1.7.0) (2021-09-06) 16 | 17 | 18 | ### Features 19 | 20 | * **options:** add silent option ([66b2574](https://github.com/kreuzerk/replace-json-property/commit/66b2574ac1b89c587a4a68891c210e26afc07e05)) 21 | 22 | ## [1.6.3](https://github.com/kreuzerk/replace-json-property/compare/v1.6.2...v1.6.3) (2020-11-21) 23 | 24 | 25 | ### Bug Fixes 26 | 27 | * 🐛 correctly handle passed spaces ([2ebe3ed](https://github.com/kreuzerk/replace-json-property/commit/2ebe3ed79cae79f960166ab185eae662a3dbfc0d)) 28 | 29 | ## [1.6.2](https://github.com/kreuzerk/replace-json-property/compare/v1.6.1...v1.6.2) (2020-11-16) 30 | 31 | 32 | ### Bug Fixes 33 | 34 | * 🐛 apply correct default options ([0b29600](https://github.com/kreuzerk/replace-json-property/commit/0b29600bbc4c98c19526e4afc7bb1daaffe2b723)) 35 | * 🐛 assign correct default options ([b222d53](https://github.com/kreuzerk/replace-json-property/commit/b222d5309eab191257b9d6f33515954b77c4476b)) 36 | * 🐛 handle null value for options ([d1c22e1](https://github.com/kreuzerk/replace-json-property/commit/d1c22e1d24b34e9bf7a7cffb267de196e2815e14)) 37 | 38 | ## [1.6.1](https://github.com/kreuzerk/replace-json-property/compare/v1.6.0...v1.6.1) (2020-09-30) 39 | 40 | 41 | ### Bug Fixes 42 | 43 | * 🐛 remove wrong import and wrong method call ([8e9d250](https://github.com/kreuzerk/replace-json-property/commit/8e9d25078b065bc4c6e204b9c66243d608f26323)), closes [#33](https://github.com/kreuzerk/replace-json-property/issues/33) 44 | 45 | # [1.6.0](https://github.com/kreuzerk/replace-json-property/compare/v1.5.1...v1.6.0) (2020-09-17) 46 | 47 | 48 | ### Features 49 | 50 | * **replace:** use replace function in code ([b0fa83b](https://github.com/kreuzerk/replace-json-property/commit/b0fa83b90f5f718de6082535c2815cced13b669c)) 51 | 52 | ## [1.5.1](https://github.com/kreuzerk/replace-json-property/compare/v1.5.0...v1.5.1) (2020-09-15) 53 | 54 | 55 | ### Bug Fixes 56 | 57 | * **docs:** copy README and assets on build ([9261f4f](https://github.com/kreuzerk/replace-json-property/commit/9261f4f348a1e4f033d6a516352995fe479a0c64)) 58 | 59 | # [1.5.0](https://github.com/kreuzerk/replace-json-property/compare/v1.4.4...v1.5.0) (2020-09-15) 60 | 61 | 62 | ### Features 63 | 64 | * **replace:** allow usage in code ([106b3ba](https://github.com/kreuzerk/replace-json-property/commit/106b3baeb945dd8d0127c12023b60eccfedf75c1)) 65 | 66 | ## [1.4.4](https://github.com/kreuzerk/replace-json-property/compare/v1.4.3...v1.4.4) (2020-09-15) 67 | 68 | 69 | ### Bug Fixes 70 | 71 | * **deps:** add chalk as missing dependency ([e4e2ec6](https://github.com/kreuzerk/replace-json-property/commit/e4e2ec6f94ff8d2c0196976ef3417ba09d7cc506)) 72 | 73 | ## [1.4.3](https://github.com/kreuzerk/replace-json-property/compare/v1.4.2...v1.4.3) (2019-12-16) 74 | 75 | 76 | ### Bug Fixes 77 | 78 | * **docs:** change logo ([fd7e979](https://github.com/kreuzerk/replace-json-property/commit/fd7e979)) 79 | 80 | ## [1.4.2](https://github.com/kreuzerk/replace-json-property/compare/v1.4.1...v1.4.2) (2019-12-07) 81 | 82 | 83 | ### Bug Fixes 84 | 85 | * **docs:** add logo to docs ([7299ad1](https://github.com/kreuzerk/replace-json-property/commit/7299ad1)) 86 | 87 | ## [1.4.1](https://github.com/kreuzerk/replace-json-property/compare/v1.4.0...v1.4.1) (2019-05-13) 88 | 89 | 90 | ### Bug Fixes 91 | 92 | * **spaces:** keep correct number of spaces ([78bd810](https://github.com/kreuzerk/replace-json-property/commit/78bd810)) 93 | 94 | # [1.4.0](https://github.com/kreuzerk/replace-json-property/compare/v1.3.0...v1.4.0) (2018-12-25) 95 | 96 | 97 | ### Features 98 | 99 | * **options:** allow options to be passed in ([55cc0cd](https://github.com/kreuzerk/replace-json-property/commit/55cc0cd)) 100 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | Contributor Covenant Code of Conduct 2 | Our Pledge 3 | In the interest of fostering an open and welcoming environment, we as contributors and maintainers pledge to making participation in our project and our community a harassment-free experience for everyone, regardless of age, body size, disability, ethnicity, gender identity and expression, level of experience, nationality, personal appearance, race, religion, or sexual identity and orientation. 4 | 5 | Our Standards 6 | Examples of behavior that contributes to creating a positive environment include: 7 | 8 | Using welcoming and inclusive language 9 | Being respectful of differing viewpoints and experiences 10 | Gracefully accepting constructive criticism 11 | Focusing on what is best for the community 12 | Showing empathy towards other community members 13 | Examples of unacceptable behavior by participants include: 14 | 15 | The use of sexualized language or imagery and unwelcome sexual attention or advances 16 | Trolling, insulting/derogatory comments, and personal or political attacks 17 | Public or private harassment 18 | Publishing others' private information, such as a physical or electronic address, without explicit permission 19 | Other conduct which could reasonably be considered inappropriate in a professional setting 20 | Our Responsibilities 21 | Project maintainers are responsible for clarifying the standards of acceptable behavior and are expected to take appropriate and fair corrective action in response to any instances of unacceptable behavior. 22 | 23 | Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, or to ban temporarily or permanently any contributor for other behaviors that they deem inappropriate, threatening, offensive, or harmful. 24 | 25 | Scope 26 | This Code of Conduct applies both within project spaces and in public spaces when an individual is representing the project or its community. Examples of representing a project or community include using an official project e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. Representation of a project may be further defined and clarified by project maintainers. 27 | 28 | Enforcement 29 | Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project team at kevin.kreuzer90@icloud.com. The project team will review and investigate all complaints, and will respond in a way that it deems appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately. 30 | 31 | Project maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent repercussions as determined by other members of the project's leadership. 32 | 33 | Attribution 34 | This Code of Conduct is adapted from the Contributor Covenant, version 1.4, available at http://contributor-covenant.org/version/1/4 -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | [![code style: prettier](https://img.shields.io/badge/code_style-prettier-ff69b4.svg?style=flat-square)](https://github.com/prettier/prettier) 4 | [![codecov](https://codecov.io/gh/kreuzerk/replace-json-property/branch/master/graph/badge.svg)](https://codecov.io/gh/kreuzerk/replace-json-property) 5 | [![semantic-release](https://img.shields.io/badge/%20%20%F0%9F%93%A6%F0%9F%9A%80-semantic--release-e10079.svg)](https://github.com/semantic-release/semantic-release) 6 | 7 | ![Logo](https://raw.githubusercontent.com/kreuzerk/replace-json-property/master/assets/logo.png) 8 | 9 | This module allows you to replace a specific property in a JSON. 10 | 11 | 12 | 13 | **Table of Contents** *generated with [DocToc](https://github.com/thlorenz/doctoc)* 14 | 15 | - [Comand line usage](#comand-line-usage) 16 | - [Short form](#short-form) 17 | - [Usage from code](#usage-from-code) 18 | - [Options](#options) 19 | - [Help command](#help-command) 20 | 21 | 22 | 23 | ## Comand line usage 24 | 25 | You can use this via the following command: 26 | 27 | ``` 28 | replace-json-property pathToFile property value 29 | ``` 30 | 31 | This command replaces all occurences of the matching property. It also replaces 32 | occurences in nested objects or objects in arrays. 33 | 34 | The following command would replace all values of the `foo` property with 'test' inside the `test.json`. 35 | ``` 36 | replace-json-property ./test.json foo test, 37 | ``` 38 | Executing the command above on the given JSON 39 | ```json 40 | { 41 | "foo": "bar", 42 | "a": { 43 | "b": 1, 44 | "foo": "bar", 45 | "c": [ 46 | {"d": 1, "foo": "bar"}, 47 | {"d": 2, "foo": "bar"}, 48 | {"d": 3, "foo": "bar"}, 49 | ] 50 | } 51 | } 52 | ``` 53 | results in: 54 | 55 | ```json 56 | { 57 | "foo": "test", 58 | "a": { 59 | "b": 1, 60 | "foo": "test", 61 | "c": [ 62 | {"d": 1, "foo": "test"}, 63 | {"d": 2, "foo": "test"}, 64 | {"d": 3, "foo": "test"}, 65 | ] 66 | } 67 | } 68 | ``` 69 | ### Short form 70 | 71 | All commands explained above can also be run with the shortcut version `rjp`. 72 | ``` 73 | rjp ./test.json foo test, 74 | ``` 75 | 76 | ## Usage from code 77 | 78 | You can also use the replace function in your JavaScript code. 79 | 80 | 1. Using module imports 81 | 82 | ```javascript 83 | import {replace} from 'replace-json-property'; 84 | 85 | replace('./environment/test.json', 'foo', 'new value'); 86 | ``` 87 | 88 | 2. Using commonjs 89 | 90 | ```javascript 91 | const replaceJSONProperty = require('replace-json-property'); 92 | 93 | replaceJSONProperty.replace('./environment/test.json', 'foo', 'new value'); 94 | ``` 95 | 96 | ## Options 97 | 98 | The following flags allow you to configure how the resulting file is written. 99 | 100 | | Option | Description | Default | 101 | |----------------|-------------------------------------------------------------------------|---------------| 102 | | -s or --spaces | Add the spaces the file should be written with, for example (2, 4) | 2 | 103 | | -e or --eol | Add the line ending the file should be written with, for example "\r\n" | "\n" | 104 | | --silent | Silent mode. Executes without log messages | False | 105 | | --limit | Limit the number of replacements | 0 (unlimited) | 106 | | --add | When set to true propertys that do not exist will be added | false | 107 | 108 | ## Help command 109 | 110 | You can always run the help command to see how the signature looks 111 | ``` 112 | replace-json-property -h 113 | ``` 114 | or 115 | ``` 116 | replace-json-property --help 117 | ``` 118 | To get the current version use the --version or -v command. 119 | 120 | -------------------------------------------------------------------------------- /__mocks__/jsonfile.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | const jsonfile = jest.genMockFromModule('jsonfile'); 3 | 4 | let mockedReadFileResponse; 5 | let throwError = false; 6 | 7 | const setup = (responseMock, errorResponse) => { 8 | if (errorResponse) { 9 | throwError = true; 10 | } 11 | mockedReadFileResponse = responseMock; 12 | }; 13 | const readFileSync = jest.fn(() => { 14 | if (throwError) { 15 | throw 'Something went wrong'; 16 | } 17 | return mockedReadFileResponse; 18 | }); 19 | const writeFileSync = jest.fn((path, file) => {}); 20 | 21 | jsonfile.setup = setup; 22 | jsonfile.readFileSync = readFileSync; 23 | jsonfile.writeFileSync = writeFileSync; 24 | 25 | module.exports = jsonfile; 26 | -------------------------------------------------------------------------------- /assets/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nivekcode/replace-json-property/d7f3a95d77a9ec4f494627be1606c24ea55b8786/assets/logo.png -------------------------------------------------------------------------------- /bin/replace-json-property.bin.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | const commander = require('commander'); 3 | const version = require('../package').version; 4 | const replaceJsonProperty = require('../src/replace-json-property'); 5 | 6 | commander 7 | .version(version) 8 | .arguments(' ') 9 | .option( 10 | '-s --spaces [number]', 11 | 'Add the spaces the file should be written with, for example (2, 4)' 12 | ) 13 | .option( 14 | '-e --eol [string]', 15 | 'Add the line ending the file should be written with, for example "\\r\\n"' 16 | ) 17 | .option('--silent [boolean]', 'Silent mode. Executes without log messages') 18 | .option( 19 | '--limit [number]', 20 | 'Limit the maximum number of replacements, for example (1, 4)' 21 | ) 22 | .option( 23 | '-a --add [number]', 24 | 'Add the property if it does not exist in the JSON (default: false)' 25 | ) 26 | .action(function(path, property, value) { 27 | replaceJsonProperty.replace(path, property, value, { 28 | spaces: commander.spaces, 29 | EOL: commander.eol, 30 | silent: commander.silent, 31 | limit: commander.limit, 32 | add: commander.add 33 | }); 34 | }) 35 | .parse(process.argv); 36 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "replace-json-property", 3 | "version": "1.4.4", 4 | "description": "CLI tool to replace a property in a JSON file", 5 | "main": "src/replace-json-property.js", 6 | "release": { 7 | "branches": ["main"] 8 | }, 9 | "husky": { 10 | "hooks": { 11 | "pre-commit": "lint-staged", 12 | "commit-msg": "commitlint -E HUSKY_GIT_PARAMS" 13 | } 14 | }, 15 | "lint-staged": { 16 | "{src,__mocks__,bin}/**/*.js": [ 17 | "prettier --write", 18 | "git add" 19 | ] 20 | }, 21 | "jest": { 22 | "collectCoverageFrom": [ 23 | "src/**/*js", 24 | "!**/node_modules/**", 25 | "!bin/**", 26 | "!src/log.js" 27 | ], 28 | "coverageDirectory": "coverage" 29 | }, 30 | "commitlint": { 31 | "extends": [ 32 | "@commitlint/config-conventional" 33 | ] 34 | }, 35 | "config": { 36 | "commitizen": { 37 | "path": "@commitlint/prompt" 38 | } 39 | }, 40 | "scripts": { 41 | "build": "npm-run-all copy:*", 42 | "copy:bin": "cpx './bin/*.*' ./dist/bin", 43 | "copy:src": "cpx './src/**/!(*.spec).js' ./dist/src", 44 | "copy:package-json": "cpx package.json ./dist", 45 | "copy:assets": "cpx README.md ./dist", 46 | "format:check": "prettier --list-different '{src,__mocks__,bin}/**/*.js'", 47 | "format:write": "prettier --write '{src,__mocks__,bin}/**/*.js'", 48 | "commit": "git-cz", 49 | "test": "jest", 50 | "test:coverage": "jest --coverage", 51 | "report:coverage": "npm run test:coverage && codecov", 52 | "semantic-release": "semantic-release" 53 | }, 54 | "bin": { 55 | "replace-json-property": "./bin/replace-json-property.bin.js", 56 | "rjp": "./bin/replace-json-property.bin.js" 57 | }, 58 | "repository": { 59 | "type": "git", 60 | "url": "git+https://github.com/kreuzerk/replace-json-property.git" 61 | }, 62 | "keywords": [ 63 | "CLI", 64 | "Replace", 65 | "JSON", 66 | "Buildtool" 67 | ], 68 | "author": "Kevin Kreuzer", 69 | "license": "MIT", 70 | "bugs": { 71 | "url": "https://github.com/kreuzerk/replace-json-property/issues" 72 | }, 73 | "homepage": "https://github.com/kreuzerk/replace-json-property#readme", 74 | "dependencies": { 75 | "chalk": "^4.1.0", 76 | "commander": "^2.19.0", 77 | "jsonfile": "^5.0.0" 78 | }, 79 | "devDependencies": { 80 | "@commitlint/cli": "^7.2.1", 81 | "@commitlint/config-conventional": "^7.1.2", 82 | "@commitlint/prompt": "^7.2.1", 83 | "@semantic-release/changelog": "^3.0.1", 84 | "@semantic-release/git": "^7.0.6", 85 | "codecov": "^3.8.1", 86 | "cpx": "^1.5.0", 87 | "husky": "^1.2.1", 88 | "jest": "^23.6.0", 89 | "lint-staged": "^8.1.0", 90 | "npm-run-all": "^4.1.5", 91 | "prettier": "^1.15.3", 92 | "semantic-release": "^15.14.0" 93 | } 94 | } 95 | -------------------------------------------------------------------------------- /src/log.js: -------------------------------------------------------------------------------- 1 | const chalk = require('chalk'); 2 | const logPrefix = chalk.inverse.bold('Replace-json-property:'); 3 | 4 | const success = message => 5 | console.log(`${logPrefix} ${chalk.green.bold(message)}`); 6 | const info = message => console.log(`${logPrefix} ${chalk.blue.bold(message)}`); 7 | const error = message => console.log(`${logPrefix} ${chalk.red.bold(message)}`); 8 | 9 | module.exports = { success, info, error }; 10 | -------------------------------------------------------------------------------- /src/replace-json-property.js: -------------------------------------------------------------------------------- 1 | const jsonfile = require('jsonfile'); 2 | const log = require('./log'); 3 | 4 | const DEFAULT_OPTIONS = { 5 | spaces: 2, 6 | EOL: '\n', 7 | LIMIT: 0, 8 | SILENT: false 9 | }; 10 | 11 | const replace = (path, property, newValue, options = {}) => { 12 | options = { 13 | spaces: options.spaces 14 | ? parseInt(options.spaces) 15 | : DEFAULT_OPTIONS.spaces, 16 | EOL: (options && options.EOL) || DEFAULT_OPTIONS.EOL, 17 | silent: (options && options.silent) || DEFAULT_OPTIONS.SILENT, 18 | limit: parseInt( 19 | (options && options.limit) || DEFAULT_OPTIONS.LIMIT, 20 | 10 21 | ), 22 | add: options && options.add 23 | }; 24 | try { 25 | const file = updateFile(path, property, newValue, options); 26 | jsonfile.writeFileSync(path, file, options); 27 | if (!options.silent) { 28 | log.success( 29 | `Properties: "${property}" in file: ${path} successfully overwritten with "${newValue}"` 30 | ); 31 | } 32 | } catch (error) { 33 | log.error(error); 34 | } 35 | }; 36 | 37 | const updateFile = (path, property, newValue, options) => { 38 | const { limit, add } = options; 39 | 40 | let limitCounter = 0; 41 | let propertyExists = false; 42 | const revivedJson = 43 | jsonfile.readFileSync(path, { 44 | reviver: (key, value) => { 45 | if (key === property) { 46 | propertyExists = true; 47 | if (limit > 0 && limitCounter >= limit) { 48 | return value; 49 | } 50 | ++limitCounter; 51 | return newValue; 52 | } 53 | return value; 54 | } 55 | }) || {}; 56 | 57 | if (!propertyExists && add) { 58 | revivedJson[property] = newValue; 59 | } 60 | return revivedJson; 61 | }; 62 | 63 | module.exports = { 64 | replace 65 | }; 66 | -------------------------------------------------------------------------------- /src/replace-json-property.spec.js: -------------------------------------------------------------------------------- 1 | const jsonfile = require('jsonfile'); 2 | 3 | const sut = require('./replace-json-property'); 4 | const log = require('./log'); 5 | 6 | describe('replace-json-property', () => { 7 | beforeEach(() => { 8 | jsonfile.setup({}, false); 9 | log.success = jest.fn(); 10 | log.error = jest.fn(); 11 | }); 12 | 13 | afterEach(() => { 14 | jest.resetAllMocks(); 15 | }); 16 | 17 | describe('Replace', () => { 18 | const extractReviver = jestFunction => 19 | jestFunction.mock.calls[0][1].reviver; 20 | 21 | test('should call readFileSync with the correct path and an object with a reviver function', () => { 22 | const path = './someFolder/someSubFolder'; 23 | sut.replace(path, 'foo', 'bar'); 24 | expect(jsonfile.readFileSync).toBeCalledWith(path, { 25 | reviver: expect.any(Function) 26 | }); 27 | }); 28 | 29 | test('should replace the value with the new value if the key matches', () => { 30 | const expectedValue = 'bar'; 31 | sut.replace('somePath', 'foo', expectedValue, {}); 32 | 33 | const reviver = extractReviver(jsonfile.readFileSync); 34 | const value = reviver('foo', 'someValue'); 35 | 36 | expect(value).toBe(expectedValue); 37 | }); 38 | 39 | test('should not replace the value with the new value if the key does not match', () => { 40 | const expectedValue = 'bar'; 41 | sut.replace('somePath', 'foo', 'someValue', {}); 42 | 43 | const reviver = extractReviver(jsonfile.readFileSync); 44 | const value = reviver('someProperty', expectedValue); 45 | 46 | expect(value).toBe(expectedValue); 47 | }); 48 | 49 | test('should log a succcess message if everything was successful', () => { 50 | sut.replace('somePath', 'foo', 'bar', {}); 51 | expect(log.success).toHaveBeenCalled(); 52 | }); 53 | 54 | test('should log an error message if something went wrong', () => { 55 | const throwError = true; 56 | jsonfile.readFileSync = jest.fn(() => { 57 | throw 'Something went wrong'; 58 | }); 59 | sut.replace('somepath', 'foo', 'bar', {}); 60 | expect(log.error).toHaveBeenCalled(); 61 | }); 62 | }); 63 | 64 | describe('add keys', () => { 65 | test('should add the property if the property does not exist and the add option is enabled', () => { 66 | sut.replace('some-path', 'foo', 'bar', { add: true }); 67 | expect(jsonfile.writeFileSync).toHaveBeenCalledWith( 68 | expect.anything(), 69 | expect.objectContaining({ foo: 'bar' }), 70 | expect.anything() 71 | ); 72 | }); 73 | 74 | test('should not add the property if the property does not exist and the add option is enabled', () => { 75 | sut.replace('some-path', 'foo', 'bar', { add: false }); 76 | expect(jsonfile.writeFileSync).toHaveBeenCalledWith( 77 | expect.anything(), 78 | expect.objectContaining({}), 79 | expect.anything() 80 | ); 81 | }); 82 | }); 83 | 84 | describe('options', () => { 85 | beforeEach(() => { 86 | jest.resetAllMocks(); 87 | }); 88 | 89 | test('should apply the provided space option and add the default EOL', () => { 90 | const path = './foo/test.json'; 91 | const property = 'foo'; 92 | const newValue = 'bar'; 93 | const options = { spaces: 4 }; 94 | 95 | sut.replace(path, property, newValue, options); 96 | expect(jsonfile.writeFileSync).toHaveBeenCalledWith( 97 | expect.anything(), 98 | expect.anything(), 99 | { 100 | spaces: 4, 101 | EOL: '\n', 102 | silent: false, 103 | limit: 0 104 | } 105 | ); 106 | }); 107 | 108 | test('should apply the default space, EOL and silent options', () => { 109 | const path = './foo/test.json'; 110 | const property = 'foo'; 111 | const newValue = 'bar'; 112 | 113 | sut.replace(path, property, newValue); 114 | expect(jsonfile.writeFileSync).toHaveBeenCalledWith( 115 | expect.anything(), 116 | expect.anything(), 117 | { 118 | spaces: 2, 119 | EOL: '\n', 120 | silent: false, 121 | limit: 0 122 | } 123 | ); 124 | }); 125 | 126 | test('should apply the provided EOL option and use the default space, limit, and silent option', () => { 127 | const path = './foo/test.json'; 128 | const property = 'foo'; 129 | const newValue = 'bar'; 130 | const options = { EOL: '\n\r' }; 131 | 132 | sut.replace(path, property, newValue, options); 133 | expect(jsonfile.writeFileSync).toHaveBeenCalledWith( 134 | expect.anything(), 135 | expect.anything(), 136 | { 137 | spaces: 2, 138 | EOL: '\n\r', 139 | silent: false, 140 | limit: 0 141 | } 142 | ); 143 | }); 144 | 145 | test('should apply the provided silent option and use the default space, limit, and EOL option', () => { 146 | const path = './foo/test.json'; 147 | const property = 'foo'; 148 | const newValue = 'bar'; 149 | const options = { silent: true }; 150 | 151 | sut.replace(path, property, newValue, options); 152 | expect(jsonfile.writeFileSync).toHaveBeenCalledWith( 153 | expect.anything(), 154 | expect.anything(), 155 | { 156 | spaces: 2, 157 | EOL: '\n', 158 | silent: true, 159 | limit: 0 160 | } 161 | ); 162 | }); 163 | 164 | test('should apply the provided limit option and use the default space, silent, and EOL option', () => { 165 | const path = './foo/test.json'; 166 | const limit = 2; 167 | const property = 'foo'; 168 | const newValue = 'bar'; 169 | const options = { limit }; 170 | 171 | sut.replace(path, property, newValue, options); 172 | expect(jsonfile.writeFileSync).toHaveBeenCalledWith( 173 | expect.anything(), 174 | expect.anything(), 175 | { 176 | spaces: 2, 177 | EOL: '\n', 178 | silent: false, 179 | limit 180 | } 181 | ); 182 | }); 183 | }); 184 | }); 185 | --------------------------------------------------------------------------------