@znight/openapi 0.0.0-stage → 0.1.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/LICENSE +21 -0
- package/README.md +572 -2
- package/dist/build-BTUC96mY.d.mts +174 -0
- package/dist/build-DXunOzlI.mjs +733 -0
- package/dist/build-DXunOzlI.mjs.map +1 -0
- package/dist/compiler.d.mts +16 -0
- package/dist/compiler.mjs +2639 -0
- package/dist/compiler.mjs.map +1 -0
- package/dist/emit-child.d.mts +1 -0
- package/dist/emit-child.mjs +124 -0
- package/dist/emit-child.mjs.map +1 -0
- package/dist/emit.d.mts +2 -0
- package/dist/emit.mjs +252 -0
- package/dist/emit.mjs.map +1 -0
- package/dist/index-CqQ6IRnN.d.mts +60 -0
- package/dist/index.d.mts +260 -0
- package/dist/index.mjs +777 -0
- package/dist/index.mjs.map +1 -0
- package/dist/openapi-CQk5dHqc.mjs +39 -0
- package/dist/openapi-CQk5dHqc.mjs.map +1 -0
- package/dist/shared-BZxtyaB0.mjs +147 -0
- package/dist/shared-BZxtyaB0.mjs.map +1 -0
- package/dist/testing.d.mts +39 -0
- package/dist/testing.mjs +155 -0
- package/dist/testing.mjs.map +1 -0
- package/dist/types-VzBexMKj.d.mts +213 -0
- package/dist/vite.d.mts +24 -0
- package/dist/vite.mjs +64 -0
- package/dist/vite.mjs.map +1 -0
- package/package.json +115 -4
- package/src/build.ts +898 -0
- package/src/compiler/analyze.ts +81 -0
- package/src/compiler/document.ts +612 -0
- package/src/compiler/generate.ts +79 -0
- package/src/compiler/index.ts +47 -0
- package/src/compiler/model.ts +141 -0
- package/src/compiler/operations.ts +1475 -0
- package/src/compiler/schemas.ts +706 -0
- package/src/compiler/validate.ts +102 -0
- package/src/compiler/values.ts +81 -0
- package/src/converters.ts +137 -0
- package/src/decorators.ts +187 -0
- package/src/emit/child.ts +144 -0
- package/src/emit/index.ts +377 -0
- package/src/emit/protocol.ts +35 -0
- package/src/index.ts +94 -0
- package/src/metadata.ts +112 -0
- package/src/openapi.ts +55 -0
- package/src/plugin.ts +810 -0
- package/src/shared.ts +154 -0
- package/src/testing.ts +224 -0
- package/src/types.ts +240 -0
- package/src/ui.ts +215 -0
- package/src/vite.ts +126 -0
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
import { n as buildOpenApiDocument } from "./build-DXunOzlI.mjs";
|
|
2
|
+
import { createRequire } from "node:module";
|
|
3
|
+
import { pathToFileURL } from "node:url";
|
|
4
|
+
import { readFileSync, writeFileSync } from "node:fs";
|
|
5
|
+
import { join } from "node:path";
|
|
6
|
+
// src/emit/child.ts
|
|
7
|
+
const payload = JSON.parse(readFileSync(process.argv[2] ?? "", "utf8"));
|
|
8
|
+
/** Imports a package as installed in the project (falls back to `@znight/openapi`'s resolution). */
|
|
9
|
+
function importFromProject(specifier) {
|
|
10
|
+
let resolved;
|
|
11
|
+
try {
|
|
12
|
+
resolved = createRequire(join(payload.root, "package.json")).resolve(specifier);
|
|
13
|
+
} catch {
|
|
14
|
+
resolved = void 0;
|
|
15
|
+
}
|
|
16
|
+
return resolved === void 0 ? import(specifier) : import(pathToFileURL(resolved).href);
|
|
17
|
+
}
|
|
18
|
+
function convertersOf(module, file) {
|
|
19
|
+
const value = module?.default;
|
|
20
|
+
if (!Array.isArray(value) || value.some((converter) => typeof converter?.convert !== "function")) throw new Error(`${file} must export an array of SchemaConverter as default export`);
|
|
21
|
+
return value;
|
|
22
|
+
}
|
|
23
|
+
async function loadWithBun() {
|
|
24
|
+
const compiler = await importFromProject("@znight/compiler");
|
|
25
|
+
const config = await compiler.loadZnightConfig(payload.root);
|
|
26
|
+
const options = config.file === void 0 ? payload.compilerOptions : config.options;
|
|
27
|
+
await compiler.createPreload({
|
|
28
|
+
...options,
|
|
29
|
+
root: payload.root,
|
|
30
|
+
mode: "serve",
|
|
31
|
+
logLevel: "silent"
|
|
32
|
+
});
|
|
33
|
+
const module = await import(pathToFileURL(payload.outFile).href);
|
|
34
|
+
const converters = payload.converters === void 0 ? [] : convertersOf(await import(pathToFileURL(payload.converters).href), payload.converters);
|
|
35
|
+
return {
|
|
36
|
+
metadata: module.__ZNIGHT_METADATA__,
|
|
37
|
+
converters,
|
|
38
|
+
close: async () => {}
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
async function loadWithVite() {
|
|
42
|
+
const vite = await importFromProject("vite");
|
|
43
|
+
const plugins = [];
|
|
44
|
+
if (payload.viteConfig === false) {
|
|
45
|
+
const compiler = await importFromProject("@znight/compiler");
|
|
46
|
+
plugins.push(compiler.znight({
|
|
47
|
+
...payload.compilerOptions,
|
|
48
|
+
root: payload.root
|
|
49
|
+
}));
|
|
50
|
+
}
|
|
51
|
+
const server = await vite.createServer({
|
|
52
|
+
root: payload.root,
|
|
53
|
+
configFile: payload.viteConfig,
|
|
54
|
+
...payload.configLoader === void 0 ? {} : { configLoader: payload.configLoader },
|
|
55
|
+
appType: "custom",
|
|
56
|
+
logLevel: "error",
|
|
57
|
+
clearScreen: false,
|
|
58
|
+
server: {
|
|
59
|
+
middlewareMode: true,
|
|
60
|
+
hmr: false,
|
|
61
|
+
watch: null,
|
|
62
|
+
ws: false
|
|
63
|
+
},
|
|
64
|
+
plugins,
|
|
65
|
+
...payload.sourceConditions === true ? { environments: { ssr: { resolve: {
|
|
66
|
+
conditions: [
|
|
67
|
+
"@znight/source",
|
|
68
|
+
"module",
|
|
69
|
+
"node",
|
|
70
|
+
"development|production"
|
|
71
|
+
],
|
|
72
|
+
externalConditions: ["@znight/source", "node"]
|
|
73
|
+
} } } } : {}
|
|
74
|
+
});
|
|
75
|
+
try {
|
|
76
|
+
const environment = server.environments["ssr"];
|
|
77
|
+
if (typeof vite.isRunnableDevEnvironment === "function" && !vite.isRunnableDevEnvironment(environment)) throw new Error("the ssr environment of the Vite config cannot run modules (isRunnableDevEnvironment)");
|
|
78
|
+
const runner = environment.runner;
|
|
79
|
+
if (runner === void 0) throw new Error("the ssr environment of the Vite config has no module runner");
|
|
80
|
+
const module = await runner.import(payload.outFile);
|
|
81
|
+
const converters = payload.converters === void 0 ? [] : convertersOf(await runner.import(payload.converters), payload.converters);
|
|
82
|
+
return {
|
|
83
|
+
metadata: module.__ZNIGHT_METADATA__,
|
|
84
|
+
converters,
|
|
85
|
+
close: () => server.close()
|
|
86
|
+
};
|
|
87
|
+
} catch (error) {
|
|
88
|
+
await server.close();
|
|
89
|
+
throw error;
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
let result;
|
|
93
|
+
try {
|
|
94
|
+
const loaded = payload.runtime === "bun" ? await loadWithBun() : await loadWithVite();
|
|
95
|
+
try {
|
|
96
|
+
const built = await buildOpenApiDocument(loaded.metadata, {
|
|
97
|
+
...payload.prefix === void 0 ? {} : { prefix: payload.prefix },
|
|
98
|
+
converters: loaded.converters
|
|
99
|
+
});
|
|
100
|
+
result = {
|
|
101
|
+
ok: true,
|
|
102
|
+
json: built.json,
|
|
103
|
+
warnings: built.warnings,
|
|
104
|
+
stats: built.stats
|
|
105
|
+
};
|
|
106
|
+
} finally {
|
|
107
|
+
await loaded.close();
|
|
108
|
+
}
|
|
109
|
+
} catch (error) {
|
|
110
|
+
const details = error;
|
|
111
|
+
result = {
|
|
112
|
+
ok: false,
|
|
113
|
+
error: {
|
|
114
|
+
code: typeof details.code === "string" ? details.code : void 0,
|
|
115
|
+
message: error instanceof Error ? error.message : String(error),
|
|
116
|
+
hints: Array.isArray(details.hints) ? details.hints : []
|
|
117
|
+
}
|
|
118
|
+
};
|
|
119
|
+
}
|
|
120
|
+
writeFileSync(payload.result, JSON.stringify(result));
|
|
121
|
+
process.exit(0);
|
|
122
|
+
export {};
|
|
123
|
+
|
|
124
|
+
//# sourceMappingURL=emit-child.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"emit-child.mjs","names":[],"sources":["../src/emit/child.ts"],"sourcesContent":["/**\n * The child process of `emitOpenApi()` (§7.5 of RFC 0002): loads the generated module of the\n * application (nothing is instantiated: no container, no plugin, no port), builds the document and\n * writes the result as JSON. Module side effects and globals stay in this process.\n *\n * - Bun: the compiler's preload serves the generated module and blanks the decorators.\n * - Node.js: a Vite server in middleware mode runs the module with the project config (the\n * `znight()` plugin serves the generated module from memory).\n *\n * Usage: `<runtime> child <payload.json>` (the payload says where to write the result).\n */\nimport type { ZnightMetadata } from '@znight/core'\nimport type { SchemaConverter } from '../converters.ts'\nimport type { ChildPayload, ChildResult } from './protocol.ts'\n\nimport { readFileSync, writeFileSync } from 'node:fs'\nimport { createRequire } from 'node:module'\nimport { join } from 'node:path'\nimport { pathToFileURL } from 'node:url'\n\nimport { buildOpenApiDocument } from '../build.ts'\n\nconst payload = JSON.parse(readFileSync(process.argv[2] ?? '', 'utf8')) as ChildPayload\n\n/** Imports a package as installed in the project (falls back to `@znight/openapi`'s resolution). */\nfunction importFromProject(specifier: string): Promise<unknown> {\n let resolved: string | undefined\n try {\n resolved = createRequire(join(payload.root, 'package.json')).resolve(specifier)\n } catch {\n resolved = undefined\n }\n return import(resolved === undefined ? specifier : pathToFileURL(resolved).href)\n}\n\nfunction convertersOf(module: unknown, file: string): SchemaConverter[] {\n const value = (module as { default?: unknown } | undefined)?.default\n if (!Array.isArray(value) || value.some(converter => typeof (converter as SchemaConverter)?.convert !== 'function')) {\n throw new Error(`${file} must export an array of SchemaConverter as default export`)\n }\n return value as SchemaConverter[]\n}\n\ninterface Loaded {\n readonly metadata: ZnightMetadata\n readonly converters: SchemaConverter[]\n close(): Promise<void>\n}\n\nasync function loadWithBun(): Promise<Loaded> {\n const compiler = (await importFromProject('@znight/compiler')) as {\n createPreload(options: Record<string, unknown>): Promise<unknown>\n loadZnightConfig(cwd: string): Promise<{ file: string | undefined; options: Record<string, unknown> }>\n }\n const config = await compiler.loadZnightConfig(payload.root)\n const options = config.file === undefined ? payload.compilerOptions : config.options\n await compiler.createPreload({ ...options, root: payload.root, mode: 'serve', logLevel: 'silent' })\n const module = (await import(pathToFileURL(payload.outFile).href)) as { __ZNIGHT_METADATA__: ZnightMetadata }\n const converters =\n payload.converters === undefined\n ? []\n : convertersOf(await import(pathToFileURL(payload.converters).href), payload.converters)\n return { metadata: module.__ZNIGHT_METADATA__, converters, close: async () => {} }\n}\n\ninterface ViteLike {\n createServer(config: Record<string, unknown>): Promise<{\n readonly environments: Record<string, unknown>\n close(): Promise<void>\n }>\n isRunnableDevEnvironment?(environment: unknown): boolean\n}\n\nasync function loadWithVite(): Promise<Loaded> {\n const vite = (await importFromProject('vite')) as ViteLike\n const plugins: unknown[] = []\n if (payload.viteConfig === false) {\n const compiler = (await importFromProject('@znight/compiler')) as { znight(options: unknown): unknown }\n plugins.push(compiler.znight({ ...payload.compilerOptions, root: payload.root }))\n }\n const server = await vite.createServer({\n root: payload.root,\n configFile: payload.viteConfig,\n ...(payload.configLoader === undefined ? {} : { configLoader: payload.configLoader }),\n appType: 'custom',\n logLevel: 'error',\n clearScreen: false,\n server: { middlewareMode: true, hmr: false, watch: null, ws: false },\n plugins,\n ...(payload.sourceConditions === true\n ? {\n environments: {\n ssr: {\n resolve: {\n conditions: ['@znight/source', 'module', 'node', 'development|production'],\n externalConditions: ['@znight/source', 'node'],\n },\n },\n },\n }\n : {}),\n })\n try {\n const environment = server.environments['ssr'] as { runner?: { import(url: string): Promise<unknown> } }\n if (typeof vite.isRunnableDevEnvironment === 'function' && !vite.isRunnableDevEnvironment(environment)) {\n throw new Error('the ssr environment of the Vite config cannot run modules (isRunnableDevEnvironment)')\n }\n const runner = environment.runner\n if (runner === undefined) throw new Error('the ssr environment of the Vite config has no module runner')\n const module = (await runner.import(payload.outFile)) as { __ZNIGHT_METADATA__: ZnightMetadata }\n const converters =\n payload.converters === undefined ? [] : convertersOf(await runner.import(payload.converters), payload.converters)\n return { metadata: module.__ZNIGHT_METADATA__, converters, close: () => server.close() }\n } catch (error) {\n await server.close()\n throw error\n }\n}\n\nlet result: ChildResult\ntry {\n const loaded = payload.runtime === 'bun' ? await loadWithBun() : await loadWithVite()\n try {\n const built = await buildOpenApiDocument(loaded.metadata, {\n ...(payload.prefix === undefined ? {} : { prefix: payload.prefix }),\n converters: loaded.converters,\n })\n result = { ok: true, json: built.json, warnings: built.warnings, stats: built.stats }\n } finally {\n await loaded.close()\n }\n} catch (error) {\n const details = error as { code?: unknown; hints?: unknown; message?: unknown }\n result = {\n ok: false,\n error: {\n code: typeof details.code === 'string' ? details.code : undefined,\n message: error instanceof Error ? error.message : String(error),\n hints: Array.isArray(details.hints) ? (details.hints as string[]) : [],\n },\n }\n}\nwriteFileSync(payload.result, JSON.stringify(result))\nprocess.exit(0)\n"],"mappings":"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"}
|
package/dist/emit.d.mts
ADDED
package/dist/emit.mjs
ADDED
|
@@ -0,0 +1,252 @@
|
|
|
1
|
+
import { r as toYaml } from "./openapi-CQk5dHqc.mjs";
|
|
2
|
+
import { createRequire } from "node:module";
|
|
3
|
+
import { ErrorCode, ZnightError } from "@znight/core";
|
|
4
|
+
import { fileURLToPath, pathToFileURL } from "node:url";
|
|
5
|
+
import { existsSync, mkdirSync, mkdtempSync, readFileSync, renameSync, rmSync, writeFileSync } from "node:fs";
|
|
6
|
+
import { dirname, join, resolve } from "node:path";
|
|
7
|
+
import { spawn } from "node:child_process";
|
|
8
|
+
import { tmpdir } from "node:os";
|
|
9
|
+
// src/emit/index.ts
|
|
10
|
+
const VITE_CONFIGS = [
|
|
11
|
+
"vite.config.js",
|
|
12
|
+
"vite.config.mjs",
|
|
13
|
+
"vite.config.ts",
|
|
14
|
+
"vite.config.cjs",
|
|
15
|
+
"vite.config.mts",
|
|
16
|
+
"vite.config.cts"
|
|
17
|
+
];
|
|
18
|
+
/** Compiles the application and writes (or checks) its OpenAPI document. */
|
|
19
|
+
async function emitOpenApi(options = {}) {
|
|
20
|
+
const root = resolve(options.root ?? process.cwd());
|
|
21
|
+
const outputs = (typeof options.output === "string" ? [options.output] : options.output ?? ["openapi.json"]).map((file) => resolve(root, file));
|
|
22
|
+
const checked = checkedOutputs(root, outputs, options.check);
|
|
23
|
+
const runtime = resolveRuntime(options.runtime ?? "auto");
|
|
24
|
+
const compiler = await importFromProject(root, "@znight/compiler");
|
|
25
|
+
const viteConfig = findViteConfig(root, options.config);
|
|
26
|
+
const compilerOptions = options.compilerOptions ?? await resolveCompilerOptions(root, compiler, viteConfig, options.configLoader);
|
|
27
|
+
const compiled = await compiler.compile({
|
|
28
|
+
...compilerOptions,
|
|
29
|
+
root: resolve(root, String(compilerOptions["root"] ?? ".")),
|
|
30
|
+
mode: "serve"
|
|
31
|
+
});
|
|
32
|
+
const base = {
|
|
33
|
+
outputs,
|
|
34
|
+
runtime,
|
|
35
|
+
diagnostics: compiled.diagnostics
|
|
36
|
+
};
|
|
37
|
+
if (!compiled.ok) return {
|
|
38
|
+
...base,
|
|
39
|
+
ok: false,
|
|
40
|
+
written: [],
|
|
41
|
+
stale: false,
|
|
42
|
+
staleFiles: [],
|
|
43
|
+
document: void 0,
|
|
44
|
+
warnings: []
|
|
45
|
+
};
|
|
46
|
+
const converters = options.converters === void 0 ? void 0 : resolve(root, options.converters);
|
|
47
|
+
if (converters !== void 0 && !existsSync(converters)) throw new ZnightError(ErrorCode.InvalidOpenApiDefinition, `The converters module ${options.converters} does not exist`);
|
|
48
|
+
const result = await runChild({
|
|
49
|
+
root,
|
|
50
|
+
runtime,
|
|
51
|
+
outFile: compiled.outFile,
|
|
52
|
+
viteConfig: viteConfig ?? false,
|
|
53
|
+
...options.configLoader === void 0 ? {} : { configLoader: options.configLoader },
|
|
54
|
+
compilerOptions: jsonOnly(compilerOptions),
|
|
55
|
+
...converters === void 0 ? {} : { converters },
|
|
56
|
+
...options.prefix === void 0 ? {} : { prefix: options.prefix }
|
|
57
|
+
}, options.env ?? process.env);
|
|
58
|
+
if (!result.ok) {
|
|
59
|
+
const code = Object.values(ErrorCode).find((candidate) => candidate === result.error.code) ?? ErrorCode.SchemaConversionFailed;
|
|
60
|
+
const message = result.error.message.replace(/^ZNIGHT\d{3}: /, "");
|
|
61
|
+
throw new ZnightError(code, message, { hints: result.error.hints });
|
|
62
|
+
}
|
|
63
|
+
const document = JSON.parse(result.json);
|
|
64
|
+
const contents = /* @__PURE__ */ new Map();
|
|
65
|
+
for (const output of outputs) contents.set(output, /\.ya?ml$/i.test(output) ? await toYaml(document) : result.json);
|
|
66
|
+
const changed = outputs.filter((output) => readText(output) !== contents.get(output));
|
|
67
|
+
const staleFiles = changed.filter((output) => checked.has(output));
|
|
68
|
+
const written = [];
|
|
69
|
+
for (const output of changed) {
|
|
70
|
+
if (checked.has(output)) continue;
|
|
71
|
+
writeAtomically(output, contents.get(output) ?? "");
|
|
72
|
+
written.push(output);
|
|
73
|
+
}
|
|
74
|
+
return {
|
|
75
|
+
...base,
|
|
76
|
+
ok: true,
|
|
77
|
+
written,
|
|
78
|
+
stale: staleFiles.length > 0,
|
|
79
|
+
staleFiles,
|
|
80
|
+
document,
|
|
81
|
+
warnings: result.warnings
|
|
82
|
+
};
|
|
83
|
+
}
|
|
84
|
+
/** The outputs that `check` checks (absolute paths). */
|
|
85
|
+
function checkedOutputs(root, outputs, check) {
|
|
86
|
+
if (check === void 0 || check === false) return /* @__PURE__ */ new Set();
|
|
87
|
+
if (check === true) return new Set(outputs);
|
|
88
|
+
const checked = new Set(check.map((file) => resolve(root, file)));
|
|
89
|
+
const unknown = check.filter((file) => !outputs.includes(resolve(root, file)));
|
|
90
|
+
if (unknown.length > 0) throw new ZnightError(ErrorCode.InvalidOpenApiDefinition, `check lists ${unknown.join(", ")}, which ${unknown.length > 1 ? "are not outputs" : "is not an output"}`, { hints: ["check: true checks every output; a list names outputs (relative to the root)"] });
|
|
91
|
+
return checked;
|
|
92
|
+
}
|
|
93
|
+
function resolveRuntime(runtime) {
|
|
94
|
+
if (runtime === "node" || runtime === "bun") return runtime;
|
|
95
|
+
return process.versions["bun"] === void 0 ? "node" : "bun";
|
|
96
|
+
}
|
|
97
|
+
/** The Vite config file of the project, if any. */
|
|
98
|
+
function findViteConfig(root, config) {
|
|
99
|
+
if (config !== void 0) return resolve(root, config);
|
|
100
|
+
return VITE_CONFIGS.map((file) => join(root, file)).find((file) => existsSync(file));
|
|
101
|
+
}
|
|
102
|
+
async function resolveCompilerOptions(root, compiler, viteConfig, configLoader) {
|
|
103
|
+
const config = await compiler.loadZnightConfig?.(root);
|
|
104
|
+
if (config?.file !== void 0) {
|
|
105
|
+
const { logLevel: _logLevel, ...rest } = config.options;
|
|
106
|
+
return {
|
|
107
|
+
...rest,
|
|
108
|
+
root: resolve(root, String(rest["root"] ?? "."))
|
|
109
|
+
};
|
|
110
|
+
}
|
|
111
|
+
if (viteConfig === void 0) return { root };
|
|
112
|
+
const vite = await importFromProject(root, "vite");
|
|
113
|
+
if (typeof vite.resolveConfig !== "function") return { root };
|
|
114
|
+
const nodeEnv = process.env["NODE_ENV"];
|
|
115
|
+
let resolved;
|
|
116
|
+
try {
|
|
117
|
+
resolved = await vite.resolveConfig({
|
|
118
|
+
configFile: viteConfig,
|
|
119
|
+
root,
|
|
120
|
+
mode: "production",
|
|
121
|
+
logLevel: "silent",
|
|
122
|
+
...configLoader === void 0 ? {} : { configLoader }
|
|
123
|
+
}, "build", "production", "production");
|
|
124
|
+
} finally {
|
|
125
|
+
if (nodeEnv === void 0) delete process.env["NODE_ENV"];
|
|
126
|
+
else process.env["NODE_ENV"] = nodeEnv;
|
|
127
|
+
}
|
|
128
|
+
for (const plugin of resolved.plugins) {
|
|
129
|
+
const api = plugin.api;
|
|
130
|
+
if (plugin.name !== "znight" || api === void 0) continue;
|
|
131
|
+
const given = typeof api.options === "object" && api.options !== null ? api.options : {};
|
|
132
|
+
const compilerRoot = api.compiler?.options?.root;
|
|
133
|
+
return {
|
|
134
|
+
...given,
|
|
135
|
+
root: typeof compilerRoot === "string" ? compilerRoot : resolve(resolved.root, String(given["root"] ?? "."))
|
|
136
|
+
};
|
|
137
|
+
}
|
|
138
|
+
return { root };
|
|
139
|
+
}
|
|
140
|
+
async function importFromProject(root, specifier) {
|
|
141
|
+
let resolved;
|
|
142
|
+
try {
|
|
143
|
+
resolved = createRequire(join(root, "package.json")).resolve(specifier);
|
|
144
|
+
} catch {
|
|
145
|
+
resolved = void 0;
|
|
146
|
+
}
|
|
147
|
+
try {
|
|
148
|
+
return await (resolved === void 0 ? import(specifier) : import(pathToFileURL(resolved).href));
|
|
149
|
+
} catch (error) {
|
|
150
|
+
throw new ZnightError(ErrorCode.InvalidOpenApiDefinition, `Cannot load ${specifier}`, {
|
|
151
|
+
cause: error,
|
|
152
|
+
hints: [`Install it in the project: npm install -D ${specifier}`]
|
|
153
|
+
});
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
/** The JSON-compatible part of the compiler options (sent to the child process). */
|
|
157
|
+
function jsonOnly(options) {
|
|
158
|
+
const result = {};
|
|
159
|
+
for (const [key, value] of Object.entries(options)) try {
|
|
160
|
+
const json = JSON.stringify(value);
|
|
161
|
+
if (json !== void 0 && JSON.stringify(JSON.parse(json)) === json && typeof value !== "function") result[key] = JSON.parse(json);
|
|
162
|
+
} catch {}
|
|
163
|
+
return result;
|
|
164
|
+
}
|
|
165
|
+
/** The script of the child process (source or built). */
|
|
166
|
+
function childScript() {
|
|
167
|
+
const source = import.meta.url.endsWith(".ts");
|
|
168
|
+
return {
|
|
169
|
+
file: fileURLToPath(new URL(source ? "./child.ts" : "./emit-child.mjs", import.meta.url)),
|
|
170
|
+
source
|
|
171
|
+
};
|
|
172
|
+
}
|
|
173
|
+
function runChild(payload, env) {
|
|
174
|
+
const directory = mkdtempSync(join(tmpdir(), "znight-openapi-emit-"));
|
|
175
|
+
const payloadFile = join(directory, "payload.json");
|
|
176
|
+
const resultFile = join(directory, "result.json");
|
|
177
|
+
const script = childScript();
|
|
178
|
+
writeFileSync(payloadFile, JSON.stringify({
|
|
179
|
+
...payload,
|
|
180
|
+
...script.source ? { sourceConditions: true } : {},
|
|
181
|
+
result: resultFile
|
|
182
|
+
}));
|
|
183
|
+
const bun = process.versions["bun"] !== void 0;
|
|
184
|
+
let executable;
|
|
185
|
+
if (payload.runtime === "bun") executable = bun ? process.execPath : "bun";
|
|
186
|
+
else executable = bun ? "node" : process.execPath;
|
|
187
|
+
const args = [
|
|
188
|
+
...script.source ? ["--conditions=@znight/source"] : [],
|
|
189
|
+
script.file,
|
|
190
|
+
payloadFile
|
|
191
|
+
];
|
|
192
|
+
const childEnv = {};
|
|
193
|
+
for (const [key, value] of Object.entries(env)) if (value !== void 0) childEnv[key] = value;
|
|
194
|
+
childEnv["NODE_ENV"] ??= "production";
|
|
195
|
+
return new Promise((resolvePromise, reject) => {
|
|
196
|
+
const child = spawn(executable, args, {
|
|
197
|
+
cwd: payload.root,
|
|
198
|
+
env: childEnv,
|
|
199
|
+
stdio: [
|
|
200
|
+
"ignore",
|
|
201
|
+
"pipe",
|
|
202
|
+
"pipe"
|
|
203
|
+
]
|
|
204
|
+
});
|
|
205
|
+
let output = "";
|
|
206
|
+
child.stdout?.on("data", (chunk) => {
|
|
207
|
+
output += chunk.toString();
|
|
208
|
+
});
|
|
209
|
+
child.stderr?.on("data", (chunk) => {
|
|
210
|
+
output += chunk.toString();
|
|
211
|
+
});
|
|
212
|
+
child.on("error", (error) => {
|
|
213
|
+
rmSync(directory, {
|
|
214
|
+
recursive: true,
|
|
215
|
+
force: true
|
|
216
|
+
});
|
|
217
|
+
reject(new ZnightError(ErrorCode.InvalidOpenApiDefinition, `Cannot start ${executable} to load the application modules`, {
|
|
218
|
+
cause: error,
|
|
219
|
+
hints: [payload.runtime === "bun" ? "Install Bun, or use --runtime node" : "Install Node.js 24, or use --runtime bun"]
|
|
220
|
+
}));
|
|
221
|
+
});
|
|
222
|
+
child.on("close", (code) => {
|
|
223
|
+
const text = readText(resultFile);
|
|
224
|
+
rmSync(directory, {
|
|
225
|
+
recursive: true,
|
|
226
|
+
force: true
|
|
227
|
+
});
|
|
228
|
+
if (text === void 0) {
|
|
229
|
+
reject(new ZnightError(ErrorCode.InvalidOpenApiDefinition, `Loading the application modules failed (${executable} exited with code ${String(code)})${output.trim() === "" ? "" : `:\n${output.trim()}`}`));
|
|
230
|
+
return;
|
|
231
|
+
}
|
|
232
|
+
resolvePromise(JSON.parse(text));
|
|
233
|
+
});
|
|
234
|
+
});
|
|
235
|
+
}
|
|
236
|
+
function readText(file) {
|
|
237
|
+
try {
|
|
238
|
+
return readFileSync(file, "utf8");
|
|
239
|
+
} catch {
|
|
240
|
+
return;
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
/** Writes through a temporary file and a rename (readers never see a partial file). */
|
|
244
|
+
function writeAtomically(file, content) {
|
|
245
|
+
mkdirSync(dirname(file), { recursive: true });
|
|
246
|
+
const temporary = `${file}.${process.pid}.tmp`;
|
|
247
|
+
writeFileSync(temporary, content);
|
|
248
|
+
renameSync(temporary, file);
|
|
249
|
+
}
|
|
250
|
+
export { emitOpenApi, findViteConfig };
|
|
251
|
+
|
|
252
|
+
//# sourceMappingURL=emit.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"emit.mjs","names":[],"sources":["../src/emit/index.ts"],"sourcesContent":["/**\n * `@znight/openapi/emit`: writes `openapi.json` (or YAML) at build time (§7.5 of RFC 0002), for\n * `znight openapi`, `znight build --openapi` and the `openapi()` Vite plugin.\n *\n * 1. the compiler options are resolved like `znight generate` does (`znight.config.*`, else the\n * `znight()` plugin of the Vite config);\n * 2. `compile()` in `serve` mode: errors stop everything before any module runs;\n * 3. a **child process** loads the generated module (Bun: the compiler preload; Node.js: a Vite\n * server in middleware mode) and runs `buildOpenApiDocument`;\n * 4. each output is written atomically, only when it changed; a checked output (`check`) is never\n * written: when it is missing or stale, it is reported.\n */\nimport type { Diagnostic } from '@znight/compiler'\nimport type { OpenApiWarning } from '../build.ts'\nimport type { OpenApiObject } from '../types.ts'\nimport type { ChildPayload, ChildResult } from './protocol.ts'\n\nimport { spawn } from 'node:child_process'\nimport { existsSync, mkdirSync, mkdtempSync, readFileSync, renameSync, rmSync, writeFileSync } from 'node:fs'\nimport { createRequire } from 'node:module'\nimport { tmpdir } from 'node:os'\nimport { dirname, join, resolve } from 'node:path'\nimport { fileURLToPath, pathToFileURL } from 'node:url'\nimport { ErrorCode, ZnightError } from '@znight/core'\n\nimport { toYaml } from '../openapi.ts'\n\n/** Runtime loading the application modules: `'auto'` is the runtime running the caller. */\nexport type EmitRuntime = 'auto' | 'node' | 'bun'\n\n/** Options of {@link emitOpenApi}. */\nexport interface EmitOpenApiOptions {\n /** Project root. Default `process.cwd()`. */\n readonly root?: string\n /** Output files, relative to `root`; `.yaml` / `.yml` write YAML (optional peer `yaml`). Default `'openapi.json'`. */\n readonly output?: string | readonly string[]\n /**\n * Never write: report missing or stale outputs (`stale`, `staleFiles`). `true` checks every output;\n * a list checks only these outputs (relative to `root`, each one an `output`) and writes the others\n * as usual (the `openapi()` Vite plugin checks the committed file and writes the build output).\n */\n readonly check?: boolean | readonly string[]\n /** The HTTP prefix: `servers` defaults to `[{ url: prefix }]` when `@OpenApiDocument` declares none. */\n readonly prefix?: string\n /** Default `'auto'`. */\n readonly runtime?: EmitRuntime\n /** Module (relative to `root`) whose default export is a `SchemaConverter[]`. */\n readonly converters?: string\n /** Vite config file (default: `vite.config.*` of the root). */\n readonly config?: string\n /** How Vite loads its config (`bundle`, `runner`, `native`). */\n readonly configLoader?: string\n /**\n * The compiler options, already resolved (the CLI and the Vite plugin pass theirs). Default:\n * `znight.config.*`, else the options of the `znight()` plugin of the Vite config.\n */\n readonly compilerOptions?: Readonly<Record<string, unknown>>\n /** Environment of the child process. Default `process.env`, with `NODE_ENV` defaulting to `production`. */\n readonly env?: Readonly<Record<string, string | undefined>>\n}\n\n/** Result of {@link emitOpenApi}. */\nexport interface EmitOpenApiResult {\n /** `false` when the compile failed (see `diagnostics`): nothing ran and nothing was written. */\n readonly ok: boolean\n /** Outputs written (absolute paths); never a checked output. */\n readonly written: readonly string[]\n /** `check`: a checked output is missing or differs (listed in `staleFiles`). */\n readonly stale: boolean\n readonly staleFiles: readonly string[]\n /** The document (`undefined` when the compile failed). */\n readonly document: OpenApiObject | undefined\n /** Compiler diagnostics (warnings included). */\n readonly diagnostics: readonly Diagnostic[]\n /** Warnings of the assembly (`unrepresentable: 'warn'`...). */\n readonly warnings: readonly OpenApiWarning[]\n /** Absolute paths of the outputs. */\n readonly outputs: readonly string[]\n readonly runtime: 'node' | 'bun'\n}\n\ninterface CompilerModule {\n compile(options: Record<string, unknown>): Promise<{\n readonly ok: boolean\n readonly diagnostics: readonly Diagnostic[]\n readonly outFile: string\n }>\n loadZnightConfig?(cwd: string): Promise<{ file: string | undefined; options: Record<string, unknown> }>\n}\n\nconst VITE_CONFIGS = [\n 'vite.config.js',\n 'vite.config.mjs',\n 'vite.config.ts',\n 'vite.config.cjs',\n 'vite.config.mts',\n 'vite.config.cts',\n]\n\n/** Compiles the application and writes (or checks) its OpenAPI document. */\nexport async function emitOpenApi(options: EmitOpenApiOptions = {}): Promise<EmitOpenApiResult> {\n const root = resolve(options.root ?? process.cwd())\n const outputs = (typeof options.output === 'string' ? [options.output] : (options.output ?? ['openapi.json'])).map(\n file => resolve(root, file),\n )\n const checked = checkedOutputs(root, outputs, options.check)\n const runtime = resolveRuntime(options.runtime ?? 'auto')\n const compiler = (await importFromProject(root, '@znight/compiler')) as CompilerModule\n const viteConfig = findViteConfig(root, options.config)\n const compilerOptions =\n options.compilerOptions ?? (await resolveCompilerOptions(root, compiler, viteConfig, options.configLoader))\n const compiled = await compiler.compile({\n ...compilerOptions,\n root: resolve(root, String(compilerOptions['root'] ?? '.')),\n mode: 'serve',\n })\n const base = { outputs, runtime, diagnostics: compiled.diagnostics }\n if (!compiled.ok) {\n return { ...base, ok: false, written: [], stale: false, staleFiles: [], document: undefined, warnings: [] }\n }\n const converters = options.converters === undefined ? undefined : resolve(root, options.converters)\n if (converters !== undefined && !existsSync(converters)) {\n throw new ZnightError(\n ErrorCode.InvalidOpenApiDefinition,\n `The converters module ${options.converters} does not exist`,\n )\n }\n const result = await runChild(\n {\n root,\n runtime,\n outFile: compiled.outFile,\n viteConfig: viteConfig ?? false,\n ...(options.configLoader === undefined ? {} : { configLoader: options.configLoader }),\n compilerOptions: jsonOnly(compilerOptions),\n ...(converters === undefined ? {} : { converters }),\n ...(options.prefix === undefined ? {} : { prefix: options.prefix }),\n },\n options.env ?? process.env,\n )\n if (!result.ok) {\n const code =\n Object.values(ErrorCode).find(candidate => candidate === result.error.code) ?? ErrorCode.SchemaConversionFailed\n const message = result.error.message.replace(/^ZNIGHT\\d{3}: /, '')\n throw new ZnightError(code, message, { hints: result.error.hints })\n }\n const document = JSON.parse(result.json) as OpenApiObject\n const contents = new Map<string, string>()\n for (const output of outputs) {\n contents.set(output, /\\.ya?ml$/i.test(output) ? await toYaml(document) : result.json)\n }\n const changed = outputs.filter(output => readText(output) !== contents.get(output))\n const staleFiles = changed.filter(output => checked.has(output))\n const written: string[] = []\n for (const output of changed) {\n if (checked.has(output)) continue\n writeAtomically(output, contents.get(output) ?? '')\n written.push(output)\n }\n return {\n ...base,\n ok: true,\n written,\n stale: staleFiles.length > 0,\n staleFiles,\n document,\n warnings: result.warnings,\n }\n}\n\n/** The outputs that `check` checks (absolute paths). */\nfunction checkedOutputs(\n root: string,\n outputs: readonly string[],\n check: EmitOpenApiOptions['check'],\n): ReadonlySet<string> {\n if (check === undefined || check === false) return new Set()\n if (check === true) return new Set(outputs)\n const checked = new Set(check.map(file => resolve(root, file)))\n const unknown = check.filter(file => !outputs.includes(resolve(root, file)))\n if (unknown.length > 0) {\n throw new ZnightError(\n ErrorCode.InvalidOpenApiDefinition,\n `check lists ${unknown.join(', ')}, which ${unknown.length > 1 ? 'are not outputs' : 'is not an output'}`,\n { hints: ['check: true checks every output; a list names outputs (relative to the root)'] },\n )\n }\n return checked\n}\n\nfunction resolveRuntime(runtime: EmitRuntime): 'node' | 'bun' {\n if (runtime === 'node' || runtime === 'bun') return runtime\n return process.versions['bun'] === undefined ? 'node' : 'bun'\n}\n\n/** The Vite config file of the project, if any. */\nexport function findViteConfig(root: string, config?: string): string | undefined {\n if (config !== undefined) return resolve(root, config)\n return VITE_CONFIGS.map(file => join(root, file)).find(file => existsSync(file))\n}\n\nasync function resolveCompilerOptions(\n root: string,\n compiler: CompilerModule,\n viteConfig: string | undefined,\n configLoader: string | undefined,\n): Promise<Record<string, unknown>> {\n const config = await compiler.loadZnightConfig?.(root)\n if (config?.file !== undefined) {\n const { logLevel: _logLevel, ...rest } = config.options\n return { ...rest, root: resolve(root, String(rest['root'] ?? '.')) }\n }\n if (viteConfig === undefined) return { root }\n const vite = (await importFromProject(root, 'vite')) as {\n resolveConfig?(\n config: Record<string, unknown>,\n command: string,\n mode?: string,\n env?: string,\n ): Promise<{ plugins: readonly unknown[]; root: string }>\n }\n if (typeof vite.resolveConfig !== 'function') return { root }\n const nodeEnv = process.env['NODE_ENV']\n let resolved: { plugins: readonly unknown[]; root: string }\n try {\n resolved = await vite.resolveConfig(\n {\n configFile: viteConfig,\n root,\n mode: 'production',\n logLevel: 'silent',\n ...(configLoader === undefined ? {} : { configLoader }),\n },\n 'build',\n 'production',\n 'production',\n )\n } finally {\n if (nodeEnv === undefined) delete process.env['NODE_ENV']\n else process.env['NODE_ENV'] = nodeEnv\n }\n for (const plugin of resolved.plugins) {\n const api = (plugin as { name?: unknown; api?: { options?: unknown; compiler?: { options?: { root?: unknown } } } })\n .api\n if ((plugin as { name?: unknown }).name !== 'znight' || api === undefined) continue\n const given =\n typeof api.options === 'object' && api.options !== null ? (api.options as Record<string, unknown>) : {}\n const compilerRoot = api.compiler?.options?.root\n return {\n ...given,\n root: typeof compilerRoot === 'string' ? compilerRoot : resolve(resolved.root, String(given['root'] ?? '.')),\n }\n }\n return { root }\n}\n\nasync function importFromProject(root: string, specifier: string): Promise<unknown> {\n let resolved: string | undefined\n try {\n resolved = createRequire(join(root, 'package.json')).resolve(specifier)\n } catch {\n resolved = undefined\n }\n try {\n return await import(resolved === undefined ? specifier : pathToFileURL(resolved).href)\n } catch (error) {\n throw new ZnightError(ErrorCode.InvalidOpenApiDefinition, `Cannot load ${specifier}`, {\n cause: error,\n hints: [`Install it in the project: npm install -D ${specifier}`],\n })\n }\n}\n\n/** The JSON-compatible part of the compiler options (sent to the child process). */\nfunction jsonOnly(options: Readonly<Record<string, unknown>>): Record<string, unknown> {\n const result: Record<string, unknown> = {}\n for (const [key, value] of Object.entries(options)) {\n try {\n const json = JSON.stringify(value)\n if (json !== undefined && JSON.stringify(JSON.parse(json)) === json && typeof value !== 'function') {\n result[key] = JSON.parse(json)\n }\n } catch {\n // functions, extension objects: the child reads znight.config.* itself\n }\n }\n return result\n}\n\n/** The script of the child process (source or built). */\nfunction childScript(): { file: string; source: boolean } {\n const source = import.meta.url.endsWith('.ts')\n return { file: fileURLToPath(new URL(source ? './child.ts' : './emit-child.mjs', import.meta.url)), source }\n}\n\nfunction runChild(\n payload: Omit<ChildPayload, 'result'>,\n env: Readonly<Record<string, string | undefined>>,\n): Promise<ChildResult> {\n const directory = mkdtempSync(join(tmpdir(), 'znight-openapi-emit-'))\n const payloadFile = join(directory, 'payload.json')\n const resultFile = join(directory, 'result.json')\n const script = childScript()\n writeFileSync(\n payloadFile,\n JSON.stringify({\n ...payload,\n ...(script.source ? { sourceConditions: true } : {}),\n result: resultFile,\n } satisfies ChildPayload),\n )\n const bun = process.versions['bun'] !== undefined\n let executable: string\n if (payload.runtime === 'bun') executable = bun ? process.execPath : 'bun'\n else executable = bun ? 'node' : process.execPath\n const args = [...(script.source ? ['--conditions=@znight/source'] : []), script.file, payloadFile]\n const childEnv: Record<string, string> = {}\n for (const [key, value] of Object.entries(env)) if (value !== undefined) childEnv[key] = value\n childEnv['NODE_ENV'] ??= 'production'\n return new Promise<ChildResult>((resolvePromise, reject) => {\n const child = spawn(executable, args, { cwd: payload.root, env: childEnv, stdio: ['ignore', 'pipe', 'pipe'] })\n let output = ''\n child.stdout?.on('data', (chunk: Buffer) => {\n output += chunk.toString()\n })\n child.stderr?.on('data', (chunk: Buffer) => {\n output += chunk.toString()\n })\n child.on('error', error => {\n rmSync(directory, { recursive: true, force: true })\n reject(\n new ZnightError(\n ErrorCode.InvalidOpenApiDefinition,\n `Cannot start ${executable} to load the application modules`,\n {\n cause: error,\n hints: [\n payload.runtime === 'bun'\n ? 'Install Bun, or use --runtime node'\n : 'Install Node.js 24, or use --runtime bun',\n ],\n },\n ),\n )\n })\n child.on('close', code => {\n const text = readText(resultFile)\n rmSync(directory, { recursive: true, force: true })\n if (text === undefined) {\n reject(\n new ZnightError(\n ErrorCode.InvalidOpenApiDefinition,\n `Loading the application modules failed (${executable} exited with code ${String(code)})${output.trim() === '' ? '' : `:\\n${output.trim()}`}`,\n ),\n )\n return\n }\n resolvePromise(JSON.parse(text) as ChildResult)\n })\n })\n}\n\nfunction readText(file: string): string | undefined {\n try {\n return readFileSync(file, 'utf8')\n } catch {\n return undefined\n }\n}\n\n/** Writes through a temporary file and a rename (readers never see a partial file). */\nfunction writeAtomically(file: string, content: string): void {\n mkdirSync(dirname(file), { recursive: true })\n const temporary = `${file}.${process.pid}.tmp`\n writeFileSync(temporary, content)\n renameSync(temporary, file)\n}\n"],"mappings":"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"}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import { _ as OpenApiObject } from "./types-VzBexMKj.mjs";
|
|
2
|
+
import { i as OpenApiWarning } from "./build-BTUC96mY.mjs";
|
|
3
|
+
import { Diagnostic } from "@znight/compiler";
|
|
4
|
+
/** Runtime loading the application modules: `'auto'` is the runtime running the caller. */
|
|
5
|
+
type EmitRuntime = "auto" | "node" | "bun";
|
|
6
|
+
/** Options of {@link emitOpenApi}. */
|
|
7
|
+
interface EmitOpenApiOptions {
|
|
8
|
+
/** Project root. Default `process.cwd()`. */
|
|
9
|
+
readonly root?: string;
|
|
10
|
+
/** Output files, relative to `root`; `.yaml` / `.yml` write YAML (optional peer `yaml`). Default `'openapi.json'`. */
|
|
11
|
+
readonly output?: string | readonly string[];
|
|
12
|
+
/**
|
|
13
|
+
* Never write: report missing or stale outputs (`stale`, `staleFiles`). `true` checks every output;
|
|
14
|
+
* a list checks only these outputs (relative to `root`, each one an `output`) and writes the others
|
|
15
|
+
* as usual (the `openapi()` Vite plugin checks the committed file and writes the build output).
|
|
16
|
+
*/
|
|
17
|
+
readonly check?: boolean | readonly string[];
|
|
18
|
+
/** The HTTP prefix: `servers` defaults to `[{ url: prefix }]` when `@OpenApiDocument` declares none. */
|
|
19
|
+
readonly prefix?: string;
|
|
20
|
+
/** Default `'auto'`. */
|
|
21
|
+
readonly runtime?: EmitRuntime;
|
|
22
|
+
/** Module (relative to `root`) whose default export is a `SchemaConverter[]`. */
|
|
23
|
+
readonly converters?: string;
|
|
24
|
+
/** Vite config file (default: `vite.config.*` of the root). */
|
|
25
|
+
readonly config?: string;
|
|
26
|
+
/** How Vite loads its config (`bundle`, `runner`, `native`). */
|
|
27
|
+
readonly configLoader?: string;
|
|
28
|
+
/**
|
|
29
|
+
* The compiler options, already resolved (the CLI and the Vite plugin pass theirs). Default:
|
|
30
|
+
* `znight.config.*`, else the options of the `znight()` plugin of the Vite config.
|
|
31
|
+
*/
|
|
32
|
+
readonly compilerOptions?: Readonly<Record<string, unknown>>;
|
|
33
|
+
/** Environment of the child process. Default `process.env`, with `NODE_ENV` defaulting to `production`. */
|
|
34
|
+
readonly env?: Readonly<Record<string, string | undefined>>;
|
|
35
|
+
}
|
|
36
|
+
/** Result of {@link emitOpenApi}. */
|
|
37
|
+
interface EmitOpenApiResult {
|
|
38
|
+
/** `false` when the compile failed (see `diagnostics`): nothing ran and nothing was written. */
|
|
39
|
+
readonly ok: boolean;
|
|
40
|
+
/** Outputs written (absolute paths); never a checked output. */
|
|
41
|
+
readonly written: readonly string[];
|
|
42
|
+
/** `check`: a checked output is missing or differs (listed in `staleFiles`). */
|
|
43
|
+
readonly stale: boolean;
|
|
44
|
+
readonly staleFiles: readonly string[];
|
|
45
|
+
/** The document (`undefined` when the compile failed). */
|
|
46
|
+
readonly document: OpenApiObject | undefined;
|
|
47
|
+
/** Compiler diagnostics (warnings included). */
|
|
48
|
+
readonly diagnostics: readonly Diagnostic[];
|
|
49
|
+
/** Warnings of the assembly (`unrepresentable: 'warn'`...). */
|
|
50
|
+
readonly warnings: readonly OpenApiWarning[];
|
|
51
|
+
/** Absolute paths of the outputs. */
|
|
52
|
+
readonly outputs: readonly string[];
|
|
53
|
+
readonly runtime: "node" | "bun";
|
|
54
|
+
}
|
|
55
|
+
/** Compiles the application and writes (or checks) its OpenAPI document. */
|
|
56
|
+
declare function emitOpenApi(options?: EmitOpenApiOptions): Promise<EmitOpenApiResult>;
|
|
57
|
+
/** The Vite config file of the project, if any. */
|
|
58
|
+
declare function findViteConfig(root: string, config?: string): string | undefined;
|
|
59
|
+
export { findViteConfig as a, emitOpenApi as i, EmitOpenApiResult as n, EmitRuntime as r, EmitOpenApiOptions as t };
|
|
60
|
+
//# sourceMappingURL=index-CqQ6IRnN.d.mts.map
|