cloesce 0.0.5-unstable.2 → 0.0.5-unstable.3
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/ast.d.ts +100 -80
- package/dist/ast.js +12 -12
- package/dist/cli.d.ts +1 -1
- package/dist/cli.js +368 -331
- package/dist/extractor/err.d.ts +26 -26
- package/dist/extractor/err.js +129 -100
- package/dist/extractor/extract.d.ts +60 -17
- package/dist/extractor/extract.js +826 -764
- package/dist/router/crud.d.ts +1 -1
- package/dist/router/crud.js +42 -43
- package/dist/router/router.d.ts +135 -98
- package/dist/router/router.js +424 -381
- package/dist/router/validator.d.ts +11 -6
- package/dist/router/validator.js +158 -144
- package/dist/router/wasm.d.ts +56 -22
- package/dist/router/wasm.js +91 -79
- package/dist/ui/backend.d.ts +214 -181
- package/dist/ui/backend.js +258 -245
- package/dist/ui/client.d.ts +1 -1
- package/dist/ui/common.d.ts +54 -31
- package/dist/ui/common.js +171 -159
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -3,369 +3,406 @@ import { WASI } from "node:wasi";
|
|
|
3
3
|
import fs from "node:fs";
|
|
4
4
|
import { readFile } from "fs/promises";
|
|
5
5
|
import path from "node:path";
|
|
6
|
-
import {
|
|
6
|
+
import {
|
|
7
|
+
command,
|
|
8
|
+
run,
|
|
9
|
+
subcommands,
|
|
10
|
+
flag,
|
|
11
|
+
string,
|
|
12
|
+
positional,
|
|
13
|
+
option,
|
|
14
|
+
optional,
|
|
15
|
+
} from "cmd-ts";
|
|
7
16
|
import { Project } from "ts-morph";
|
|
8
17
|
import { CidlExtractor } from "./extractor/extract.js";
|
|
9
|
-
import {
|
|
18
|
+
import {
|
|
19
|
+
ExtractorError,
|
|
20
|
+
ExtractorErrorCode,
|
|
21
|
+
getErrorInfo,
|
|
22
|
+
} from "./extractor/err.js";
|
|
10
23
|
let debugPhase = "npm cloesce";
|
|
11
24
|
function debug(...args) {
|
|
12
|
-
|
|
25
|
+
console.log(`[${debugPhase}]: `, ...args);
|
|
13
26
|
}
|
|
14
27
|
const cmds = subcommands({
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
28
|
+
name: "cloesce",
|
|
29
|
+
cmds: {
|
|
30
|
+
compile: command({
|
|
31
|
+
name: "compile",
|
|
32
|
+
description: "Run through the full compilation process.",
|
|
33
|
+
args: {},
|
|
34
|
+
handler: async () => {
|
|
35
|
+
const config = loadCloesceConfig(process.cwd());
|
|
36
|
+
if (!config) {
|
|
37
|
+
process.exit(1);
|
|
38
|
+
}
|
|
39
|
+
await extract(config);
|
|
40
|
+
debugPhase = "npm cloesce";
|
|
41
|
+
const outputDir = config.outPath ?? ".generated";
|
|
42
|
+
const generateConfig = {
|
|
43
|
+
name: "generate",
|
|
44
|
+
wasmFile: "generator.wasm",
|
|
45
|
+
args: [
|
|
46
|
+
"generate",
|
|
47
|
+
path.join(outputDir, "cidl.pre.json"),
|
|
48
|
+
path.join(outputDir, "cidl.json"),
|
|
49
|
+
"wrangler.toml",
|
|
50
|
+
path.join(outputDir, "workers.ts"),
|
|
51
|
+
path.join(outputDir, "client.ts"),
|
|
52
|
+
config.workersUrl,
|
|
53
|
+
],
|
|
54
|
+
};
|
|
55
|
+
await generate(generateConfig);
|
|
56
|
+
},
|
|
57
|
+
}),
|
|
58
|
+
extract: command({
|
|
59
|
+
name: "extract",
|
|
60
|
+
description: "Extract models and write cidl.pre.json",
|
|
61
|
+
args: {
|
|
62
|
+
projectName: option({
|
|
63
|
+
long: "project-name",
|
|
64
|
+
type: optional(string),
|
|
65
|
+
description: "Project name",
|
|
44
66
|
}),
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
projectName: option({
|
|
50
|
-
long: "project-name",
|
|
51
|
-
type: optional(string),
|
|
52
|
-
description: "Project name",
|
|
53
|
-
}),
|
|
54
|
-
out: option({
|
|
55
|
-
long: "out",
|
|
56
|
-
short: "o",
|
|
57
|
-
type: optional(string),
|
|
58
|
-
}),
|
|
59
|
-
inp: option({
|
|
60
|
-
long: "in",
|
|
61
|
-
short: "i",
|
|
62
|
-
type: optional(string),
|
|
63
|
-
description: "Input file or directory",
|
|
64
|
-
}),
|
|
65
|
-
truncateSourcePaths: flag({
|
|
66
|
-
long: "truncateSourcePaths",
|
|
67
|
-
description: "Sets all source paths to just their file name",
|
|
68
|
-
}),
|
|
69
|
-
skipTsCheck: flag({
|
|
70
|
-
long: "skipTsCheck",
|
|
71
|
-
description: "Skip TypeScript compilation checks",
|
|
72
|
-
}),
|
|
73
|
-
},
|
|
74
|
-
handler: async (args) => {
|
|
75
|
-
const config = {
|
|
76
|
-
projectName: args.projectName,
|
|
77
|
-
outPath: args.out,
|
|
78
|
-
paths: [args.inp],
|
|
79
|
-
truncateSourcePaths: false,
|
|
80
|
-
workersUrl: "",
|
|
81
|
-
migrationsPath: "",
|
|
82
|
-
};
|
|
83
|
-
await extract(config, {
|
|
84
|
-
truncateSourcePaths: args.truncateSourcePaths,
|
|
85
|
-
skipTsCheck: args.skipTsCheck,
|
|
86
|
-
});
|
|
87
|
-
},
|
|
67
|
+
out: option({
|
|
68
|
+
long: "out",
|
|
69
|
+
short: "o",
|
|
70
|
+
type: optional(string),
|
|
88
71
|
}),
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
debug: flag({
|
|
95
|
-
long: "debug",
|
|
96
|
-
short: "d",
|
|
97
|
-
description: "Show debug output",
|
|
98
|
-
}),
|
|
99
|
-
},
|
|
100
|
-
handler: async (args) => {
|
|
101
|
-
const config = loadCloesceConfig(process.cwd());
|
|
102
|
-
if (!config) {
|
|
103
|
-
process.exit(1);
|
|
104
|
-
}
|
|
105
|
-
const cidlPath = path.join(config.outPath, "cidl.json");
|
|
106
|
-
if (!fs.existsSync(cidlPath)) {
|
|
107
|
-
console.error("Err: No cloesce file found, have you ran `cloesce compile`?");
|
|
108
|
-
process.exit(1);
|
|
109
|
-
}
|
|
110
|
-
if (!fs.existsSync(config.migrationsPath)) {
|
|
111
|
-
fs.mkdirSync(config.migrationsPath);
|
|
112
|
-
}
|
|
113
|
-
const migrationPrefix = path.join(config.migrationsPath, `${timestamp()}_${args.name}`);
|
|
114
|
-
let wasmArgs = [
|
|
115
|
-
"migrations",
|
|
116
|
-
cidlPath,
|
|
117
|
-
`${migrationPrefix}.json`,
|
|
118
|
-
`${migrationPrefix}.sql`,
|
|
119
|
-
];
|
|
120
|
-
// Add last migration if exists
|
|
121
|
-
{
|
|
122
|
-
const files = fs.readdirSync(config.migrationsPath);
|
|
123
|
-
const jsonFiles = files.filter((f) => f.endsWith(".json"));
|
|
124
|
-
// Sort descending by filename
|
|
125
|
-
jsonFiles.sort((a, b) => b.localeCompare(a, undefined, { numeric: true }));
|
|
126
|
-
if (jsonFiles.length > 0) {
|
|
127
|
-
wasmArgs.push(path.join(config.migrationsPath, jsonFiles[0]));
|
|
128
|
-
}
|
|
129
|
-
}
|
|
130
|
-
const migrateConfig = {
|
|
131
|
-
name: "migrations",
|
|
132
|
-
wasmFile: "generator.wasm",
|
|
133
|
-
args: wasmArgs,
|
|
134
|
-
};
|
|
135
|
-
// Runs a generator command. Exits the process on failure.
|
|
136
|
-
await generate(migrateConfig);
|
|
137
|
-
},
|
|
72
|
+
inp: option({
|
|
73
|
+
long: "in",
|
|
74
|
+
short: "i",
|
|
75
|
+
type: optional(string),
|
|
76
|
+
description: "Input file or directory",
|
|
138
77
|
}),
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
78
|
+
truncateSourcePaths: flag({
|
|
79
|
+
long: "truncateSourcePaths",
|
|
80
|
+
description: "Sets all source paths to just their file name",
|
|
81
|
+
}),
|
|
82
|
+
skipTsCheck: flag({
|
|
83
|
+
long: "skipTsCheck",
|
|
84
|
+
description: "Skip TypeScript compilation checks",
|
|
85
|
+
}),
|
|
86
|
+
},
|
|
87
|
+
handler: async (args) => {
|
|
88
|
+
const config = {
|
|
89
|
+
projectName: args.projectName,
|
|
90
|
+
outPath: args.out,
|
|
91
|
+
paths: [args.inp],
|
|
92
|
+
truncateSourcePaths: false,
|
|
93
|
+
workersUrl: "",
|
|
94
|
+
migrationsPath: "",
|
|
95
|
+
};
|
|
96
|
+
await extract(config, {
|
|
97
|
+
truncateSourcePaths: args.truncateSourcePaths,
|
|
98
|
+
skipTsCheck: args.skipTsCheck,
|
|
99
|
+
});
|
|
100
|
+
},
|
|
101
|
+
}),
|
|
102
|
+
migrate: command({
|
|
103
|
+
name: "migrate",
|
|
104
|
+
description: "Creates a database migration.",
|
|
105
|
+
args: {
|
|
106
|
+
name: positional({ type: string, displayName: "name" }),
|
|
107
|
+
debug: flag({
|
|
108
|
+
long: "debug",
|
|
109
|
+
short: "d",
|
|
110
|
+
description: "Show debug output",
|
|
111
|
+
}),
|
|
112
|
+
},
|
|
113
|
+
handler: async (args) => {
|
|
114
|
+
const config = loadCloesceConfig(process.cwd());
|
|
115
|
+
if (!config) {
|
|
116
|
+
process.exit(1);
|
|
157
117
|
}
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
skipAddingFilesFromTsConfig: true,
|
|
165
|
-
compilerOptions: {
|
|
166
|
-
skipLibCheck: true,
|
|
167
|
-
strictNullChecks: true,
|
|
168
|
-
experimentalDecorators: true,
|
|
169
|
-
emitDecoratorMetadata: true,
|
|
170
|
-
},
|
|
171
|
-
});
|
|
172
|
-
findCloesceProject(root, searchPaths, project);
|
|
173
|
-
const fileCount = project.getSourceFiles().length;
|
|
174
|
-
if (fileCount === 0) {
|
|
175
|
-
new ExtractorError(ExtractorErrorCode.MissingFile);
|
|
176
|
-
}
|
|
177
|
-
debug(`Found ${fileCount} .cloesce.ts files`);
|
|
178
|
-
// Run typescript compiler checks to before extraction
|
|
179
|
-
if (!args.skipTsCheck) {
|
|
180
|
-
const tscStart = Date.now();
|
|
181
|
-
debug("Running TypeScript compiler checks...");
|
|
182
|
-
const diagnostics = project.getPreEmitDiagnostics();
|
|
183
|
-
if (diagnostics.length > 0) {
|
|
184
|
-
console.error("TypeScript errors detected in provided files:");
|
|
185
|
-
console.error(project.formatDiagnosticsWithColorAndContext(diagnostics));
|
|
186
|
-
process.exit(1);
|
|
118
|
+
const cidlPath = path.join(config.outPath, "cidl.json");
|
|
119
|
+
if (!fs.existsSync(cidlPath)) {
|
|
120
|
+
console.error(
|
|
121
|
+
"Err: No cloesce file found, have you ran `cloesce compile`?",
|
|
122
|
+
);
|
|
123
|
+
process.exit(1);
|
|
187
124
|
}
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
try {
|
|
191
|
-
const extractorStart = Date.now();
|
|
192
|
-
debug("Extracting CIDL...");
|
|
193
|
-
const extractor = new CidlExtractor(cloesceProjectName, "v0.0.4");
|
|
194
|
-
const result = extractor.extract(project);
|
|
195
|
-
if (result.isLeft()) {
|
|
196
|
-
console.error(formatErr(result.value));
|
|
197
|
-
process.exit(1);
|
|
125
|
+
if (!fs.existsSync(config.migrationsPath)) {
|
|
126
|
+
fs.mkdirSync(config.migrationsPath);
|
|
198
127
|
}
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
128
|
+
const migrationPrefix = path.join(
|
|
129
|
+
config.migrationsPath,
|
|
130
|
+
`${timestamp()}_${args.name}`,
|
|
131
|
+
);
|
|
132
|
+
let wasmArgs = [
|
|
133
|
+
"migrations",
|
|
134
|
+
cidlPath,
|
|
135
|
+
`${migrationPrefix}.json`,
|
|
136
|
+
`${migrationPrefix}.sql`,
|
|
137
|
+
];
|
|
138
|
+
// Add last migration if exists
|
|
139
|
+
{
|
|
140
|
+
const files = fs.readdirSync(config.migrationsPath);
|
|
141
|
+
const jsonFiles = files.filter((f) => f.endsWith(".json"));
|
|
142
|
+
// Sort descending by filename
|
|
143
|
+
jsonFiles.sort((a, b) =>
|
|
144
|
+
b.localeCompare(a, undefined, { numeric: true }),
|
|
145
|
+
);
|
|
146
|
+
if (jsonFiles.length > 0) {
|
|
147
|
+
wasmArgs.push(path.join(config.migrationsPath, jsonFiles[0]));
|
|
148
|
+
}
|
|
217
149
|
}
|
|
218
|
-
const
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
150
|
+
const migrateConfig = {
|
|
151
|
+
name: "migrations",
|
|
152
|
+
wasmFile: "generator.wasm",
|
|
153
|
+
args: wasmArgs,
|
|
154
|
+
};
|
|
155
|
+
// Runs a generator command. Exits the process on failure.
|
|
156
|
+
await generate(migrateConfig);
|
|
157
|
+
},
|
|
158
|
+
}),
|
|
159
|
+
},
|
|
160
|
+
});
|
|
161
|
+
async function extract(config, args = {}) {
|
|
162
|
+
const startTime = Date.now();
|
|
163
|
+
debugPhase = "extractor";
|
|
164
|
+
debug("Preparing extraction...");
|
|
165
|
+
const root = process.cwd();
|
|
166
|
+
const projectRoot = process.cwd();
|
|
167
|
+
const searchPaths = config.paths ?? [root];
|
|
168
|
+
const outPath = (() => {
|
|
169
|
+
// If the out path is a directory, join it with "cidl.pre.json"
|
|
170
|
+
if (
|
|
171
|
+
fs.existsSync(config.outPath) &&
|
|
172
|
+
fs.statSync(config.outPath).isDirectory()
|
|
173
|
+
) {
|
|
174
|
+
return path.join(config.outPath, "cidl.pre.json");
|
|
175
|
+
}
|
|
176
|
+
// If the out path is a file, use it directly
|
|
177
|
+
if (config.outPath && config.outPath.endsWith(".json")) {
|
|
178
|
+
return config.outPath;
|
|
179
|
+
}
|
|
180
|
+
// Default to .generated/cidl.pre.json
|
|
181
|
+
return path.join(config.outPath ?? ".generated", "cidl.pre.json");
|
|
182
|
+
})();
|
|
183
|
+
const truncate =
|
|
184
|
+
args.truncateSourcePaths ?? config.truncateSourcePaths ?? false;
|
|
185
|
+
const cloesceProjectName =
|
|
186
|
+
config.projectName ?? readPackageJsonProjectName(projectRoot);
|
|
187
|
+
const project = new Project({
|
|
188
|
+
skipAddingFilesFromTsConfig: true,
|
|
189
|
+
compilerOptions: {
|
|
190
|
+
skipLibCheck: true,
|
|
191
|
+
strictNullChecks: true,
|
|
192
|
+
experimentalDecorators: true,
|
|
193
|
+
emitDecoratorMetadata: true,
|
|
194
|
+
},
|
|
195
|
+
});
|
|
196
|
+
findCloesceProject(root, searchPaths, project);
|
|
197
|
+
const fileCount = project.getSourceFiles().length;
|
|
198
|
+
if (fileCount === 0) {
|
|
199
|
+
new ExtractorError(ExtractorErrorCode.MissingFile);
|
|
200
|
+
}
|
|
201
|
+
debug(`Found ${fileCount} .cloesce.ts files`);
|
|
202
|
+
// Run typescript compiler checks to before extraction
|
|
203
|
+
if (!args.skipTsCheck) {
|
|
204
|
+
const tscStart = Date.now();
|
|
205
|
+
debug("Running TypeScript compiler checks...");
|
|
206
|
+
const diagnostics = project.getPreEmitDiagnostics();
|
|
207
|
+
if (diagnostics.length > 0) {
|
|
208
|
+
console.error("TypeScript errors detected in provided files:");
|
|
209
|
+
console.error(project.formatDiagnosticsWithColorAndContext(diagnostics));
|
|
210
|
+
process.exit(1);
|
|
223
211
|
}
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
212
|
+
debug(`TypeScript checks completed in ${Date.now() - tscStart}ms`);
|
|
213
|
+
}
|
|
214
|
+
try {
|
|
215
|
+
const extractorStart = Date.now();
|
|
216
|
+
debug("Extracting CIDL...");
|
|
217
|
+
const extractor = new CidlExtractor(cloesceProjectName, "v0.0.4");
|
|
218
|
+
const result = extractor.extract(project);
|
|
219
|
+
if (result.isLeft()) {
|
|
220
|
+
console.error(formatErr(result.value));
|
|
221
|
+
process.exit(1);
|
|
227
222
|
}
|
|
228
|
-
|
|
229
|
-
|
|
223
|
+
let ast = result.unwrap();
|
|
224
|
+
if (truncate) {
|
|
225
|
+
if (ast.wrangler_env) {
|
|
226
|
+
ast.wrangler_env.source_path =
|
|
227
|
+
"./" + path.basename(ast.wrangler_env.source_path);
|
|
228
|
+
}
|
|
229
|
+
if (ast.app_source) {
|
|
230
|
+
ast.app_source = "./" + path.basename(ast.app_source);
|
|
231
|
+
}
|
|
232
|
+
for (const model of Object.values(ast.models)) {
|
|
233
|
+
model.source_path = "./" + path.basename(model.source_path);
|
|
234
|
+
}
|
|
235
|
+
for (const poo of Object.values(ast.poos)) {
|
|
236
|
+
poo.source_path = "./" + path.basename(poo.source_path);
|
|
237
|
+
}
|
|
238
|
+
for (const service of Object.values(ast.services)) {
|
|
239
|
+
service.source_path = "./" + path.basename(service.source_path);
|
|
240
|
+
}
|
|
230
241
|
}
|
|
242
|
+
const json = JSON.stringify(ast, null, 4);
|
|
243
|
+
fs.mkdirSync(path.dirname(outPath), { recursive: true });
|
|
244
|
+
fs.writeFileSync(outPath, json);
|
|
245
|
+
debug(
|
|
246
|
+
`Successfully extracted cidl.pre.json ${outPath} in ${Date.now() - extractorStart}ms`,
|
|
247
|
+
);
|
|
248
|
+
return { outPath, projectName: cloesceProjectName };
|
|
249
|
+
} catch (err) {
|
|
250
|
+
console.error(
|
|
251
|
+
"Critical uncaught error in extractor. \nSubmit a ticket to https://github.com/bens-schreiber/cloesce\n\n",
|
|
252
|
+
err?.message ?? "No error message.",
|
|
253
|
+
"\n",
|
|
254
|
+
err?.stack ?? "No error stack.",
|
|
255
|
+
);
|
|
256
|
+
process.exit(1);
|
|
257
|
+
} finally {
|
|
258
|
+
debug(`Extraction process completed in ${Date.now() - startTime}ms`);
|
|
259
|
+
}
|
|
231
260
|
}
|
|
232
261
|
async function generate(config) {
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
262
|
+
const debugStart = Date.now();
|
|
263
|
+
debug(`Starting generator`);
|
|
264
|
+
const root = process.cwd();
|
|
265
|
+
// Look for wrangler.toml in the root directory
|
|
266
|
+
const wranglerPath = path.join(root, "wrangler.toml");
|
|
267
|
+
if (!fs.existsSync(wranglerPath)) {
|
|
268
|
+
debug("No wrangler.toml found, creating empty file.");
|
|
269
|
+
fs.writeFileSync(wranglerPath, "");
|
|
270
|
+
}
|
|
271
|
+
debug(`Using wrangler.toml at ${wranglerPath}`);
|
|
272
|
+
const wasi = new WASI({
|
|
273
|
+
version: "preview1",
|
|
274
|
+
args: ["generate", ...config.args],
|
|
275
|
+
env: { ...process.env, ...config.env },
|
|
276
|
+
preopens: { ".": root },
|
|
277
|
+
});
|
|
278
|
+
const readWasmStart = Date.now();
|
|
279
|
+
debug(`Reading generator binary...`);
|
|
280
|
+
const wasm = await readFile(new URL("./generator.wasm", import.meta.url));
|
|
281
|
+
const mod = await WebAssembly.compile(new Uint8Array(wasm));
|
|
282
|
+
let instance = await WebAssembly.instantiate(mod, {
|
|
283
|
+
wasi_snapshot_preview1: wasi.wasiImport,
|
|
284
|
+
});
|
|
285
|
+
debug(
|
|
286
|
+
`Read and compiled generator wasm binary in ${Date.now() - readWasmStart}ms`,
|
|
287
|
+
);
|
|
288
|
+
try {
|
|
289
|
+
wasi.start(instance);
|
|
290
|
+
} catch (err) {
|
|
291
|
+
console.error(`WASM execution failed for ${config.name}:`, err);
|
|
292
|
+
throw err;
|
|
293
|
+
} finally {
|
|
294
|
+
debug(`Generator ${config.name} completed in ${Date.now() - debugStart}ms`);
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
function loadCloesceConfig(root) {
|
|
298
|
+
const configPath = path.join(root, "cloesce.config.json");
|
|
299
|
+
if (!fs.existsSync(configPath)) {
|
|
300
|
+
debug("No cloesce.config.json found");
|
|
301
|
+
return undefined;
|
|
302
|
+
}
|
|
303
|
+
try {
|
|
304
|
+
const config = JSON.parse(fs.readFileSync(configPath, "utf8"));
|
|
305
|
+
debug(`Loaded config from ${configPath}`);
|
|
306
|
+
if (!config.paths || !Array.isArray(config.paths)) {
|
|
307
|
+
debug("No paths specified in cloesce.config.json, defaulting to root");
|
|
308
|
+
config.paths = [root];
|
|
241
309
|
}
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
preopens: { ".": root },
|
|
248
|
-
});
|
|
249
|
-
const readWasmStart = Date.now();
|
|
250
|
-
debug(`Reading generator binary...`);
|
|
251
|
-
const wasm = await readFile(new URL("./generator.wasm", import.meta.url));
|
|
252
|
-
const mod = await WebAssembly.compile(new Uint8Array(wasm));
|
|
253
|
-
let instance = await WebAssembly.instantiate(mod, {
|
|
254
|
-
wasi_snapshot_preview1: wasi.wasiImport,
|
|
255
|
-
});
|
|
256
|
-
debug(`Read and compiled generator wasm binary in ${Date.now() - readWasmStart}ms`);
|
|
257
|
-
try {
|
|
258
|
-
wasi.start(instance);
|
|
310
|
+
if (!config.projectName) {
|
|
311
|
+
debug(
|
|
312
|
+
"No projectName specified in cloesce.config.json, reading from package.json",
|
|
313
|
+
);
|
|
314
|
+
config.projectName = readPackageJsonProjectName(root);
|
|
259
315
|
}
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
316
|
+
if (!config.outPath) {
|
|
317
|
+
debug(
|
|
318
|
+
"No outPath specified in cloesce.config.json, defaulting to .generated",
|
|
319
|
+
);
|
|
320
|
+
config.outPath = ".generated";
|
|
263
321
|
}
|
|
264
|
-
|
|
265
|
-
|
|
322
|
+
if (!config.workersUrl) {
|
|
323
|
+
debug(
|
|
324
|
+
"No workersUrl specified in cloesce.config.json, localhost:8787 will be used",
|
|
325
|
+
);
|
|
326
|
+
config.workersUrl = "http://localhost:8787";
|
|
266
327
|
}
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
return undefined;
|
|
328
|
+
if (!config.migrationsPath) {
|
|
329
|
+
debug(
|
|
330
|
+
"No migrationsPath specified in cloesce.config.json, defaulting to ./migrations",
|
|
331
|
+
);
|
|
332
|
+
config.migrationsPath = "./migrations";
|
|
273
333
|
}
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
debug(`Loaded config from ${configPath}`);
|
|
277
|
-
if (!config.paths || !Array.isArray(config.paths)) {
|
|
278
|
-
debug("No paths specified in cloesce.config.json, defaulting to root");
|
|
279
|
-
config.paths = [root];
|
|
280
|
-
}
|
|
281
|
-
if (!config.projectName) {
|
|
282
|
-
debug("No projectName specified in cloesce.config.json, reading from package.json");
|
|
283
|
-
config.projectName = readPackageJsonProjectName(root);
|
|
284
|
-
}
|
|
285
|
-
if (!config.outPath) {
|
|
286
|
-
debug("No outPath specified in cloesce.config.json, defaulting to .generated");
|
|
287
|
-
config.outPath = ".generated";
|
|
288
|
-
}
|
|
289
|
-
if (!config.workersUrl) {
|
|
290
|
-
debug("No workersUrl specified in cloesce.config.json, localhost:8787 will be used");
|
|
291
|
-
config.workersUrl = "http://localhost:8787";
|
|
292
|
-
}
|
|
293
|
-
if (!config.migrationsPath) {
|
|
294
|
-
debug("No migrationsPath specified in cloesce.config.json, defaulting to ./migrations");
|
|
295
|
-
config.migrationsPath = "./migrations";
|
|
296
|
-
}
|
|
297
|
-
if (typeof config.truncateSourcePaths !== "boolean") {
|
|
298
|
-
config.truncateSourcePaths = false;
|
|
299
|
-
}
|
|
300
|
-
debug(`Cloesce Config: ${JSON.stringify({ ...config, truncateSourcePaths: undefined }, null, 4)}`);
|
|
301
|
-
return config;
|
|
302
|
-
}
|
|
303
|
-
catch (err) {
|
|
304
|
-
console.warn(`Failed to parse cloesce.config.json: ${err}`);
|
|
305
|
-
throw err;
|
|
334
|
+
if (typeof config.truncateSourcePaths !== "boolean") {
|
|
335
|
+
config.truncateSourcePaths = false;
|
|
306
336
|
}
|
|
337
|
+
debug(
|
|
338
|
+
`Cloesce Config: ${JSON.stringify({ ...config, truncateSourcePaths: undefined }, null, 4)}`,
|
|
339
|
+
);
|
|
340
|
+
return config;
|
|
341
|
+
} catch (err) {
|
|
342
|
+
console.warn(`Failed to parse cloesce.config.json: ${err}`);
|
|
343
|
+
throw err;
|
|
344
|
+
}
|
|
307
345
|
}
|
|
308
346
|
function timestamp() {
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
347
|
+
const d = new Date();
|
|
348
|
+
return (
|
|
349
|
+
d.getFullYear().toString() +
|
|
350
|
+
String(d.getMonth() + 1).padStart(2, "0") +
|
|
351
|
+
String(d.getDate()).padStart(2, "0") +
|
|
352
|
+
"T" +
|
|
353
|
+
String(d.getHours()).padStart(2, "0") +
|
|
354
|
+
String(d.getMinutes()).padStart(2, "0") +
|
|
355
|
+
String(d.getSeconds()).padStart(2, "0")
|
|
356
|
+
);
|
|
317
357
|
}
|
|
318
358
|
function readPackageJsonProjectName(cwd) {
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
359
|
+
const pkgPath = path.join(cwd, "package.json");
|
|
360
|
+
let projectName = path.basename(cwd);
|
|
361
|
+
if (fs.existsSync(pkgPath)) {
|
|
362
|
+
const pkg = JSON.parse(fs.readFileSync(pkgPath, "utf8"));
|
|
363
|
+
projectName = pkg.name ?? projectName;
|
|
364
|
+
}
|
|
365
|
+
return projectName;
|
|
326
366
|
}
|
|
327
367
|
function findCloesceProject(root, searchPaths, project) {
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
fullPath = path.resolve(root, searchPath);
|
|
335
|
-
}
|
|
336
|
-
if (!fs.existsSync(fullPath)) {
|
|
337
|
-
console.warn(`Warning: Path "${searchPath}" does not exist`);
|
|
338
|
-
continue;
|
|
339
|
-
}
|
|
340
|
-
const stats = fs.statSync(fullPath);
|
|
341
|
-
if (stats.isFile() && /\.cloesce\.ts$/i.test(fullPath)) {
|
|
342
|
-
debug(`Found file: ${fullPath}`);
|
|
343
|
-
project.addSourceFileAtPath(fullPath);
|
|
344
|
-
}
|
|
345
|
-
else if (stats.isDirectory()) {
|
|
346
|
-
debug(`Searching directory: ${fullPath}`);
|
|
347
|
-
walkDirectory(fullPath);
|
|
348
|
-
}
|
|
368
|
+
for (const searchPath of searchPaths) {
|
|
369
|
+
let fullPath;
|
|
370
|
+
if (path.isAbsolute(searchPath) || searchPath.startsWith(root)) {
|
|
371
|
+
fullPath = path.normalize(searchPath);
|
|
372
|
+
} else {
|
|
373
|
+
fullPath = path.resolve(root, searchPath);
|
|
349
374
|
}
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
}
|
|
375
|
+
if (!fs.existsSync(fullPath)) {
|
|
376
|
+
console.warn(`Warning: Path "${searchPath}" does not exist`);
|
|
377
|
+
continue;
|
|
378
|
+
}
|
|
379
|
+
const stats = fs.statSync(fullPath);
|
|
380
|
+
if (stats.isFile() && /\.cloesce\.ts$/i.test(fullPath)) {
|
|
381
|
+
debug(`Found file: ${fullPath}`);
|
|
382
|
+
project.addSourceFileAtPath(fullPath);
|
|
383
|
+
} else if (stats.isDirectory()) {
|
|
384
|
+
debug(`Searching directory: ${fullPath}`);
|
|
385
|
+
walkDirectory(fullPath);
|
|
362
386
|
}
|
|
387
|
+
}
|
|
388
|
+
function walkDirectory(dir) {
|
|
389
|
+
for (const entry of fs.readdirSync(dir, { withFileTypes: true })) {
|
|
390
|
+
const fullPath = path.join(dir, entry.name);
|
|
391
|
+
if (entry.isDirectory() && !entry.name.startsWith(".")) {
|
|
392
|
+
debug(`Entering directory: ${fullPath}`);
|
|
393
|
+
walkDirectory(fullPath);
|
|
394
|
+
} else if (entry.isFile() && /\.cloesce\.ts$/i.test(entry.name)) {
|
|
395
|
+
debug(`Found file: ${fullPath}`);
|
|
396
|
+
project.addSourceFileAtPath(fullPath);
|
|
397
|
+
}
|
|
398
|
+
}
|
|
399
|
+
}
|
|
363
400
|
}
|
|
364
401
|
function formatErr(e) {
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
402
|
+
const { description, suggestion } = getErrorInfo(e.code);
|
|
403
|
+
const contextLine = e.context ? `Context: ${e.context}\n` : "";
|
|
404
|
+
const snippetLine = e.snippet ? `${e.snippet}\n\n` : "";
|
|
405
|
+
return `
|
|
369
406
|
==== CLOESCE ERROR ====
|
|
370
407
|
Error [${ExtractorErrorCode[e.code]}]: ${description}
|
|
371
408
|
Phase: TypeScript IDL Extraction
|
|
@@ -374,6 +411,6 @@ ${contextLine}${snippetLine}Suggested fix: ${suggestion}
|
|
|
374
411
|
`;
|
|
375
412
|
}
|
|
376
413
|
run(cmds, process.argv.slice(2)).catch((err) => {
|
|
377
|
-
|
|
378
|
-
|
|
414
|
+
console.error(err);
|
|
415
|
+
process.exit(1);
|
|
379
416
|
});
|