Repository navigation
feat(core): add a @jsonforms/core/core-only entry point that never loads Ajv - #1
Open
andrewperry wants to merge 7 commits into
Open
andrewperry wants to merge 7 commits into
andrewperry wants to merge 7 commits into
Conversation
Adds a `validator` init/updateCore option taking a factory `(schema) => FormValidator`. Core calls it whenever the schema changes and stores the result in `state.validator`; without the option, Ajv is the default Form Validator via `createAjvValidator`. Like the other options, `validator: undefined` keeps the current value. - `FormValidator.validate(data)` returns `ValidationIssue`s, which `issuesToErrors` converts to the Ajv error objects stored in the state. A missing `key` becomes `custom`; a `required` issue addressed to the missing property is split into parent path plus `params.missingProperty`. Ajv errors round-trip unchanged. - `compiledAjvValidator(validateFn)` wraps an already compiled Ajv function, e.g. generated via Ajv standalone code. - `getValidator(state)` exposes the current Form Validator. - Switching to NoValidation via setValidationMode now also drops the validator, so later data updates no longer reintroduce errors. `JsonFormsCore.validator` changes from the compiled Ajv function to a Form Validator, see MIGRATION.md. Part of eclipsesource#1498 Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01B36yeVG6EUUhwMCEzybGEH
…ing them with Ajv Combinator tab selection compiled every oneOf/anyOf/allOf branch with Ajv and then ignored all errors except the structural keywords required, additionalProperties, type, enum and const. The new isStructuralMatch evaluates exactly those keywords itself, recursively through properties, patternProperties, items, $ref, nested combinators and if/then/else. Tab selection therefore no longer depends on an Ajv instance or on code generation, so it also works under a Content Security Policy without unsafe-eval. Equivalence with the previous filtered-Ajv behaviour is covered by tests comparing both approaches keyword by keyword. Part of eclipsesource#1498 Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Schema based rule conditions were always evaluated with ajv.validate(condition.schema, value), the last place where a custom Form Validator did not apply. - Widen the `ajv` parameter of isVisible, isEnabled, isReadonly, the eval* functions and Runtime.isVisible/isEnabled to RuleValidator: an Ajv instance (unchanged behaviour), a FormValidatorFactory (one Form Validator per condition schema, cached) or undefined (the Structural Matcher). - Add matchesConditionSchema and the getRuleValidator selector (`validatorFactory`, otherwise `ajv`); core's own mappers pass getRuleValidator(state) instead of getAjv(state). Part of eclipsesource#1498 Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01B36yeVG6EUUhwMCEzybGEH
While a `validator` factory is configured and no `ajv` is passed, INIT and UPDATE_CORE no longer create a default Ajv instance, so `core.ajv` stays undefined and nothing compiles schemas with an instance nobody configured. getOrCreateAjv gains an optional `createDefault` parameter; an explicitly passed `ajv` is still stored. vue-vuetify's AdditionalProperties reads Ajv optionally. Part of eclipsesource#1498 Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01B36yeVG6EUUhwMCEzybGEH
…s Ajv Adopters who use core directly with a Form Validator still bundle Ajv and ajv-formats (about 40 kB gzip), because core imports them for its fallback createAjv(). The new opt-in entry point exports everything @jsonforms/core does except createAjv, createAjvValidator and validate, and never loads either package. @jsonforms/core itself is unchanged. Ajv now lives in util/validator.ts, which also holds createAjvValidator and registers itself as the default when loaded (util/ajvDefault.ts). The reducer asks for that default instead of importing createAjv. Without Ajv, init and updateCore need a validator, an ajv instance or validationMode 'NoValidation', and throw otherwise. Rollup builds both entry points with a shared chunk, so importing both does not duplicate core. main, module and typings are unchanged and no exports map is added; the subpath resolves through core-only/package.json. Refs eclipsesource#1557
…ds in use The specs call clearAllIds() before each mount so that snapshots always get the same ids. Under Vitest, core's CommonJS build used to be evaluated twice (by the module runner and by Node's require), so clearAllIds() reset one copy of the id state while the renderers allocated from the other, and ids grew across tests (eclipsesource#6, eclipsesource#8, ...). With core's code in a shared chunk, both copies of the entry file require the same chunk, so there is one id state and the first control gets '#' as intended. Only id numbers change: 96 lines each way, identical once '#<n>' is replaced by '#'.
mountJsonForms() attaches to document.body and nothing unmounted the forms, so every test left its form in the document. Now that clearAllIds() resets the ids in use, the next form reuses the same ids, and the document briefly held duplicates (e.g. two id="#-input"). Enable Vue Test Utils' auto-unmount in the shared setup so each test starts with an empty document. Snapshots are unchanged.
This file contains hidden or 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
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Refs eclipsesource#1557, following the discussion there.
Stacked on eclipsesource#2644, eclipsesource#2645 and eclipsesource#2648 (and eclipsesource#2643, which eclipsesource#2645 includes). Until those are merged this PR shows their commits too; only the last three commits are new here:
feat(core): add @jsonforms/core/core-only entry point that never loads Ajvtest(vue-vuetify): update snapshots now that clearAllIds resets the ids in usetest(vue-vuetify): unmount mounted forms after each testI'll rebase onto master once the stack lands, and mark this ready for review then.
What changed
@jsonforms/core/core-only. It exports everything@jsonforms/coredoes exceptcreateAjv,createAjvValidatorandvalidate, and never loadsajvorajv-formats.@jsonforms/coreis unchanged: same exports, Ajv still the default. The bindings and renderer sets are untouched.util/validator.tsnow also holdscreateAjvValidator(moved fromutil/formValidator.ts). It is the only module importing Ajv at runtime, and when loaded it registers itself as the default through the newutil/ajvDefault.ts. The reducer askscreateDefaultAjv()instead of importingcreateAjv. Theutilbarrel no longer re-exportsvalidator.ts; the main entry point does.init/updateCoreneed avalidator, anajvinstance orvalidationMode: 'NoValidation', and otherwise throw an error naming those options.lib/jsonforms-core-shared.{esm,cjs}.js), so importing both doesn't duplicate core. Themain/module/typingspaths are unchanged and noexportsmap is added, so deep imports keep working. The subpath resolves throughcore-only/package.json, which is added tofiles.Vue Vuetify test changes
Snapshots (ID numbers only). 20 snapshots now show
#where they showed#6/#8. Under Vitest, core's CommonJS build was evaluated twice (by the module runner and by Node'srequire), soclearAllIds()in the specs reset a different copy of the ID state from the one the renderers used. With the shared chunk, both copies of the small entry filerequirethe same chunk, soclearAllIds()works as the specs intend. The diff is 96 lines each way, identical once#<n>is replaced by#.Unmount after each test.
mountJsonForms()attaches todocument.bodyand nothing unmounted the forms. The duplicate core copy had hidden this by keeping IDs unique; now the next form reused the same IDs while the previous one was still in the document.tests/setup.tsenables Vue Test Utils'enableAutoUnmount(afterEach). In the Time spec, 0 to 6 leftover forms were in the document before each test without it, and 0 with it. Snapshots are unchanged.Tests
test/coreOnly.test.tsmakes requiringajv/ajv-formatsthrow, then loads the entry point. It covers:ajvinstance passed in the options;NoValidation;initwithout a validator throwing;If the entry point imports
validator.ts, all six fail with'ajv' was loaded by @jsonforms/core/core-only.test/defaultAjv.test.ts:@jsonforms/corestill creates and uses a default Ajv instance and exports the Ajv helpers.Core: 538 passing, plus the existing known failure. Lint and Prettier are clean, and all packages build.
Other packages, all passing: React, Vanilla, Material (465), Vue, Vue Vanilla (93), Vue Vuetify (111), Angular and Angular Material.
Bundle size
An app using core directly with a Form Validator (
@cfworker/json-schema) and its own Lit renderers, measured with esbuild, minified, gzip -9:@jsonforms/core@jsonforms/core/core-onlyThe core-only bundle contains no
ajvorajv-formatsmodules. It also runs under a CSP without'unsafe-eval'with no violations.Open question
With no validator configured, the core-only entry point throws rather than silently skipping validation, so forms relying on
requiredchecks can't lose them unnoticed. Happy to change that if you'd prefer otherwise.