Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
4 changes: 2 additions & 2 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ The workspace boundary is configured in [pnpm-workspace.yaml](pnpm-workspace.yam
| [types.ts](src/types.ts) | `JsonEditorProps` and all exported types |
| [localisation.ts](src/localisation.ts) | UI strings and `TranslateFunction` |
| [Icons.tsx](src/Icons.tsx), [ButtonPanels.tsx](src/ButtonPanels.tsx), [KeyDisplay.tsx](src/KeyDisplay.tsx), [AutogrowTextArea.tsx](src/AutogrowTextArea.tsx) | UI building blocks. `AutogrowTextArea` is part of the public API (added in v2). |
| [style.css](src/style.css) + [injectStyles.ts](src/injectStyles.ts) | Bundled CSS (inlined as a string by `rollup-plugin-styles`) and the injector that puts it in `<head>` |
| [style.css](src/style.css) + [injectStyles.ts](src/injectStyles.ts) | Bundled CSS (inlined as a minified string by the `inlineCss` rollup plugin in [scripts/rollup-inline-css.mjs](scripts/rollup-inline-css.mjs)) and the injector that puts it in `<head>` |

Themes are no longer in core — see [packages/themes/](packages/themes/).

Expand Down Expand Up @@ -115,7 +115,7 @@ The demo can resolve `json-edit-react`, `@json-edit-react/themes`, and `@json-ed
- Core's `prebuild` runs `pnpm lint && node scripts/run-prebuild-tests.mjs` — a failing lint OR test halts the build. `SKIP_TESTS=1 pnpm build` skips the test step only (lint stays mandatory). No equivalent skip for lint; if you genuinely need to bypass everything, run `rollup -c && rm -R build/dts` directly.
- **Every package ships each entry as a single bundled file, so `sideEffects: false` can't tree-shake them** — it's module-granular, and there's only one module. What makes unused exports droppable is the `/*#__PURE__*/` annotations each rollup config stamps onto eager top-level factory calls (`React.memo`, `createContext`, `lazy`, `createDefinitionFactory`, core's `mergeIcons`, and utils' `intern` / `internRef` / `internRefs`), plus terser's `format.preserve_annotations`. Core additionally annotates the compiled `jsx` calls in [defaultTheme.tsx](src/contexts/ThemeProvider/defaultTheme.tsx), whose JSX is top-level *data* rather than a render body. All four packages have an esbuild-based `verify-treeshake.mjs` wired into their `build` script as the end-to-end guard — esbuild deliberately, since rollup infers local purity on its own and would mask a regression. Renaming any annotated internal function without updating the config's `pureNames` list silently regresses the bundle; the guard is what catches it. Background in issues #382 (themes), #388 (components), #389 (core), #406 (utils).
- **Sub-packages typecheck against core's `build/`, not its source.** Each resolves `json-edit-react` through a symlink to the repo root, whose `types` field points at the gitignored `build/index.d.ts` — so a stale or missing core build produces phantom errors *in the sub-package* (`UpdateFunctionProps is not generic`, `no exported member 'IconDefinition'`), all pointing at the wrong package. Rebuild core first. `pnpm -r build` and `pnpm pack-all` both order core ahead of the sub-packages, so those are safe; a lone `pnpm compile` / `pnpm build` / `pub:<sub-package>` in one sub-package is not. The errors don't stop a build either: `@rollup/plugin-typescript` only escalates a diagnostic to fatal when `noEmitOnError` is set, and no package sets it, so the build emits its `.d.ts` anyway and the errors scroll past as warnings.
- **Core's stylesheet is a plain string constant, not a top-level injection** — no annotation can drop a bare side-effecting statement, so the `styles` plugin is configured with `mode: ['inject', () => '']` to emit the CSS without an injector call, and [injectStyles.ts](src/injectStyles.ts) injects it from a `useInsertionEffect` in [JsonEditor.tsx](src/JsonEditor.tsx). That keeps the CSS reachable only from the editor path (so a helper-only import shakes it out) while guaranteeing the rules are in `<head>` before any layout effect runs or the browser can paint. `--jer-form-border`, a custom property that appears only in the stylesheet, is a `verify-treeshake.mjs` marker because of this. The import specifier is `./style.css?inline` — Vite serves a plain `.css` import as an injected side effect with no default export, which breaks any direct consumer of `src/` (the demo's `local` mode); rollup has no query convention, so `stripCssQuery` in the config resolves it back to the plain file, and jest's `moduleNameMapper` matches the query too. One upshot: `pnpm dev` exercises the same injection path as the published bundle. Issue #396.
- **Core's stylesheet is a plain string constant, not a top-level injection** — no annotation can drop a bare side-effecting statement, so the `inlineCss` plugin ([scripts/rollup-inline-css.mjs](scripts/rollup-inline-css.mjs), shared with components) emits the CSS as `export default '<minified css>'` with no injector call, and [injectStyles.ts](src/injectStyles.ts) injects it from a `useInsertionEffect` in [JsonEditor.tsx](src/JsonEditor.tsx). That keeps the CSS reachable only from the editor path (so a helper-only import shakes it out) while guaranteeing the rules are in `<head>` before any layout effect runs or the browser can paint. `--jer-form-border`, a custom property that appears only in the stylesheet, is a `verify-treeshake.mjs` marker because of this. The import specifier is `./style.css?inline` — Vite serves a plain `.css` import as an injected side effect with no default export, which breaks any direct consumer of `src/` (the demo's `local` mode); rollup has no query convention, so `inlineCss` resolves the specifier itself (keeping the `?inline` marker on the module id so its `load` hook handles exactly these imports), minifies the file with esbuild, and jest's `moduleNameMapper` matches the query too. One upshot: `pnpm dev` exercises the same injection path as the published bundle. Issue #396.

### Linting / formatting
- ESLint flat config at [eslint.config.mjs](eslint.config.mjs) covers core only. The `packages/` and `demo/` directories are ignored from the root lint and have (or will have) their own configs.
Expand Down
13 changes: 12 additions & 1 deletion demo/eslint.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,11 @@ export default tseslint.config(
'react-refresh': reactRefresh,
},
rules: {
...reactHooks.configs.recommended.rules,
// The two classic hooks rules, listed explicitly rather than via the
// plugin's `recommended` preset, which also enables the React Compiler
// rule set. Adopting those is a separate decision.
'react-hooks/rules-of-hooks': 'error',
'react-hooks/exhaustive-deps': 'warn',
'react-refresh/only-export-components': ['warn', { allowConstantExport: true }],
'@typescript-eslint/no-unused-vars': ['error', { argsIgnorePattern: '^_' }],
},
Expand All @@ -33,5 +37,12 @@ export default tseslint.config(
// of Fast Refresh for these files instead.
files: ['src/examples/**/*.tsx'],
rules: { 'react-refresh/only-export-components': 'off' },
},
{
// The entry module defines its lazy route components inline and exports
// nothing, so there is nothing for Fast Refresh to preserve there; a
// change to it reloads the page regardless.
files: ['src/main.tsx'],
rules: { 'react-refresh/only-export-components': 'off' },
}
)
21 changes: 10 additions & 11 deletions demo/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@
"@emotion/styled": "^11.11.0",
"@json-edit-react/components": "0.9.0-beta.6",
"@json-edit-react/themes": "0.9.0-beta.2",
"@json-edit-react/utils": "0.9.0-beta.2",
"@json-edit-react/utils": "0.9.0-beta.3",
"@uiw/codemirror-theme-console": "^4.23.13",
"@uiw/codemirror-theme-dracula": "^4.23.13",
"@uiw/codemirror-theme-github": "^4.23.13",
Expand All @@ -43,7 +43,7 @@
"@uiw/react-codemirror": "^4.23.13",
"ajv": "^8.16.0",
"colord": "^2.9.3",
"firebase": "^10.13.0",
"firebase": "^12.19.0",
"framer-motion": "^11.0.3",
"json-edit-react": "2.0.0-beta.10",
"json5": "^2.2.3",
Expand All @@ -62,22 +62,21 @@
"wouter": "^3.7.1"
},
"devDependencies": {
"@eslint/js": "^9.28.0",
"@eslint/js": "^10.0.1",
"@types/fs-extra": "^11.0.4",
"@types/node": "^20.19.0",
"@types/react": "^19.0.0",
"@types/react-dom": "^19.0.0",
"@vitejs/plugin-react": "^4.5.2",
"cross-env": "^7.0.3",
"eslint": "^9.28.0",
"eslint-plugin-react-hooks": "^5.1.0",
"eslint-plugin-react-refresh": "^0.4.20",
"@vitejs/plugin-react": "^5.2.0",
"eslint": "^10.10.0",
"eslint-plugin-react-hooks": "^7.1.1",
"eslint-plugin-react-refresh": "^0.5.7",
"fs-extra": "^11.3.0",
"gh-pages": "^6.1.1",
"globals": "^15.15.0",
"globals": "^17.12.0",
"node-fetch": "^3.3.2",
"typescript": "~5.7.2",
"typescript": "^5.9.3",
"typescript-eslint": "^8.34.0",
"vite": "^6.3.5"
"vite": "^7.3.6"
}
}
Loading
Loading