Skip to content

Commit

Permalink
feat: upgrade to Svelte 5 (#490)
Browse files Browse the repository at this point in the history
BREAKING CHANGE:
The library is upgraded to Svelte 5. Everything is backward compatible and works in Svelte 3, 4, and 5. There may be subtle breaking changes in specific edge cases though, hence the release as a new major version.
josdejong authored Oct 28, 2024

Verified

This commit was signed with the committer’s verified signature.
withinfocus Matt Bishop
1 parent 43dfe41 commit 588caa5
Showing 26 changed files with 2,977 additions and 1,247 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -429,7 +429,7 @@ To adjust the text color of keys or values, the color of the classes `.jse-key`
onRenderValue(props: RenderValueProps) : RenderValueComponentDescription[]
```
Customize rendering of the values. By default, `renderValue` is used, which renders a value as an editable div and depending on the value can also render a boolean toggle, a color picker, and a timestamp tag. Multiple components can be rendered alongside each other, like the boolean toggle and color picker being rendered left from the editable div. Built in value renderer components: `EditableValue`, `ReadonlyValue`, `BooleanToggle`, `ColorPicker`, `TimestampTag`, `EnumValue`.
Customize rendering of the values. By default, `renderValue` is used, which renders a value as an editable div and depending on the value can also render a boolean toggle, a color picker, and a timestamp tag. Multiple components can be rendered alongside each other, like the boolean toggle and color picker being rendered left from the editable div. In order to disable for example the built-in color picker or timestamp tag, you can look up the source code of `renderValue`, copy it, and then remove the components that you do not want from the function. Built in value renderer components: `EditableValue`, `ReadonlyValue`, `BooleanToggle`, `ColorPicker`, `TimestampTag`, `EnumValue`.
For JSON Schema enums, there is a ready-made value renderer `renderJSONSchemaEnum` which renders enums using the `EnumValue` component. This can be used like:
2,544 changes: 1,846 additions & 698 deletions package-lock.json

Large diffs are not rendered by default.

19 changes: 10 additions & 9 deletions package.json
Original file line number Diff line number Diff line change
@@ -105,11 +105,11 @@
"vanilla-picker": "^2.12.3"
},
"peerDependencies": {
"svelte": "^3.54.0 || ^4.0.0"
"svelte": "^3.54.0 || ^4.0.0 || ^5.0.0"
},
"devDependencies": {
"@babel/core": "7.25.9",
"@babel/preset-env": "7.25.9",
"@babel/core": "7.26.0",
"@babel/preset-env": "7.26.0",
"@commitlint/cli": "19.5.0",
"@commitlint/config-conventional": "19.5.0",
"@eslint/compat": "1.2.1",
@@ -121,10 +121,10 @@
"@rollup/plugin-node-resolve": "15.3.0",
"@rollup/plugin-terser": "0.4.4",
"@rollup/plugin-typescript": "12.1.1",
"@sveltejs/adapter-auto": "3.3.0",
"@sveltejs/kit": "2.7.2",
"@sveltejs/package": "2.3.6",
"@sveltejs/vite-plugin-svelte": "3.1.2",
"@sveltejs/adapter-auto": "3.3.1",
"@sveltejs/kit": "2.7.3",
"@sveltejs/package": "2.3.7",
"@sveltejs/vite-plugin-svelte": "4.0.0",
"@testing-library/jest-dom": "6.6.2",
"@testing-library/svelte": "5.2.4",
"@types/cookie": "0.6.0",
@@ -147,11 +147,12 @@
"npm-run-all": "4.1.5",
"prettier": "3.3.3",
"prettier-plugin-svelte": "3.2.7",
"rollup": "4.24.0",
"rollup": "4.24.2",
"rollup-plugin-dts": "6.1.1",
"rollup-plugin-postcss": "4.0.2",
"rollup-plugin-svelte": "7.2.2",
"standard-version": "9.5.0",
"svelte": "^4.2.19",
"svelte": "5.1.3",
"svelte-check": "4.0.5",
"svelte-eslint-parser": "0.43.0",
"svelte-preprocess": "6.0.3",
8 changes: 6 additions & 2 deletions rollup.config.vanilla-library.js
Original file line number Diff line number Diff line change
@@ -2,6 +2,7 @@ import commonjs from '@rollup/plugin-commonjs'
import json from '@rollup/plugin-json'
import resolve from '@rollup/plugin-node-resolve'
import typescript from '@rollup/plugin-typescript'
import postcss from 'rollup-plugin-postcss'
import { getBabelOutputPlugin } from '@rollup/plugin-babel'
import path from 'path'
import svelte from 'rollup-plugin-svelte'
@@ -31,12 +32,15 @@ export default {
dev: !production
},

// we want to embed the CSS in the generated JS bundle
emitCss: false,
emitCss: true,

preprocess: sveltePreprocess()
}),

postcss({
plugins: []
}),

resolve({
browser: true,
exportConditions: ['svelte']
20 changes: 1 addition & 19 deletions rollup.config.vanilla-types.js
Original file line number Diff line number Diff line change
@@ -12,23 +12,5 @@ export default {
format: 'es'
}
],
plugins: [dts(), exportJsonEditorClass()]
}

// The DTS plugin doesn't export the JsonEditor class that is generated by Svelte whilst compiling,
// so we append an export for it manually. Similarly, the CreateJSONEditorProps interface is not
// exported by DTS, so we add an export for it too.
function exportJsonEditorClass() {
return {
name: 'export-json-editor',
transform: (code, id) => {
if (id.endsWith('index-vanilla.d.ts')) {
const customExports = 'export type { JsonEditor, CreateJSONEditorProps }'

return `${code}\n${customExports}\n`
}

return code
}
}
plugins: [dts()]
}
211 changes: 170 additions & 41 deletions src/lib/components/JSONEditor.svelte
Original file line number Diff line number Diff line change
@@ -54,45 +54,73 @@
// TODO: document how to enable debugging in the readme: localStorage.debug="jsoneditor:*", then reload
const debug = createDebug('jsoneditor:JSONEditor')
export let content: Content = { text: '' }
export let selection: JSONEditorSelection | undefined = undefined
export let readOnly = false
export let indentation: number | string = 2
export let tabSize = 4
export let mode: Mode = Mode.tree
export let mainMenuBar = true
export let navigationBar = true
export let statusBar = true
export let askToFormat = true
export let escapeControlCharacters = false
export let escapeUnicodeCharacters = false
export let flattenColumns = true
export let parser: JSONParser = JSON
export let validator: Validator | undefined = undefined
export let validationParser: JSONParser = JSON
export let pathParser: JSONPathParser = {
const contentDefault = { text: '' }
const selectionDefault = undefined
const readOnlyDefault = false
const indentationDefault = 2
const tabSizeDefault = 4
const modeDefault = Mode.tree
const mainMenuBarDefault = true
const navigationBarDefault = true
const statusBarDefault = true
const askToFormatDefault = true
const escapeControlCharactersDefault = false
const escapeUnicodeCharactersDefault = false
const flattenColumnsDefault = true
const parserDefault = JSON
const validatorDefault = undefined
const validationParserDefault = JSON
const pathParserDefault = {
parse: parseJSONPath,
stringify: stringifyJSONPath
}
export let queryLanguages: QueryLanguage[] = [jsonQueryLanguage]
export let queryLanguageId: string = queryLanguages[0].id
export let onChangeQueryLanguage: OnChangeQueryLanguage = noop
export let onChange: OnChange | undefined = undefined
export let onSelect: OnSelect | undefined = undefined
export let onRenderValue: OnRenderValue = renderValue
export let onClassName: OnClassName = () => undefined
export let onRenderMenu: OnRenderMenu = noop
export let onRenderContextMenu: OnRenderContextMenu = noop
export let onChangeMode: OnChangeMode = noop
export let onError: OnError = (err) => {
const queryLanguagesDefault = [jsonQueryLanguage]
const queryLanguageIdDefault = queryLanguagesDefault[0].id
const onChangeQueryLanguageDefault = noop
const onChangeDefault = undefined
const onSelectDefault = undefined
const onRenderValueDefault = renderValue
const onClassNameDefault = noop
const onRenderMenuDefault = noop
const onRenderContextMenuDefault = noop
const onChangeModeDefault = noop
const onErrorDefault: OnError = (err) => {
console.error(err)
alert(err.toString()) // TODO: create a nice alert modal
}
export let onFocus: OnFocus = noop
export let onBlur: OnBlur = noop
const onFocusDefault = noop
const onBlurDefault = noop
export let content: Content = contentDefault
export let selection: JSONEditorSelection | undefined = selectionDefault
export let readOnly: boolean = readOnlyDefault
export let indentation: number | string = indentationDefault
export let tabSize: number = tabSizeDefault
export let mode: Mode = modeDefault
export let mainMenuBar: boolean = mainMenuBarDefault
export let navigationBar: boolean = navigationBarDefault
export let statusBar: boolean = statusBarDefault
export let askToFormat: boolean = askToFormatDefault
export let escapeControlCharacters: boolean = escapeControlCharactersDefault
export let escapeUnicodeCharacters: boolean = escapeUnicodeCharactersDefault
export let flattenColumns: boolean = flattenColumnsDefault
export let parser: JSONParser = parserDefault
export let validator: Validator | undefined = validatorDefault
export let validationParser: JSONParser = validationParserDefault
export let pathParser: JSONPathParser = pathParserDefault
export let queryLanguages: QueryLanguage[] = queryLanguagesDefault
export let queryLanguageId: string = queryLanguageIdDefault
export let onChangeQueryLanguage: OnChangeQueryLanguage = onChangeQueryLanguageDefault
export let onChange: OnChange | undefined = onChangeDefault
export let onSelect: OnSelect | undefined = onSelectDefault
export let onRenderValue: OnRenderValue = onRenderValueDefault
export let onClassName: OnClassName = onClassNameDefault
export let onRenderMenu: OnRenderMenu = onRenderMenuDefault
export let onRenderContextMenu: OnRenderContextMenu = onRenderContextMenuDefault
export let onChangeMode: OnChangeMode = onChangeModeDefault
export let onError: OnError = onErrorDefault
export let onFocus: OnFocus = onFocusDefault
export let onBlur: OnBlur = onBlurDefault
let instanceId = uniqueId()
let hasFocus = false
@@ -252,19 +280,120 @@
}
export async function updateProps(props: JSONEditorPropsOptional): Promise<void> {
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
this.$set(props)
const names = Object.keys(props) as (keyof JSONEditorPropsOptional)[]
for (const name of names) {
switch (name) {
case 'content':
content = props[name] ?? contentDefault
break
case 'readOnly':
readOnly = props[name] ?? readOnlyDefault
break
case 'indentation':
indentation = props[name] ?? indentationDefault
break
case 'tabSize':
tabSize = props[name] ?? tabSizeDefault
break
case 'mode':
mode = props[name] ?? modeDefault
break
case 'mainMenuBar':
mainMenuBar = props[name] ?? mainMenuBarDefault
break
case 'navigationBar':
navigationBar = props[name] ?? navigationBarDefault
break
case 'statusBar':
statusBar = props[name] ?? statusBarDefault
break
case 'askToFormat':
askToFormat = props[name] ?? askToFormatDefault
break
case 'escapeControlCharacters':
escapeControlCharacters = props[name] ?? escapeControlCharactersDefault
break
case 'escapeUnicodeCharacters':
escapeUnicodeCharacters = props[name] ?? escapeUnicodeCharactersDefault
break
case 'flattenColumns':
flattenColumns = props[name] ?? flattenColumnsDefault
break
case 'parser':
parser = props[name] ?? parserDefault
break
case 'validator':
validator = props[name] ?? validatorDefault
break
case 'validationParser':
validationParser = props[name] ?? validationParserDefault
break
case 'pathParser':
pathParser = props[name] ?? pathParserDefault
break
case 'queryLanguages':
queryLanguages = props[name] ?? queryLanguagesDefault
break
case 'queryLanguageId':
queryLanguageId = props[name] ?? queryLanguageIdDefault
break
case 'onChangeQueryLanguage':
onChangeQueryLanguage = props[name] ?? onChangeQueryLanguageDefault
break
case 'onChange':
onChange = props[name] ?? onChangeDefault
break
case 'onRenderValue':
onRenderValue = props[name] ?? onRenderValueDefault
break
case 'onClassName':
onClassName = props[name] ?? onClassNameDefault
break
case 'onRenderMenu':
onRenderMenu = props[name] ?? onRenderMenuDefault
break
case 'onRenderContextMenu':
onRenderContextMenu = props[name] ?? onRenderContextMenuDefault
break
case 'onChangeMode':
onChangeMode = props[name] ?? onChangeModeDefault
break
case 'onSelect':
onSelect = props[name] ?? onSelectDefault
break
case 'onError':
onError = props[name] ?? onErrorDefault
break
case 'onFocus':
onFocus = props[name] ?? onFocusDefault
break
case 'onBlur':
onBlur = props[name] ?? onBlurDefault
break
default:
// We should never reach this default case
unknownProperty(name)
}
}
if (!queryLanguages.some((queryLanguage) => queryLanguage.id === queryLanguageId)) {
queryLanguageId = queryLanguages[0].id
}
function unknownProperty(name: never) {
debug(`Unknown property "${name}"`)
}
await tick() // await rerender
}
export async function destroy() {
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
this.$destroy()
await tick() // await destroying
throw new Error(
'class method destroy() is deprecated. ' +
'It is replaced with a method destroy() in the vanilla library.'
)
}
function handleChange(updatedContent: Content, previousContent: Content, status: OnChangeStatus) {
Loading

0 comments on commit 588caa5

Please sign in to comment.