@amritk/mjst 0.12.3 → 0.13.1
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 +28 -1
- package/dist/cli.js +9 -0
- package/dist/compile-api/collect-route-contracts.d.ts +9 -0
- package/dist/compile-api/collect-route-contracts.js +15 -0
- package/dist/compile-api/default-routes-import.d.ts +9 -0
- package/dist/compile-api/default-routes-import.js +8 -0
- package/dist/compile-api/help-text.d.ts +6 -0
- package/dist/compile-api/help-text.js +27 -0
- package/dist/compile-api/load-routes-module.d.ts +8 -0
- package/dist/compile-api/load-routes-module.js +13 -0
- package/dist/compile-api/parse-compile-api-args.d.ts +25 -0
- package/dist/compile-api/parse-compile-api-args.js +73 -0
- package/dist/compile-api/read-compile-options.d.ts +9 -0
- package/dist/compile-api/read-compile-options.js +24 -0
- package/dist/compile-api/run.d.ts +9 -0
- package/dist/compile-api/run.js +75 -0
- package/dist/help-text.d.ts +1 -1
- package/dist/help-text.js +4 -0
- package/package.json +2 -1
package/README.md
CHANGED
|
@@ -21,7 +21,7 @@
|
|
|
21
21
|
|
|
22
22
|
Options can be supplied via CLI flags or a JSON config file. **CLI flags always take precedence over config file values.**
|
|
23
23
|
|
|
24
|
-
It also carries a `lint` subcommand — `mjst lint <files>` — a format-agnostic JSON/YAML style-guide linter powered by [`@amritk/lint`](../lint)
|
|
24
|
+
It also carries a `lint` subcommand — `mjst lint <files>` — a format-agnostic JSON/YAML style-guide linter powered by [`@amritk/lint`](../lint) (see [Linting](#linting)), and a `compile-api` subcommand — `mjst compile-api <routes-module> --out <file>` — that compiles [`@amritk/api`](../api) route contracts into a production fetch-handler module (see [Compiling an API](#compiling-an-api)).
|
|
25
25
|
|
|
26
26
|
---
|
|
27
27
|
|
|
@@ -219,6 +219,33 @@ The exit code is `0` when no finding reaches `--fail-severity`, `1` when one doe
|
|
|
219
219
|
|
|
220
220
|
---
|
|
221
221
|
|
|
222
|
+
## Compiling an API
|
|
223
|
+
|
|
224
|
+
`mjst compile-api` compiles [`@amritk/api`](../api) route contracts into a fused fetch-handler module — the production counterpart to `createApi`, with inlined guards, schema-derived serializers, and a precomputed OpenAPI document. It wraps the library's `compileToModule` so you do not have to hand-write a build script. Like `lint`, it has its own flags (and its own `--help`), independent of the generation flags above.
|
|
225
|
+
|
|
226
|
+
```bash
|
|
227
|
+
# Compile every route contract exported by the routes module
|
|
228
|
+
npx mjst compile-api src/routes.ts --out dist/handler.js
|
|
229
|
+
|
|
230
|
+
# Wire in the exports-by-name options (context factory, mounts, OpenAPI info, ...)
|
|
231
|
+
npx mjst compile-api src/routes.ts --out dist/handler.js --options compile-options.json
|
|
232
|
+
```
|
|
233
|
+
|
|
234
|
+
Every export of the routes module that looks like a route contract (`method`, `path`, `responses`) is compiled; the generated module imports those contracts back through `--routes-import` (default: the relative path from the out file to the routes module). The routes module is imported at build time, so it must be loadable by the runtime running mjst — run via `bunx` for TypeScript sources, or under Node install a loader such as `tsx` (`node --import tsx`).
|
|
235
|
+
|
|
236
|
+
| Flag | Description |
|
|
237
|
+
| --- | --- |
|
|
238
|
+
| `<routes-module>` | Module exporting the route contracts (positional). |
|
|
239
|
+
| `--out` | Output file for the generated module (required; parent directories are created). |
|
|
240
|
+
| `--routes-import` | Import specifier the generated module uses for the routes module. Defaults to the relative path from the out file. |
|
|
241
|
+
| `--options` | JSON file spread into the `compileToModule` options — `contextExport`, `mounts`, `info`, `servers`, `security`, `securitySchemes`, and friends. Flags take precedence. |
|
|
242
|
+
| `--open-api-path` | Serve the precomputed OpenAPI JSON at this path (default `/openapi.json`). |
|
|
243
|
+
| `--max-body-bytes` | Reject request bodies larger than this many bytes with a 413 (default `1048576`; `Infinity` disables the cap). |
|
|
244
|
+
|
|
245
|
+
The exit code is `0` on success, `1` when compilation fails (unloadable module, no contracts found, invalid options JSON), and `2` on a usage error. Run `mjst compile-api --help` for details.
|
|
246
|
+
|
|
247
|
+
---
|
|
248
|
+
|
|
222
249
|
## Configuration reference
|
|
223
250
|
|
|
224
251
|
<!-- config-table-start -->
|
package/dist/cli.js
CHANGED
|
@@ -244,6 +244,15 @@ const run = async () => {
|
|
|
244
244
|
process.stderr.write(result.stderr);
|
|
245
245
|
process.exit(result.code);
|
|
246
246
|
}
|
|
247
|
+
if (args[0] === "compile-api") {
|
|
248
|
+
const { run: compileApiRun } = await import("./compile-api/run.js");
|
|
249
|
+
const result = await compileApiRun(args.slice(1));
|
|
250
|
+
if (result.stdout)
|
|
251
|
+
process.stdout.write(result.stdout);
|
|
252
|
+
if (result.stderr)
|
|
253
|
+
process.stderr.write(result.stderr);
|
|
254
|
+
process.exit(result.code);
|
|
255
|
+
}
|
|
247
256
|
if (isVersionRequest(args)) {
|
|
248
257
|
console.log(await readVersion());
|
|
249
258
|
return;
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { AnyRouteContract } from '@amritk/api';
|
|
2
|
+
/**
|
|
3
|
+
* Picks the route contracts out of a loaded routes module, keyed by export
|
|
4
|
+
* name — exactly the record `compileToModule` expects, since the generated
|
|
5
|
+
* module imports each contract back by that name. Non-contract exports
|
|
6
|
+
* (context factories, error formatters, hooks) are left alone; they are wired
|
|
7
|
+
* in by name through the compile options instead.
|
|
8
|
+
*/
|
|
9
|
+
export declare const collectRouteContracts: (moduleExports: Record<string, unknown>) => Record<string, AnyRouteContract>;
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
const isRecord = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
|
|
2
|
+
const isRouteContract = (value) => isRecord(value) && typeof value["method"] === "string" && typeof value["path"] === "string" && isRecord(value["responses"]);
|
|
3
|
+
const collectRouteContracts = (moduleExports) => {
|
|
4
|
+
const routes = {};
|
|
5
|
+
for (const [name, value] of Object.entries(moduleExports)) {
|
|
6
|
+
if (name === "default")
|
|
7
|
+
continue;
|
|
8
|
+
if (isRouteContract(value))
|
|
9
|
+
routes[name] = value;
|
|
10
|
+
}
|
|
11
|
+
return routes;
|
|
12
|
+
};
|
|
13
|
+
export {
|
|
14
|
+
collectRouteContracts
|
|
15
|
+
};
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The default `routesImport` specifier: the relative path from the out file's
|
|
3
|
+
* directory to the routes module. The module's real on-disk extension is kept
|
|
4
|
+
* — same convention as the generator's default `--import-ext ts` (see
|
|
5
|
+
* resolve-import-ext.ts): literal paths load under Bun, Node's type
|
|
6
|
+
* stripping, and bundlers alike, whereas an extensionless specifier only
|
|
7
|
+
* resolves through a bundler.
|
|
8
|
+
*/
|
|
9
|
+
export declare const defaultRoutesImport: (outFile: string, routesModulePath: string) => string;
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { dirname, relative, sep } from "node:path";
|
|
2
|
+
const defaultRoutesImport = (outFile, routesModulePath) => {
|
|
3
|
+
const specifier = relative(dirname(outFile), routesModulePath).split(sep).join("/");
|
|
4
|
+
return specifier.startsWith(".") ? specifier : `./${specifier}`;
|
|
5
|
+
};
|
|
6
|
+
export {
|
|
7
|
+
defaultRoutesImport
|
|
8
|
+
};
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Usage text for `mjst compile-api --help`. Hand-written like the top-level
|
|
3
|
+
* HELP_TEXT: the flag list is small and stable, and the test cross-checks that
|
|
4
|
+
* every flag is listed.
|
|
5
|
+
*/
|
|
6
|
+
export declare const COMPILE_API_HELP_TEXT = "mjst compile-api \u2014 compile @amritk/api route contracts into a fused fetch-handler module\n\nUsage:\n mjst compile-api <routes-module> --out <file> [options]\n\nThe routes module is imported at build time and every export that looks like a\nroute contract (method, path, responses) becomes a compiled route. The module\nmust be loadable by the runtime that runs mjst \u2014 run via bunx for TypeScript\nsources, or under Node install a loader such as tsx (node --import tsx).\n\nOptions:\n --out <file> Output file for the generated module (required; parent dirs are created)\n --routes-import <spec> Import specifier the generated module uses to import the routes\n (default: the relative path from the out file to the routes module)\n --options <json-file> JSON file spread into the compileToModule options for everything\n not expressible as a flag: contextExport, mounts, info, servers,\n security, securitySchemes, errorsExport, onErrorExport, ...\n Flags take precedence over the file.\n --open-api-path <path> Serve the precomputed OpenAPI JSON at this path (default: /openapi.json)\n --max-body-bytes <n> Reject larger request bodies with a 413 (default: 1048576; Infinity disables)\n --help, -h Print this help\n\nDocs: https://github.com/amritk/mjst/tree/main/packages/cli#readme\n";
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
const COMPILE_API_HELP_TEXT = `mjst compile-api \u2014 compile @amritk/api route contracts into a fused fetch-handler module
|
|
2
|
+
|
|
3
|
+
Usage:
|
|
4
|
+
mjst compile-api <routes-module> --out <file> [options]
|
|
5
|
+
|
|
6
|
+
The routes module is imported at build time and every export that looks like a
|
|
7
|
+
route contract (method, path, responses) becomes a compiled route. The module
|
|
8
|
+
must be loadable by the runtime that runs mjst \u2014 run via bunx for TypeScript
|
|
9
|
+
sources, or under Node install a loader such as tsx (node --import tsx).
|
|
10
|
+
|
|
11
|
+
Options:
|
|
12
|
+
--out <file> Output file for the generated module (required; parent dirs are created)
|
|
13
|
+
--routes-import <spec> Import specifier the generated module uses to import the routes
|
|
14
|
+
(default: the relative path from the out file to the routes module)
|
|
15
|
+
--options <json-file> JSON file spread into the compileToModule options for everything
|
|
16
|
+
not expressible as a flag: contextExport, mounts, info, servers,
|
|
17
|
+
security, securitySchemes, errorsExport, onErrorExport, ...
|
|
18
|
+
Flags take precedence over the file.
|
|
19
|
+
--open-api-path <path> Serve the precomputed OpenAPI JSON at this path (default: /openapi.json)
|
|
20
|
+
--max-body-bytes <n> Reject larger request bodies with a 413 (default: 1048576; Infinity disables)
|
|
21
|
+
--help, -h Print this help
|
|
22
|
+
|
|
23
|
+
Docs: https://github.com/amritk/mjst/tree/main/packages/cli#readme
|
|
24
|
+
`;
|
|
25
|
+
export {
|
|
26
|
+
COMPILE_API_HELP_TEXT
|
|
27
|
+
};
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Imports the user's routes module and returns the full export record —
|
|
3
|
+
* compile-api needs every export (each one is a route-contract candidate), so
|
|
4
|
+
* it cannot reuse loadSchemaModule, which selects a single export. The import
|
|
5
|
+
* mechanics and the friendly `.ts` hint mirror load-schema-module.ts: a `.ts`
|
|
6
|
+
* module only loads under a TypeScript-capable runtime.
|
|
7
|
+
*/
|
|
8
|
+
export declare const loadRoutesModule: (modulePath: string) => Promise<Record<string, unknown>>;
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { pathToFileURL } from "node:url";
|
|
2
|
+
const loadRoutesModule = async (modulePath) => {
|
|
3
|
+
try {
|
|
4
|
+
return await import(pathToFileURL(modulePath).href);
|
|
5
|
+
} catch (error) {
|
|
6
|
+
const hint = modulePath.endsWith(".ts") ? " Importing a .ts routes module requires a TypeScript-capable runtime \u2014 run mjst via `bunx`, or install `tsx` and run Node with `--import tsx`." : "";
|
|
7
|
+
throw new Error(`Failed to load routes module at ${modulePath}.${hint}
|
|
8
|
+
${String(error)}`);
|
|
9
|
+
}
|
|
10
|
+
};
|
|
11
|
+
export {
|
|
12
|
+
loadRoutesModule
|
|
13
|
+
};
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/** The flags and positional the `compile-api` subcommand understands. */
|
|
2
|
+
export type CompileApiArgs = {
|
|
3
|
+
/** Path to the module exporting the route contracts (the sole positional). */
|
|
4
|
+
routesModule?: string;
|
|
5
|
+
/** Output file for the generated module (`--out`). */
|
|
6
|
+
out?: string;
|
|
7
|
+
/** Import specifier override for the routes module (`--routes-import`). */
|
|
8
|
+
routesImport?: string;
|
|
9
|
+
/** Path to a JSON file spread into the compile options (`--options`). */
|
|
10
|
+
optionsFile?: string;
|
|
11
|
+
/** OpenAPI document path override (`--open-api-path`). */
|
|
12
|
+
openApiPath?: string;
|
|
13
|
+
/** Request body size cap override (`--max-body-bytes`). */
|
|
14
|
+
maxBodyBytes?: number;
|
|
15
|
+
/** True when `--help`/`-h` was passed. */
|
|
16
|
+
help?: boolean;
|
|
17
|
+
};
|
|
18
|
+
/**
|
|
19
|
+
* Parses the `compile-api` argv into a typed args object. Mirrors the main
|
|
20
|
+
* CLI parser's conventions: kebab-case and camelCase flag spellings, both
|
|
21
|
+
* `--flag value` and `--flag=value`, and a loud error on unknown flags so a
|
|
22
|
+
* typo cannot silently drop an option. Throws on usage errors; the caller
|
|
23
|
+
* turns that into the exit-code-2 path.
|
|
24
|
+
*/
|
|
25
|
+
export declare const parseCompileApiArgs: (argv: readonly string[]) => CompileApiArgs;
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
const VALUE_KEYS = /* @__PURE__ */ new Set(["out", "routesImport", "options", "openApiPath", "maxBodyBytes"]);
|
|
2
|
+
const toCamelCase = (key) => key.replace(/-([a-z])/g, (_, c) => c.toUpperCase());
|
|
3
|
+
const parseMaxBodyBytes = (value) => {
|
|
4
|
+
if (value.toLowerCase() === "infinity")
|
|
5
|
+
return Number.POSITIVE_INFINITY;
|
|
6
|
+
const parsed = Number(value);
|
|
7
|
+
if (!Number.isFinite(parsed) || parsed < 0) {
|
|
8
|
+
throw new Error(`Invalid --max-body-bytes value "${value}". Expected a non-negative number or Infinity.`);
|
|
9
|
+
}
|
|
10
|
+
return parsed;
|
|
11
|
+
};
|
|
12
|
+
const assignValue = (args, key, value) => {
|
|
13
|
+
switch (key) {
|
|
14
|
+
case "out":
|
|
15
|
+
args.out = value;
|
|
16
|
+
return;
|
|
17
|
+
case "routesImport":
|
|
18
|
+
args.routesImport = value;
|
|
19
|
+
return;
|
|
20
|
+
case "options":
|
|
21
|
+
args.optionsFile = value;
|
|
22
|
+
return;
|
|
23
|
+
case "openApiPath":
|
|
24
|
+
args.openApiPath = value;
|
|
25
|
+
return;
|
|
26
|
+
case "maxBodyBytes":
|
|
27
|
+
args.maxBodyBytes = parseMaxBodyBytes(value);
|
|
28
|
+
return;
|
|
29
|
+
default:
|
|
30
|
+
throw new Error(`Unknown flag "--${key}".`);
|
|
31
|
+
}
|
|
32
|
+
};
|
|
33
|
+
const parseCompileApiArgs = (argv) => {
|
|
34
|
+
const args = {};
|
|
35
|
+
for (let i = 0; i < argv.length; i++) {
|
|
36
|
+
const arg = argv[i];
|
|
37
|
+
if (arg === void 0)
|
|
38
|
+
continue;
|
|
39
|
+
if (arg === "--help" || arg === "-h") {
|
|
40
|
+
args.help = true;
|
|
41
|
+
continue;
|
|
42
|
+
}
|
|
43
|
+
if (!arg.startsWith("--")) {
|
|
44
|
+
if (args.routesModule !== void 0) {
|
|
45
|
+
throw new Error(`Unexpected argument "${arg}". compile-api takes a single routes module path.`);
|
|
46
|
+
}
|
|
47
|
+
args.routesModule = arg;
|
|
48
|
+
continue;
|
|
49
|
+
}
|
|
50
|
+
const equalsIndex = arg.indexOf("=");
|
|
51
|
+
if (equalsIndex !== -1) {
|
|
52
|
+
const key2 = toCamelCase(arg.slice(2, equalsIndex));
|
|
53
|
+
if (!VALUE_KEYS.has(key2))
|
|
54
|
+
throw new Error(`Unknown flag "--${arg.slice(2, equalsIndex)}".`);
|
|
55
|
+
assignValue(args, key2, arg.slice(equalsIndex + 1));
|
|
56
|
+
continue;
|
|
57
|
+
}
|
|
58
|
+
const flagName = arg.slice(2);
|
|
59
|
+
const key = toCamelCase(flagName);
|
|
60
|
+
if (!VALUE_KEYS.has(key))
|
|
61
|
+
throw new Error(`Unknown flag "--${flagName}".`);
|
|
62
|
+
const value = argv[i + 1];
|
|
63
|
+
if (value === void 0 || value.startsWith("--")) {
|
|
64
|
+
throw new Error(`Flag "--${flagName}" expects a value.`);
|
|
65
|
+
}
|
|
66
|
+
assignValue(args, key, value);
|
|
67
|
+
i++;
|
|
68
|
+
}
|
|
69
|
+
return args;
|
|
70
|
+
};
|
|
71
|
+
export {
|
|
72
|
+
parseCompileApiArgs
|
|
73
|
+
};
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { CompileModuleOptions } from '@amritk/api';
|
|
2
|
+
/**
|
|
3
|
+
* Reads the `--options` JSON file: everything `compileToModule` accepts that
|
|
4
|
+
* has no dedicated flag (contextExport, mounts, info, servers, security,
|
|
5
|
+
* securitySchemes, hook exports, ...) rides in here and is spread into the
|
|
6
|
+
* compile options. Throws with the file path on unreadable or invalid input
|
|
7
|
+
* so the CLI can report exactly which file to fix.
|
|
8
|
+
*/
|
|
9
|
+
export declare const readCompileOptions: (optionsPath: string) => Promise<Partial<CompileModuleOptions>>;
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { readFile } from "node:fs/promises";
|
|
2
|
+
const readCompileOptions = async (optionsPath) => {
|
|
3
|
+
let raw;
|
|
4
|
+
try {
|
|
5
|
+
raw = await readFile(optionsPath, "utf-8");
|
|
6
|
+
} catch (error) {
|
|
7
|
+
throw new Error(`Failed to read --options file at ${optionsPath}.
|
|
8
|
+
${String(error)}`);
|
|
9
|
+
}
|
|
10
|
+
let parsed;
|
|
11
|
+
try {
|
|
12
|
+
parsed = JSON.parse(raw);
|
|
13
|
+
} catch (error) {
|
|
14
|
+
throw new Error(`Invalid JSON in --options file at ${optionsPath}.
|
|
15
|
+
${String(error)}`);
|
|
16
|
+
}
|
|
17
|
+
if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed)) {
|
|
18
|
+
throw new Error(`Invalid --options file at ${optionsPath}: expected a JSON object of compileToModule options.`);
|
|
19
|
+
}
|
|
20
|
+
return parsed;
|
|
21
|
+
};
|
|
22
|
+
export {
|
|
23
|
+
readCompileOptions
|
|
24
|
+
};
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { RunResult } from '../lint/run.js';
|
|
2
|
+
/**
|
|
3
|
+
* Runs `mjst compile-api` over `argv`: loads the routes module, collects its
|
|
4
|
+
* route-contract exports, compiles them with `compileToModule`, and writes
|
|
5
|
+
* the emitted module to `--out`. Returns the exit code and the text it would
|
|
6
|
+
* print (rather than writing to the process streams) so tests can drive it
|
|
7
|
+
* in-process — the same shape as the lint subcommand's `run`.
|
|
8
|
+
*/
|
|
9
|
+
export declare const run: (argv: string[]) => Promise<RunResult>;
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import { mkdir, writeFile } from "node:fs/promises";
|
|
2
|
+
import { dirname, relative, resolve } from "node:path";
|
|
3
|
+
import { compileToModule } from "@amritk/api";
|
|
4
|
+
import { collectRouteContracts } from "./collect-route-contracts.js";
|
|
5
|
+
import { defaultRoutesImport } from "./default-routes-import.js";
|
|
6
|
+
import { COMPILE_API_HELP_TEXT } from "./help-text.js";
|
|
7
|
+
import { loadRoutesModule } from "./load-routes-module.js";
|
|
8
|
+
import { parseCompileApiArgs } from "./parse-compile-api-args.js";
|
|
9
|
+
import { readCompileOptions } from "./read-compile-options.js";
|
|
10
|
+
const usageError = (message) => ({ code: 2, stdout: "", stderr: `Error: ${message}
|
|
11
|
+
` });
|
|
12
|
+
const failure = (message) => ({ code: 1, stdout: "", stderr: `Error: ${message}
|
|
13
|
+
` });
|
|
14
|
+
const run = async (argv) => {
|
|
15
|
+
let args;
|
|
16
|
+
try {
|
|
17
|
+
args = parseCompileApiArgs(argv);
|
|
18
|
+
} catch (error) {
|
|
19
|
+
return usageError(error instanceof Error ? error.message : String(error));
|
|
20
|
+
}
|
|
21
|
+
if (args.help || argv.length === 0) {
|
|
22
|
+
return { code: 0, stdout: COMPILE_API_HELP_TEXT, stderr: "" };
|
|
23
|
+
}
|
|
24
|
+
if (args.routesModule === void 0) {
|
|
25
|
+
return usageError("A routes module path is required. Usage: mjst compile-api <routes-module> --out <file>");
|
|
26
|
+
}
|
|
27
|
+
if (args.out === void 0) {
|
|
28
|
+
return usageError("--out is required. Provide an output file for the generated module.");
|
|
29
|
+
}
|
|
30
|
+
const modulePath = resolve(args.routesModule);
|
|
31
|
+
let moduleExports;
|
|
32
|
+
try {
|
|
33
|
+
moduleExports = await loadRoutesModule(modulePath);
|
|
34
|
+
} catch (error) {
|
|
35
|
+
return failure(error instanceof Error ? error.message : String(error));
|
|
36
|
+
}
|
|
37
|
+
const routes = collectRouteContracts(moduleExports);
|
|
38
|
+
const routeNames = Object.keys(routes);
|
|
39
|
+
if (routeNames.length === 0) {
|
|
40
|
+
return failure(`No route contracts found in ${modulePath}. A route contract is a named export declaring method, path, and responses.`);
|
|
41
|
+
}
|
|
42
|
+
let fileOptions = {};
|
|
43
|
+
if (args.optionsFile !== void 0) {
|
|
44
|
+
try {
|
|
45
|
+
fileOptions = await readCompileOptions(resolve(args.optionsFile));
|
|
46
|
+
} catch (error) {
|
|
47
|
+
return failure(error instanceof Error ? error.message : String(error));
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
const outFile = resolve(args.out);
|
|
51
|
+
const routesImport = args.routesImport ?? defaultRoutesImport(outFile, modulePath);
|
|
52
|
+
let source;
|
|
53
|
+
try {
|
|
54
|
+
source = compileToModule({
|
|
55
|
+
...fileOptions,
|
|
56
|
+
...args.openApiPath !== void 0 ? { openApiPath: args.openApiPath } : {},
|
|
57
|
+
...args.maxBodyBytes !== void 0 ? { maxBodyBytes: args.maxBodyBytes } : {},
|
|
58
|
+
routesImport,
|
|
59
|
+
routes
|
|
60
|
+
});
|
|
61
|
+
} catch (error) {
|
|
62
|
+
return failure(error instanceof Error ? error.message : String(error));
|
|
63
|
+
}
|
|
64
|
+
await mkdir(dirname(outFile), { recursive: true });
|
|
65
|
+
await writeFile(outFile, source, "utf-8");
|
|
66
|
+
const stdout = [
|
|
67
|
+
`Compiled ${routeNames.length} route(s): ${routeNames.join(", ")}`,
|
|
68
|
+
`Generated: ${relative(process.cwd(), outFile)}`,
|
|
69
|
+
""
|
|
70
|
+
].join("\n");
|
|
71
|
+
return { code: 0, stdout, stderr: "" };
|
|
72
|
+
};
|
|
73
|
+
export {
|
|
74
|
+
run
|
|
75
|
+
};
|
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 parsers and type definitions 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\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\n --export <name> Which export of the schema module to use when --input is not json\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 into a validators/ subdir\n --examples Also emit fast-check arbitraries + example values under examples/\n --build Compile the generated files to .js/.d.ts (implies --import-ext js)\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\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\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 parsers and type definitions 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\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\n --export <name> Which export of the schema module to use when --input is not json\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 into a validators/ subdir\n --examples Also emit fast-check arbitraries + example values under examples/\n --build Compile the generated files to .js/.d.ts (implies --import-ext js)\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\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\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
|
@@ -5,6 +5,10 @@ Usage:
|
|
|
5
5
|
mjst --schema-dir <dir> --out-dir <dir> [options]
|
|
6
6
|
mjst --schema <path> --out-file <file> --types-only [options]
|
|
7
7
|
|
|
8
|
+
Subcommands (each with its own flags and --help):
|
|
9
|
+
mjst lint <files> Lint JSON/YAML documents against a ruleset
|
|
10
|
+
mjst compile-api <module> --out <file> Compile @amritk/api route contracts into a fetch-handler module
|
|
11
|
+
|
|
8
12
|
Input:
|
|
9
13
|
--schema <path> Schema to process: a JSON Schema file, or a module when --input is set
|
|
10
14
|
--schema-dir <dir> Directory of JSON Schemas, processed recursively (instead of --schema)
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@amritk/mjst",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.13.1",
|
|
4
4
|
"description": "Generate TypeScript parsers and type definitions from JSON Schemas.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
@@ -42,6 +42,7 @@
|
|
|
42
42
|
},
|
|
43
43
|
"dependencies": {
|
|
44
44
|
"@amritk/adapters": "0.3.3",
|
|
45
|
+
"@amritk/api": "0.6.0",
|
|
45
46
|
"@amritk/generate-examples": "0.5.3",
|
|
46
47
|
"@amritk/generate-parsers": "0.17.0",
|
|
47
48
|
"@amritk/generate-validators": "0.11.9",
|