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