@ata-project/unplugin 0.2.0 → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -6,6 +6,11 @@ The generated module imports nothing. A small schema compiles to about 1.3 KB gz
6
6
 
7
7
  Schemas can be authored as `.json`, `.js` or `.ts`.
8
8
 
9
+ It also compiles away `new Validator(schema)` in your own code wherever the schema
10
+ is known at build time and the result is the same, so the runtime compiler stays
11
+ out of the bundle: for the test entry, 122.5 KB gzipped becomes 16.7 KB. See
12
+ [compileAway](#compileaway-keep-new-validator-drop-the-compiler).
13
+
9
14
  ## Install
10
15
 
11
16
  ```bash
@@ -145,12 +150,12 @@ Other sources (`.json` without the `.schema` suffix, `.js`, `.ts`) produce
145
150
  | `nameFromFile` | file name in PascalCase | Type name for schemas without `title` or `$id`. |
146
151
  | `root` | from the bundler | Where the globs resolve from. Vite's root, Webpack's and Rspack's `context` and esbuild's `absWorkingDir` are read; Rollup and Rolldown need it passed. |
147
152
  | `alias` | Vite's `resolve.alias` | Import aliases inside `.ts` schema files. tsconfig `paths` work without configuration. |
148
- | `compileAway` | `false` | Replace `new Validator(schema)` in your code with a validator compiled at build time, where that gives the same results. See below. Needs ata-validator 1.36.0. |
153
+ | `compileAway` | `true` | Replace `new Validator(schema)` in your code with a validator compiled at build time, where that gives the same results. See below. Needs ata-validator 1.36.0; with an older version the runtime stays, and a warning is printed only if you set the option yourself. |
149
154
 
150
155
  ## compileAway: keep `new Validator`, drop the compiler
151
156
 
152
- With `compileAway: true`, code written against the runtime API is compiled at
153
- build time without being changed:
157
+ Code written against the runtime API is compiled at build time without being
158
+ changed. This is on by default; `compileAway: false` turns it off.
154
159
 
155
160
  ```js
156
161
  import { Validator } from 'ata-validator'
@@ -168,8 +173,8 @@ The plugin puts a compiled validator in place of the `new Validator(...)` call,
168
173
  and once nothing else in the file uses the `ata-validator` import, the import
169
174
  goes with it, so the runtime compiler is not in the bundle. For the three-schema
170
175
  entry in `test/fixtures/compile-away`, one of them with defaults, a minified Vite
171
- library build is 115.7 KB gzipped without it and 15.5 KB with it, on
172
- ata-validator 1.36.0. Across the 977 schemas of SchemaStore, 725 can be compiled
176
+ library build is 122.5 KB gzipped without it and 16.7 KB with it (gzip level 9), on
177
+ ata-validator 1.39.2. Across the 977 schemas of SchemaStore, 725 can be compiled
173
178
  away this way.
174
179
 
175
180
  The replacement answers `validate()`, `isValidObject()`, `validateJSON()` and
@@ -183,9 +188,13 @@ A call is replaced only when all of this is true, and left to the runtime
183
188
  otherwise:
184
189
 
185
190
  - `Validator` is a named import from `ata-validator`;
186
- - the call has one argument, and it is an object literal, a top-level `const`
187
- bound to one, the default import of a relative `.json` file, or
188
- `defineSchema(...)` around one of those;
191
+ - the schema argument is an object literal, a top-level `const` bound to one,
192
+ the default import of a relative `.json` file, or `defineSchema(...)` around
193
+ one of those;
194
+ - there is no second argument, or it is `{ useDefaults: false }` (or
195
+ `{ useDefaults: true }`), written as a literal or a top-level `const`; that
196
+ one option needs ata-validator 1.37.0, and older versions keep such calls on
197
+ the runtime;
189
198
  - the result goes into a `const` that is not exported and is only used as
190
199
  `name.validate(...)`, `name.isValidObject(...)`, `name.validateJSON(...)` or
191
200
  `name.isValidJSON(...)`;
@@ -193,7 +202,7 @@ otherwise:
193
202
  `errorMessage`s and shapes its code generator cannot express, which the
194
203
  runtime answers with its interpreted engine.
195
204
 
196
- Anything with options (`new Validator(schema, { coerceTypes: true })`), a schema
205
+ Any other option (`new Validator(schema, { coerceTypes: true })`), a schema
197
206
  built at run time, or an instance passed around stays as written.
198
207
 
199
208
  ## How it works
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@ata-project/unplugin",
3
- "version": "0.2.0",
4
- "description": "Compile JSON, JS and TS schema files to ata-validator standalone modules at build time, in Vite, Webpack, Rollup, Rolldown, esbuild and Rspack.",
3
+ "version": "0.4.0",
4
+ "description": "Build-time JSON Schema for Vite, Webpack, Rollup, Rolldown, esbuild and Rspack. compileAway takes the runtime compiler out of the bundle.",
5
5
  "type": "module",
6
6
  "main": "./src/index.js",
7
7
  "exports": {
@@ -78,7 +78,7 @@
78
78
  },
79
79
  "devDependencies": {
80
80
  "@rspack/core": "^1.7.0",
81
- "ata-validator": "^1.36.0",
81
+ "ata-validator": "^1.39.2",
82
82
  "esbuild": "^0.28.0",
83
83
  "rolldown": "^1.2.8",
84
84
  "rollup": "^4.60.0",
@@ -185,8 +185,24 @@ function inlineModule(src, index) {
185
185
  return `const __ataCompiled${index} = (() => {\n${body}\nreturn { validate, isValid };\n})();\n`
186
186
  }
187
187
 
188
+ // The options of `new Validator(schema, options)` as a third argument for
189
+ // fromCompiled(), or NOT_STATIC when the call has to stay on the runtime: options
190
+ // that are not a literal the build can read, or that name anything this
191
+ // ata-validator's wrapper does not reproduce. `useDefaults: true` is the default
192
+ // and needs no argument.
193
+ function compiledOptionsArg(node, ctx, supported) {
194
+ const opts = staticValue(node, ctx)
195
+ if (opts === NOT_STATIC || opts === null || typeof opts !== 'object' || Array.isArray(opts)) return NOT_STATIC
196
+ for (const [key, value] of Object.entries(opts)) {
197
+ if (!supported.includes(key)) return NOT_STATIC
198
+ if (key === 'useDefaults' && typeof value !== 'boolean') return NOT_STATIC
199
+ }
200
+ return opts.useDefaults === false ? '{"useDefaults":false}' : ''
201
+ }
202
+
188
203
  export function compileAway(code, id, ata) {
189
204
  const { compiledModuleFor, compiledSchemaFor } = ata
205
+ const supportedOptions = Array.isArray(ata.compiledOptions) ? ata.compiledOptions : []
190
206
  if (!code.includes('ata-validator')) return null
191
207
  let ast
192
208
  try {
@@ -221,7 +237,13 @@ export function compileAway(code, id, ata) {
221
237
  for (const r of refs) {
222
238
  if (r.node.name !== validatorName) continue
223
239
  const expr = r.parent
224
- if (!expr || expr.type !== 'NewExpression' || r.key !== 'callee' || expr.arguments.length !== 1) continue
240
+ if (!expr || expr.type !== 'NewExpression' || r.key !== 'callee') continue
241
+ if (expr.arguments.length !== 1 && expr.arguments.length !== 2) continue
242
+ let optionsArg = ''
243
+ if (expr.arguments.length === 2) {
244
+ optionsArg = compiledOptionsArg(expr.arguments[1], ctx, supportedOptions)
245
+ if (optionsArg === NOT_STATIC) continue
246
+ }
225
247
  if (!replaceableBinding(expr, ctx)) continue
226
248
  const schema = staticValue(expr.arguments[0], ctx)
227
249
  if (schema === NOT_STATIC || schema === null || typeof schema !== 'object' || Array.isArray(schema)) continue
@@ -230,7 +252,7 @@ export function compileAway(code, id, ata) {
230
252
  if (!src) continue
231
253
  const index = modules.length
232
254
  modules.push(inlineModule(src, index))
233
- s.overwrite(expr.start, expr.end, `__ataFromCompiled(__ataCompiled${index}, ${JSON.stringify(compiledSchemaFor(schema))})`)
255
+ s.overwrite(expr.start, expr.end, `__ataFromCompiled(__ataCompiled${index}, ${JSON.stringify(compiledSchemaFor(schema))}${optionsArg ? ', ' + optionsArg : ''})`)
234
256
  done.add(expr)
235
257
  }
236
258
  if (done.size === 0) return null
package/src/core.js CHANGED
@@ -41,7 +41,7 @@ const DEFAULT_OPTIONS = {
41
41
  format: 'esm',
42
42
  abortEarly: false,
43
43
  types: true,
44
- compileAway: false,
44
+ compileAway: true,
45
45
  nameFromFile: (file) => {
46
46
  const base = path.basename(file, path.extname(file)).replace(/\.schema$/i, '')
47
47
  return pascal(base)
@@ -69,6 +69,9 @@ async function loadAta() {
69
69
  toStandaloneModule: build.toStandaloneModule,
70
70
  compiledModuleFor: canCompileAway ? build.compiledModuleFor : null,
71
71
  compiledSchemaFor: canCompileAway ? build.compiledSchemaFor : null,
72
+ // The Validator options fromCompiled() reproduces, from ata-validator
73
+ // 1.37.0; before it, only calls without options are replaced.
74
+ compiledOptions: canCompileAway && Array.isArray(build.compiledOptions) ? build.compiledOptions : [],
72
75
  }
73
76
  }
74
77
 
package/src/index.d.ts CHANGED
@@ -27,7 +27,7 @@ export interface Options {
27
27
  * only used through validate(), isValidObject(), validateJSON() and
28
28
  * isValidJSON(), so the runtime compiler stays out of the bundle. Results are
29
29
  * the ones the runtime gives. Needs ata-validator 1.35.0 or later.
30
- * Default: `false`.
30
+ * Default: `true`; `false` turns it off.
31
31
  */
32
32
  compileAway?: boolean
33
33
  }
package/src/index.js CHANGED
@@ -34,8 +34,11 @@ export const unpluginFactory = (userOptions = {}) => {
34
34
  // compileAway: `new Validator(schema)` with a schema the build can read
35
35
  // becomes a validator compiled here, and the runtime compiler leaves the
36
36
  // bundle. See src/compile-away.js for what is replaced and what is not.
37
+ // On unless the caller turned it off. It replaces a call only where the
38
+ // compiled validator answers exactly as the runtime would, so being on by
39
+ // default can cost a saving but not change a result.
37
40
  transformInclude(id) {
38
- if (!userOptions.compileAway) return false
41
+ if (userOptions.compileAway === false) return false
39
42
  const file = id.split('?')[0]
40
43
  return SOURCE.test(file) && !file.split(/[\\/]/).includes('node_modules')
41
44
  },
@@ -44,7 +47,9 @@ export const unpluginFactory = (userOptions = {}) => {
44
47
  if (!code.includes('ata-validator')) return null
45
48
  const api = await session.api()
46
49
  if (!api.compiledModuleFor) {
47
- if (!session.warnedCompileAway) {
50
+ // Only a caller who asked for it hears that it could not happen; on
51
+ // by default, an older ata-validator just keeps the runtime.
52
+ if (userOptions.compileAway === true && !session.warnedCompileAway) {
48
53
  session.warnedCompileAway = true
49
54
  session.logger?.warn?.('[unplugin-ata] compileAway needs ata-validator 1.36.0 or later; nothing was replaced')
50
55
  }