Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
22 changed files
with
2,076 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
node_modules | ||
coverage | ||
dist | ||
yarn* |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,96 @@ | ||
[![npm version][npm-image]][npm-url] | ||
[![downloads][downloads-image]][npm-url] | ||
[![build status][build-image]][build-url] | ||
[![coverage status][coverage-image]][coverage-url] | ||
[![Language grade: JavaScript][lgtm-image]][lgtm-url] | ||
|
||
|
||
# edit-json | ||
|
||
Edit a _textual_ JSON (not a JavaScript object) for a minimal diff, either programatically or by applying a [_JSON Patch_ (RFC6902)](https://www.rfc-editor.org/rfc/rfc6902). | ||
|
||
When serializing the result down to a string, it'll resemble the source JSON as much as possible with regards to property order, whitespace (indentation) and _flow types_ (arrays and objects on one line). | ||
|
||
Editing JSON is easy, just `JSON.parse()` and play around, then `JSON.stringify()`. To apply a _JSON Patch_, there are [several](https://www.npmjs.com/package/fast-json-patch) [packages](https://www.npmjs.com/package/rfc6902) [out](https://www.npmjs.com/package/json-bigint-patch) [there](https://www.npmjs.com/package/jsonpatch). | ||
|
||
This package focuses not on working with JSON as a JavaScript object, but as its textual representation. The package parses the JSON string (as e.g. from a file) as tokens, builds up a logical representation of it, and then applies transformations to that representation. Whitespace (tabs, spaces) as well as multi-line or single-line arrays/objects are remembered. | ||
|
||
To do the same with YAML, check out [yaml-diff-patch](https://www.npmjs.com/package/yaml-diff-patch). | ||
|
||
|
||
# Example | ||
|
||
Given: | ||
|
||
```json | ||
{ | ||
"x": "non-alphanumerically ordered properties, obviously", | ||
"foo": [ "same", "line", "array" ], | ||
"bar": { | ||
"some": "object" | ||
} | ||
} | ||
``` | ||
|
||
Applying the JSON Patch: | ||
|
||
```json | ||
[ { | ||
"op": "move", | ||
"from": "/foo", | ||
"path": "/bar/herenow" | ||
} ] | ||
``` | ||
|
||
Produces: | ||
|
||
```json | ||
{ | ||
"x": "non-alphanumerically ordered properties, obviously", | ||
"bar": { | ||
"herenow": [ "same", "line", "array" ], | ||
"some": "object" | ||
} | ||
} | ||
``` | ||
|
||
Properties aren't re-ordered ("x" is still first), but by default, it will try to _insert_ properties orderly, such as when creating "herenow" in "bar". It'll be added before "some", "h" < "s". This is done with a best effort, since it's not always possible (the object might have unordered properties). | ||
|
||
Note also that the array is not split into multiple lines, which would happen with default `JSON.stringify` (unless the whole document is one line of course). The source format is kept if possible. | ||
|
||
|
||
# Install | ||
|
||
`npm i edit-json` or `yarn add edit-json` | ||
|
||
This is a [pure ESM][pure-esm] package, and requires Node.js >=14.13.1 | ||
|
||
|
||
# Simple usage | ||
|
||
### Exports | ||
|
||
The package exports `parseJson` (to be documented) and `jsonPatch`. | ||
|
||
### Definition | ||
|
||
`jsonPatch( json: string, operations: Operations[], options: Options ): string` | ||
|
||
Applies a list of _JSON Patch_ operations to the source `json` and returns the new json string. | ||
|
||
The options are: | ||
|
||
- `whitespace` ('auto' | 'tabs' | number): Specifies whitespace strategy. Defaults to 'auto'. Force tabs using 'tabs' or spaces using number (e.g. 2 or 4). | ||
- `ordered` (boolean): Try to insert new properties in order. | ||
|
||
|
||
[npm-image]: https://img.shields.io/npm/v/edit-json.svg | ||
[npm-url]: https://npmjs.org/package/edit-json | ||
[downloads-image]: https://img.shields.io/npm/dm/edit-json.svg | ||
[build-image]: https://img.shields.io/github/workflow/status/grantila/edit-json/Master.svg | ||
[build-url]: https://github.com/grantila/edit-json/actions?query=workflow%3AMaster | ||
[coverage-image]: https://coveralls.io/repos/github/grantila/edit-json/badge.svg?branch=master | ||
[coverage-url]: https://coveralls.io/github/grantila/edit-json?branch=master | ||
[lgtm-image]: https://img.shields.io/lgtm/grade/javascript/g/grantila/edit-json.svg?logo=lgtm&logoWidth=18 | ||
[lgtm-url]: https://lgtm.com/projects/g/grantila/edit-json/context:javascript | ||
[pure-esm]: https://gist.github.com/sindresorhus/a39789f98801d908bbc7ff3ecc99d99c |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
module.exports = { | ||
presets: [ | ||
[ | ||
'@babel/preset-env', | ||
{ | ||
modules: false, | ||
targets: { | ||
node: 'current', | ||
}, | ||
}, | ||
], | ||
'@babel/preset-typescript', | ||
], | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
export default { | ||
resolver: 'ts-jest-resolver', | ||
testEnvironment: 'node', | ||
testMatch: [ | ||
'<rootDir>/lib/**/*.test.ts', | ||
], | ||
modulePathIgnorePatterns: [], | ||
collectCoverageFrom: ['<rootDir>/lib/**/*.ts', 'index.ts'], | ||
coveragePathIgnorePatterns: [ '/node_modules/' ], | ||
coverageReporters: ['lcov', 'text', 'html'], | ||
collectCoverage: true, | ||
extensionsToTreatAsEsm: ['.ts'], | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,105 @@ | ||
import { fileURLToPath } from 'node:url' | ||
import { resolve as resolvePath, dirname } from 'node:path' | ||
import { readFile } from 'node:fs/promises' | ||
|
||
import { parseJson } from './document.js' | ||
|
||
|
||
const __dirname = dirname( fileURLToPath( import.meta.url ) ); | ||
const rootDir = resolvePath( __dirname, '..', '..' ); | ||
|
||
describe( 'document', ( ) => | ||
{ | ||
describe( 'primitives', ( ) => | ||
{ | ||
it( 'null', ( ) => | ||
{ | ||
const parsed = parseJson( 'null' ); | ||
|
||
expect( parsed.toJSON( ) ).toBe( 'null' ); | ||
} ); | ||
|
||
it( 'boolean', ( ) => | ||
{ | ||
const parsed = parseJson( 'false' ); | ||
|
||
expect( parsed.toJSON( ) ).toBe( 'false' ); | ||
} ); | ||
|
||
it( 'boolean with whitespace', ( ) => | ||
{ | ||
const parsed = parseJson( ' true ' ); | ||
|
||
expect( parsed.toJSON( ) ).toBe( ' true' ); | ||
} ); | ||
|
||
it( 'string', ( ) => | ||
{ | ||
const parsed = parseJson( ' "foo bar\\nnext line"' ); | ||
|
||
expect( parsed.toJSON( ) ).toBe( ' "foo bar\\nnext line"' ); | ||
} ); | ||
|
||
it( 'number pos', ( ) => | ||
{ | ||
const parsed = parseJson( '3.14' ); | ||
|
||
expect( parsed.toJSON( ) ).toBe( '3.14' ); | ||
} ); | ||
|
||
it( 'number neg', ( ) => | ||
{ | ||
const parsed = parseJson( '-3.14' ); | ||
|
||
expect( parsed.toJSON( ) ).toBe( '-3.14' ); | ||
} ); | ||
|
||
it( 'number sci pos', ( ) => | ||
{ | ||
const parsed = parseJson( '123e5' ); | ||
|
||
expect( parsed.toJSON( ) ).toBe( '123e5' ); | ||
} ); | ||
|
||
it( 'number sci neg', ( ) => | ||
{ | ||
const parsed = parseJson( '-123e5' ); | ||
|
||
expect( parsed.toJSON( ) ).toBe( '-123e5' ); | ||
} ); | ||
} ); | ||
|
||
describe( 'objects', ( ) => | ||
{ | ||
it( 'empty', ( ) => | ||
{ | ||
const parsed = parseJson( ' {}' ); | ||
|
||
expect( parsed.toJSON( ) ).toBe( ' { }' ); | ||
} ); | ||
|
||
it( 'flow', ( ) => | ||
{ | ||
const parsed = parseJson( '{ "foo": "bar" }' ); | ||
|
||
expect( parsed.toJSON( ) ).toBe( '{ "foo": "bar" }' ); | ||
} ); | ||
|
||
it( 'non-flow', ( ) => | ||
{ | ||
const parsed = parseJson( '{\n "foo": "bar"}' ); | ||
|
||
expect( parsed.toJSON( ) ).toBe( '{\n "foo": "bar"\n}' ); | ||
} ); | ||
|
||
it( 'self packge.json', async ( ) => | ||
{ | ||
const pkgJsonFile = resolvePath( rootDir, 'package.json' ); | ||
const pkgJson = await readFile( pkgJsonFile, 'utf-8' ); | ||
|
||
const parsed = parseJson( pkgJson ); | ||
|
||
expect( parsed.toJSON( ).trimEnd( ) ).toBe( pkgJson.trimEnd( ) ); | ||
} ); | ||
} ); | ||
} ); |
Oops, something went wrong.