Skip to content

Commit

Permalink
feat: export one bundle with and one without external dependencies in…
Browse files Browse the repository at this point in the history
… `vanilla-jsoneditor` (#353)

BREAKING CHANGE:

When using `vanilla-jsoneditor` directly in the browser, you now have to `import { JSONEditor } from 'vanilla-jsoneditor/standalone.js'` instead of `import { JSONEditor } from 'vanilla-jsoneditor'`. For projects with a build setup (React, Vue, Angular) it should be a drop-in replacement.
  • Loading branch information
josdejong authored Dec 6, 2023
1 parent ec4b788 commit 9c1ad15
Show file tree
Hide file tree
Showing 14 changed files with 94 additions and 52 deletions.
30 changes: 24 additions & 6 deletions README-VANILLA.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,25 @@ Install using npm:
npm install vanilla-jsoneditor
```

Remark: for usage in a Svelte project, install `svelte-jsoneditor` instead.
Remark: for usage in a Svelte project, install and use `svelte-jsoneditor` instead of `vanilla-jsoneditor`.

## Use

If you have a setup for your project with a bundler (like Vite, Rollup, or Webpack), it is best to use the default ES import:

```ts
// for use in a React, Vue, or Angular project
import { JSONEditor } from 'vanilla-jsoneditor'
```

If you want to use the library straight in the browser, use the provided standalone ES bundle:

```ts
// for use directly in the browser
import { JSONEditor } from 'vanilla-jsoneditor/standalone.js'
```

The standalone bundle contains all dependencies of `vanilla-jsoneditor`, for example `lodash-es` and `Ajv`. If you use some of these dependencies in your project too, it means that they will be bundled twice in your web application, leading to a needlessly large application size. In general, it is preferable to use the default `import { JSONEditor } from 'vanilla-jsoneditor'` so dependencies can be reused.

## Use (Browser example loading the ES module):

Expand All @@ -44,7 +62,7 @@ Remark: for usage in a Svelte project, install `svelte-jsoneditor` instead.
<div id="jsoneditor"></div>

<script type="module">
import { JSONEditor } from 'vanilla-jsoneditor'
import { JSONEditor } from 'vanilla-jsoneditor/standalone.js'
// Or use it through a CDN (not recommended for use in production):
// import { JSONEditor } from 'https://unpkg.com/vanilla-jsoneditor/index.js'
Expand Down Expand Up @@ -84,7 +102,7 @@ Depending on whether you are using JavaScript of TypeScript, create either a JSX

### TypeScript:

```typescript
```tsx
//
// JSONEditorReact.tsx
//
Expand Down Expand Up @@ -126,7 +144,7 @@ export default JSONEditorReact

### JavaScript

```javascript
```jsx
//
// JSONEditorReact.jsx
//
Expand Down Expand Up @@ -172,7 +190,7 @@ If you are using NextJS, you will need to use a dynamic import to only render th

If you are using React in an conventional non-NextJS browser app, you can import the component using a standard import statement like `import JSONEditorReact from '../JSONEditorReact'`

```typescript
```tsx
//
// demo.tsx for use with NextJS
//
Expand Down Expand Up @@ -228,7 +246,7 @@ export default function Demo() {
}
```

```typescript
```tsx
//
// TextContent.tsx
//
Expand Down
30 changes: 22 additions & 8 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -101,13 +101,27 @@ Or one-way binding:
</div>
```

### Standalone bundle (use in React, Vue, Angular, plain JavaScript, ...)
### Vanilla bundle (use in React, Vue, Angular, plain JavaScript, ...)

The library provides a standalone bundle of the editor via the npm library `vanilla-jsoneditor` (instead of `svelte-jsoneditor`) which can be used in any browser environment and framework. In a framework like React, Vue, or Angular, you'll need to write some wrapper code around the class interface.
The library provides a vanilla bundle of the editor via the npm library `vanilla-jsoneditor` (instead of `svelte-jsoneditor`) which can be used in any browser environment and framework. In a framework like React, Vue, or Angular, you'll need to write some wrapper code around the class interface.

Note that the `vanilla-jsoneditor` package contains all dependencies. These are purely needed for the TypeScript types that they export.
If you have a setup for your project with a bundler (like Vite, Rollup, or Webpack), it is best to use the default ES import:

Browser example loading the ES module:
```ts
// for use in a React, Vue, or Angular project
import { JSONEditor } from 'vanilla-jsoneditor'
```

If you want to use the library straight in the browser, use the provided standalone ES bundle:

```ts
// for use directly in the browser
import { JSONEditor } from 'vanilla-jsoneditor/standalone.js'
```

The standalone bundle contains all dependencies of `vanilla-jsoneditor`, for example `lodash-es` and `Ajv`. If you use some of these dependencies in your project too, it means that they will be bundled twice in your web application, leading to a needlessly large application size. In general, it is preferable to use the default `import { JSONEditor } from 'vanilla-jsoneditor'` so dependencies can be reused.

Browser example loading the standalone ES module:

```html
<!doctype html>
Expand All @@ -119,11 +133,11 @@ Browser example loading the ES module:
<div id="jsoneditor"></div>

<script type="module">
import { JSONEditor } from 'vanilla-jsoneditor'
import { JSONEditor } from 'vanilla-jsoneditor/standalone.js'
// Or use it through a CDN (not recommended for use in production):
// import { JSONEditor } from 'https://unpkg.com/vanilla-jsoneditor/index.js'
// import { JSONEditor } from 'https://cdn.jsdelivr.net/npm/vanilla-jsoneditor/index.js'
// import { JSONEditor } from 'https://unpkg.com/vanilla-jsoneditor/standalone.js'
// import { JSONEditor } from 'https://cdn.jsdelivr.net/npm/vanilla-jsoneditor/standalone.js'
let content = {
text: undefined,
Expand Down Expand Up @@ -180,7 +194,7 @@ Svelte component:
JavasScript class:

```js
import { JSONEditor } from 'vanilla-jsoneditor'
import { JSONEditor } from 'vanilla-jsoneditor' // or 'vanilla-jsoneditor/standalone.js'

const content = { text: '[1,2,3]' }

Expand Down
2 changes: 1 addition & 1 deletion examples/browser/basic_usage.html
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
<div id="jsoneditor"></div>

<script type="module">
import { JSONEditor } from '../../package-vanilla/index.js'
import { JSONEditor } from '../../package-vanilla/standalone.js'

// create the editor
const editor = new JSONEditor({
Expand Down
2 changes: 1 addition & 1 deletion examples/browser/custom_theme_color.html
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ <h1>JSONEditor with a custom theme color and large font</h1>
<div id="my-jsoneditor"></div>

<script type="module">
import { JSONEditor } from '../../package-vanilla/index.js'
import { JSONEditor } from '../../package-vanilla/standalone.js'

const editor = new JSONEditor({
target: document.getElementById('my-jsoneditor'),
Expand Down
2 changes: 1 addition & 1 deletion examples/browser/json_schema_validation.html
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ <h1>JSON schema validation</h1>
<div id="jsoneditor"></div>

<script type="module">
import { JSONEditor, createAjvValidator } from '../../package-vanilla/index.js'
import { JSONEditor, createAjvValidator } from '../../package-vanilla/standalone.js'

const schema = {
title: 'Employee',
Expand Down
2 changes: 1 addition & 1 deletion examples/browser/toggle_options.html
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@
<div id="jsoneditor"></div>

<script type="module">
import { JSONEditor } from '../../package-vanilla/index.js'
import { JSONEditor } from '../../package-vanilla/standalone.js'

const content = {
text: undefined,
Expand Down
5 changes: 3 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -41,8 +41,9 @@
"build:svelte:package-json": "node tools/createSveltePackageJson.js",
"build:vanilla": "npm-run-all build:vanilla:**",
"build:vanilla:clean": "del-cli package-vanilla",
"build:vanilla:bundle": "rollup --config rollup.config.bundle.js",
"build:vanilla:bundle-types": "rollup --config rollup.config.bundle-types.js",
"build:vanilla:library": "rollup --config rollup.config.vanilla-library.js",
"build:vanilla:bundle": "rollup --config rollup.config.vanilla-bundle.js",
"build:vanilla:types": "rollup --config rollup.config.vanilla-types.js",
"build:vanilla:copy:files": "cpy CHANGELOG.md LICENSE.md SECURITY.md package-vanilla",
"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",
Expand Down
18 changes: 18 additions & 0 deletions rollup.config.vanilla-bundle.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import config from './rollup.config.vanilla-library.js'
import path from 'path'

const packageFolder = 'package-vanilla'
const file = path.join(packageFolder, 'standalone.js')

export default {
...config,
external: undefined,
output: [
{
file,
format: 'es',
sourcemap: true,
inlineDynamicImports: true
}
]
}
2 changes: 2 additions & 0 deletions rollup.config.bundle.js → rollup.config.vanilla-library.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,15 @@ import path from 'path'
import svelte from 'rollup-plugin-svelte'
import terser from '@rollup/plugin-terser'
import sveltePreprocess from 'svelte-preprocess'
import { getVanillaDependencies } from './tools/getExternalDependencies.js'

const production = !process.env.ROLLUP_WATCH
const packageFolder = 'package-vanilla'
const file = path.join(packageFolder, 'index.js')

export default {
input: 'src/lib/index.ts',
external: getVanillaDependencies(),
output: [
{
file,
Expand Down
File renamed without changes.
5 changes: 2 additions & 3 deletions src/lib/components/modes/textmode/TextMode.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -43,8 +43,7 @@
highlightSpecialChars,
keymap,
lineNumbers,
rectangularSelection,
ViewUpdate
rectangularSelection
} from '@codemirror/view'
import {
defaultKeymap,
Expand Down Expand Up @@ -578,7 +577,7 @@
EditorView.domEventHandlers({
dblclick: handleDoubleClick
}),
EditorView.updateListener.of((update: ViewUpdate) => {
EditorView.updateListener.of((update) => {
editorState = update.state
if (update.docChanged) {
Expand Down
35 changes: 7 additions & 28 deletions tools/createVanillaPackageJson.js
Original file line number Diff line number Diff line change
@@ -1,36 +1,16 @@
// create package.json and copy files like LICENSE.md to package-vanilla

import path from 'path'
import assert from 'assert'
import { readFileSync, writeFileSync } from 'fs'
import { getAbsolutePath } from './utils/getAbsolutePath.mjs'
import { getVanillaDependencies } from './getExternalDependencies.js'

const vanillaPackageFolder = getAbsolutePath(import.meta.url, '..', 'package-vanilla')

const pkg = JSON.parse(String(readFileSync(getAbsolutePath(import.meta.url, '..', 'package.json'))))
const typesPath = getAbsolutePath(import.meta.url, '..', 'package-vanilla', 'index.d.ts')
const types = String(readFileSync(typesPath))

// scan the index.d.ts bundle file for all occurrences of "import { ...} from '...'" and extract the name
const usedDependencyNames = uniq(
Array.from(types.matchAll(/(import|export) .+ from '(.+)'/g))
.map((match) => match[2])
.sort()
)
const expectedDependencyNames = [
'@fortawesome/free-solid-svg-icons',
'ajv',
'immutable-json-patch',
'svelte'
]

// We do not want to get surprises
assert.deepStrictEqual(
usedDependencyNames,
expectedDependencyNames,
`Used dependencies found in "${typesPath}" does not equal the expected dependencies. ` +
'Please update the list in createVanillaPackageJson.js manually.'
)
// We add svelte here: this is needed to export the TypeScript types
const usedDependencyNames = [...getVanillaDependencies(), 'svelte']

const usedDependencies = usedDependencyNames.reduce((deps, name) => {
deps[name] = pkg.dependencies[name]
Expand All @@ -44,17 +24,16 @@ const vanillaPackage = {
dependencies: usedDependencies, // needed for the TypeScript types
devDependencies: {},
svelte: undefined,
browser: './standalone.js',
exports: {
...pkg.exports,
'./index.js.map': './index.js.map'
'./index.js.map': './index.js.map',
'./standalone.js': './standalone.js',
'./standalone.js.map': './standalone.js.map'
}
}

writeFileSync(
path.join(vanillaPackageFolder, 'package.json'),
JSON.stringify(vanillaPackage, null, 2)
)

function uniq(array) {
return [...new Set(array)]
}
2 changes: 1 addition & 1 deletion tools/develop-vanilla.html
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,7 @@
JSONEditor,
lodashQueryLanguage,
jmespathQueryLanguage
} from '../package-vanilla/index.js'
} from '../package-vanilla/standalone.js'

const json = {
array: [
Expand Down
11 changes: 11 additions & 0 deletions tools/getExternalDependencies.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import { readFileSync } from 'fs'
import { getAbsolutePath } from './utils/getAbsolutePath.mjs'

// Get all dependencies for the vanilla package: dependencies that do not start with "svelte*"
// (for example jsonrepair, lodash-es, ...)
export function getVanillaDependencies() {
const sveltePackageFolder = getAbsolutePath(import.meta.url, '..', 'package.json')

const pkg = JSON.parse(String(readFileSync(sveltePackageFolder)))
return Object.keys(pkg.dependencies).filter((name) => !name.startsWith('svelte'))
}

0 comments on commit 9c1ad15

Please sign in to comment.