@adonisjs/assembler 8.0.0-next.9 → 8.0.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.
Files changed (35) hide show
  1. package/README.md +260 -0
  2. package/build/chunk-DF48asd8.js +9 -0
  3. package/build/codemod_exception-BMNJZ0i1.js +280 -0
  4. package/build/index.d.ts +1 -1
  5. package/build/index.js +1854 -1724
  6. package/build/main-Cpfvmdw6.js +562 -0
  7. package/build/main-INOi9swJ.js +471 -0
  8. package/build/src/bundler.d.ts +2 -0
  9. package/build/src/code_scanners/routes_scanner/main.d.ts +16 -2
  10. package/build/src/code_scanners/routes_scanner/main.js +4 -445
  11. package/build/src/code_transformer/main.d.ts +14 -1
  12. package/build/src/code_transformer/main.js +981 -622
  13. package/build/src/code_transformer/rc_file_transformer.d.ts +28 -2
  14. package/build/src/debug.d.ts +1 -1
  15. package/build/src/dev_server.d.ts +60 -12
  16. package/build/src/exceptions/codemod_exception.d.ts +178 -0
  17. package/build/src/file_buffer.d.ts +19 -0
  18. package/build/src/file_system.d.ts +3 -3
  19. package/build/src/helpers.js +205 -16
  20. package/build/src/index_generator/main.js +4 -7
  21. package/build/src/paths_resolver.d.ts +2 -1
  22. package/build/src/test_runner.d.ts +3 -2
  23. package/build/src/types/code_scanners.d.ts +29 -13
  24. package/build/src/types/code_transformer.d.ts +127 -0
  25. package/build/src/types/common.d.ts +98 -2
  26. package/build/src/types/hooks.d.ts +4 -1
  27. package/build/src/types/main.js +2 -0
  28. package/build/src/utils.d.ts +7 -3
  29. package/build/src/virtual_file_system.d.ts +1 -1
  30. package/build/virtual_file_system-dzfXNwEp.js +572 -0
  31. package/package.json +41 -39
  32. package/build/chunk-7XU453QB.js +0 -418
  33. package/build/chunk-PORDZS62.js +0 -391
  34. package/build/chunk-TIKQQRMX.js +0 -116
  35. package/build/src/hooks.d.ts +0 -224
