Repository navigation
feat: precompile a schema into a check for validate - #231
Conversation
Compiling a schema at runtime loads `ajv`, about 150ms for the first schema of a process and a few to tens of milliseconds for every other one, which every loader and plugin validating its options pays on every start. `schema-utils precompile <schema.json>` compiles a schema ahead of time into `<schema>.check.js`, and `validate` takes it as its fourth argument. A valid value is only checked, `ajv` is loaded to report the errors of an invalid one, so they stay the same. `--check` fails when a check is missing or out of date, for CI. The keywords `validate` compiles with a function (`instanceof`, `absolutePath`, `undefinedAsNull`) get code twins with the same semantics, the generated module loads the `ajv` runtime helpers it needs through `schema-utils`, which the package depends on, and the paths of the schemas are relative so the output is the same on every machine. Every case of the validation tests runs the precompiled check too and has to agree with it.
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## main #231 +/- ##
==========================================
+ Coverage 96.72% 97.11% +0.38%
==========================================
Files 10 13 +3
Lines 1009 1180 +171
Branches 415 457 +42
==========================================
+ Hits 976 1146 +170
- Misses 31 32 +1
Partials 2 2
Flags with carried forward coverage won't be shown. Click here to find out more. ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info
WalkthroughAdds an API to compile schemas into standalone CommonJS checks and an optional check parameter to Priority: ➖ Normal Merge Risk: ⚪ Minimal · up to
|
There was a problem hiding this comment.
Actionable comments posted: 1
ℹ️ Review info
⚙️ Run configuration
- Configuration used: Organization UI
- Review profile: CHILL
- Plan: Advanced
- Run ID:
cfb06bdb-e125-4bbf-a633-73ea834668d3
📒 Files selected for processing: 18
.changeset/precompiled-schemas.mdREADME.mdbin/schema-utils.jsdeclarations/cli.d.tsdeclarations/index.d.tsdeclarations/precompile.d.tsdeclarations/runtime.d.tsdeclarations/validate.d.tseslint.config.mjspackage.jsonsrc/cli.jssrc/index.jssrc/precompile.jssrc/runtime.jssrc/validate.jstest/helpers/precompiled.jstest/index.test.jstest/precompile.test.js
Included review availability: This review used your included allowance. Your plan provides up to 2 included reviews per hour; 1 remain after this review.
Before Node.js 15 an unhandled rejection only warns and the process exits with 0, so a check that could not be written passed in CI.
Fixes GHSA-hrr3-gc8f-f4qj, which fails `npm audit` in the lint job.
Summary
Compiling a schema at runtime loads
ajv. That costs about 150 ms for the first schema of a process, plus a few to tens of milliseconds for each later one, and every loader and plugin that validates its options pays it on every start.This adds:
schema-utils precompile [--check] <schema.json>...: compiles a schema ahead of time into<schema>.check.jsnext to it.--checkwrites nothing and fails when a check is missing or out of date, for CI.checkfourth argument tovalidate(schema, options, configuration, check): a valid value is only checked andajvis never loaded. An invalid value still runs the full validation, so the error messages are unchanged.precompile(schema, { loadSchema }): the same as an API. It returns the module source and is loaded lazily from the main entry.Details
instanceof,absolutePathandundefinedAsNullare compiled with functions at runtime, which ajv standalone can't emit. They get code-generating twins with the same semantics.undefinedAsNullalso setsundefinedtonullin the parent, as the full validation does.equal,ucs2length,uri, formats) throughschema-utils/dist/runtime. The packages shipping a check depend onschema-utils, not onajv. Each helper is a getter, so only the ones a schema uses get loaded.title,description,link, …) are stripped before compiling. Thefile:URLs of the compiled and loaded schemas are made relative, so the output is the same on every machine. Afile:URL that is a schema value is left alone./* eslint-disable */and// @ts-nocheckso consumers don't lint or type-check generated code. It is CommonJS.Measurements
Node 22, fresh process, packed tarball installed into a scratch project, css-loader's
options.json:validate(schema, options)validate(schema, options, config, check)For css-loader, style-loader and minimizer-webpack-plugin together: ~280 ms with ajv at runtime against ~12 ms precompiled.
Tests
test/index.test.js(about 400 cases, including the webpack schema) also runs the precompiled check and must agree. Pass cases must also leave the same value behind, which coversundefinedAsNull.test/precompile.test.jscovervalidatewith a check, the keywords, external$ref, path relativizing, the runtime helpers and the CLI.schemas/**/*.jsonwith the CLI. The resultingWebpackOptionscheck agrees with webpack's committed one on all 3,698 configs from webpack'sconfigCases,watchCasesandhotCases.Follow-ups (separate PRs)
this.getOptions(schema, check)for loaders, and switchingtooling/precompile-schemas.jsto this command.webpack/require-precompiled-schemarule.🤖 Generated with Claude Code
https://claude.ai/code/session_01QYPkbvwyRWt2SHgXDoYRdo
Generated by Claude Code
Summary by CodeRabbit
validateaccepts an optional precompiled check, avoiding schema compilation when values pass and retaining standard validation errors when they don’t.