@rexeus/typeweaver 0.6.3 → 0.6.5
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/dist/{cli-gJQliCVf.mjs → cli-AH4H-B8Q.mjs} +184 -4
- package/dist/cli.cjs +184 -4
- package/dist/cli.mjs +184 -4
- package/dist/cli.mjs.map +1 -1
- package/dist/entry.mjs +1 -1
- package/package.json +10 -8
|
@@ -5,9 +5,147 @@ import { fileURLToPath, pathToFileURL } from "node:url";
|
|
|
5
5
|
import { Command } from "commander";
|
|
6
6
|
import { PluginContextBuilder, PluginRegistry } from "@rexeus/typeweaver-gen";
|
|
7
7
|
import TypesPlugin from "@rexeus/typeweaver-types";
|
|
8
|
+
import { transformSync } from "oxc-transform";
|
|
8
9
|
import { render } from "ejs";
|
|
9
10
|
import { HttpMethod, HttpOperationDefinition, HttpResponseDefinition, HttpStatusCode, HttpStatusCodeNameMap } from "@rexeus/typeweaver-core";
|
|
10
11
|
|
|
12
|
+
//#region src/generators/errors/DefinitionCompilationError.ts
|
|
13
|
+
var DefinitionCompilationError = class extends Error {
|
|
14
|
+
constructor(filePath, details) {
|
|
15
|
+
super(`Failed to compile definition at \`${filePath}\`. ${details}`);
|
|
16
|
+
this.filePath = filePath;
|
|
17
|
+
this.details = details;
|
|
18
|
+
}
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
//#endregion
|
|
22
|
+
//#region src/generators/DefinitionCompiler.ts
|
|
23
|
+
/**
|
|
24
|
+
* Compiles TypeScript definition files to JavaScript + declaration stubs.
|
|
25
|
+
*
|
|
26
|
+
* Definition files contain Zod schemas that cause tsc to exhaust memory
|
|
27
|
+
* when type-checking due to Zod v4's deeply recursive type inference.
|
|
28
|
+
* By pre-compiling definitions to .js + .d.ts, tsc only sees lightweight
|
|
29
|
+
* type stubs (everything typed as `any`) while runtime behavior is preserved.
|
|
30
|
+
*/
|
|
31
|
+
var DefinitionCompiler = class DefinitionCompiler {
|
|
32
|
+
static GENERATED_HEADER = [
|
|
33
|
+
"/* eslint-disable */",
|
|
34
|
+
"/**",
|
|
35
|
+
" * This file was automatically generated by typeweaver.",
|
|
36
|
+
" * DO NOT EDIT. Instead, modify the source definition file and generate again.",
|
|
37
|
+
" *",
|
|
38
|
+
" * @generated by @rexeus/typeweaver",
|
|
39
|
+
" */"
|
|
40
|
+
].join("\n");
|
|
41
|
+
/**
|
|
42
|
+
* Compiles all .ts definition files in-place to .js + .d.ts stubs.
|
|
43
|
+
* The original .ts files are removed after compilation.
|
|
44
|
+
*/
|
|
45
|
+
compileInPlace(definitionDir) {
|
|
46
|
+
const errors = [];
|
|
47
|
+
this.processDirectory(definitionDir, errors);
|
|
48
|
+
if (errors.length > 0) {
|
|
49
|
+
const summary = errors.map((e) => ` - ${e.filePath}: ${e.details}`).join("\n");
|
|
50
|
+
throw new DefinitionCompilationError(definitionDir, `${errors.length} file(s) failed to compile:\n${summary}`);
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
processDirectory(dir, errors) {
|
|
54
|
+
const entries = fs.readdirSync(dir, { withFileTypes: true });
|
|
55
|
+
for (const entry of entries) {
|
|
56
|
+
const fullPath = path.join(dir, entry.name);
|
|
57
|
+
if (entry.isDirectory()) {
|
|
58
|
+
this.processDirectory(fullPath, errors);
|
|
59
|
+
continue;
|
|
60
|
+
}
|
|
61
|
+
if (entry.isFile() && entry.name.endsWith(".ts") && !entry.name.endsWith(".d.ts")) try {
|
|
62
|
+
this.compileFile(fullPath, dir, entry.name);
|
|
63
|
+
} catch (error) {
|
|
64
|
+
errors.push(error instanceof DefinitionCompilationError ? error : new DefinitionCompilationError(fullPath, String(error)));
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
compileFile(srcPath, dir, fileName) {
|
|
69
|
+
try {
|
|
70
|
+
const source = fs.readFileSync(srcPath, "utf-8");
|
|
71
|
+
const baseName = fileName.replace(/\.ts$/, "");
|
|
72
|
+
const jsCode = this.transpileToJs(fileName, source);
|
|
73
|
+
const dtsCode = this.generateDtsStub(source);
|
|
74
|
+
fs.writeFileSync(path.join(dir, `${baseName}.js`), jsCode);
|
|
75
|
+
fs.writeFileSync(path.join(dir, `${baseName}.d.ts`), dtsCode);
|
|
76
|
+
fs.unlinkSync(srcPath);
|
|
77
|
+
} catch (error) {
|
|
78
|
+
throw new DefinitionCompilationError(srcPath, error instanceof DefinitionCompilationError ? error.details : error instanceof Error ? error.message : String(error));
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
transpileToJs(fileName, source) {
|
|
82
|
+
const result = transformSync(fileName, source, {
|
|
83
|
+
lang: "ts",
|
|
84
|
+
sourceType: "module"
|
|
85
|
+
});
|
|
86
|
+
const errors = result.errors.filter((e) => e.severity === "Error");
|
|
87
|
+
if (errors.length > 0) throw new DefinitionCompilationError(fileName, errors.map((e) => e.message).join("; "));
|
|
88
|
+
return result.code;
|
|
89
|
+
}
|
|
90
|
+
/**
|
|
91
|
+
* Generates a minimal .d.ts stub from TypeScript source.
|
|
92
|
+
* All value exports are typed as `any` to avoid pulling in Zod's type system.
|
|
93
|
+
* Re-exports are preserved so barrel files chain correctly.
|
|
94
|
+
*/
|
|
95
|
+
generateDtsStub(source) {
|
|
96
|
+
if (source.startsWith(DefinitionCompiler.GENERATED_HEADER)) return source;
|
|
97
|
+
const lines = [DefinitionCompiler.GENERATED_HEADER];
|
|
98
|
+
let hasDefaultExport = false;
|
|
99
|
+
let pastLeadingComments = false;
|
|
100
|
+
let exportBlock = null;
|
|
101
|
+
for (const line of source.split("\n")) {
|
|
102
|
+
const trimmed = line.trim();
|
|
103
|
+
if (exportBlock !== null) {
|
|
104
|
+
exportBlock += " " + trimmed;
|
|
105
|
+
if (trimmed.includes("}")) {
|
|
106
|
+
lines.push(exportBlock);
|
|
107
|
+
exportBlock = null;
|
|
108
|
+
}
|
|
109
|
+
continue;
|
|
110
|
+
}
|
|
111
|
+
if (!pastLeadingComments) {
|
|
112
|
+
if (trimmed.startsWith("/*") || trimmed.startsWith("//") || trimmed.startsWith("*") || trimmed === "") {
|
|
113
|
+
if (trimmed !== "" && !trimmed.includes("@generated")) lines.push(trimmed);
|
|
114
|
+
continue;
|
|
115
|
+
}
|
|
116
|
+
pastLeadingComments = true;
|
|
117
|
+
}
|
|
118
|
+
if (trimmed.startsWith("export {") || trimmed.startsWith("export *")) {
|
|
119
|
+
if (trimmed.startsWith("export {") && !trimmed.includes("}")) {
|
|
120
|
+
exportBlock = trimmed;
|
|
121
|
+
continue;
|
|
122
|
+
}
|
|
123
|
+
lines.push(trimmed);
|
|
124
|
+
continue;
|
|
125
|
+
}
|
|
126
|
+
if (trimmed.startsWith("export default") && !hasDefaultExport) {
|
|
127
|
+
lines.push("declare const _default: any;");
|
|
128
|
+
lines.push("export default _default;");
|
|
129
|
+
hasDefaultExport = true;
|
|
130
|
+
continue;
|
|
131
|
+
}
|
|
132
|
+
const constMatch = trimmed.match(/^export\s+(?:const|let|var)\s+(\w+)/);
|
|
133
|
+
if (constMatch) {
|
|
134
|
+
lines.push(`export declare const ${constMatch[1]}: any;`);
|
|
135
|
+
continue;
|
|
136
|
+
}
|
|
137
|
+
const funcMatch = trimmed.match(/^export\s+(?:async\s+)?function\s*\*?\s+(\w+)/);
|
|
138
|
+
if (funcMatch) {
|
|
139
|
+
lines.push(`export declare function ${funcMatch[1]}(...args: any[]): any;`);
|
|
140
|
+
continue;
|
|
141
|
+
}
|
|
142
|
+
if (trimmed.startsWith("export") && !trimmed.startsWith("export {") && !trimmed.startsWith("export *") && !trimmed.startsWith("export default") && !trimmed.startsWith("export type")) console.warn(`[DefinitionCompiler] Unrecognized export pattern in definition stub: ${trimmed}`);
|
|
143
|
+
}
|
|
144
|
+
return lines.join("\n") + "\n";
|
|
145
|
+
}
|
|
146
|
+
};
|
|
147
|
+
|
|
148
|
+
//#endregion
|
|
11
149
|
//#region src/generators/Formatter.ts
|
|
12
150
|
var Formatter = class {
|
|
13
151
|
constructor(outputDir) {
|
|
@@ -46,10 +184,48 @@ var IndexFileGenerator = class {
|
|
|
46
184
|
generate(context) {
|
|
47
185
|
const templateFilePath = path.join(this.templateDir, "Index.ejs");
|
|
48
186
|
const template = fs.readFileSync(templateFilePath, "utf8");
|
|
49
|
-
const
|
|
50
|
-
|
|
51
|
-
const
|
|
52
|
-
|
|
187
|
+
const generatedFiles = context.getGeneratedFiles();
|
|
188
|
+
const groups = /* @__PURE__ */ new Map();
|
|
189
|
+
const rootFiles = /* @__PURE__ */ new Set();
|
|
190
|
+
const existingBarrels = /* @__PURE__ */ new Set();
|
|
191
|
+
for (const file of generatedFiles) {
|
|
192
|
+
const stripped = file.replace(/\.ts$/, "");
|
|
193
|
+
const firstSlash = stripped.indexOf("/");
|
|
194
|
+
if (firstSlash === -1) {
|
|
195
|
+
rootFiles.add(`./${stripped}`);
|
|
196
|
+
continue;
|
|
197
|
+
}
|
|
198
|
+
const firstSegment = stripped.slice(0, firstSlash);
|
|
199
|
+
if (firstSegment === "lib") {
|
|
200
|
+
const secondSlash = stripped.indexOf("/", firstSlash + 1);
|
|
201
|
+
const groupKey = secondSlash === -1 ? stripped : stripped.slice(0, secondSlash);
|
|
202
|
+
const entryName = stripped.slice(groupKey.length + 1);
|
|
203
|
+
if (entryName === "index") existingBarrels.add(groupKey);
|
|
204
|
+
else {
|
|
205
|
+
if (!groups.has(groupKey)) groups.set(groupKey, /* @__PURE__ */ new Set());
|
|
206
|
+
groups.get(groupKey).add(`./${entryName}`);
|
|
207
|
+
}
|
|
208
|
+
} else {
|
|
209
|
+
const entryName = stripped.slice(firstSlash + 1);
|
|
210
|
+
if (entryName === "index") existingBarrels.add(firstSegment);
|
|
211
|
+
else {
|
|
212
|
+
if (!groups.has(firstSegment)) groups.set(firstSegment, /* @__PURE__ */ new Set());
|
|
213
|
+
groups.get(firstSegment).add(`./${entryName}`);
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
for (const [groupKey, entries] of groups) {
|
|
218
|
+
if (existingBarrels.has(groupKey)) continue;
|
|
219
|
+
const domainBarrelContent = render(template, { indexPaths: Array.from(entries).sort() });
|
|
220
|
+
const domainIndexPath = path.join(context.outputDir, groupKey, "index.ts");
|
|
221
|
+
fs.mkdirSync(path.dirname(domainIndexPath), { recursive: true });
|
|
222
|
+
fs.writeFileSync(domainIndexPath, domainBarrelContent);
|
|
223
|
+
}
|
|
224
|
+
const rootIndexPaths = new Set(rootFiles);
|
|
225
|
+
for (const groupKey of groups.keys()) rootIndexPaths.add(`./${groupKey}`);
|
|
226
|
+
for (const barrelKey of existingBarrels) rootIndexPaths.add(`./${barrelKey}`);
|
|
227
|
+
const rootContent = render(template, { indexPaths: Array.from(rootIndexPaths).sort() });
|
|
228
|
+
fs.writeFileSync(path.join(context.outputDir, "index.ts"), rootContent);
|
|
53
229
|
}
|
|
54
230
|
};
|
|
55
231
|
|
|
@@ -4116,6 +4292,7 @@ var Generator = class {
|
|
|
4116
4292
|
contextBuilder;
|
|
4117
4293
|
pluginLoader;
|
|
4118
4294
|
indexFileGenerator;
|
|
4295
|
+
definitionCompiler;
|
|
4119
4296
|
resourceReader = null;
|
|
4120
4297
|
formatter = null;
|
|
4121
4298
|
inputDir = "";
|
|
@@ -4129,6 +4306,7 @@ var Generator = class {
|
|
|
4129
4306
|
this.contextBuilder = contextBuilder ?? new PluginContextBuilder();
|
|
4130
4307
|
this.pluginLoader = pluginLoader ?? new PluginLoader(this.registry, requiredPlugins);
|
|
4131
4308
|
this.indexFileGenerator = indexFileGenerator ?? new IndexFileGenerator(this.templateDir);
|
|
4309
|
+
this.definitionCompiler = new DefinitionCompiler();
|
|
4132
4310
|
}
|
|
4133
4311
|
/**
|
|
4134
4312
|
* Generate code using the plugin system
|
|
@@ -4187,6 +4365,8 @@ var Generator = class {
|
|
|
4187
4365
|
this.indexFileGenerator.generate(generatorContext);
|
|
4188
4366
|
console.info("Finalizing plugins...");
|
|
4189
4367
|
for (const registration of this.registry.getAll()) if (registration.plugin.finalize) await registration.plugin.finalize(pluginContext);
|
|
4368
|
+
console.info("Compiling definitions...");
|
|
4369
|
+
this.definitionCompiler.compileInPlace(this.sourceDir);
|
|
4190
4370
|
if (config?.format ?? true) await this.formatter.formatCode();
|
|
4191
4371
|
console.info("Generation complete!");
|
|
4192
4372
|
console.info(`Generated files: ${this.contextBuilder.getGeneratedFiles().length}`);
|
package/dist/cli.cjs
CHANGED
|
@@ -34,9 +34,147 @@ let commander = require("commander");
|
|
|
34
34
|
let _rexeus_typeweaver_gen = require("@rexeus/typeweaver-gen");
|
|
35
35
|
let _rexeus_typeweaver_types = require("@rexeus/typeweaver-types");
|
|
36
36
|
_rexeus_typeweaver_types = __toESM(_rexeus_typeweaver_types);
|
|
37
|
+
let oxc_transform = require("oxc-transform");
|
|
37
38
|
let ejs = require("ejs");
|
|
38
39
|
let _rexeus_typeweaver_core = require("@rexeus/typeweaver-core");
|
|
39
40
|
|
|
41
|
+
//#region src/generators/errors/DefinitionCompilationError.ts
|
|
42
|
+
var DefinitionCompilationError = class extends Error {
|
|
43
|
+
constructor(filePath, details) {
|
|
44
|
+
super(`Failed to compile definition at \`${filePath}\`. ${details}`);
|
|
45
|
+
this.filePath = filePath;
|
|
46
|
+
this.details = details;
|
|
47
|
+
}
|
|
48
|
+
};
|
|
49
|
+
|
|
50
|
+
//#endregion
|
|
51
|
+
//#region src/generators/DefinitionCompiler.ts
|
|
52
|
+
/**
|
|
53
|
+
* Compiles TypeScript definition files to JavaScript + declaration stubs.
|
|
54
|
+
*
|
|
55
|
+
* Definition files contain Zod schemas that cause tsc to exhaust memory
|
|
56
|
+
* when type-checking due to Zod v4's deeply recursive type inference.
|
|
57
|
+
* By pre-compiling definitions to .js + .d.ts, tsc only sees lightweight
|
|
58
|
+
* type stubs (everything typed as `any`) while runtime behavior is preserved.
|
|
59
|
+
*/
|
|
60
|
+
var DefinitionCompiler = class DefinitionCompiler {
|
|
61
|
+
static GENERATED_HEADER = [
|
|
62
|
+
"/* eslint-disable */",
|
|
63
|
+
"/**",
|
|
64
|
+
" * This file was automatically generated by typeweaver.",
|
|
65
|
+
" * DO NOT EDIT. Instead, modify the source definition file and generate again.",
|
|
66
|
+
" *",
|
|
67
|
+
" * @generated by @rexeus/typeweaver",
|
|
68
|
+
" */"
|
|
69
|
+
].join("\n");
|
|
70
|
+
/**
|
|
71
|
+
* Compiles all .ts definition files in-place to .js + .d.ts stubs.
|
|
72
|
+
* The original .ts files are removed after compilation.
|
|
73
|
+
*/
|
|
74
|
+
compileInPlace(definitionDir) {
|
|
75
|
+
const errors = [];
|
|
76
|
+
this.processDirectory(definitionDir, errors);
|
|
77
|
+
if (errors.length > 0) {
|
|
78
|
+
const summary = errors.map((e) => ` - ${e.filePath}: ${e.details}`).join("\n");
|
|
79
|
+
throw new DefinitionCompilationError(definitionDir, `${errors.length} file(s) failed to compile:\n${summary}`);
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
processDirectory(dir, errors) {
|
|
83
|
+
const entries = node_fs.default.readdirSync(dir, { withFileTypes: true });
|
|
84
|
+
for (const entry of entries) {
|
|
85
|
+
const fullPath = node_path.default.join(dir, entry.name);
|
|
86
|
+
if (entry.isDirectory()) {
|
|
87
|
+
this.processDirectory(fullPath, errors);
|
|
88
|
+
continue;
|
|
89
|
+
}
|
|
90
|
+
if (entry.isFile() && entry.name.endsWith(".ts") && !entry.name.endsWith(".d.ts")) try {
|
|
91
|
+
this.compileFile(fullPath, dir, entry.name);
|
|
92
|
+
} catch (error) {
|
|
93
|
+
errors.push(error instanceof DefinitionCompilationError ? error : new DefinitionCompilationError(fullPath, String(error)));
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
compileFile(srcPath, dir, fileName) {
|
|
98
|
+
try {
|
|
99
|
+
const source = node_fs.default.readFileSync(srcPath, "utf-8");
|
|
100
|
+
const baseName = fileName.replace(/\.ts$/, "");
|
|
101
|
+
const jsCode = this.transpileToJs(fileName, source);
|
|
102
|
+
const dtsCode = this.generateDtsStub(source);
|
|
103
|
+
node_fs.default.writeFileSync(node_path.default.join(dir, `${baseName}.js`), jsCode);
|
|
104
|
+
node_fs.default.writeFileSync(node_path.default.join(dir, `${baseName}.d.ts`), dtsCode);
|
|
105
|
+
node_fs.default.unlinkSync(srcPath);
|
|
106
|
+
} catch (error) {
|
|
107
|
+
throw new DefinitionCompilationError(srcPath, error instanceof DefinitionCompilationError ? error.details : error instanceof Error ? error.message : String(error));
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
transpileToJs(fileName, source) {
|
|
111
|
+
const result = (0, oxc_transform.transformSync)(fileName, source, {
|
|
112
|
+
lang: "ts",
|
|
113
|
+
sourceType: "module"
|
|
114
|
+
});
|
|
115
|
+
const errors = result.errors.filter((e) => e.severity === "Error");
|
|
116
|
+
if (errors.length > 0) throw new DefinitionCompilationError(fileName, errors.map((e) => e.message).join("; "));
|
|
117
|
+
return result.code;
|
|
118
|
+
}
|
|
119
|
+
/**
|
|
120
|
+
* Generates a minimal .d.ts stub from TypeScript source.
|
|
121
|
+
* All value exports are typed as `any` to avoid pulling in Zod's type system.
|
|
122
|
+
* Re-exports are preserved so barrel files chain correctly.
|
|
123
|
+
*/
|
|
124
|
+
generateDtsStub(source) {
|
|
125
|
+
if (source.startsWith(DefinitionCompiler.GENERATED_HEADER)) return source;
|
|
126
|
+
const lines = [DefinitionCompiler.GENERATED_HEADER];
|
|
127
|
+
let hasDefaultExport = false;
|
|
128
|
+
let pastLeadingComments = false;
|
|
129
|
+
let exportBlock = null;
|
|
130
|
+
for (const line of source.split("\n")) {
|
|
131
|
+
const trimmed = line.trim();
|
|
132
|
+
if (exportBlock !== null) {
|
|
133
|
+
exportBlock += " " + trimmed;
|
|
134
|
+
if (trimmed.includes("}")) {
|
|
135
|
+
lines.push(exportBlock);
|
|
136
|
+
exportBlock = null;
|
|
137
|
+
}
|
|
138
|
+
continue;
|
|
139
|
+
}
|
|
140
|
+
if (!pastLeadingComments) {
|
|
141
|
+
if (trimmed.startsWith("/*") || trimmed.startsWith("//") || trimmed.startsWith("*") || trimmed === "") {
|
|
142
|
+
if (trimmed !== "" && !trimmed.includes("@generated")) lines.push(trimmed);
|
|
143
|
+
continue;
|
|
144
|
+
}
|
|
145
|
+
pastLeadingComments = true;
|
|
146
|
+
}
|
|
147
|
+
if (trimmed.startsWith("export {") || trimmed.startsWith("export *")) {
|
|
148
|
+
if (trimmed.startsWith("export {") && !trimmed.includes("}")) {
|
|
149
|
+
exportBlock = trimmed;
|
|
150
|
+
continue;
|
|
151
|
+
}
|
|
152
|
+
lines.push(trimmed);
|
|
153
|
+
continue;
|
|
154
|
+
}
|
|
155
|
+
if (trimmed.startsWith("export default") && !hasDefaultExport) {
|
|
156
|
+
lines.push("declare const _default: any;");
|
|
157
|
+
lines.push("export default _default;");
|
|
158
|
+
hasDefaultExport = true;
|
|
159
|
+
continue;
|
|
160
|
+
}
|
|
161
|
+
const constMatch = trimmed.match(/^export\s+(?:const|let|var)\s+(\w+)/);
|
|
162
|
+
if (constMatch) {
|
|
163
|
+
lines.push(`export declare const ${constMatch[1]}: any;`);
|
|
164
|
+
continue;
|
|
165
|
+
}
|
|
166
|
+
const funcMatch = trimmed.match(/^export\s+(?:async\s+)?function\s*\*?\s+(\w+)/);
|
|
167
|
+
if (funcMatch) {
|
|
168
|
+
lines.push(`export declare function ${funcMatch[1]}(...args: any[]): any;`);
|
|
169
|
+
continue;
|
|
170
|
+
}
|
|
171
|
+
if (trimmed.startsWith("export") && !trimmed.startsWith("export {") && !trimmed.startsWith("export *") && !trimmed.startsWith("export default") && !trimmed.startsWith("export type")) console.warn(`[DefinitionCompiler] Unrecognized export pattern in definition stub: ${trimmed}`);
|
|
172
|
+
}
|
|
173
|
+
return lines.join("\n") + "\n";
|
|
174
|
+
}
|
|
175
|
+
};
|
|
176
|
+
|
|
177
|
+
//#endregion
|
|
40
178
|
//#region src/generators/Formatter.ts
|
|
41
179
|
var Formatter = class {
|
|
42
180
|
constructor(outputDir) {
|
|
@@ -75,10 +213,48 @@ var IndexFileGenerator = class {
|
|
|
75
213
|
generate(context) {
|
|
76
214
|
const templateFilePath = node_path.default.join(this.templateDir, "Index.ejs");
|
|
77
215
|
const template = node_fs.default.readFileSync(templateFilePath, "utf8");
|
|
78
|
-
const
|
|
79
|
-
|
|
80
|
-
const
|
|
81
|
-
|
|
216
|
+
const generatedFiles = context.getGeneratedFiles();
|
|
217
|
+
const groups = /* @__PURE__ */ new Map();
|
|
218
|
+
const rootFiles = /* @__PURE__ */ new Set();
|
|
219
|
+
const existingBarrels = /* @__PURE__ */ new Set();
|
|
220
|
+
for (const file of generatedFiles) {
|
|
221
|
+
const stripped = file.replace(/\.ts$/, "");
|
|
222
|
+
const firstSlash = stripped.indexOf("/");
|
|
223
|
+
if (firstSlash === -1) {
|
|
224
|
+
rootFiles.add(`./${stripped}`);
|
|
225
|
+
continue;
|
|
226
|
+
}
|
|
227
|
+
const firstSegment = stripped.slice(0, firstSlash);
|
|
228
|
+
if (firstSegment === "lib") {
|
|
229
|
+
const secondSlash = stripped.indexOf("/", firstSlash + 1);
|
|
230
|
+
const groupKey = secondSlash === -1 ? stripped : stripped.slice(0, secondSlash);
|
|
231
|
+
const entryName = stripped.slice(groupKey.length + 1);
|
|
232
|
+
if (entryName === "index") existingBarrels.add(groupKey);
|
|
233
|
+
else {
|
|
234
|
+
if (!groups.has(groupKey)) groups.set(groupKey, /* @__PURE__ */ new Set());
|
|
235
|
+
groups.get(groupKey).add(`./${entryName}`);
|
|
236
|
+
}
|
|
237
|
+
} else {
|
|
238
|
+
const entryName = stripped.slice(firstSlash + 1);
|
|
239
|
+
if (entryName === "index") existingBarrels.add(firstSegment);
|
|
240
|
+
else {
|
|
241
|
+
if (!groups.has(firstSegment)) groups.set(firstSegment, /* @__PURE__ */ new Set());
|
|
242
|
+
groups.get(firstSegment).add(`./${entryName}`);
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
for (const [groupKey, entries] of groups) {
|
|
247
|
+
if (existingBarrels.has(groupKey)) continue;
|
|
248
|
+
const domainBarrelContent = (0, ejs.render)(template, { indexPaths: Array.from(entries).sort() });
|
|
249
|
+
const domainIndexPath = node_path.default.join(context.outputDir, groupKey, "index.ts");
|
|
250
|
+
node_fs.default.mkdirSync(node_path.default.dirname(domainIndexPath), { recursive: true });
|
|
251
|
+
node_fs.default.writeFileSync(domainIndexPath, domainBarrelContent);
|
|
252
|
+
}
|
|
253
|
+
const rootIndexPaths = new Set(rootFiles);
|
|
254
|
+
for (const groupKey of groups.keys()) rootIndexPaths.add(`./${groupKey}`);
|
|
255
|
+
for (const barrelKey of existingBarrels) rootIndexPaths.add(`./${barrelKey}`);
|
|
256
|
+
const rootContent = (0, ejs.render)(template, { indexPaths: Array.from(rootIndexPaths).sort() });
|
|
257
|
+
node_fs.default.writeFileSync(node_path.default.join(context.outputDir, "index.ts"), rootContent);
|
|
82
258
|
}
|
|
83
259
|
};
|
|
84
260
|
|
|
@@ -4145,6 +4321,7 @@ var Generator = class {
|
|
|
4145
4321
|
contextBuilder;
|
|
4146
4322
|
pluginLoader;
|
|
4147
4323
|
indexFileGenerator;
|
|
4324
|
+
definitionCompiler;
|
|
4148
4325
|
resourceReader = null;
|
|
4149
4326
|
formatter = null;
|
|
4150
4327
|
inputDir = "";
|
|
@@ -4158,6 +4335,7 @@ var Generator = class {
|
|
|
4158
4335
|
this.contextBuilder = contextBuilder ?? new _rexeus_typeweaver_gen.PluginContextBuilder();
|
|
4159
4336
|
this.pluginLoader = pluginLoader ?? new PluginLoader(this.registry, requiredPlugins);
|
|
4160
4337
|
this.indexFileGenerator = indexFileGenerator ?? new IndexFileGenerator(this.templateDir);
|
|
4338
|
+
this.definitionCompiler = new DefinitionCompiler();
|
|
4161
4339
|
}
|
|
4162
4340
|
/**
|
|
4163
4341
|
* Generate code using the plugin system
|
|
@@ -4216,6 +4394,8 @@ var Generator = class {
|
|
|
4216
4394
|
this.indexFileGenerator.generate(generatorContext);
|
|
4217
4395
|
console.info("Finalizing plugins...");
|
|
4218
4396
|
for (const registration of this.registry.getAll()) if (registration.plugin.finalize) await registration.plugin.finalize(pluginContext);
|
|
4397
|
+
console.info("Compiling definitions...");
|
|
4398
|
+
this.definitionCompiler.compileInPlace(this.sourceDir);
|
|
4219
4399
|
if (config?.format ?? true) await this.formatter.formatCode();
|
|
4220
4400
|
console.info("Generation complete!");
|
|
4221
4401
|
console.info(`Generated files: ${this.contextBuilder.getGeneratedFiles().length}`);
|
package/dist/cli.mjs
CHANGED
|
@@ -4,9 +4,147 @@ import { fileURLToPath, pathToFileURL } from "node:url";
|
|
|
4
4
|
import { Command } from "commander";
|
|
5
5
|
import { PluginContextBuilder, PluginRegistry } from "@rexeus/typeweaver-gen";
|
|
6
6
|
import TypesPlugin from "@rexeus/typeweaver-types";
|
|
7
|
+
import { transformSync } from "oxc-transform";
|
|
7
8
|
import { render } from "ejs";
|
|
8
9
|
import { HttpMethod, HttpOperationDefinition, HttpResponseDefinition, HttpStatusCode, HttpStatusCodeNameMap } from "@rexeus/typeweaver-core";
|
|
9
10
|
|
|
11
|
+
//#region src/generators/errors/DefinitionCompilationError.ts
|
|
12
|
+
var DefinitionCompilationError = class extends Error {
|
|
13
|
+
constructor(filePath, details) {
|
|
14
|
+
super(`Failed to compile definition at \`${filePath}\`. ${details}`);
|
|
15
|
+
this.filePath = filePath;
|
|
16
|
+
this.details = details;
|
|
17
|
+
}
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
//#endregion
|
|
21
|
+
//#region src/generators/DefinitionCompiler.ts
|
|
22
|
+
/**
|
|
23
|
+
* Compiles TypeScript definition files to JavaScript + declaration stubs.
|
|
24
|
+
*
|
|
25
|
+
* Definition files contain Zod schemas that cause tsc to exhaust memory
|
|
26
|
+
* when type-checking due to Zod v4's deeply recursive type inference.
|
|
27
|
+
* By pre-compiling definitions to .js + .d.ts, tsc only sees lightweight
|
|
28
|
+
* type stubs (everything typed as `any`) while runtime behavior is preserved.
|
|
29
|
+
*/
|
|
30
|
+
var DefinitionCompiler = class DefinitionCompiler {
|
|
31
|
+
static GENERATED_HEADER = [
|
|
32
|
+
"/* eslint-disable */",
|
|
33
|
+
"/**",
|
|
34
|
+
" * This file was automatically generated by typeweaver.",
|
|
35
|
+
" * DO NOT EDIT. Instead, modify the source definition file and generate again.",
|
|
36
|
+
" *",
|
|
37
|
+
" * @generated by @rexeus/typeweaver",
|
|
38
|
+
" */"
|
|
39
|
+
].join("\n");
|
|
40
|
+
/**
|
|
41
|
+
* Compiles all .ts definition files in-place to .js + .d.ts stubs.
|
|
42
|
+
* The original .ts files are removed after compilation.
|
|
43
|
+
*/
|
|
44
|
+
compileInPlace(definitionDir) {
|
|
45
|
+
const errors = [];
|
|
46
|
+
this.processDirectory(definitionDir, errors);
|
|
47
|
+
if (errors.length > 0) {
|
|
48
|
+
const summary = errors.map((e) => ` - ${e.filePath}: ${e.details}`).join("\n");
|
|
49
|
+
throw new DefinitionCompilationError(definitionDir, `${errors.length} file(s) failed to compile:\n${summary}`);
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
processDirectory(dir, errors) {
|
|
53
|
+
const entries = fs.readdirSync(dir, { withFileTypes: true });
|
|
54
|
+
for (const entry of entries) {
|
|
55
|
+
const fullPath = path.join(dir, entry.name);
|
|
56
|
+
if (entry.isDirectory()) {
|
|
57
|
+
this.processDirectory(fullPath, errors);
|
|
58
|
+
continue;
|
|
59
|
+
}
|
|
60
|
+
if (entry.isFile() && entry.name.endsWith(".ts") && !entry.name.endsWith(".d.ts")) try {
|
|
61
|
+
this.compileFile(fullPath, dir, entry.name);
|
|
62
|
+
} catch (error) {
|
|
63
|
+
errors.push(error instanceof DefinitionCompilationError ? error : new DefinitionCompilationError(fullPath, String(error)));
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
compileFile(srcPath, dir, fileName) {
|
|
68
|
+
try {
|
|
69
|
+
const source = fs.readFileSync(srcPath, "utf-8");
|
|
70
|
+
const baseName = fileName.replace(/\.ts$/, "");
|
|
71
|
+
const jsCode = this.transpileToJs(fileName, source);
|
|
72
|
+
const dtsCode = this.generateDtsStub(source);
|
|
73
|
+
fs.writeFileSync(path.join(dir, `${baseName}.js`), jsCode);
|
|
74
|
+
fs.writeFileSync(path.join(dir, `${baseName}.d.ts`), dtsCode);
|
|
75
|
+
fs.unlinkSync(srcPath);
|
|
76
|
+
} catch (error) {
|
|
77
|
+
throw new DefinitionCompilationError(srcPath, error instanceof DefinitionCompilationError ? error.details : error instanceof Error ? error.message : String(error));
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
transpileToJs(fileName, source) {
|
|
81
|
+
const result = transformSync(fileName, source, {
|
|
82
|
+
lang: "ts",
|
|
83
|
+
sourceType: "module"
|
|
84
|
+
});
|
|
85
|
+
const errors = result.errors.filter((e) => e.severity === "Error");
|
|
86
|
+
if (errors.length > 0) throw new DefinitionCompilationError(fileName, errors.map((e) => e.message).join("; "));
|
|
87
|
+
return result.code;
|
|
88
|
+
}
|
|
89
|
+
/**
|
|
90
|
+
* Generates a minimal .d.ts stub from TypeScript source.
|
|
91
|
+
* All value exports are typed as `any` to avoid pulling in Zod's type system.
|
|
92
|
+
* Re-exports are preserved so barrel files chain correctly.
|
|
93
|
+
*/
|
|
94
|
+
generateDtsStub(source) {
|
|
95
|
+
if (source.startsWith(DefinitionCompiler.GENERATED_HEADER)) return source;
|
|
96
|
+
const lines = [DefinitionCompiler.GENERATED_HEADER];
|
|
97
|
+
let hasDefaultExport = false;
|
|
98
|
+
let pastLeadingComments = false;
|
|
99
|
+
let exportBlock = null;
|
|
100
|
+
for (const line of source.split("\n")) {
|
|
101
|
+
const trimmed = line.trim();
|
|
102
|
+
if (exportBlock !== null) {
|
|
103
|
+
exportBlock += " " + trimmed;
|
|
104
|
+
if (trimmed.includes("}")) {
|
|
105
|
+
lines.push(exportBlock);
|
|
106
|
+
exportBlock = null;
|
|
107
|
+
}
|
|
108
|
+
continue;
|
|
109
|
+
}
|
|
110
|
+
if (!pastLeadingComments) {
|
|
111
|
+
if (trimmed.startsWith("/*") || trimmed.startsWith("//") || trimmed.startsWith("*") || trimmed === "") {
|
|
112
|
+
if (trimmed !== "" && !trimmed.includes("@generated")) lines.push(trimmed);
|
|
113
|
+
continue;
|
|
114
|
+
}
|
|
115
|
+
pastLeadingComments = true;
|
|
116
|
+
}
|
|
117
|
+
if (trimmed.startsWith("export {") || trimmed.startsWith("export *")) {
|
|
118
|
+
if (trimmed.startsWith("export {") && !trimmed.includes("}")) {
|
|
119
|
+
exportBlock = trimmed;
|
|
120
|
+
continue;
|
|
121
|
+
}
|
|
122
|
+
lines.push(trimmed);
|
|
123
|
+
continue;
|
|
124
|
+
}
|
|
125
|
+
if (trimmed.startsWith("export default") && !hasDefaultExport) {
|
|
126
|
+
lines.push("declare const _default: any;");
|
|
127
|
+
lines.push("export default _default;");
|
|
128
|
+
hasDefaultExport = true;
|
|
129
|
+
continue;
|
|
130
|
+
}
|
|
131
|
+
const constMatch = trimmed.match(/^export\s+(?:const|let|var)\s+(\w+)/);
|
|
132
|
+
if (constMatch) {
|
|
133
|
+
lines.push(`export declare const ${constMatch[1]}: any;`);
|
|
134
|
+
continue;
|
|
135
|
+
}
|
|
136
|
+
const funcMatch = trimmed.match(/^export\s+(?:async\s+)?function\s*\*?\s+(\w+)/);
|
|
137
|
+
if (funcMatch) {
|
|
138
|
+
lines.push(`export declare function ${funcMatch[1]}(...args: any[]): any;`);
|
|
139
|
+
continue;
|
|
140
|
+
}
|
|
141
|
+
if (trimmed.startsWith("export") && !trimmed.startsWith("export {") && !trimmed.startsWith("export *") && !trimmed.startsWith("export default") && !trimmed.startsWith("export type")) console.warn(`[DefinitionCompiler] Unrecognized export pattern in definition stub: ${trimmed}`);
|
|
142
|
+
}
|
|
143
|
+
return lines.join("\n") + "\n";
|
|
144
|
+
}
|
|
145
|
+
};
|
|
146
|
+
|
|
147
|
+
//#endregion
|
|
10
148
|
//#region src/generators/Formatter.ts
|
|
11
149
|
var Formatter = class {
|
|
12
150
|
constructor(outputDir) {
|
|
@@ -45,10 +183,48 @@ var IndexFileGenerator = class {
|
|
|
45
183
|
generate(context) {
|
|
46
184
|
const templateFilePath = path.join(this.templateDir, "Index.ejs");
|
|
47
185
|
const template = fs.readFileSync(templateFilePath, "utf8");
|
|
48
|
-
const
|
|
49
|
-
|
|
50
|
-
const
|
|
51
|
-
|
|
186
|
+
const generatedFiles = context.getGeneratedFiles();
|
|
187
|
+
const groups = /* @__PURE__ */ new Map();
|
|
188
|
+
const rootFiles = /* @__PURE__ */ new Set();
|
|
189
|
+
const existingBarrels = /* @__PURE__ */ new Set();
|
|
190
|
+
for (const file of generatedFiles) {
|
|
191
|
+
const stripped = file.replace(/\.ts$/, "");
|
|
192
|
+
const firstSlash = stripped.indexOf("/");
|
|
193
|
+
if (firstSlash === -1) {
|
|
194
|
+
rootFiles.add(`./${stripped}`);
|
|
195
|
+
continue;
|
|
196
|
+
}
|
|
197
|
+
const firstSegment = stripped.slice(0, firstSlash);
|
|
198
|
+
if (firstSegment === "lib") {
|
|
199
|
+
const secondSlash = stripped.indexOf("/", firstSlash + 1);
|
|
200
|
+
const groupKey = secondSlash === -1 ? stripped : stripped.slice(0, secondSlash);
|
|
201
|
+
const entryName = stripped.slice(groupKey.length + 1);
|
|
202
|
+
if (entryName === "index") existingBarrels.add(groupKey);
|
|
203
|
+
else {
|
|
204
|
+
if (!groups.has(groupKey)) groups.set(groupKey, /* @__PURE__ */ new Set());
|
|
205
|
+
groups.get(groupKey).add(`./${entryName}`);
|
|
206
|
+
}
|
|
207
|
+
} else {
|
|
208
|
+
const entryName = stripped.slice(firstSlash + 1);
|
|
209
|
+
if (entryName === "index") existingBarrels.add(firstSegment);
|
|
210
|
+
else {
|
|
211
|
+
if (!groups.has(firstSegment)) groups.set(firstSegment, /* @__PURE__ */ new Set());
|
|
212
|
+
groups.get(firstSegment).add(`./${entryName}`);
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
for (const [groupKey, entries] of groups) {
|
|
217
|
+
if (existingBarrels.has(groupKey)) continue;
|
|
218
|
+
const domainBarrelContent = render(template, { indexPaths: Array.from(entries).sort() });
|
|
219
|
+
const domainIndexPath = path.join(context.outputDir, groupKey, "index.ts");
|
|
220
|
+
fs.mkdirSync(path.dirname(domainIndexPath), { recursive: true });
|
|
221
|
+
fs.writeFileSync(domainIndexPath, domainBarrelContent);
|
|
222
|
+
}
|
|
223
|
+
const rootIndexPaths = new Set(rootFiles);
|
|
224
|
+
for (const groupKey of groups.keys()) rootIndexPaths.add(`./${groupKey}`);
|
|
225
|
+
for (const barrelKey of existingBarrels) rootIndexPaths.add(`./${barrelKey}`);
|
|
226
|
+
const rootContent = render(template, { indexPaths: Array.from(rootIndexPaths).sort() });
|
|
227
|
+
fs.writeFileSync(path.join(context.outputDir, "index.ts"), rootContent);
|
|
52
228
|
}
|
|
53
229
|
};
|
|
54
230
|
|
|
@@ -4115,6 +4291,7 @@ var Generator = class {
|
|
|
4115
4291
|
contextBuilder;
|
|
4116
4292
|
pluginLoader;
|
|
4117
4293
|
indexFileGenerator;
|
|
4294
|
+
definitionCompiler;
|
|
4118
4295
|
resourceReader = null;
|
|
4119
4296
|
formatter = null;
|
|
4120
4297
|
inputDir = "";
|
|
@@ -4128,6 +4305,7 @@ var Generator = class {
|
|
|
4128
4305
|
this.contextBuilder = contextBuilder ?? new PluginContextBuilder();
|
|
4129
4306
|
this.pluginLoader = pluginLoader ?? new PluginLoader(this.registry, requiredPlugins);
|
|
4130
4307
|
this.indexFileGenerator = indexFileGenerator ?? new IndexFileGenerator(this.templateDir);
|
|
4308
|
+
this.definitionCompiler = new DefinitionCompiler();
|
|
4131
4309
|
}
|
|
4132
4310
|
/**
|
|
4133
4311
|
* Generate code using the plugin system
|
|
@@ -4186,6 +4364,8 @@ var Generator = class {
|
|
|
4186
4364
|
this.indexFileGenerator.generate(generatorContext);
|
|
4187
4365
|
console.info("Finalizing plugins...");
|
|
4188
4366
|
for (const registration of this.registry.getAll()) if (registration.plugin.finalize) await registration.plugin.finalize(pluginContext);
|
|
4367
|
+
console.info("Compiling definitions...");
|
|
4368
|
+
this.definitionCompiler.compileInPlace(this.sourceDir);
|
|
4189
4369
|
if (config?.format ?? true) await this.formatter.formatCode();
|
|
4190
4370
|
console.info("Generation complete!");
|
|
4191
4371
|
console.info(`Generated files: ${this.contextBuilder.getGeneratedFiles().length}`);
|