Skip to content
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

feat: implemented options parser and validationParser to support alternative JSON parsers like lossless-json #151

Merged
merged 26 commits into from
Sep 29, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
45d2452
feat: custom parser largely working (WIP)
josdejong Sep 6, 2022
9cbe48e
fix: extend `isTimestamp` to support `LosslessNumber` and `bigint`
josdejong Sep 7, 2022
3dfdcde
fix: show error when sorting throws an error
josdejong Sep 7, 2022
09bc99c
fix: use the custom parser when validating content in `text` mode
josdejong Sep 7, 2022
50f8c6e
docs: describe the new property `parser` in the readme
josdejong Sep 7, 2022
1495cba
fix: truncate raw contents in development page
josdejong Sep 7, 2022
d083d25
fix: enforce string not working with custom parser
josdejong Sep 7, 2022
f0588a0
chore: create a real `getAbsolutePath` function for use in the build …
josdejong Sep 9, 2022
9f0ba47
fix: mark the package as side-effects free, allowing better optimizat…
josdejong Sep 9, 2022
62dc994
Merge branch 'main' into feat/lossless_json
josdejong Sep 12, 2022
e956e44
chore: fix merge conflicts
josdejong Sep 12, 2022
ed6c5e9
Merge branch 'main' into feat/lossless_json
josdejong Sep 12, 2022
79ca278
fix: upgrade to `immutable-json-patch@5.0.0`, fixing search
josdejong Sep 13, 2022
4b4092a
chore: cleanup logging
josdejong Sep 13, 2022
e00d9a4
fix: support `bigint` in the query languages
josdejong Sep 13, 2022
3f4c8c4
feat: rerender the editor when parser changes
josdejong Sep 13, 2022
2914168
feat: implement `validationParser`
josdejong Sep 14, 2022
a608c68
fix: only validate when a validator function is provided
josdejong Sep 14, 2022
b006acc
fix: do not show parse or validation errors when the editor is disabled
josdejong Sep 14, 2022
6ec96a8
chore: make all properties of the internal TextMode and TreeMode requ…
josdejong Sep 14, 2022
70668cc
Merge branch 'main' into feat/lossless_json
josdejong Sep 19, 2022
97b6309
docs: add homepage in package.json
josdejong Sep 19, 2022
80bce85
chore: reorder fields in package.json
josdejong Sep 19, 2022
ca27b22
fix: export util function `isEqualParser`
josdejong Sep 22, 2022
9afe144
Merge branch 'main' into feat/lossless_json
josdejong Sep 28, 2022
56ccf1a
chore: update all dependencies, including `lossless-json@2.0.0`
josdejong Sep 28, 2022
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
1 change: 1 addition & 0 deletions .eslintrc.cjs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ module.exports = {
env: {
browser: true,
es2017: true,
es2020: true,
node: true,
mocha: true
}
Expand Down
4 changes: 4 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -200,6 +200,10 @@ const editor = new JSONEditor({
const validator = createAjvValidator(schema, schemaDefinitions)
```

- `parser: JSON = JSON`. Configure a custom JSON parser, like [`lossless-json`](https://github.com/josdejong/lossless-json). By default, the native `JSON` parser of JavaScript is used. The `JSON` interface is an object with a `parse` and `stringify` function.

- `validationParser: JSON = JSON`. Only applicable when a `validator` is provided. This is the same as `parser`, except that this parser is used to parse the data before sending it to the validator. Configure a custom JSON parser that is used to parse JSON before passing it to the `validator`. By default, the built-in `JSON` parser is used. When passing a custom `validationParser`, make sure the output of the parser is supported by the configured `validator`. So, when the `validationParser` can output `bigint` numbers or other numeric types, the `validator` must also support that. In tree mode, when `parser` is not equal to `validationParser`, the JSON document will be converted before it is passed to the `validator` via `validationParser.parse(parser.stringify(json))`.

- `onError(err: Error)`.
Callback fired when an error occurs. Default implementation is to log an error in the console and show a simple alert message to the user.
- `onChange(content: Content, previousContent: Content, changeStatus: { contentErrors: ContentErrors, patchResult: JSONPatchResult | null })`. The callback which is invoked on every of the contents, both changes made by a user and programmatic changes made via methods like `.set()`, `.update()`, or `.patch()`. The parameter `patchResult` is only available in `tree` mode, and not in `text` mode, since a change in arbitrary text cannot be expressed as a JSON Patch document.
Expand Down
1,948 changes: 716 additions & 1,232 deletions package-lock.json

Large diffs are not rendered by default.

57 changes: 30 additions & 27 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,17 @@
"name": "svelte-jsoneditor",
"description": "A web-based tool to view, edit, format, transform, and validate JSON",
"version": "0.7.6",
"homepage": "https://jsoneditoronline.org",
"repository": {
"type": "git",
"url": "https://github.com/josdejong/svelte-jsoneditor.git"
},
"type": "module",
"svelte": "index.js",
"module": "index.js",
"main": "index.js",
"types": "index.d.ts",
"sideEffects": false,
"repository": {
"type": "git",
"url": "https://github.com/josdejong/svelte-jsoneditor.git"
},
"license": "ISC",
"scripts": {
"dev": "vite dev",
Expand All @@ -31,6 +32,7 @@
"build:vanilla:copy:readme": "cpy --rename README.md README-VANILLA.md package-vanilla",
"build:vanilla:copy:themes": "cpy --flat src/lib/themes package-vanilla/themes",
"build:vanilla:package-json": "node tools/createVanillaPackageJson.js",
"build-and-test": "npm test && npm run lint && npm run build",
"test": "mocha",
"check": "svelte-check --tsconfig ./tsconfig.json",
"check:watch": "svelte-check --tsconfig ./tsconfig.json --watch",
Expand All @@ -50,73 +52,74 @@
"dry-run": "standard-version --dry-run"
},
"dependencies": {
"@codemirror/commands": "^6.1.0",
"@codemirror/commands": "^6.1.1",
"@codemirror/lang-json": "^6.0.0",
"@codemirror/language": "^6.2.1",
"@codemirror/lint": "^6.0.0",
"@codemirror/search": "^6.2.0",
"@codemirror/state": "^6.1.1",
"@codemirror/view": "^6.2.3",
"@codemirror/search": "^6.2.1",
"@codemirror/state": "^6.1.2",
"@codemirror/view": "^6.3.0",
"@fontsource/fira-mono": "^4.5.9",
"@fortawesome/free-regular-svg-icons": "^6.2.0",
"@fortawesome/free-solid-svg-icons": "^6.2.0",
"ajv-dist": "^8.11.0",
"classnames": "^2.3.1",
"classnames": "^2.3.2",
"codemirror": "^6.0.1",
"diff-sequences": "^29.0.0",
"immutable-json-patch": "^4.0.1",
"immutable-json-patch": "^5.1.0",
"jmespath": "^0.16.0",
"json-source-map": "^0.6.1",
"jsonrepair": "^2.2.1",
"lodash-es": "^4.17.21",
"memoize-one": "^6.0.0",
"natural-compare-lite": "^1.4.0",
"sass": "^1.54.9",
"sass": "^1.55.0",
"svelte": "^3.50.1",
"svelte-awesome": "^3.0.0",
"svelte-select": "^4.4.7",
"svelte-simple-modal": "^1.4.1",
"vanilla-picker": "^2.12.1"
},
"devDependencies": {
"@babel/core": "7.19.0",
"@babel/preset-env": "7.19.0",
"@babel/core": "7.19.3",
"@babel/preset-env": "7.19.3",
"@commitlint/cli": "17.1.2",
"@commitlint/config-conventional": "17.1.0",
"@rollup/plugin-babel": "5.3.1",
"@rollup/plugin-commonjs": "22.0.2",
"@rollup/plugin-json": "4.1.0",
"@rollup/plugin-node-resolve": "14.0.1",
"@rollup/plugin-node-resolve": "14.1.0",
"@rollup/plugin-typescript": "8.5.0",
"@sveltejs/adapter-auto": "1.0.0-next.72",
"@sveltejs/kit": "1.0.0-next.480",
"@sveltejs/package": "1.0.0-next.3",
"@sveltejs/adapter-auto": "1.0.0-next.80",
"@sveltejs/kit": "1.0.0-next.504",
"@sveltejs/package": "1.0.0-next.5",
"@types/cookie": "0.5.1",
"@types/lodash-es": "4.17.6",
"@types/mocha": "9.1.1",
"@typescript-eslint/eslint-plugin": "5.36.2",
"@typescript-eslint/parser": "5.36.2",
"@types/mocha": "10.0.0",
"@typescript-eslint/eslint-plugin": "5.38.1",
"@typescript-eslint/parser": "5.38.1",
"cpy-cli": "4.2.0",
"del-cli": "5.0.0",
"eslint": "8.22.0",
"eslint": "8.24.0",
"eslint-config-prettier": "8.5.0",
"eslint-plugin-svelte3": "4.0.0",
"husky": "8.0.1",
"lossless-json": "2.0.0",
"mocha": "10.0.0",
"npm-run-all": "4.1.5",
"prettier": "2.7.1",
"prettier-plugin-svelte": "2.7.0",
"rollup": "2.79.0",
"prettier-plugin-svelte": "2.7.1",
"rollup": "2.79.1",
"rollup-plugin-dts": "4.2.2",
"rollup-plugin-svelte": "7.1.0",
"rollup-plugin-terser": "7.0.2",
"standard-version": "9.5.0",
"svelte-check": "2.9.0",
"svelte-check": "2.9.1",
"svelte-preprocess": "4.10.7",
"svelte2tsx": "0.5.16",
"svelte2tsx": "0.5.19",
"ts-node": "10.9.1",
"tslib": "2.4.0",
"typescript": "4.8.3",
"vite": "3.1.0"
"typescript": "4.8.4",
"vite": "3.1.4"
}
}
25 changes: 23 additions & 2 deletions src/lib/components/JSONEditor.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
import Modal from 'svelte-simple-modal'
import { SORT_MODAL_OPTIONS, TRANSFORM_MODAL_OPTIONS } from '../constants.js'
import { uniqueId } from '../utils/uniqueId.js'
import { isTextContent, validateContentType } from '../utils/jsonUtils'
import { isEqualParser, isTextContent, validateContentType } from '../utils/jsonUtils'
import AbsolutePopup from './modals/popup/AbsolutePopup.svelte'
import TextMode from './modes/textmode/TextMode.svelte'
import TreeMode from './modes/treemode/TreeMode.svelte'
Expand All @@ -19,6 +19,7 @@
Content,
ContentErrors,
JSONEditorPropsOptional,
JSONParser,
JSONPatchResult,
MenuItem,
MenuSeparatorItem,
Expand Down Expand Up @@ -57,7 +58,9 @@
export let statusBar = true
export let escapeControlCharacters = false
export let escapeUnicodeCharacters = false
export let parser: JSONParser = JSON
export let validator: Validator | null = null
export let validationParser: JSONParser = JSON

export let queryLanguages: QueryLanguage[] = [javascriptQueryLanguage]
export let queryLanguageId: string = queryLanguages[0].id
Expand Down Expand Up @@ -92,6 +95,19 @@
}
}

// rerender the full editor when the parser changes. This is needed because
// numeric state is hold at many places in the editor.
let previousParser = parser
$: {
if (!isEqualParser(parser, previousParser)) {
debug('parser changed, recreate editor')
previousParser = parser

// new editor id -> will re-create the editor
instanceId = uniqueId()
}
}

export function get(): Content {
return content
}
Expand Down Expand Up @@ -125,7 +141,7 @@
if (isTextContent(content)) {
try {
content = {
json: JSON.parse(content.text),
json: parser.parse(content.text),
text: undefined
}
} catch (err) {
Expand Down Expand Up @@ -348,6 +364,7 @@
selectedPath,
escapeControlCharacters,
escapeUnicodeCharacters,
parser,
queryLanguages,
queryLanguageId,
onChangeQueryLanguage: handleChangeQueryLanguage,
Expand Down Expand Up @@ -410,7 +427,9 @@
{mainMenuBar}
{statusBar}
{escapeUnicodeCharacters}
{parser}
{validator}
{validationParser}
onChange={handleChange}
onSwitchToTreeMode={handleSwitchToTreeMode}
{onError}
Expand All @@ -431,7 +450,9 @@
{navigationBar}
{escapeControlCharacters}
{escapeUnicodeCharacters}
{parser}
{validator}
{validationParser}
{onError}
onChange={handleChange}
onRequestRepair={handleRequestRepair}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<svelte:options immutable={true} />

<script lang="ts">
import type { JSONData, JSONPath } from 'immutable-json-patch'
import type { JSONValue, JSONPath } from 'immutable-json-patch'
import { getIn } from 'immutable-json-patch'
import { range } from 'lodash-es'
import { isObject, isObjectOrArray } from '../../../utils/typeUtils'
Expand All @@ -13,7 +13,7 @@

const debug = createDebug('jsoneditor:NavigationBar')

export let json: JSONData
export let json: JSONValue
export let documentState: DocumentState
export let onSelect: OnSelect

Expand Down
4 changes: 4 additions & 0 deletions src/lib/components/modals/SortModal.scss
Original file line number Diff line number Diff line change
Expand Up @@ -42,5 +42,9 @@

.jse-space {
height: 200px; // Trick for the property select box dropdown to be fully visible

.jse-error {
color: var(--jse-error-color);
}
}
}
53 changes: 33 additions & 20 deletions src/lib/components/modals/SortModal.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -10,15 +10,15 @@
import { stringifyPath } from '../../utils/pathUtils.js'
import { sortArray, sortObjectKeys } from '../../logic/sort.js'
import { sortModalState } from './sortModalState.js'
import type { JSONData, JSONPath } from 'immutable-json-patch'
import type { JSONValue, JSONPath } from 'immutable-json-patch'
import { compileJSONPointer, getIn } from 'immutable-json-patch'
import { createDebug } from '../../utils/debug'
import type { OnSort } from '../../types'

const debug = createDebug('jsoneditor:SortModal')

export let id: string
export let json: JSONData // the whole document
export let json: JSONValue // the whole document
export let selectedPath: JSONPath
export let onSort: OnSort

Expand All @@ -44,6 +44,7 @@
(sortModalState[stateId] && sortModalState[stateId].selectedProperty) || undefined
let selectedDirection =
(sortModalState[stateId] && sortModalState[stateId].selectedDirection) || asc
let sortError: string | undefined = undefined

$: {
// if there is only one option, select it and do not render the select box
Expand Down Expand Up @@ -71,26 +72,32 @@
}

function handleSort() {
if (jsonIsArray) {
if (!selectedProperty) {
return
try {
sortError = undefined

if (jsonIsArray) {
if (!selectedProperty) {
return
}

const property = selectedProperty.value
const direction = selectedDirection.value
const operations = sortArray(json, selectedPath, property, direction)

onSort(operations)
} else if (isObject(selectedJson)) {
const direction = selectedDirection.value
const operations = sortObjectKeys(json, selectedPath, direction)

onSort(operations)
} else {
console.error('Cannot sort: no array or object')
}

const property = selectedProperty.value
const direction = selectedDirection.value
const operations = sortArray(json, selectedPath, property, direction)

onSort(operations)
} else if (isObject(selectedJson)) {
const direction = selectedDirection.value
const operations = sortObjectKeys(json, selectedPath, direction)

onSort(operations)
} else {
console.error('Cannot sort: no array or object')
close()
} catch (err) {
sortError = err.toString()
}

close()
}

function focus(element: HTMLElement) {
Expand Down Expand Up @@ -142,7 +149,13 @@
</tbody>
</table>

<div class="jse-space" />
<div class="jse-space">
{#if sortError}
<div class="jse-error">
{sortError}
</div>
{/if}
</div>

<div class="jse-actions">
<button
Expand Down
Loading