@amritk/mjst 0.26.2 → 0.27.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 +13 -3
- package/dist/cli.js +1 -11
- package/dist/help-text.d.ts +1 -1
- package/dist/help-text.js +2 -1
- package/dist/resolve-banner.d.ts +11 -0
- package/dist/resolve-banner.js +22 -0
- package/package.json +8 -8
package/README.md
CHANGED
|
@@ -134,6 +134,16 @@ alongside it:
|
|
|
134
134
|
|
|
135
135
|
### Running the generated output
|
|
136
136
|
|
|
137
|
+
> [!IMPORTANT]
|
|
138
|
+
> **Type-checking the output as part of your own project?** Pass `--import-ext js`. The `.ts`
|
|
139
|
+
> default is for running the sources directly, and without `allowImportingTsExtensions` in your
|
|
140
|
+
> `tsconfig.json`, tsc rejects every one of those imports with `TS5097`. The output itself
|
|
141
|
+
> compiles under `strict`, `exactOptionalPropertyTypes`, `noUncheckedIndexedAccess` and
|
|
142
|
+
> `noPropertyAccessFromIndexSignature`. If your project turns on something it does not satisfy,
|
|
143
|
+
> `--banner "// @ts-nocheck"` writes the directive at the top of every generated file outside
|
|
144
|
+
> `_helpers/` (text made only of `//` lines is emitted as written, not wrapped in a JSDoc block, which TypeScript
|
|
145
|
+
> would not read a directive from).
|
|
146
|
+
|
|
137
147
|
By default mjst emits relative imports with the literal `.ts` extension (e.g.
|
|
138
148
|
`import { parseUser } from './user.ts'`), so the generated sources run **as-is** — no build
|
|
139
149
|
step — under:
|
|
@@ -462,7 +472,7 @@ usage error. Run `mjst markdown --help` for details.
|
|
|
462
472
|
<td align="center"><code>false</code></td>
|
|
463
473
|
</tr>
|
|
464
474
|
<tr>
|
|
465
|
-
<td colspan="4">Also emit a coerceX alongside each validateX. It moves scalars toward the type the schema declares and then runs the very same validateX over the result, so a config value written the YAML way reads the same whether it came from a config file or an extension. The input is never modified and nothing is substituted: a value that cannot be coerced into a valid one reaches the validator untouched, so the error names what the caller actually wrote, with the keyword and params that rejected it. The table is Ajv's coerceTypes minus the cells where Ajv guesses — no whitespace-to-zero, no hex or Infinity strings, and nothing coerced to or from null, deliberately, since null usually means not set — so every scalar this coerces, Ajv coerces to the same value, and the rest become errors rather than silent repairs. Coercion reaches through $ref, allOf, if/then/else and unions. At a union a value that already matches a branch as written is left alone; otherwise each branch coerces it its own way, and the result is taken only when every branch that then accepts it agrees on it. So the answer does not depend on the order the union was written in, and a const false branch keeps false where Ajv turns it into the string false through an earlier string branch. A valid document comes back as the very same object. Where the caller cannot let Ajv mutate its input, so Ajv has to clone it first, coerceX runs 5–36× faster on valid input and 1.4–2.1× faster on input that needs coercing; where the caller lets Ajv coerce its document in place there is no clone to save, and on a large document Ajv can come out modestly ahead. Requires validators.</td>
|
|
475
|
+
<td colspan="4">Also emit a coerceX alongside each validateX. It moves scalars toward the type the schema declares and then runs the very same validateX over the result, so a config value written the YAML way reads the same whether it came from a config file or an extension. The input is never modified and nothing is substituted: a value that cannot be coerced into a valid one reaches the validator untouched, so the error names what the caller actually wrote, with the keyword and params that rejected it. The table is Ajv's coerceTypes minus the cells where Ajv guesses — no whitespace-to-zero, no hex or Infinity strings, and nothing coerced to or from null, deliberately, since null usually means not set — so every scalar this coerces, Ajv coerces to the same value, and the rest become errors rather than silent repairs. Moving off Ajv, check how your callers treat a rejected value: a caller that reads rejection as absence silently drops any block holding a null that Ajv used to coerce, so surface the errors or allow null in the schema. Coercion reaches through $ref, allOf, if/then/else and unions. At a union a value that already matches a branch as written is left alone; otherwise each branch coerces it its own way, and the result is taken only when every branch that then accepts it agrees on it. So the answer does not depend on the order the union was written in, and a const false branch keeps false where Ajv turns it into the string false through an earlier string branch. A valid document comes back as the very same object. Where the caller cannot let Ajv mutate its input, so Ajv has to clone it first, coerceX runs 5–36× faster on valid input and 1.4–2.1× faster on input that needs coercing; where the caller lets Ajv coerce its document in place there is no clone to save, and on a large document Ajv can come out modestly ahead. Requires validators.</td>
|
|
466
476
|
</tr>
|
|
467
477
|
<tr>
|
|
468
478
|
<td>🔧 <code>repair</code></td>
|
|
@@ -480,7 +490,7 @@ usage error. Run `mjst markdown --help` for details.
|
|
|
480
490
|
<td align="center"><code>false</code></td>
|
|
481
491
|
</tr>
|
|
482
492
|
<tr>
|
|
483
|
-
<td colspan="4">Explain a failing anyOf/oneOf with the errors of the branch it meant, instead of the bare "must match a schema in anyOf" that names no field and no reason. The branch that rejected the value's kind is dropped first, so a string-or-object union is left with the one branch that was talking about this value; where several remain, a discriminator breaks the tie; where none stands out, nothing extra is reported. Off by default and off costs nothing — the generated code is exactly what it would be without the option. On, a combinator that passes costs what it costs with the option off, since its branches are tested by the same functions; only an anyOf/oneOf that has already failed asks its branches again for what each one complained about, so the price is paid on invalid input alone. Requires validators.</td>
|
|
493
|
+
<td colspan="4">Explain a failing anyOf/oneOf with the errors of the branch it meant, instead of the bare "must match a schema in anyOf" that names no field and no reason. The branch that rejected the value's kind is dropped first, so a string-or-object union is left with the one branch that was talking about this value; where several remain, a discriminator breaks the tie; where every branch rejected the value's kind, the kinds they accept are named instead ("must be object or boolean"); otherwise, where none stands out, nothing extra is reported. Off by default and off costs nothing — the generated code is exactly what it would be without the option. On, a combinator that passes costs what it costs with the option off, since its branches are tested by the same functions; only an anyOf/oneOf that has already failed asks its branches again for what each one complained about, so the price is paid on invalid input alone. Requires validators.</td>
|
|
484
494
|
</tr>
|
|
485
495
|
<tr>
|
|
486
496
|
<td>🎲 <code>examples</code></td>
|
|
@@ -615,7 +625,7 @@ usage error. Run `mjst markdown --help` for details.
|
|
|
615
625
|
<td align="center"><code>false</code></td>
|
|
616
626
|
</tr>
|
|
617
627
|
<tr>
|
|
618
|
-
<td colspan="4">Prepend a comment header to every generated file (excluding _helpers/). true uses the default message ('This file was auto-generated by @amritk/mjst…'); a string uses that text as the message body, wrapped in a JSDoc block; false or omitted emits no header. On the CLI, --banner alone enables the default message and --banner <text> sets a custom one.</td>
|
|
628
|
+
<td colspan="4">Prepend a comment header to every generated file (excluding _helpers/). true uses the default message ('This file was auto-generated by @amritk/mjst…'); a string uses that text as the message body, wrapped in a JSDoc block, unless every line of it is a // comment — then it is emitted as written, which is how to add a directive such as // @ts-nocheck that TypeScript only honors as a line comment; false or omitted emits no header. On the CLI, --banner alone enables the default message and --banner <text> sets a custom one.</td>
|
|
619
629
|
</tr>
|
|
620
630
|
<tr>
|
|
621
631
|
<td>🔗 <code>importExt</code></td>
|
package/dist/cli.js
CHANGED
|
@@ -14,6 +14,7 @@ import { loadConfig } from "./load-config.js";
|
|
|
14
14
|
import { parseCliArgs } from "./parse-cli-args.js";
|
|
15
15
|
import { parseMetaRequest } from "./parse-meta-request.js";
|
|
16
16
|
import { readVersion } from "./read-version.js";
|
|
17
|
+
import { resolveBanner } from "./resolve-banner.js";
|
|
17
18
|
const ROOT_TYPE_PATTERN = /^[A-Za-z_$][A-Za-z0-9_$]*$/;
|
|
18
19
|
const DISCRIMINATOR_PATTERN = /^[A-Za-z_$][\w$-]*$/;
|
|
19
20
|
const schemaBaseName = (schemaPath) => basename(schemaPath).replace(/\.[^.]+$/, "");
|
|
@@ -41,17 +42,6 @@ const extractConfigPath = (args) => {
|
|
|
41
42
|
}
|
|
42
43
|
return void 0;
|
|
43
44
|
};
|
|
44
|
-
const DEFAULT_BANNER_TEXT = "This file was auto-generated by @amritk/mjst.\n * Do not edit it manually \u2014 your changes will be overwritten on the next generation.";
|
|
45
|
-
const resolveBanner = (banner) => {
|
|
46
|
-
if (!banner)
|
|
47
|
-
return "";
|
|
48
|
-
const text = banner === true ? DEFAULT_BANNER_TEXT : banner;
|
|
49
|
-
return `/**
|
|
50
|
-
* ${text}
|
|
51
|
-
*/
|
|
52
|
-
|
|
53
|
-
`;
|
|
54
|
-
};
|
|
55
45
|
const commitOrDiscard = async (writer, stageAll) => {
|
|
56
46
|
try {
|
|
57
47
|
await stageAll();
|
package/dist/help-text.d.ts
CHANGED
|
@@ -4,4 +4,4 @@
|
|
|
4
4
|
* documents these flags (config.schema.json) is not shipped in the published
|
|
5
5
|
* package; the help-text test cross-checks that every flag is listed.
|
|
6
6
|
*/
|
|
7
|
-
export declare const HELP_TEXT = "mjst \u2014 generate TypeScript types, validators and parsers from JSON Schemas\n\nUsage:\n mjst --schema <path> --out-dir <dir> [options]\n mjst --schema-dir <dir> --out-dir <dir> [options]\n mjst --schema <path> --out-file <file> --types-only [options]\n\nSubcommands (each with its own flags and --help):\n mjst lint <files> Lint JSON/YAML documents against a ruleset\n mjst compile-api <module> --out <file> Compile @amritk/api route contracts into a fetch-handler module\n mjst markdown <schema> --out-dir <dir> Generate markdown documentation from a JSON Schema\n\nInput:\n --schema <path> Schema to process: a JSON Schema file, or a module when --input is set\n --schema-dir <dir> Directory of JSON Schemas, processed recursively (instead of --schema)\n --input <format> Schema source format: json (default), typebox, zod, valibot, effect, avro,\n or asyncapi (an AsyncAPI 2.x/3.0 document, JSON or YAML)\n --export <name> Which export of the schema module to use when --input names a module format\n\nOutput:\n --out-dir <dir> Output directory for the generated files\n --out-file <file> Single-file output instead of a directory (requires --types-only)\n --types-only Generate type definitions only, without parser functions\n --validators Also emit validateX/isX validators beside the parsers, over one\n shared declaration of each generated type\n --validators-only Emit the validator half and no parser: the type, isX, validateX and\n whichever of checkX/coerceX/repairX were asked for (implies\n --validators; the mirror of --types-only)\n --check Also emit checkX, the same result as validateX but stopped at the\n first violation (requires --validators)\n --coerce Also emit coerceX, which coerces scalars the way Ajv's coerceTypes\n does and then validates (requires --validators)\n --repair Also emit repairX, which coerces, validates, then repairs each\n rejected position to a schema-supplied value and reports the\n errors it repaired (implies --coerce; requires --validators)\n --branch-errors Explain a failing anyOf/oneOf with the errors of the branch it\n meant (requires --validators; costs a little on unions)\n --examples Also emit fast-check arbitraries + example values under examples/\n --message-contracts Also emit @amritk/api channel contracts under contracts/ (--input asyncapi only)\n --discriminator <p> Property naming which message a frame is, for --message-contracts (default: type)\n --build Compile the generated files to .js/.d.ts (implies --import-ext js)\n --force Deprecated; ignored (generated files always overwrite)\n --import-ext <ext> Extension on generated relative imports: ts (default) or js\n --helpers <mode> Runtime helpers: package or embedded (default: auto-detect from package.json)\n --root-type <name> Root type name for a single --schema run (default: schema title or filename)\n --type-suffix <s> Suffix appended to every $ref-derived type name\n --banner [text] Prepend a header comment to every generated file\n --readonly Emit deeply readonly type definitions\n\nValidation:\n --strict Throw on type/shape mismatches instead of coercing to defaults\n --strip-unknown Silently drop undeclared input keys at every nesting level\n --case-insensitive Normalize mis-cased enum/const strings to their exact casing\n --log-warnings console.warn on input keys not declared in the schema\n --unknown-keys <s> How closed objects are proven free of undeclared keys:\n count-keys (default, fastest on Bun) or count-enumerable (fastest on Node)\n --formats <f> String formats the generated validators enforce: \"all\", or a\n comma-separated list (e.g. uuid,date-time,email). Off by default,\n which leaves \"format\" an annotation as JSON Schema reads it.\n\nReference resolution:\n --resolve-remote Fetch http(s) $refs while resolving cross-file/remote refs (off by default)\n --allowed-hosts <h> Restrict remote $ref fetches to these hosts (comma-separated; implies --resolve-remote)\n --allow-private-hosts Permit remote $refs to private/loopback hosts (SSRF guard, off by default)\n --allowed-roots <d> Extra directories a local $ref may resolve into, on top of the schema's own\n (comma-separated; relative to the current directory)\n\nMisc:\n --config <path> JSON config file with the same keys; CLI flags take precedence\n --version, -v Print the CLI version\n --help, -h Print this help\n\nDocs: https://github.com/amritk/mjst/tree/main/packages/cli#readme\n";
|
|
7
|
+
export declare const HELP_TEXT = "mjst \u2014 generate TypeScript types, validators and parsers from JSON Schemas\n\nUsage:\n mjst --schema <path> --out-dir <dir> [options]\n mjst --schema-dir <dir> --out-dir <dir> [options]\n mjst --schema <path> --out-file <file> --types-only [options]\n\nSubcommands (each with its own flags and --help):\n mjst lint <files> Lint JSON/YAML documents against a ruleset\n mjst compile-api <module> --out <file> Compile @amritk/api route contracts into a fetch-handler module\n mjst markdown <schema> --out-dir <dir> Generate markdown documentation from a JSON Schema\n\nInput:\n --schema <path> Schema to process: a JSON Schema file, or a module when --input is set\n --schema-dir <dir> Directory of JSON Schemas, processed recursively (instead of --schema)\n --input <format> Schema source format: json (default), typebox, zod, valibot, effect, avro,\n or asyncapi (an AsyncAPI 2.x/3.0 document, JSON or YAML)\n --export <name> Which export of the schema module to use when --input names a module format\n\nOutput:\n --out-dir <dir> Output directory for the generated files\n --out-file <file> Single-file output instead of a directory (requires --types-only)\n --types-only Generate type definitions only, without parser functions\n --validators Also emit validateX/isX validators beside the parsers, over one\n shared declaration of each generated type\n --validators-only Emit the validator half and no parser: the type, isX, validateX and\n whichever of checkX/coerceX/repairX were asked for (implies\n --validators; the mirror of --types-only)\n --check Also emit checkX, the same result as validateX but stopped at the\n first violation (requires --validators)\n --coerce Also emit coerceX, which coerces scalars the way Ajv's coerceTypes\n does and then validates (requires --validators)\n --repair Also emit repairX, which coerces, validates, then repairs each\n rejected position to a schema-supplied value and reports the\n errors it repaired (implies --coerce; requires --validators)\n --branch-errors Explain a failing anyOf/oneOf with the errors of the branch it\n meant (requires --validators; costs a little on unions)\n --examples Also emit fast-check arbitraries + example values under examples/\n --message-contracts Also emit @amritk/api channel contracts under contracts/ (--input asyncapi only)\n --discriminator <p> Property naming which message a frame is, for --message-contracts (default: type)\n --build Compile the generated files to .js/.d.ts (implies --import-ext js)\n --force Deprecated; ignored (generated files always overwrite)\n --import-ext <ext> Extension on generated relative imports: ts (default) or js\n --helpers <mode> Runtime helpers: package or embedded (default: auto-detect from package.json)\n --root-type <name> Root type name for a single --schema run (default: schema title or filename)\n --type-suffix <s> Suffix appended to every $ref-derived type name\n --banner [text] Prepend a header comment to every generated file (text made of\n // lines is emitted as written, e.g. \"// @ts-nocheck\")\n --readonly Emit deeply readonly type definitions\n\nValidation:\n --strict Throw on type/shape mismatches instead of coercing to defaults\n --strip-unknown Silently drop undeclared input keys at every nesting level\n --case-insensitive Normalize mis-cased enum/const strings to their exact casing\n --log-warnings console.warn on input keys not declared in the schema\n --unknown-keys <s> How closed objects are proven free of undeclared keys:\n count-keys (default, fastest on Bun) or count-enumerable (fastest on Node)\n --formats <f> String formats the generated validators enforce: \"all\", or a\n comma-separated list (e.g. uuid,date-time,email). Off by default,\n which leaves \"format\" an annotation as JSON Schema reads it.\n\nReference resolution:\n --resolve-remote Fetch http(s) $refs while resolving cross-file/remote refs (off by default)\n --allowed-hosts <h> Restrict remote $ref fetches to these hosts (comma-separated; implies --resolve-remote)\n --allow-private-hosts Permit remote $refs to private/loopback hosts (SSRF guard, off by default)\n --allowed-roots <d> Extra directories a local $ref may resolve into, on top of the schema's own\n (comma-separated; relative to the current directory)\n\nMisc:\n --config <path> JSON config file with the same keys; CLI flags take precedence\n --version, -v Print the CLI version\n --help, -h Print this help\n\nDocs: https://github.com/amritk/mjst/tree/main/packages/cli#readme\n";
|
package/dist/help-text.js
CHANGED
|
@@ -44,7 +44,8 @@ Output:
|
|
|
44
44
|
--helpers <mode> Runtime helpers: package or embedded (default: auto-detect from package.json)
|
|
45
45
|
--root-type <name> Root type name for a single --schema run (default: schema title or filename)
|
|
46
46
|
--type-suffix <s> Suffix appended to every $ref-derived type name
|
|
47
|
-
--banner [text] Prepend a header comment to every generated file
|
|
47
|
+
--banner [text] Prepend a header comment to every generated file (text made of
|
|
48
|
+
// lines is emitted as written, e.g. "// @ts-nocheck")
|
|
48
49
|
--readonly Emit deeply readonly type definitions
|
|
49
50
|
|
|
50
51
|
Validation:
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Turns the `banner` option into the header prepended to each generated file.
|
|
3
|
+
*
|
|
4
|
+
* A message is wrapped in a JSDoc block, which reads well and is what most
|
|
5
|
+
* headers want. Text made only of `//` lines is emitted as written,
|
|
6
|
+
* because some headers are directives rather than prose, and only the
|
|
7
|
+
* line-comment form counts as one: TypeScript ignores a `@ts-nocheck` inside
|
|
8
|
+
* a block comment, so `--banner "// @ts-nocheck"` was the one header a strict
|
|
9
|
+
* project needed and the one the wrapping made impossible.
|
|
10
|
+
*/
|
|
11
|
+
export declare const resolveBanner: (banner: boolean | string | undefined) => string;
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
const DEFAULT_BANNER_TEXT = "This file was auto-generated by @amritk/mjst.\n * Do not edit it manually \u2014 your changes will be overwritten on the next generation.";
|
|
2
|
+
const isLineComments = (text) => {
|
|
3
|
+
const lines = text.split("\n").filter((line) => line.trim() !== "");
|
|
4
|
+
return lines.length > 0 && lines.every((line) => line.trimStart().startsWith("//"));
|
|
5
|
+
};
|
|
6
|
+
const resolveBanner = (banner) => {
|
|
7
|
+
if (!banner)
|
|
8
|
+
return "";
|
|
9
|
+
const text = banner === true ? DEFAULT_BANNER_TEXT : banner;
|
|
10
|
+
if (isLineComments(text))
|
|
11
|
+
return `${text.trim()}
|
|
12
|
+
|
|
13
|
+
`;
|
|
14
|
+
return `/**
|
|
15
|
+
* ${text}
|
|
16
|
+
*/
|
|
17
|
+
|
|
18
|
+
`;
|
|
19
|
+
};
|
|
20
|
+
export {
|
|
21
|
+
resolveBanner
|
|
22
|
+
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@amritk/mjst",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.27.0",
|
|
4
4
|
"description": "Generate TypeScript parsers and type definitions from JSON Schemas.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"engines": {
|
|
@@ -49,14 +49,14 @@
|
|
|
49
49
|
"./package.json": "./package.json"
|
|
50
50
|
},
|
|
51
51
|
"dependencies": {
|
|
52
|
-
"@amritk/adapters": "^0.6.
|
|
53
|
-
"@amritk/api": "^0.16.
|
|
54
|
-
"@amritk/asyncapi": "^0.3.
|
|
55
|
-
"@amritk/generate-examples": "^0.8.
|
|
52
|
+
"@amritk/adapters": "^0.6.9",
|
|
53
|
+
"@amritk/api": "^0.16.7",
|
|
54
|
+
"@amritk/asyncapi": "^0.3.7",
|
|
55
|
+
"@amritk/generate-examples": "^0.8.11",
|
|
56
56
|
"@amritk/generate-markdown": "^0.12.0",
|
|
57
|
-
"@amritk/helpers": "^0.
|
|
58
|
-
"@amritk/lint": "^0.7.
|
|
59
|
-
"@amritk/validation": "^0.
|
|
57
|
+
"@amritk/helpers": "^0.25.0",
|
|
58
|
+
"@amritk/lint": "^0.7.1",
|
|
59
|
+
"@amritk/validation": "^0.5.0",
|
|
60
60
|
"@amritk/resolve-refs": "^0.9.0",
|
|
61
61
|
"@amritk/yaml": "^0.8.0",
|
|
62
62
|
"fast-glob": "^3.3.2",
|