@@ -0,0 +1,572 @@
1
+ import { copyFile, mkdir, readFile } from "node:fs/promises";
2
+ import { fileURLToPath } from "node:url";
3
+ import string from "@poppinss/utils/string";
4
+ import { relative } from "node:path/posix";
5
+ import Cache from "tmp-cache";
6
+ import { isJunk } from "junk";
7
+ import fastGlob from "fast-glob";
8
+ import Hooks from "@poppinss/hooks";
9
+ import { existsSync } from "node:fs";
10
+ import getRandomPort from "get-port";
11
+ import { execa, execaNode } from "execa";
12
+ import { importDefault, naturalSort } from "@poppinss/utils";
13
+ import { EnvLoader, EnvParser } from "@adonisjs/env";
14
+ import chokidar from "chokidar";
15
+ import { parseTsconfig } from "get-tsconfig";
16
+ import { basename, dirname as dirname$1, isAbsolute, join as join$1, relative as relative$1 } from "node:path";
17
+ import { debuglog } from "node:util";
18
+ import { fdir } from "fdir";
19
+ import lodash from "@poppinss/utils/lodash";
20
+ import { Lang, parse } from "@ast-grep/napi";
21
+ import picomatch from "picomatch";
22
+ //#region src/debug.ts
23
+ /**
24
+ * Debug logger for the AdonisJS assembler package.
25
+ *
26
+ * This debug instance is configured to log messages under the 'adonisjs:assembler'
27
+ * namespace. Debug messages are only shown when the NODE_DEBUG environment variable
28
+ * includes 'adonisjs:assembler'.
29
+ *
30
+ * @example
31
+ * // Enable debug logging
32
+ * // NODE_DEBUG=adonisjs:assembler node ace serve
33
+ * debug('Starting development server...')
34
+ */
35
+ var debug_default = debuglog("adonisjs:assembler");
36
+ //#endregion
37
+ //#region src/utils.ts
38
+ /**
39
+ * Default set of args to pass in order to run TypeScript
40
+ * source. Used by "run" and "runNode" scripts
41
+ */
42
+ const DEFAULT_NODE_ARGS = ["--import=@poppinss/ts-exec", "--enable-source-maps"];
43
+ /**
44
+ * Parses tsconfig.json and prints errors using typescript compiler host
45
+ *
46
+ * This function reads and parses the tsconfig.json file from the given directory,
47
+ * handling diagnostic errors and returning a parsed configuration that can be
48
+ * used by other TypeScript operations.
49
+ *
50
+ * @deprecated While we are experimenting with the readTsConfig method
51
+ *
52
+ * @param cwd - The current working directory URL or string path
53
+ * @param ts - TypeScript module reference
54
+ * @returns Parsed TypeScript configuration or undefined if parsing failed
55
+ */
56
+ function parseConfig(cwd, ts) {
57
+ const cwdPath = typeof cwd === "string" ? cwd : fileURLToPath(cwd);
58
+ const configFile = join$1(cwdPath, "tsconfig.json");
59
+ debug_default("parsing config file \"%s\"", configFile);
60
+ let hardException = null;
61
+ const parsedConfig = ts.getParsedCommandLineOfConfigFile(configFile, {}, {
62
+ ...ts.sys,
63
+ useCaseSensitiveFileNames: true,
64
+ getCurrentDirectory: () => cwdPath,
65
+ onUnRecoverableConfigFileDiagnostic: (error) => hardException = error
66
+ });
67
+ if (hardException) {
68
+ const compilerHost = ts.createCompilerHost({});
69
+ console.log(ts.formatDiagnosticsWithColorAndContext([hardException], compilerHost));
70
+ return;
71
+ }
72
+ if (parsedConfig.errors.length) {
73
+ const compilerHost = ts.createCompilerHost({});
74
+ console.log(ts.formatDiagnosticsWithColorAndContext(parsedConfig.errors, compilerHost));
75
+ return;
76
+ }
77
+ if (parsedConfig.raw.include) parsedConfig.raw.include = parsedConfig.raw.include.map((includePath) => {
78
+ return includePath.replace("${configDir}/", "");
79
+ });
80
+ if (parsedConfig.raw.exclude) parsedConfig.raw.exclude = parsedConfig.raw.exclude.map((excludePath) => {
81
+ return excludePath.replace("${configDir}/", "");
82
+ });
83
+ return parsedConfig;
84
+ }
85
+ function readTsConfig(cwd) {
86
+ const tsConfigPath = join$1(cwd, "tsconfig.json");
87
+ debug_default("reading config file from location \"%s\"", tsConfigPath);
88
+ try {
89
+ const tsConfig = parseTsconfig(tsConfigPath);
90
+ if (tsConfig.include) tsConfig.include = tsConfig.include.map((resolvedPath) => {
91
+ return resolvedPath.replace(cwd, "");
92
+ });
93
+ if (tsConfig.exclude) tsConfig.exclude = tsConfig.exclude.map((resolvedPath) => {
94
+ return resolvedPath.replace(cwd, "");
95
+ });
96
+ debug_default("read tsconfig %O", tsConfig);
97
+ return {
98
+ path: tsConfigPath,
99
+ config: tsConfig
100
+ };
101
+ } catch {
102
+ return null;
103
+ }
104
+ }
105
+ /**
106
+ * Runs a Node.js script as a child process and inherits the stdio streams
107
+ *
108
+ * This function spawns a Node.js child process with TypeScript support enabled
109
+ * by default through ts-exec. It's primarily used for running development
110
+ * servers and test scripts.
111
+ *
112
+ * @param cwd - The current working directory URL or string path
113
+ * @param options - Script execution options including args, environment, etc.
114
+ * @returns Child process instance from execa
115
+ */
116
+ function runNode(cwd, options) {
117
+ return execaNode(options.script, options.scriptArgs, {
118
+ nodeOptions: DEFAULT_NODE_ARGS.concat(options.nodeArgs),
119
+ preferLocal: true,
120
+ windowsHide: false,
121
+ localDir: cwd,
122
+ cwd,
123
+ reject: options.reject ?? false,
124
+ buffer: false,
125
+ stdio: options.stdio || "inherit",
126
+ env: {
127
+ ...options.stdio === "pipe" ? { FORCE_COLOR: "true" } : {},
128
+ ...options.env
129
+ }
130
+ });
131
+ }
132
+ /**
133
+ * Runs a script as a child process and inherits the stdio streams
134
+ *
135
+ * This function spawns a generic child process for running any executable
136
+ * script. Unlike runNode, this doesn't include TypeScript-specific Node.js arguments.
137
+ *
138
+ * @param cwd - The current working directory URL or string path
139
+ * @param options - Script execution options (excluding nodeArgs)
140
+ * @returns Child process instance from execa
141
+ */
142
+ function run(cwd, options) {
143
+ return execa(options.script, options.scriptArgs, {
144
+ preferLocal: true,
145
+ windowsHide: false,
146
+ localDir: cwd,
147
+ cwd,
148
+ buffer: false,
149
+ stdio: options.stdio || "inherit",
150
+ env: {
151
+ ...options.stdio === "pipe" ? { FORCE_COLOR: "true" } : {},
152
+ ...options.env
153
+ }
154
+ });
155
+ }
156
+ /**
157
+ * Watches the file system using chokidar with the provided options
158
+ *
159
+ * Creates a file system watcher that monitors the current directory
160
+ * for changes, supporting various chokidar options for customization.
161
+ *
162
+ * @param options - Chokidar watch options
163
+ * @returns Chokidar FSWatcher instance
164
+ */
165
+ function watch(options) {
166
+ return chokidar.watch(["."], options);
167
+ }
168
+ /**
169
+ * Returns the port to use after inspecting the dot-env files inside
170
+ * a given directory.
171
+ *
172
+ * A random port is used when the specified port is in use. Following
173
+ * is the logic for finding a specified port:
174
+ *
175
+ * - The "process.env.PORT" value is used if exists.
176
+ * - The dot-env files are loaded using the "EnvLoader" and the PORT
177
+ * value is used by iterating over all the loaded files. The
178
+ * iteration stops after first find.
179
+ * - Falls back to port 3333 if no PORT is found in environment files.
180
+ *
181
+ * @param cwd - The current working directory URL
182
+ * @returns Promise resolving to an available port number
183
+ */
184
+ async function getPort(cwd) {
185
+ /**
186
+ * Use existing port if exists
187
+ */
188
+ if (process.env.PORT) return getRandomPort({ port: Number(process.env.PORT) });
189
+ /**
190
+ * Loop over files and use the port from their contents. Stops
191
+ * after first match
192
+ */
193
+ const files = await new EnvLoader(cwd).load();
194
+ for (let file of files) {
195
+ const envVariables = await new EnvParser(file.contents, cwd).parse();
196
+ if (envVariables.PORT) return getRandomPort({ port: Number(envVariables.PORT) });
197
+ }
198
+ /**
199
+ * Use 3333 as the port
200
+ */
201
+ return getRandomPort({ port: 3333 });
202
+ }
203
+ /**
204
+ * Helper function to copy files from relative paths or glob patterns
205
+ *
206
+ * This function handles copying files and directories while preserving
207
+ * directory structure. It supports both direct file paths and glob patterns,
208
+ * and automatically filters out junk files.
209
+ *
210
+ * @param files - Array of file paths or glob patterns to copy
211
+ * @param cwd - Source directory path
212
+ * @param outDir - Destination directory path
213
+ * @returns Promise resolving when all files are copied
214
+ */
215
+ async function copyFiles(files, cwd, outDir) {
216
+ /**
217
+ * Looping over files and create a new collection with paths
218
+ * and glob patterns
219
+ */
220
+ const { paths, patterns } = files.reduce((result, file) => {
221
+ /**
222
+ * If file is a glob pattern, then push it to patterns
223
+ */
224
+ if (fastGlob.isDynamicPattern(file)) {
225
+ result.patterns.push(file);
226
+ return result;
227
+ }
228
+ /**
229
+ * Otherwise, check if file exists and push it to paths to copy
230
+ */
231
+ if (existsSync(join$1(cwd, file))) result.paths.push(file);
232
+ return result;
233
+ }, {
234
+ patterns: [],
235
+ paths: []
236
+ });
237
+ debug_default("copyFiles inputs: %O, paths: %O, patterns: %O", files, paths, patterns);
238
+ /**
239
+ * Getting list of relative paths from glob patterns
240
+ */
241
+ const filePaths = paths.concat(await fastGlob(patterns, {
242
+ cwd,
243
+ dot: true
244
+ })).filter((file) => {
245
+ return !isJunk(basename(file));
246
+ });
247
+ /**
248
+ * Finally copy files to the destination by keeping the same
249
+ * directory structure and ignoring junk files
250
+ */
251
+ debug_default("copying files %O to destination \"%s\"", filePaths, outDir);
252
+ const copyPromises = filePaths.map(async (file) => {
253
+ const src = isAbsolute(file) ? file : join$1(cwd, file);
254
+ const dest = join$1(outDir, relative$1(cwd, src));
255
+ await mkdir(dirname$1(dest), { recursive: true });
256
+ return copyFile(src, dest);
257
+ });
258
+ return await Promise.all(copyPromises);
259
+ }
260
+ /**
261
+ * Memoize a function using an LRU cache. The function must accept
262
+ * only one argument as a string value.
263
+ *
264
+ * This utility provides caching for expensive function calls to improve
265
+ * performance by storing results in memory.
266
+ *
267
+ * @param fn - Function to memoize (only first argument is considered for memoization)
268
+ * @param maxKeys - Optional maximum number of cached keys
269
+ * @returns Memoized version of the function
270
+ */
271
+ function memoize(fn, maxKeys) {
272
+ const cache = new Cache({ max: maxKeys });
273
+ return (input, ...args) => {
274
+ if (cache.has(input)) return cache.get(input);
275
+ return fn(input, ...args);
276
+ };
277
+ }
278
+ /**
279
+ * Returns a boolean telling if the path value is a relative
280
+ * path starting with "./" or "../"
281
+ *
282
+ * @param pathValue - The path string to check
283
+ * @returns True if the path is relative, false otherwise
284
+ */
285
+ function isRelative(pathValue) {
286
+ return pathValue.startsWith("./") || pathValue.startsWith("../");
287
+ }
288
+ /**
289
+ * Imports a selected set of lazy hooks and creates an instance of the
290
+ * Hooks class
291
+ *
292
+ * This function dynamically imports and initializes hooks based on the
293
+ * provided configuration, supporting different types of hooks for various
294
+ * assembler operations.
295
+ *
296
+ * @param rcFileHooks - Hook configuration from the RC file
297
+ * @param names - Array of hook names to load
298
+ * @returns Promise resolving to configured Hooks instance
299
+ */
300
+ async function loadHooks(rcFileHooks, names) {
301
+ const groups = names.map((name) => {
302
+ return {
303
+ group: name,
304
+ hooks: rcFileHooks?.[name] ?? []
305
+ };
306
+ });
307
+ const hooks = new Hooks();
308
+ for (const { group, hooks: collection } of groups) for (const item of collection) if ("run" in item) hooks.add(group, item.run);
309
+ else hooks.add(group, await importDefault(item));
310
+ return hooks;
311
+ }
312
+ /**
313
+ * Wraps a function inside another function that throttles the concurrent
314
+ * executions of a function. If the function is called too quickly, then
315
+ * it may result in two invocations at max.
316
+ *
317
+ * This utility prevents overwhelming the system with rapid successive calls
318
+ * by ensuring only one execution happens at a time, with at most one queued call.
319
+ *
320
+ * @param fn - Function to throttle
321
+ * @param name - Optional name for debugging purposes
322
+ * @returns Throttled version of the function
323
+ */
324
+ function throttle(fn, name) {
325
+ name = name || "throttled";
326
+ let isBusy = false;
327
+ let hasQueuedCalls = false;
328
+ let lastCallArgs;
329
+ async function throttled(...args) {
330
+ if (isBusy) {
331
+ debug_default("ignoring \"%s\" invocation as current execution is in progress", name);
332
+ hasQueuedCalls = true;
333
+ lastCallArgs = args;
334
+ return;
335
+ }
336
+ isBusy = true;
337
+ debug_default("executing throttled function \"%s\"", name);
338
+ await fn(...args);
339
+ debug_default("executed throttled function \"%s\"", name);
340
+ isBusy = false;
341
+ if (hasQueuedCalls) {
342
+ hasQueuedCalls = false;
343
+ debug_default("resuming and running latest \"%s\" invocation", name);
344
+ await throttled(...lastCallArgs);
345
+ }
346
+ }
347
+ return throttled;
348
+ }
349
+ /**
350
+ * Removes the file extension from a file path
351
+ *
352
+ * @param filePath - The file path with extension
353
+ * @returns The file path without extension
354
+ */
355
+ function removeExtension(filePath) {
356
+ return filePath.substring(0, filePath.lastIndexOf("."));
357
+ }
358
+ //#endregion
359
+ //#region src/virtual_file_system.ts
360
+ const BYPASS_FN = (input) => input;
361
+ const DEFAULT_GLOB = [
362
+ "**/!(*.d).ts",
363
+ "**/*.tsx",
364
+ "**/*.js"
365
+ ];
366
+ /**
367
+ * Virtual file system for managing and tracking files with AST parsing capabilities.
368
+ *
369
+ * The VirtualFileSystem provides an abstraction layer over the physical file system,
370
+ * allowing efficient file scanning, filtering, and AST parsing with caching. It's
371
+ * designed to work with TypeScript/JavaScript files and provides various output
372
+ * formats for different use cases.
373
+ *
374
+ * @example
375
+ * const vfs = new VirtualFileSystem('/src', { glob: ['**\/*.ts'] })
376
+ * await vfs.scan()
377
+ * const fileTree = vfs.asTree()
378
+ * const astNode = await vfs.get('/src/app.ts')
379
+ */
380
+ var VirtualFileSystem = class {
381
+ /**
382
+ * Absolute path to the source directory from where to read the files.
383
+ * Additionally, a glob pattern or a filter could be specified to
384
+ * narrow down the scanned files list.
385
+ */
386
+ #source;
387
+ /**
388
+ * Filesystem options
389
+ */
390
+ #options;
391
+ /**
392
+ * Files collected from the initial scan with pre-computed relative paths
393
+ */
394
+ #files = /* @__PURE__ */ new Map();
395
+ /**
396
+ * LRU cache storing parsed AST nodes by file path with size limit
397
+ */
398
+ #astCache = new Cache({ max: 60 });
399
+ /**
400
+ * Matcher is defined when glob is defined via the options
401
+ */
402
+ #matcher;
403
+ /**
404
+ * Picomatch options used for file pattern matching
405
+ */
406
+ #picoMatchOptions;
407
+ /**
408
+ * Create a new VirtualFileSystem instance
409
+ *
410
+ * @param source - Absolute path to the source directory
411
+ * @param options - Optional configuration for file filtering and processing
412
+ */
413
+ constructor(source, options) {
414
+ this.#source = source;
415
+ this.#options = options ?? {};
416
+ this.#picoMatchOptions = { cwd: this.#source };
417
+ this.#matcher = picomatch(this.#options.glob ?? DEFAULT_GLOB, this.#picoMatchOptions);
418
+ }
419
+ /**
420
+ * Scans the filesystem to collect the files. Newly files must
421
+ * be added via the ".add" method.
422
+ *
423
+ * This method performs an initial scan of the source directory using
424
+ * the configured glob patterns and populates the internal file list.
425
+ */
426
+ async scan() {
427
+ debug_default("fetching entities from source \"%s\"", this.#source);
428
+ const crawler = new fdir().globWithOptions(this.#options.glob ?? DEFAULT_GLOB, this.#picoMatchOptions).withFullPaths();
429
+ if (this.#options.filter) crawler.filter(this.#options.filter);
430
+ const filesList = await crawler.crawl(this.#source).withPromise();
431
+ debug_default("scanned files %O", filesList);
432
+ const sortedFiles = filesList.sort(naturalSort);
433
+ this.#files.clear();
434
+ for (let filePath of sortedFiles) {
435
+ filePath = string.toUnixSlash(filePath);
436
+ const relativePath = removeExtension(relative(this.#source, filePath));
437
+ this.#files.set(filePath, relativePath);
438
+ }
439
+ }
440
+ /**
441
+ * Check if a given file is part of the virtual file system. The method
442
+ * checks for the scanned files as well as glob pattern matches.
443
+ *
444
+ * @param filePath - Absolute file path to check
445
+ * @returns True if the file is tracked or matches the configured patterns
446
+ */
447
+ has(filePath) {
448
+ /**
449
+ * File is already tracked as part of the initial scan
450
+ */
451
+ if (this.#files.has(filePath)) return true;
452
+ /**
453
+ * Return false if file is not within the source dir
454
+ */
455
+ if (!filePath.startsWith(this.#source)) return false;
456
+ /**
457
+ * If a match exists, then check if the file matches via the
458
+ * matcher test
459
+ */
460
+ return this.#matcher(filePath);
461
+ }
462
+ /**
463
+ * Returns the files as a flat list of key-value pairs
464
+ *
465
+ * Converts the tracked files into a flat object where keys are relative
466
+ * paths (without extensions) and values are absolute file paths.
467
+ *
468
+ * @param options - Optional transformation functions for keys and values
469
+ * @returns Object with file mappings
470
+ */
471
+ asList(options) {
472
+ const list = {};
473
+ const transformKey = options?.transformKey ?? BYPASS_FN;
474
+ const transformValue = options?.transformValue ?? BYPASS_FN;
475
+ for (const [filePath, relativePath] of this.#files) list[transformKey(relativePath)] = transformValue(filePath);
476
+ return list;
477
+ }
478
+ /**
479
+ * Returns the files as a nested tree structure
480
+ *
481
+ * Converts the tracked files into a hierarchical object structure that
482
+ * mirrors the directory structure of the source files.
483
+ *
484
+ * @param options - Optional transformation functions for keys and values
485
+ * @returns Nested object representing the file tree
486
+ */
487
+ asTree(options) {
488
+ const list = {};
489
+ const transformKey = options?.transformKey ?? BYPASS_FN;
490
+ const transformValue = options?.transformValue ?? BYPASS_FN;
491
+ for (const [filePath, relativePath] of this.#files) {
492
+ const key = transformKey(relativePath);
493
+ lodash.set(list, key.split("/"), transformValue(filePath, key));
494
+ }
495
+ return list;
496
+ }
497
+ /**
498
+ * Add a new file to the virtual file system. File is only added when it
499
+ * matches the pre-defined filters.
500
+ *
501
+ * @param filePath - Absolute path of the file to add
502
+ * @returns True if the file was added, false if it doesn't match filters
503
+ */
504
+ add(filePath) {
505
+ if (this.has(filePath)) {
506
+ debug_default("adding new \"%s\" file to the virtual file system", filePath);
507
+ const relativePath = removeExtension(relative(this.#source, filePath));
508
+ this.#files.set(filePath, relativePath);
509
+ return true;
510
+ }
511
+ return false;
512
+ }
513
+ /**
514
+ * Remove a file from the virtual file system
515
+ *
516
+ * @param filePath - Absolute path of the file to remove
517
+ * @returns True if the file was removed, false if it wasn't tracked
518
+ */
519
+ remove(filePath) {
520
+ debug_default("removing \"%s\" file from virtual file system", filePath);
521
+ return this.#files.delete(filePath);
522
+ }
523
+ /**
524
+ * Returns the file contents as AST-grep node and caches it
525
+ * forever. Use the "invalidate" method to remove it from the cache.
526
+ *
527
+ * This method reads the file content, parses it into an AST using ast-grep,
528
+ * and caches the result for future requests to improve performance.
529
+ *
530
+ * @param filePath - The absolute path to the file to parse
531
+ * @returns Promise resolving to the AST-grep node
532
+ */
533
+ async get(filePath) {
534
+ const cached = this.#astCache.get(filePath);
535
+ if (cached) {
536
+ debug_default("returning AST nodes from cache \"%s\"", filePath);
537
+ return cached;
538
+ }
539
+ const fileContents = await readFile(filePath, "utf-8");
540
+ debug_default("parsing \"%s\" file to AST", filePath);
541
+ this.#astCache.set(filePath, parse(Lang.TypeScript, fileContents).root());
542
+ return this.#astCache.get(filePath);
543
+ }
544
+ /**
545
+ * Invalidates AST cache for a single file or all files
546
+ *
547
+ * Use this method when files have been modified to ensure fresh
548
+ * AST parsing on subsequent get() calls.
549
+ *
550
+ * @param filePath - Optional file path to clear. If omitted, clears entire cache
551
+ */
552
+ invalidate(filePath) {
553
+ if (filePath) {
554
+ debug_default("invalidate AST cache \"%s\"", filePath);
555
+ this.#astCache.delete(filePath);
556
+ } else {
557
+ debug_default("clear AST cache");
558
+ this.#astCache.clear();
559
+ }
560
+ }
561
+ /**
562
+ * Clear all scanned files from memory
563
+ *
564
+ * Removes all tracked files from the internal file list, effectively
565
+ * resetting the virtual file system.
566
+ */
567
+ clear() {
568
+ this.#files.clear();
569
+ }
570
+ };
571
+ //#endregion
572
+ export { loadHooks as a, readTsConfig as c, runNode as d, throttle as f, isRelative as i, removeExtension as l, debug_default as m, copyFiles as n, memoize as o, watch as p, getPort as r, parseConfig as s, VirtualFileSystem as t, run as u };