Permalink to InstallationInstallation
Permalink to Quick TakeQuick Take
Permalink to ExamplesExamples
- Add a key that is not there yet, at the indentation the file already uses
- Delete an array item by its dot-path index
- Delete a nested object key
- Minimal example,
del() - Minimal example,
set() - Replace an existing array item by its dot-path index
- Replace an existing nested value with null
- Set a numeric value without changing surrounding formatting
- Replace an object value while preserving outer formatting
Purpose
Edit JSON contents as strings to guarantee the formatting will be intact.
API uses object-path notation to set values on any path in JSON — one that’s already there or one that isn’t yet.
It’s powering the update-versions CLI.
API — set()
The function set() is imported like this:
It’s a function which takes three input arguments:
| Input argument | Type | Obligatory | Description |
|---|---|---|---|
strType: String Obligatory: yes | |||
str | String | yes | JSON file contents |
pathType: String Obligatory: yes | |||
path | String | yes | Path in the object, must follow object-path notation |
valToInsertType: Whatever Obligatory: yes | |||
valToInsert | Whatever | yes | What to insert at the given path |
Function returns an amended string.
If the path is already there, its value is replaced. If it’s not, it’s written in — see writing new paths below.
import { set } from "edit-package-json";
// the path is there, so the value is replaced
set('{\n "version": "1.0.0"\n}', "version", "2.0.0");
// => '{\n "version": "2.0.0"\n}'
// the path is not there, so it's written in
set('{\n "version": "1.0.0"\n}', "license", "MIT");
// => '{\n "version": "1.0.0",\n "license": "MIT"\n}'
Setting a key which is already there replaces it, it’s never duplicated.
The only thing set() throws on is a missing or empty first input argument.
Writing new paths
Earlier versions could only amend paths which already existed — asking for anything else got you your string back, untouched. Now set() writes those paths in.
// two-space indentation stays two-space
set('{\n "a": "b"\n}', "c", "d");
// => '{\n "a": "b",\n "c": "d"\n}'
// tabs stay tabs
set('{\n\t"a": "b"\n}', "c", "d");
// => '{\n\t"a": "b",\n\t"c": "d"\n}'
// minified stays minified
set('{"a":"b"}', "c", "d");
// => '{"a":"b","c":"d"}'
// the gap after the colon is copied as well
set('{"a": "b"}', "c", "d");
// => '{"a": "b","c": "d"}'
// the gap in front of a member is copied too
set('{ "a": "b" }', "c", "d");
// => '{ "a": "b", "c": "d" }'
Missing containers are created
You don’t have to write a path one level at a time. Whichever part of the chain is missing gets built for you, indented to match:
set('{\n "name": "demo"\n}', "dependencies.uvu", "^0.5.0");
// => '{\n "name": "demo",\n "dependencies": {\n "uvu": "^0.5.0"\n }\n}'
// several levels at once
set('{"a":"b"}', "c.d.e", "f");
// => '{"a":"b","c":{"d":{"e":"f"}}}'
// an empty container gets opened up
set('{\n "scripts": {}\n}', "scripts.test", "uvu");
// => '{\n "scripts": {\n "test": "uvu"\n }\n}'
Whether a created container is an object or an array is decided the way object-path decides it — an all-digits path segment asks for an array, anything else asks for an object:
set('{"a":"b"}', "files.0", "dist");
// => '{"a":"b","files":["dist"]}'
// "01" is not an array index, it's an object key
set('{"a":"b"}', "c.01", "d");
// => '{"a":"b","c":{"01":"d"}}'
Arrays
An index within an array’s existing length is amended in place, an index at the end appends, and an index past the end pads with null — again, matching object-path:
set('{"a":["p","q"]}', "a.0", "z");
// => '{"a":["z","q"]}'
set('{"a":["p"]}', "a.1", "q");
// => '{"a":["p","q"]}'
set('{"a":[]}', "a.2", "x");
// => '{"a":[null,null,"x"]}'
Values which are not strings
Numbers, booleans, null, objects and arrays are all written in as JSON:
set('{"a":"b"}', "c", 42);
// => '{"a":"b","c":42}'
set('{"a":"b"}', "c", true);
// => '{"a":"b","c":true}'
set('{"a":"b"}', "c", null);
// => '{"a":"b","c":null}'
set('{"a":"b"}', "publishConfig", { access: "public" });
// => '{"a":"b","publishConfig":{"access":"public"}}'
set('{"a":"b"}', "files", ["dist"]);
// => '{"a":"b","files":["dist"]}'
Mind you, a string which merely looks like JSON is written in as a string, not as a container. Pass the real object or array if that’s what you’re after.
When there’s nowhere to write
object-path throws on the cases below. Since this package never parses your string, there’s nothing it could throw about with any confidence, so your input comes back exactly as it went in:
// there's nothing to add a key to - "a" is a string
set('{"a":"b"}', "a.c", "x");
// => '{"a":"b"}'
// an array takes indexes, not keys
set('{"a":[]}', "a.x", "y");
// => '{"a":[]}'
// an empty path segment
set('{"a":"b"}', "c..d", "x");
// => '{"a":"b"}'
API — del()
The function del() is imported like this:
It’s a function which takes two input arguments:
| Input argument | Type | Obligatory | Description |
|---|---|---|---|
strType: String Obligatory: yes | |||
str | String | yes | JSON file contents |
pathType: String Obligatory: yes | |||
path | String | yes | Desired path in the object to delete, must follow object-path notation |
Function returns an amended string.
API — version
You can import version:
API — types
This package is written in TypeScript and exports the type Inputs — the internal request shape set() and del() build — the source str, the path, the mode ("set", "del" or "locate", the last one being how set() finds the deepest container a new path can hang off) and, for set(), the valToInsert.
import type { Inputs } from "edit-package-json";