@adonisjs/assembler 8.0.0-next.5 → 8.0.0-next.7
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/README.md +87 -59
- package/build/chunk-25Q3N5JR.js +392 -0
- package/build/chunk-PORDZS62.js +391 -0
- package/build/chunk-TIKQQRMX.js +116 -0
- package/build/index.d.ts +2 -0
- package/build/index.js +825 -430
- package/build/src/bundler.d.ts +44 -3
- package/build/src/code_scanners/routes_scanner/main.d.ts +49 -9
- package/build/src/code_scanners/routes_scanner/main.js +445 -0
- package/build/src/code_scanners/routes_scanner/validator_extractor.d.ts +12 -4
- package/build/src/code_transformer/main.d.ts +44 -43
- package/build/src/code_transformer/main.js +123 -101
- package/build/src/code_transformer/rc_file_transformer.d.ts +56 -4
- package/build/src/debug.d.ts +12 -0
- package/build/src/dev_server.d.ts +38 -9
- package/build/src/file_buffer.d.ts +67 -0
- package/build/src/file_system.d.ts +45 -7
- package/build/src/helpers.d.ts +79 -4
- package/build/src/helpers.js +16 -0
- package/build/src/hooks.d.ts +224 -0
- package/build/src/index_generator/main.d.ts +64 -0
- package/build/src/index_generator/main.js +7 -0
- package/build/src/index_generator/source.d.ts +60 -0
- package/build/src/paths_resolver.d.ts +27 -2
- package/build/src/shortcuts_manager.d.ts +42 -4
- package/build/src/test_runner.d.ts +56 -10
- package/build/src/types/code_scanners.d.ts +138 -24
- package/build/src/types/code_transformer.d.ts +61 -19
- package/build/src/types/common.d.ts +199 -55
- package/build/src/types/hooks.d.ts +235 -22
- package/build/src/types/main.d.ts +13 -0
- package/build/src/utils.d.ts +88 -13
- package/build/src/virtual_file_system.d.ts +112 -0
- package/package.json +9 -3
- package/build/chunk-RR4HCA4M.js +0 -7
- package/build/src/ast_file_system.d.ts +0 -17
|
@@ -0,0 +1,391 @@
|
|
|
1
|
+
// src/debug.ts
|
|
2
|
+
import { debuglog } from "util";
|
|
3
|
+
var debug_default = debuglog("adonisjs:assembler");
|
|
4
|
+
|
|
5
|
+
// src/virtual_file_system.ts
|
|
6
|
+
import { fdir } from "fdir";
|
|
7
|
+
import Cache2 from "tmp-cache";
|
|
8
|
+
import { relative as relative2 } from "path/posix";
|
|
9
|
+
import lodash from "@poppinss/utils/lodash";
|
|
10
|
+
import string from "@poppinss/utils/string";
|
|
11
|
+
import { readFile } from "fs/promises";
|
|
12
|
+
import { naturalSort } from "@poppinss/utils";
|
|
13
|
+
import { Lang, parse } from "@ast-grep/napi";
|
|
14
|
+
import picomatch from "picomatch";
|
|
15
|
+
|
|
16
|
+
// src/utils.ts
|
|
17
|
+
import Cache from "tmp-cache";
|
|
18
|
+
import { isJunk } from "junk";
|
|
19
|
+
import fastGlob from "fast-glob";
|
|
20
|
+
import Hooks from "@poppinss/hooks";
|
|
21
|
+
import { existsSync } from "fs";
|
|
22
|
+
import getRandomPort from "get-port";
|
|
23
|
+
import { fileURLToPath } from "url";
|
|
24
|
+
import { execaNode, execa } from "execa";
|
|
25
|
+
import { importDefault } from "@poppinss/utils";
|
|
26
|
+
import { copyFile, mkdir } from "fs/promises";
|
|
27
|
+
import { EnvLoader, EnvParser } from "@adonisjs/env";
|
|
28
|
+
import chokidar from "chokidar";
|
|
29
|
+
import { basename, dirname, isAbsolute, join, relative } from "path";
|
|
30
|
+
var DEFAULT_NODE_ARGS = ["--import=@poppinss/ts-exec", "--enable-source-maps"];
|
|
31
|
+
function parseConfig(cwd, ts) {
|
|
32
|
+
const cwdPath = typeof cwd === "string" ? cwd : fileURLToPath(cwd);
|
|
33
|
+
const configFile = join(cwdPath, "tsconfig.json");
|
|
34
|
+
debug_default('parsing config file "%s"', configFile);
|
|
35
|
+
let hardException = null;
|
|
36
|
+
const parsedConfig = ts.getParsedCommandLineOfConfigFile(
|
|
37
|
+
configFile,
|
|
38
|
+
{},
|
|
39
|
+
{
|
|
40
|
+
...ts.sys,
|
|
41
|
+
useCaseSensitiveFileNames: true,
|
|
42
|
+
getCurrentDirectory: () => cwdPath,
|
|
43
|
+
onUnRecoverableConfigFileDiagnostic: (error) => hardException = error
|
|
44
|
+
}
|
|
45
|
+
);
|
|
46
|
+
if (hardException) {
|
|
47
|
+
const compilerHost = ts.createCompilerHost({});
|
|
48
|
+
console.log(ts.formatDiagnosticsWithColorAndContext([hardException], compilerHost));
|
|
49
|
+
return;
|
|
50
|
+
}
|
|
51
|
+
if (parsedConfig.errors.length) {
|
|
52
|
+
const compilerHost = ts.createCompilerHost({});
|
|
53
|
+
console.log(ts.formatDiagnosticsWithColorAndContext(parsedConfig.errors, compilerHost));
|
|
54
|
+
return;
|
|
55
|
+
}
|
|
56
|
+
return parsedConfig;
|
|
57
|
+
}
|
|
58
|
+
function runNode(cwd, options) {
|
|
59
|
+
const childProcess = execaNode(options.script, options.scriptArgs, {
|
|
60
|
+
nodeOptions: DEFAULT_NODE_ARGS.concat(options.nodeArgs),
|
|
61
|
+
preferLocal: true,
|
|
62
|
+
windowsHide: false,
|
|
63
|
+
localDir: cwd,
|
|
64
|
+
cwd,
|
|
65
|
+
reject: options.reject ?? false,
|
|
66
|
+
buffer: false,
|
|
67
|
+
stdio: options.stdio || "inherit",
|
|
68
|
+
env: {
|
|
69
|
+
...options.stdio === "pipe" ? { FORCE_COLOR: "true" } : {},
|
|
70
|
+
...options.env
|
|
71
|
+
}
|
|
72
|
+
});
|
|
73
|
+
return childProcess;
|
|
74
|
+
}
|
|
75
|
+
function run(cwd, options) {
|
|
76
|
+
const childProcess = execa(options.script, options.scriptArgs, {
|
|
77
|
+
preferLocal: true,
|
|
78
|
+
windowsHide: false,
|
|
79
|
+
localDir: cwd,
|
|
80
|
+
cwd,
|
|
81
|
+
buffer: false,
|
|
82
|
+
stdio: options.stdio || "inherit",
|
|
83
|
+
env: {
|
|
84
|
+
...options.stdio === "pipe" ? { FORCE_COLOR: "true" } : {},
|
|
85
|
+
...options.env
|
|
86
|
+
}
|
|
87
|
+
});
|
|
88
|
+
return childProcess;
|
|
89
|
+
}
|
|
90
|
+
function watch(options) {
|
|
91
|
+
return chokidar.watch(["."], options);
|
|
92
|
+
}
|
|
93
|
+
async function getPort(cwd) {
|
|
94
|
+
if (process.env.PORT) {
|
|
95
|
+
return getRandomPort({ port: Number(process.env.PORT) });
|
|
96
|
+
}
|
|
97
|
+
const files = await new EnvLoader(cwd).load();
|
|
98
|
+
for (let file of files) {
|
|
99
|
+
const envVariables = await new EnvParser(file.contents, cwd).parse();
|
|
100
|
+
if (envVariables.PORT) {
|
|
101
|
+
return getRandomPort({ port: Number(envVariables.PORT) });
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
return getRandomPort({ port: 3333 });
|
|
105
|
+
}
|
|
106
|
+
async function copyFiles(files, cwd, outDir) {
|
|
107
|
+
const { paths, patterns } = files.reduce(
|
|
108
|
+
(result, file) => {
|
|
109
|
+
if (fastGlob.isDynamicPattern(file)) {
|
|
110
|
+
result.patterns.push(file);
|
|
111
|
+
return result;
|
|
112
|
+
}
|
|
113
|
+
if (existsSync(join(cwd, file))) {
|
|
114
|
+
result.paths.push(file);
|
|
115
|
+
}
|
|
116
|
+
return result;
|
|
117
|
+
},
|
|
118
|
+
{ patterns: [], paths: [] }
|
|
119
|
+
);
|
|
120
|
+
debug_default("copyFiles inputs: %O, paths: %O, patterns: %O", files, paths, patterns);
|
|
121
|
+
const filePaths = paths.concat(await fastGlob(patterns, { cwd, dot: true })).filter((file) => {
|
|
122
|
+
return !isJunk(basename(file));
|
|
123
|
+
});
|
|
124
|
+
debug_default('copying files %O to destination "%s"', filePaths, outDir);
|
|
125
|
+
const copyPromises = filePaths.map(async (file) => {
|
|
126
|
+
const src = isAbsolute(file) ? file : join(cwd, file);
|
|
127
|
+
const dest = join(outDir, relative(cwd, src));
|
|
128
|
+
await mkdir(dirname(dest), { recursive: true });
|
|
129
|
+
return copyFile(src, dest);
|
|
130
|
+
});
|
|
131
|
+
return await Promise.all(copyPromises);
|
|
132
|
+
}
|
|
133
|
+
function memoize(fn, maxKeys) {
|
|
134
|
+
const cache = new Cache({ max: maxKeys });
|
|
135
|
+
return (input, ...args) => {
|
|
136
|
+
if (cache.has(input)) {
|
|
137
|
+
return cache.get(input);
|
|
138
|
+
}
|
|
139
|
+
return fn(input, ...args);
|
|
140
|
+
};
|
|
141
|
+
}
|
|
142
|
+
function isRelative(pathValue) {
|
|
143
|
+
return pathValue.startsWith("./") || pathValue.startsWith("../");
|
|
144
|
+
}
|
|
145
|
+
async function loadHooks(rcFileHooks, names) {
|
|
146
|
+
const groups = names.map((name) => {
|
|
147
|
+
return {
|
|
148
|
+
group: name,
|
|
149
|
+
hooks: rcFileHooks?.[name] ?? []
|
|
150
|
+
};
|
|
151
|
+
});
|
|
152
|
+
const hooks = new Hooks();
|
|
153
|
+
for (const { group, hooks: collection } of groups) {
|
|
154
|
+
for (const item of collection) {
|
|
155
|
+
if ("run" in item) {
|
|
156
|
+
hooks.add(group, item.run);
|
|
157
|
+
} else {
|
|
158
|
+
hooks.add(group, await importDefault(item));
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
return hooks;
|
|
163
|
+
}
|
|
164
|
+
function throttle(fn, name) {
|
|
165
|
+
name = name || "throttled";
|
|
166
|
+
let isBusy = false;
|
|
167
|
+
let hasQueuedCalls = false;
|
|
168
|
+
let lastCallArgs;
|
|
169
|
+
async function throttled(...args) {
|
|
170
|
+
if (isBusy) {
|
|
171
|
+
debug_default('ignoring "%s" invocation as current execution is in progress', name);
|
|
172
|
+
hasQueuedCalls = true;
|
|
173
|
+
lastCallArgs = args;
|
|
174
|
+
return;
|
|
175
|
+
}
|
|
176
|
+
isBusy = true;
|
|
177
|
+
debug_default('executing "%s" function', name);
|
|
178
|
+
await fn(...args);
|
|
179
|
+
isBusy = false;
|
|
180
|
+
if (hasQueuedCalls) {
|
|
181
|
+
hasQueuedCalls = false;
|
|
182
|
+
debug_default('resuming and running latest "%s" invocation', name);
|
|
183
|
+
await throttled(...lastCallArgs);
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
return throttled;
|
|
187
|
+
}
|
|
188
|
+
function removeExtension(filePath) {
|
|
189
|
+
return filePath.substring(0, filePath.lastIndexOf("."));
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
// src/virtual_file_system.ts
|
|
193
|
+
var BYPASS_FN = (input) => input;
|
|
194
|
+
var DEFAULT_GLOB = ["**/!(*.d).ts", "**/*.tsx", "**/*.js"];
|
|
195
|
+
var VirtualFileSystem = class {
|
|
196
|
+
/**
|
|
197
|
+
* Absolute path to the source directory from where to read the files.
|
|
198
|
+
* Additionally, a glob pattern or a filter could be specified to
|
|
199
|
+
* narrow down the scanned files list.
|
|
200
|
+
*/
|
|
201
|
+
#source;
|
|
202
|
+
/**
|
|
203
|
+
* Filesystem options
|
|
204
|
+
*/
|
|
205
|
+
#options;
|
|
206
|
+
/**
|
|
207
|
+
* Files collected from the initial scan with pre-computed relative paths
|
|
208
|
+
*/
|
|
209
|
+
#files = /* @__PURE__ */ new Map();
|
|
210
|
+
/**
|
|
211
|
+
* LRU cache storing parsed AST nodes by file path with size limit
|
|
212
|
+
*/
|
|
213
|
+
#astCache = new Cache2({ max: 60 });
|
|
214
|
+
/**
|
|
215
|
+
* Matcher is defined when glob is defined via the options
|
|
216
|
+
*/
|
|
217
|
+
#matcher;
|
|
218
|
+
/**
|
|
219
|
+
* Picomatch options used for file pattern matching
|
|
220
|
+
*/
|
|
221
|
+
#picoMatchOptions;
|
|
222
|
+
/**
|
|
223
|
+
* Create a new VirtualFileSystem instance
|
|
224
|
+
*
|
|
225
|
+
* @param source - Absolute path to the source directory
|
|
226
|
+
* @param options - Optional configuration for file filtering and processing
|
|
227
|
+
*/
|
|
228
|
+
constructor(source, options) {
|
|
229
|
+
this.#source = source;
|
|
230
|
+
this.#options = options ?? {};
|
|
231
|
+
this.#picoMatchOptions = {
|
|
232
|
+
cwd: this.#source
|
|
233
|
+
};
|
|
234
|
+
this.#matcher = picomatch(this.#options.glob ?? DEFAULT_GLOB, this.#picoMatchOptions);
|
|
235
|
+
}
|
|
236
|
+
/**
|
|
237
|
+
* Scans the filesystem to collect the files. Newly files must
|
|
238
|
+
* be added via the ".add" method.
|
|
239
|
+
*
|
|
240
|
+
* This method performs an initial scan of the source directory using
|
|
241
|
+
* the configured glob patterns and populates the internal file list.
|
|
242
|
+
*/
|
|
243
|
+
async scan() {
|
|
244
|
+
debug_default('fetching entities from source "%s"', this.#source);
|
|
245
|
+
const filesList = await new fdir().globWithOptions(this.#options.glob ?? DEFAULT_GLOB, this.#picoMatchOptions).withFullPaths().crawl(this.#source).withPromise();
|
|
246
|
+
debug_default("scanned files %O", filesList);
|
|
247
|
+
const sortedFiles = filesList.sort(naturalSort);
|
|
248
|
+
this.#files.clear();
|
|
249
|
+
for (let filePath of sortedFiles) {
|
|
250
|
+
filePath = string.toUnixSlash(filePath);
|
|
251
|
+
const relativePath = removeExtension(relative2(this.#source, filePath));
|
|
252
|
+
this.#files.set(filePath, relativePath);
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
/**
|
|
256
|
+
* Check if a given file is part of the virtual file system. The method
|
|
257
|
+
* checks for the scanned files as well as glob pattern matches.
|
|
258
|
+
*
|
|
259
|
+
* @param filePath - Absolute file path to check
|
|
260
|
+
* @returns True if the file is tracked or matches the configured patterns
|
|
261
|
+
*/
|
|
262
|
+
has(filePath) {
|
|
263
|
+
if (this.#files.has(filePath)) {
|
|
264
|
+
return true;
|
|
265
|
+
}
|
|
266
|
+
if (!filePath.startsWith(this.#source)) {
|
|
267
|
+
return false;
|
|
268
|
+
}
|
|
269
|
+
return this.#matcher(filePath);
|
|
270
|
+
}
|
|
271
|
+
/**
|
|
272
|
+
* Returns the files as a flat list of key-value pairs
|
|
273
|
+
*
|
|
274
|
+
* Converts the tracked files into a flat object where keys are relative
|
|
275
|
+
* paths (without extensions) and values are absolute file paths.
|
|
276
|
+
*
|
|
277
|
+
* @param options - Optional transformation functions for keys and values
|
|
278
|
+
* @returns Object with file mappings
|
|
279
|
+
*/
|
|
280
|
+
asList(options) {
|
|
281
|
+
const list = {};
|
|
282
|
+
const transformKey = options?.transformKey ?? BYPASS_FN;
|
|
283
|
+
const transformValue = options?.transformValue ?? BYPASS_FN;
|
|
284
|
+
for (const [filePath, relativePath] of this.#files) {
|
|
285
|
+
list[transformKey(relativePath)] = transformValue(filePath);
|
|
286
|
+
}
|
|
287
|
+
return list;
|
|
288
|
+
}
|
|
289
|
+
/**
|
|
290
|
+
* Returns the files as a nested tree structure
|
|
291
|
+
*
|
|
292
|
+
* Converts the tracked files into a hierarchical object structure that
|
|
293
|
+
* mirrors the directory structure of the source files.
|
|
294
|
+
*
|
|
295
|
+
* @param options - Optional transformation functions for keys and values
|
|
296
|
+
* @returns Nested object representing the file tree
|
|
297
|
+
*/
|
|
298
|
+
asTree(options) {
|
|
299
|
+
const list = {};
|
|
300
|
+
const transformKey = options?.transformKey ?? BYPASS_FN;
|
|
301
|
+
const transformValue = options?.transformValue ?? BYPASS_FN;
|
|
302
|
+
for (const [filePath, relativePath] of this.#files) {
|
|
303
|
+
lodash.set(list, transformKey(relativePath).split("/"), transformValue(filePath));
|
|
304
|
+
}
|
|
305
|
+
return list;
|
|
306
|
+
}
|
|
307
|
+
/**
|
|
308
|
+
* Add a new file to the virtual file system. File is only added when it
|
|
309
|
+
* matches the pre-defined filters.
|
|
310
|
+
*
|
|
311
|
+
* @param filePath - Absolute path of the file to add
|
|
312
|
+
* @returns True if the file was added, false if it doesn't match filters
|
|
313
|
+
*/
|
|
314
|
+
add(filePath) {
|
|
315
|
+
if (this.has(filePath)) {
|
|
316
|
+
debug_default('adding new "%s" file to the virtual file system', filePath);
|
|
317
|
+
const relativePath = removeExtension(relative2(this.#source, filePath));
|
|
318
|
+
this.#files.set(filePath, relativePath);
|
|
319
|
+
return true;
|
|
320
|
+
}
|
|
321
|
+
return false;
|
|
322
|
+
}
|
|
323
|
+
/**
|
|
324
|
+
* Remove a file from the virtual file system
|
|
325
|
+
*
|
|
326
|
+
* @param filePath - Absolute path of the file to remove
|
|
327
|
+
* @returns True if the file was removed, false if it wasn't tracked
|
|
328
|
+
*/
|
|
329
|
+
remove(filePath) {
|
|
330
|
+
debug_default('removing "%s" file from virtual file system', filePath);
|
|
331
|
+
return this.#files.delete(filePath);
|
|
332
|
+
}
|
|
333
|
+
/**
|
|
334
|
+
* Returns the file contents as AST-grep node and caches it
|
|
335
|
+
* forever. Use the "invalidate" method to remove it from the cache.
|
|
336
|
+
*
|
|
337
|
+
* This method reads the file content, parses it into an AST using ast-grep,
|
|
338
|
+
* and caches the result for future requests to improve performance.
|
|
339
|
+
*
|
|
340
|
+
* @param filePath - The absolute path to the file to parse
|
|
341
|
+
* @returns Promise resolving to the AST-grep node
|
|
342
|
+
*/
|
|
343
|
+
async get(filePath) {
|
|
344
|
+
const cached = this.#astCache.get(filePath);
|
|
345
|
+
if (cached) {
|
|
346
|
+
debug_default('returning AST nodes from cache "%s"', filePath);
|
|
347
|
+
return cached;
|
|
348
|
+
}
|
|
349
|
+
const fileContents = await readFile(filePath, "utf-8");
|
|
350
|
+
debug_default('parsing "%s" file to AST', filePath);
|
|
351
|
+
this.#astCache.set(filePath, parse(Lang.TypeScript, fileContents).root());
|
|
352
|
+
return this.#astCache.get(filePath);
|
|
353
|
+
}
|
|
354
|
+
/**
|
|
355
|
+
* Invalidates AST cache for a single file or all files
|
|
356
|
+
*
|
|
357
|
+
* Use this method when files have been modified to ensure fresh
|
|
358
|
+
* AST parsing on subsequent get() calls.
|
|
359
|
+
*
|
|
360
|
+
* @param filePath - Optional file path to clear. If omitted, clears entire cache
|
|
361
|
+
*/
|
|
362
|
+
invalidate(filePath) {
|
|
363
|
+
debug_default('invalidate AST cache "%s"', filePath);
|
|
364
|
+
filePath ? this.#astCache.delete(filePath) : this.#astCache.clear();
|
|
365
|
+
}
|
|
366
|
+
/**
|
|
367
|
+
* Clear all scanned files from memory
|
|
368
|
+
*
|
|
369
|
+
* Removes all tracked files from the internal file list, effectively
|
|
370
|
+
* resetting the virtual file system.
|
|
371
|
+
*/
|
|
372
|
+
clear() {
|
|
373
|
+
this.#files.clear();
|
|
374
|
+
}
|
|
375
|
+
};
|
|
376
|
+
|
|
377
|
+
export {
|
|
378
|
+
debug_default,
|
|
379
|
+
parseConfig,
|
|
380
|
+
runNode,
|
|
381
|
+
run,
|
|
382
|
+
watch,
|
|
383
|
+
getPort,
|
|
384
|
+
copyFiles,
|
|
385
|
+
memoize,
|
|
386
|
+
isRelative,
|
|
387
|
+
loadHooks,
|
|
388
|
+
throttle,
|
|
389
|
+
removeExtension,
|
|
390
|
+
VirtualFileSystem
|
|
391
|
+
};
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
// src/helpers.ts
|
|
2
|
+
import { parseImports } from "parse-imports";
|
|
3
|
+
async function findImport(code, importReference) {
|
|
4
|
+
const importIdentifier = importReference.split(".")[0];
|
|
5
|
+
for (const $import of await parseImports(code, {})) {
|
|
6
|
+
if (!$import.importClause) {
|
|
7
|
+
continue;
|
|
8
|
+
}
|
|
9
|
+
if (!$import.moduleSpecifier.value) {
|
|
10
|
+
continue;
|
|
11
|
+
}
|
|
12
|
+
if ($import.importClause.default === importIdentifier) {
|
|
13
|
+
return {
|
|
14
|
+
specifier: $import.moduleSpecifier.value,
|
|
15
|
+
isConstant: $import.moduleSpecifier.isConstant,
|
|
16
|
+
clause: {
|
|
17
|
+
type: "default",
|
|
18
|
+
value: importIdentifier
|
|
19
|
+
}
|
|
20
|
+
};
|
|
21
|
+
}
|
|
22
|
+
if ($import.importClause.namespace === importIdentifier) {
|
|
23
|
+
return {
|
|
24
|
+
specifier: $import.moduleSpecifier.value,
|
|
25
|
+
isConstant: $import.moduleSpecifier.isConstant,
|
|
26
|
+
clause: {
|
|
27
|
+
type: "namespace",
|
|
28
|
+
value: importIdentifier
|
|
29
|
+
}
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
const namedImport = $import.importClause.named.find(({ binding }) => {
|
|
33
|
+
return binding === importIdentifier;
|
|
34
|
+
});
|
|
35
|
+
if (namedImport) {
|
|
36
|
+
return {
|
|
37
|
+
specifier: $import.moduleSpecifier.value,
|
|
38
|
+
isConstant: $import.moduleSpecifier.isConstant,
|
|
39
|
+
clause: {
|
|
40
|
+
type: "named",
|
|
41
|
+
value: namedImport.specifier,
|
|
42
|
+
...namedImport.binding !== namedImport.specifier ? {
|
|
43
|
+
alias: namedImport.binding
|
|
44
|
+
} : {}
|
|
45
|
+
}
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
return null;
|
|
50
|
+
}
|
|
51
|
+
function inspectClass(node) {
|
|
52
|
+
return node.find({
|
|
53
|
+
rule: {
|
|
54
|
+
kind: "class_declaration"
|
|
55
|
+
}
|
|
56
|
+
});
|
|
57
|
+
}
|
|
58
|
+
function inspectClassMethods(node) {
|
|
59
|
+
return node.findAll({
|
|
60
|
+
rule: {
|
|
61
|
+
kind: "method_definition"
|
|
62
|
+
}
|
|
63
|
+
});
|
|
64
|
+
}
|
|
65
|
+
function nodeToPlainText(node) {
|
|
66
|
+
let out = [];
|
|
67
|
+
function toText(one) {
|
|
68
|
+
const children = one.children();
|
|
69
|
+
if (!children.length) {
|
|
70
|
+
out.push(one.text());
|
|
71
|
+
} else {
|
|
72
|
+
children.forEach((child) => toText(child));
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
toText(node);
|
|
76
|
+
return out.join("");
|
|
77
|
+
}
|
|
78
|
+
function inspectMethodArguments(node, methodCalls) {
|
|
79
|
+
const matchingExpressions = node.findAll({
|
|
80
|
+
rule: {
|
|
81
|
+
any: methodCalls.map((methodCall) => {
|
|
82
|
+
return {
|
|
83
|
+
pattern: {
|
|
84
|
+
context: `${methodCall}($$$ARGUMENTS)`,
|
|
85
|
+
selector: "call_expression"
|
|
86
|
+
}
|
|
87
|
+
};
|
|
88
|
+
})
|
|
89
|
+
}
|
|
90
|
+
});
|
|
91
|
+
return matchingExpressions.flatMap((matchingExpression) => {
|
|
92
|
+
return matchingExpression.findAll({ rule: { kind: "arguments" } });
|
|
93
|
+
});
|
|
94
|
+
}
|
|
95
|
+
function searchValidatorDirectUsage(node) {
|
|
96
|
+
const matchingExpressions = node.findAll({
|
|
97
|
+
rule: {
|
|
98
|
+
pattern: {
|
|
99
|
+
context: "$$$VALIDATOR.validate($$$)",
|
|
100
|
+
selector: "call_expression"
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
});
|
|
104
|
+
return matchingExpressions.flatMap((expression) => {
|
|
105
|
+
return expression.getMultipleMatches("VALIDATOR");
|
|
106
|
+
});
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
export {
|
|
110
|
+
findImport,
|
|
111
|
+
inspectClass,
|
|
112
|
+
inspectClassMethods,
|
|
113
|
+
nodeToPlainText,
|
|
114
|
+
inspectMethodArguments,
|
|
115
|
+
searchValidatorDirectUsage
|
|
116
|
+
};
|
package/build/index.d.ts
CHANGED