@flint.fyi/typescript-language 0.18.3 → 0.19.0

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 (70) hide show
  1. package/{lib/types/ast.d.ts → dist/index.d.mts} +777 -4
  2. package/dist/index.mjs +1104 -0
  3. package/package.json +9 -8
  4. package/lib/_virtual/_rolldown/runtime.js +0 -13
  5. package/lib/collectReferencedFilePaths.d.ts +0 -4
  6. package/lib/collectReferencedFilePaths.js +0 -40
  7. package/lib/convertTypeScriptDiagnosticToLanguageReport.d.ts +0 -18
  8. package/lib/convertTypeScriptDiagnosticToLanguageReport.js +0 -114
  9. package/lib/createTypeScriptServerHost.d.ts +0 -4
  10. package/lib/createTypeScriptServerHost.js +0 -103
  11. package/lib/directives/parseDirectivesFromTypeScriptFile.d.ts +0 -13
  12. package/lib/directives/parseDirectivesFromTypeScriptFile.js +0 -66
  13. package/lib/getFirstEnumValues.d.ts +0 -36
  14. package/lib/getFirstEnumValues.js +0 -48
  15. package/lib/getTSNodeRange.d.ts +0 -9
  16. package/lib/getTSNodeRange.js +0 -11
  17. package/lib/getTypeScriptFileCacheImpacts.d.ts +0 -4
  18. package/lib/getTypeScriptFileCacheImpacts.js +0 -9
  19. package/lib/index.d.ts +0 -26
  20. package/lib/index.js +0 -22
  21. package/lib/language.d.ts +0 -416
  22. package/lib/language.js +0 -119
  23. package/lib/nodes.d.ts +0 -204
  24. package/lib/normalizeRange.d.ts +0 -4
  25. package/lib/normalizeRange.js +0 -19
  26. package/lib/orderTypeScriptFilePaths.d.ts +0 -3
  27. package/lib/orderTypeScriptFilePaths.js +0 -92
  28. package/lib/package.js +0 -7
  29. package/lib/scope/identifierReferences.d.ts +0 -4
  30. package/lib/scope/identifierReferences.js +0 -91
  31. package/lib/scope/scopeManager.d.ts +0 -15
  32. package/lib/scope/scopeManager.js +0 -149
  33. package/lib/scope/scopes.d.ts +0 -8
  34. package/lib/scope/scopes.js +0 -66
  35. package/lib/scope/types.d.ts +0 -39
  36. package/lib/types/checker.d.ts +0 -23
  37. package/lib/utils/createRuleTesterTSConfig.d.ts +0 -8
  38. package/lib/utils/createRuleTesterTSConfig.js +0 -18
  39. package/lib/utils/declarationIncludesGlobal.d.ts +0 -7
  40. package/lib/utils/declarationIncludesGlobal.js +0 -9
  41. package/lib/utils/declarationsIncludeGlobal.d.ts +0 -3
  42. package/lib/utils/declarationsIncludeGlobal.js +0 -9
  43. package/lib/utils/forEachChild.d.ts +0 -8
  44. package/lib/utils/forEachChild.js +0 -7
  45. package/lib/utils/getDeclarationsIfGlobal.d.ts +0 -9
  46. package/lib/utils/getDeclarationsIfGlobal.js +0 -10
  47. package/lib/utils/getModifyingReferences.d.ts +0 -11
  48. package/lib/utils/getModifyingReferences.js +0 -15
  49. package/lib/utils/hasSameTokens.d.ts +0 -7
  50. package/lib/utils/hasSameTokens.js +0 -32
  51. package/lib/utils/isBuiltinArrayMethod.d.ts +0 -14
  52. package/lib/utils/isBuiltinArrayMethod.js +0 -9
  53. package/lib/utils/isFunction.d.ts +0 -7
  54. package/lib/utils/isFunction.js +0 -8
  55. package/lib/utils/isGlobalDeclaration.d.ts +0 -7
  56. package/lib/utils/isGlobalDeclaration.js +0 -9
  57. package/lib/utils/isGlobalDeclarationOfName.d.ts +0 -11
  58. package/lib/utils/isGlobalDeclarationOfName.js +0 -23
  59. package/lib/utils/isGlobalVariable.d.ts +0 -13
  60. package/lib/utils/isGlobalVariable.js +0 -19
  61. package/lib/utils/isInlineArrayCreation.d.ts +0 -12
  62. package/lib/utils/isInlineArrayCreation.js +0 -45
  63. package/lib/utils/isStaticString.d.ts +0 -6
  64. package/lib/utils/isStaticString.js +0 -9
  65. package/lib/utils/isStringRawNoSubstitution.d.ts +0 -8
  66. package/lib/utils/isStringRawNoSubstitution.js +0 -11
  67. package/lib/utils/unwrapParentParenthesizedExpressions.d.ts +0 -7
  68. package/lib/utils/unwrapParentParenthesizedExpressions.js +0 -9
  69. package/lib/utils/unwrapParenthesizedNode.d.ts +0 -7
  70. package/lib/utils/unwrapParenthesizedNode.js +0 -9
package/dist/index.mjs ADDED
@@ -0,0 +1,1104 @@
1
+ import ts, { SyntaxKind, isClassDeclaration, isFunctionDeclaration, isIdentifier, isInterfaceDeclaration, isPropertySignature, isVariableDeclaration } from "typescript";
2
+ import { DirectivesCollector, commonlyIgnoredPaths, createLanguage, getColumnAndLineOfPosition, getPositionOfColumnAndLine } from "@flint.fyi/core";
3
+ import * as tsutils from "ts-api-utils";
4
+ import { FlintAssertionError, assert, normalizePath, nullThrows, pathKey } from "@flint.fyi/utils";
5
+ import * as path$1 from "node:path";
6
+ import path from "node:path";
7
+ import { createProjectService } from "@typescript-eslint/project-service";
8
+ import { debugForFile } from "debug-for-file";
9
+ import fs from "node:fs";
10
+ import timers from "node:timers";
11
+ import { dirname, resolve } from "pathe";
12
+ import { WeakCachedFactory } from "cached-factory";
13
+ //#region src/convertTypeScriptDiagnosticToLanguageReport.ts
14
+ function convertTypeScriptDiagnosticToLanguageReport(diagnostic) {
15
+ return {
16
+ code: `TS${diagnostic.code}`,
17
+ source: "typescript",
18
+ text: formatReport(diagnostic),
19
+ ...diagnostic.file !== void 0 && diagnostic.start !== void 0 && { range: {
20
+ begin: diagnostic.start,
21
+ end: diagnostic.start + (diagnostic.length ?? 0)
22
+ } }
23
+ };
24
+ }
25
+ function color(text, formatStyle) {
26
+ return formatStyle + text + resetEscapeSequence;
27
+ }
28
+ function formatReport(diagnostic) {
29
+ let output = "";
30
+ if (diagnostic.file !== void 0) {
31
+ output += formatLocation(diagnostic.file, diagnostic.start);
32
+ output += " - ";
33
+ }
34
+ output += color(`TS${diagnostic.code}`, COLOR.Grey);
35
+ output += ": ";
36
+ output += ts.flattenDiagnosticMessageText(diagnostic.messageText, "\n");
37
+ if (diagnostic.file !== void 0) {
38
+ output += "\n";
39
+ output += formatCodeSpan(diagnostic.file, diagnostic.start, diagnostic.length, "", COLOR.Red);
40
+ }
41
+ if (diagnostic.relatedInformation) {
42
+ output += "\n";
43
+ for (const { file, length, messageText, start } of diagnostic.relatedInformation) {
44
+ const indent = " ";
45
+ if (file) {
46
+ output += "\n";
47
+ output += " " + formatLocation(file, start);
48
+ output += formatCodeSpan(file, start, length, indent, COLOR.Cyan);
49
+ }
50
+ output += "\n";
51
+ output += indent + ts.flattenDiagnosticMessageText(messageText, "\n");
52
+ }
53
+ }
54
+ return output;
55
+ }
56
+ const gutterStyleSequence = "\x1B[7m";
57
+ const ellipsis = "...";
58
+ const gutterSeparator = " ";
59
+ const resetEscapeSequence = "\x1B[0m";
60
+ const COLOR = {
61
+ Blue: "\x1B[94m",
62
+ Cyan: "\x1B[96m",
63
+ Grey: "\x1B[90m",
64
+ Red: "\x1B[91m",
65
+ Yellow: "\x1B[93m"
66
+ };
67
+ function displayFilename(name) {
68
+ if (name.startsWith("./")) return name.slice(2);
69
+ return name.slice(process.cwd().length + 1);
70
+ }
71
+ function formatCodeSpan(file, start, length, indent, squiggleColor) {
72
+ const { column: firstLineChar, line: firstLine } = getColumnAndLineOfPosition(file, start);
73
+ const { column: lastLineChar, line: lastLine } = getColumnAndLineOfPosition(file, start + length);
74
+ const lastLineInFile = getColumnAndLineOfPosition(file, file.text.length).line;
75
+ const hasMoreThanFiveLines = lastLine - firstLine >= 4;
76
+ let gutterWidth = (lastLine + 1 + "").length;
77
+ if (hasMoreThanFiveLines) gutterWidth = Math.max(3, gutterWidth);
78
+ let context = "";
79
+ for (let i = firstLine; i <= lastLine; i++) {
80
+ context += "\n";
81
+ if (hasMoreThanFiveLines && firstLine + 1 < i && i < lastLine - 1) {
82
+ context += indent + color(ellipsis.padStart(gutterWidth), gutterStyleSequence) + " \n";
83
+ i = lastLine - 1;
84
+ }
85
+ const lineStart = getPositionOfColumnAndLine(file, {
86
+ column: 0,
87
+ line: i
88
+ });
89
+ const lineEnd = i < lastLineInFile ? getPositionOfColumnAndLine(file, {
90
+ column: 0,
91
+ line: i + 1
92
+ }) : file.text.length;
93
+ let lineContent = file.text.slice(lineStart, lineEnd);
94
+ lineContent = lineContent.trimEnd();
95
+ lineContent = lineContent.replace(/\t/g, " ");
96
+ context += indent + color((i + 1 + "").padStart(gutterWidth), gutterStyleSequence) + gutterSeparator;
97
+ context += lineContent + "\n";
98
+ context += indent + color("".padStart(gutterWidth), gutterStyleSequence) + gutterSeparator;
99
+ context += squiggleColor;
100
+ if (i === firstLine) {
101
+ const lastCharForLine = i === lastLine ? lastLineChar : void 0;
102
+ context += lineContent.slice(0, firstLineChar).replace(/\S/g, " ");
103
+ context += lineContent.slice(firstLineChar, lastCharForLine).replace(/./g, "~");
104
+ } else if (i === lastLine) context += lineContent.slice(0, lastLineChar).replace(/./g, "~");
105
+ else context += lineContent.replace(/./g, "~");
106
+ context += resetEscapeSequence;
107
+ }
108
+ return context;
109
+ }
110
+ function formatLocation(file, start) {
111
+ const { column, line } = getColumnAndLineOfPosition(file, start);
112
+ const relativeFileName = displayFilename(file.fileName);
113
+ let output = "";
114
+ output += color(relativeFileName, COLOR.Cyan);
115
+ output += ":";
116
+ output += color(`${line + 1}`, COLOR.Yellow);
117
+ output += ":";
118
+ output += color(`${column + 1}`, COLOR.Yellow);
119
+ return output;
120
+ }
121
+ //#endregion
122
+ //#region src/normalizeRange.ts
123
+ function normalizeRange(original, sourceFile) {
124
+ const onCharacters = isNode(original) ? {
125
+ begin: original.getStart(sourceFile),
126
+ end: original.getEnd()
127
+ } : original;
128
+ return {
129
+ begin: getColumnAndLineOfPosition(sourceFile, onCharacters.begin),
130
+ end: getColumnAndLineOfPosition(sourceFile, onCharacters.end)
131
+ };
132
+ }
133
+ function isNode(value) {
134
+ return typeof value === "object" && value !== null && "kind" in value;
135
+ }
136
+ //#endregion
137
+ //#region src/directives/parseDirectivesFromTypeScriptFile.ts
138
+ function extractDirectivesFromTypeScriptFile(sourceFile) {
139
+ const directives = [];
140
+ tsutils.forEachComment(sourceFile, (fullText, sourceRange) => {
141
+ const commentText = fullText.slice(sourceRange.pos, sourceRange.end);
142
+ const match = /^\/\/\s*flint-(\S+)(?:\s+(.+))?/.exec(commentText);
143
+ if (!match) return;
144
+ let range = normalizeRange({
145
+ begin: sourceRange.pos,
146
+ end: sourceRange.end
147
+ }, sourceFile);
148
+ const matches = match.slice(1);
149
+ const type = nullThrows(matches[0], "First match is expected to be present by the regex match");
150
+ const selection = matches[1] ?? "";
151
+ if (type === "disable-next-line") range = extendRangeToNextCodeLine(sourceFile, range);
152
+ directives.push({
153
+ range,
154
+ selection,
155
+ type
156
+ });
157
+ });
158
+ return directives;
159
+ }
160
+ function parseDirectivesFromTypeScriptFile(sourceFile) {
161
+ const collector = new DirectivesCollector(sourceFile.statements.at(0)?.getStart(sourceFile) ?? sourceFile.text.length);
162
+ for (const { range, selection, type } of extractDirectivesFromTypeScriptFile(sourceFile)) collector.add(range, selection, type);
163
+ return collector.collect();
164
+ }
165
+ function computeNextCodeLine(sourceFile, directiveLine) {
166
+ const lineStarts = sourceFile.getLineStarts();
167
+ const nextLineStart = lineStarts[directiveLine + 1];
168
+ if (nextLineStart === void 0) return;
169
+ const scanner = ts.createScanner(sourceFile.languageVersion, true, sourceFile.languageVariant, sourceFile.text, void 0, nextLineStart);
170
+ if (scanner.scan() === SyntaxKind.EndOfFileToken) return;
171
+ const tokenPos = scanner.getTokenStart();
172
+ const codeLine = sourceFile.getLineAndCharacterOfPosition(tokenPos).line;
173
+ for (let line = directiveLine + 1; line < codeLine; line++) {
174
+ const start = lineStarts[line];
175
+ const end = lineStarts[line + 1] ?? sourceFile.text.length;
176
+ if (sourceFile.text.slice(start, end).trim() === "") return;
177
+ }
178
+ return codeLine;
179
+ }
180
+ function extendRangeToNextCodeLine(sourceFile, range) {
181
+ const codeLine = computeNextCodeLine(sourceFile, range.begin.line);
182
+ if (codeLine === void 0 || codeLine <= range.end.line + 1) return range;
183
+ const endPosition = nullThrows(sourceFile.getLineStarts()[codeLine], "Code line start is expected to be present by the computed code line") - 1;
184
+ const { character, line } = sourceFile.getLineAndCharacterOfPosition(endPosition);
185
+ return {
186
+ ...range,
187
+ end: {
188
+ column: character,
189
+ line,
190
+ raw: endPosition
191
+ }
192
+ };
193
+ }
194
+ //#endregion
195
+ //#region src/getTSNodeRange.ts
196
+ function getTSNodeRange(node, sourceFile) {
197
+ return {
198
+ begin: node.getStart(sourceFile),
199
+ end: node.getEnd()
200
+ };
201
+ }
202
+ //#endregion
203
+ //#region package.json
204
+ var name = "@flint.fyi/typescript-language";
205
+ var version = "0.19.0";
206
+ //#endregion
207
+ //#region src/createTypeScriptServerHost.ts
208
+ function serverHostMethodNotImplemented(methodName) {
209
+ throw new FlintAssertionError(`ts.ServerHost's method '${methodName}' is not implemented.`);
210
+ }
211
+ const UV_DIRENT_TYPE = {
212
+ UV_DIRENT_DIR: 2,
213
+ UV_DIRENT_FILE: 1
214
+ };
215
+ const DirentCtor = fs.Dirent;
216
+ function createTypeScriptServerHost(host) {
217
+ return {
218
+ ...ts.sys,
219
+ args: [],
220
+ clearImmediate: timers.clearImmediate,
221
+ clearTimeout: timers.clearTimeout,
222
+ createDirectory() {
223
+ serverHostMethodNotImplemented("createDirectory");
224
+ },
225
+ directoryExists(directoryPath) {
226
+ return host.fileTypeSync(resolve(host.getCurrentDirectory(), directoryPath)) === "directory";
227
+ },
228
+ exit() {
229
+ serverHostMethodNotImplemented("exit");
230
+ },
231
+ fileExists(filePath) {
232
+ return host.fileTypeSync(resolve(host.getCurrentDirectory(), filePath)) === "file";
233
+ },
234
+ readDirectory(directoryPath, extensions, exclude, include, depth) {
235
+ const originalCwd = process.cwd.bind(process);
236
+ process.cwd = () => host.getCurrentDirectory();
237
+ const originalReadDirSync = fs.readdirSync;
238
+ const patchedReaddirSync = (readPath, options) => {
239
+ assert(typeof options === "object" && options != null && Object.keys(options).length === 1 && options.withFileTypes === true, `ts.sys.readDirectory passed unexpected options to fs.readdirSync: ${JSON.stringify(options)}`);
240
+ assert(typeof readPath === "string", "ts.sys.readDirectory passed unexpected path to fs.readdirSync");
241
+ try {
242
+ fs.readdirSync = originalReadDirSync;
243
+ return host.readDirectorySync(resolve(host.getCurrentDirectory(), readPath)).map((dirent) => new DirentCtor(dirent.name, dirent.type === "file" ? UV_DIRENT_TYPE.UV_DIRENT_FILE : UV_DIRENT_TYPE.UV_DIRENT_DIR, readPath));
244
+ } finally {
245
+ fs.readdirSync = patchedReaddirSync;
246
+ }
247
+ };
248
+ fs.readdirSync = patchedReaddirSync;
249
+ try {
250
+ return ts.sys.readDirectory(directoryPath, extensions, exclude, include, depth);
251
+ } finally {
252
+ process.cwd = originalCwd;
253
+ fs.readdirSync = originalReadDirSync;
254
+ }
255
+ },
256
+ readFile(filePath) {
257
+ return host.readFileSync(resolve(host.getCurrentDirectory(), filePath));
258
+ },
259
+ setImmediate: timers.setImmediate,
260
+ setTimeout: timers.setTimeout,
261
+ watchDirectory(directoryPath, callback, recursive = false) {
262
+ const watcher = host.watchDirectorySync(resolve(host.getCurrentDirectory(), directoryPath), (filePathAbsolute) => {
263
+ callback(filePathAbsolute);
264
+ }, {
265
+ ignoredPaths: commonlyIgnoredPaths,
266
+ recursive
267
+ });
268
+ return { close() {
269
+ watcher[Symbol.dispose]();
270
+ } };
271
+ },
272
+ watchFile(filePath, callback) {
273
+ const watcher = host.watchFileSync(resolve(host.getCurrentDirectory(), filePath), (event) => {
274
+ let eventKind;
275
+ switch (event) {
276
+ case "changed":
277
+ eventKind = ts.FileWatcherEventKind.Changed;
278
+ break;
279
+ case "created":
280
+ eventKind = ts.FileWatcherEventKind.Created;
281
+ break;
282
+ case "deleted":
283
+ eventKind = ts.FileWatcherEventKind.Deleted;
284
+ break;
285
+ }
286
+ callback(filePath, eventKind);
287
+ }, { ignoredPaths: commonlyIgnoredPaths });
288
+ return { close() {
289
+ watcher[Symbol.dispose]();
290
+ } };
291
+ },
292
+ write() {
293
+ serverHostMethodNotImplemented("write");
294
+ },
295
+ writeFile() {
296
+ serverHostMethodNotImplemented("writeFile");
297
+ }
298
+ };
299
+ }
300
+ //#endregion
301
+ //#region src/getFirstEnumValues.ts
302
+ /**
303
+ * Removes TypeScript enum values that alias earlier values.
304
+ * @param original Any TypeScript enum with numeric values.
305
+ * @returns A version of the enum with only the first occurrence of each value.
306
+ * @example
307
+ * Given the following enum:
308
+ * ```ts
309
+ * enum Example {
310
+ * A = 1,
311
+ * B = 2,
312
+ * C = A
313
+ * }
314
+ * ```
315
+ * Its original value looks like:
316
+ * ```ts
317
+ * {
318
+ * 1: 'C', // <-- Points to 'C', the alias of 'A'
319
+ * 2: 'B',
320
+ * A: 1,
321
+ * B: 2,
322
+ * C: 1,
323
+ * }
324
+ * ```
325
+ * Calling `getFirstEnumValues(Example)` would return:
326
+ * ```ts
327
+ * {
328
+ * 1: 'A', // <-- Corrected to the original 'A'
329
+ * 2: 'B',
330
+ * A: 1,
331
+ * B: 2,
332
+ * C: 1,
333
+ * }
334
+ * ```
335
+ */
336
+ function getFirstEnumValues(original) {
337
+ const result = {};
338
+ for (const key in original) {
339
+ if (typeof original[key] !== "number") continue;
340
+ if (!(key in result)) result[key] = original[key];
341
+ if (!(original[key] in result)) result[original[key]] = key;
342
+ }
343
+ return result;
344
+ }
345
+ //#endregion
346
+ //#region src/collectReferencedFilePaths.ts
347
+ function collectReferencedFilePaths(program, sourceFile) {
348
+ const modulePaths = /* @__PURE__ */ new Set();
349
+ function resolveModulePath(moduleSpecifier) {
350
+ const resolved = ts.resolveModuleName(moduleSpecifier, sourceFile.fileName, program.getCompilerOptions(), ts.sys);
351
+ if (resolved.resolvedModule?.isExternalLibraryImport === false) return path$1.relative(process.cwd(), resolved.resolvedModule.resolvedFileName);
352
+ }
353
+ function visit(node) {
354
+ let path;
355
+ if (isImportDeclaration(node)) path = node.moduleSpecifier.text;
356
+ else if (isImportCall(node)) path = node.arguments[0].text;
357
+ else if (isAwaitImportCall(node)) path = node.expression.arguments[0].text;
358
+ else if (isImportTypeNode(node)) path = node.argument.literal.text;
359
+ const resolvedPath = path && resolveModulePath(path);
360
+ if (resolvedPath) modulePaths.add(resolvedPath);
361
+ ts.forEachChild(node, visit);
362
+ }
363
+ visit(sourceFile);
364
+ return Array.from(modulePaths);
365
+ }
366
+ function isAwaitImportCall(node) {
367
+ return ts.isAwaitExpression(node) && isImportCall(node.expression);
368
+ }
369
+ function isImportCall(node) {
370
+ return ts.isCallExpression(node) && tsutils.isImportExpression(node.expression) && !!node.arguments.length && ts.isStringLiteral(nullThrows(node.arguments[0], "First argument is expected to be present by prior length check"));
371
+ }
372
+ function isImportDeclaration(node) {
373
+ return ts.isImportDeclaration(node) && ts.isStringLiteral(node.moduleSpecifier);
374
+ }
375
+ function isImportTypeNode(node) {
376
+ return ts.isImportTypeNode(node) && ts.isLiteralTypeNode(node.argument) && ts.isStringLiteral(node.argument.literal);
377
+ }
378
+ //#endregion
379
+ //#region src/utils/containsGlobalDeclarations.ts
380
+ /**
381
+ * Inspects top-level statements of a TS source file to determine
382
+ * if it introduces or modifies entities in the global scope.
383
+ */
384
+ function containsGlobalDeclarations(sourceFileNode) {
385
+ const isModule = ts.isExternalModule(sourceFileNode);
386
+ return sourceFileNode.statements.some((statement) => {
387
+ if (ts.isModuleDeclaration(statement) && statement.name.text === "global") return true;
388
+ if (isModule) return false;
389
+ if (!ts.canHaveModifiers(statement)) return false;
390
+ return ts.getModifiers(statement)?.some((mod) => mod.kind === SyntaxKind.DeclareKeyword);
391
+ });
392
+ }
393
+ //#endregion
394
+ //#region src/getTypeScriptFileCacheImpacts.ts
395
+ function getTypeScriptFileCacheImpacts(file) {
396
+ return {
397
+ dependencies: ["tsconfig.json", ...collectReferencedFilePaths(file.services.program, file.services.sourceFile)],
398
+ invalidatesCache: containsGlobalDeclarations(file.services.sourceFile)
399
+ };
400
+ }
401
+ //#endregion
402
+ //#region src/orderTypeScriptFilePaths.ts
403
+ const tsConfigFileName = "tsconfig.json";
404
+ function orderTypeScriptFilePaths(filePaths, host) {
405
+ if (filePaths.length < 2) return [...filePaths];
406
+ const caseSensitiveFS = host.isCaseSensitiveFS();
407
+ const cwd = host.getCurrentDirectory();
408
+ const parseHost = {
409
+ ...createTypeScriptServerHost(host),
410
+ onUnRecoverableConfigFileDiagnostic() {}
411
+ };
412
+ const configByDirectory = /* @__PURE__ */ new Map();
413
+ const parsedConfigByPath = /* @__PURE__ */ new Map();
414
+ const configByFile = /* @__PURE__ */ new Map();
415
+ function comparePaths(a, b) {
416
+ return pathKey(a, caseSensitiveFS).localeCompare(pathKey(b, caseSensitiveFS));
417
+ }
418
+ function findConfigFile(directoryPath) {
419
+ const directoryPathNormalized = normalizePath(directoryPath);
420
+ const directoryKey = pathKey(directoryPathNormalized, caseSensitiveFS);
421
+ if (configByDirectory.has(directoryKey)) return configByDirectory.get(directoryKey);
422
+ const configPath = ts.findConfigFile(directoryPathNormalized, (path) => parseHost.fileExists(path), tsConfigFileName);
423
+ const normalizedConfigPath = configPath == null ? void 0 : resolve(cwd, configPath);
424
+ configByDirectory.set(directoryKey, normalizedConfigPath);
425
+ return normalizedConfigPath;
426
+ }
427
+ function getParsedConfig(configPath) {
428
+ const configKey = pathKey(configPath, caseSensitiveFS);
429
+ if (parsedConfigByPath.has(configKey)) return parsedConfigByPath.get(configKey);
430
+ const parsed = ts.getParsedCommandLineOfConfigFile(configPath, {}, parseHost);
431
+ const parsedConfig = parsed == null ? void 0 : {
432
+ fileNames: parsed.fileNames.map((fileName) => resolve(cwd, fileName)),
433
+ references: (parsed.projectReferences ?? []).map((reference) => resolve(cwd, ts.resolveProjectReferencePath(reference))).sort(comparePaths)
434
+ };
435
+ parsedConfigByPath.set(configKey, parsedConfig);
436
+ return parsedConfig;
437
+ }
438
+ function collectConfigsTopologically(configPath) {
439
+ const orderedConfigs = [];
440
+ const seen = /* @__PURE__ */ new Set();
441
+ const visiting = /* @__PURE__ */ new Set();
442
+ function visit(currentConfigPath) {
443
+ const configKey = pathKey(currentConfigPath, caseSensitiveFS);
444
+ if (seen.has(configKey) || visiting.has(configKey)) return;
445
+ visiting.add(configKey);
446
+ for (const reference of getParsedConfig(currentConfigPath)?.references ?? []) visit(reference);
447
+ visiting.delete(configKey);
448
+ seen.add(configKey);
449
+ orderedConfigs.push(currentConfigPath);
450
+ }
451
+ visit(configPath);
452
+ return orderedConfigs;
453
+ }
454
+ const fileInfos = filePaths.map((original) => {
455
+ const absolute = resolve(cwd, original);
456
+ const rootConfig = findConfigFile(dirname(absolute));
457
+ return {
458
+ absolute,
459
+ original,
460
+ rootConfig,
461
+ rootConfigKey: rootConfig == null ? void 0 : pathKey(rootConfig, caseSensitiveFS)
462
+ };
463
+ });
464
+ const rootConfigs = Array.from(new Set(fileInfos.map(({ rootConfig }) => rootConfig).filter((rootConfig) => rootConfig != null))).sort(comparePaths);
465
+ const fileInfoByPath = new Map(fileInfos.map((fileInfo) => [pathKey(fileInfo.absolute, caseSensitiveFS), fileInfo]));
466
+ const configRanks = /* @__PURE__ */ new Map();
467
+ for (const rootConfig of rootConfigs) {
468
+ const rootConfigKey = pathKey(rootConfig, caseSensitiveFS);
469
+ for (const config of collectConfigsTopologically(rootConfig)) {
470
+ const configKey = pathKey(config, caseSensitiveFS);
471
+ if (!configRanks.has(configKey)) configRanks.set(configKey, configRanks.size);
472
+ for (const fileName of getParsedConfig(config)?.fileNames ?? []) {
473
+ const fileInfo = fileInfoByPath.get(pathKey(fileName, caseSensitiveFS));
474
+ if (fileInfo?.rootConfigKey !== rootConfigKey) continue;
475
+ const fileKey = pathKey(fileInfo.absolute, caseSensitiveFS);
476
+ if (!configByFile.has(fileKey)) configByFile.set(fileKey, config);
477
+ }
478
+ }
479
+ }
480
+ return fileInfos.toSorted((a, b) => {
481
+ const configA = configByFile.get(pathKey(a.absolute, caseSensitiveFS)) ?? a.rootConfig;
482
+ const configB = configByFile.get(pathKey(b.absolute, caseSensitiveFS)) ?? b.rootConfig;
483
+ return (configA == null ? Number.MAX_SAFE_INTEGER : configRanks.get(pathKey(configA, caseSensitiveFS)) ?? Number.MAX_SAFE_INTEGER) - (configB == null ? Number.MAX_SAFE_INTEGER : configRanks.get(pathKey(configB, caseSensitiveFS)) ?? Number.MAX_SAFE_INTEGER) || comparePaths(a.absolute, b.absolute);
484
+ }).map(({ original }) => original);
485
+ }
486
+ //#endregion
487
+ //#region src/language.ts
488
+ const log = debugForFile(import.meta.filename);
489
+ const NodeSyntaxKinds = getFirstEnumValues(SyntaxKind);
490
+ const stateSymbol = Symbol.for("@flint.fyi/typescript-language/state");
491
+ const globalTyped = globalThis;
492
+ assert(globalTyped[stateSymbol] == null, `Two different versions of ${name} are imported: ${version} and ${globalTyped[stateSymbol]?.packageVersion}`);
493
+ const languageState = globalTyped[stateSymbol] = {
494
+ packageVersion: version,
495
+ volarCreateFile: null
496
+ };
497
+ function setVolarCreateFile(create) {
498
+ assert(languageState.volarCreateFile == null, "setVolarCreateFile is expected to be called only once");
499
+ languageState.volarCreateFile = create;
500
+ }
501
+ const typescriptLanguage = createLanguage({
502
+ about: { name: "TypeScript" },
503
+ createFileFactory: (host) => {
504
+ const { service } = createProjectService({ host: createTypeScriptServerHost(host) });
505
+ function createFile(data) {
506
+ log("Opening client file:", data.filePathAbsolute);
507
+ service.openClientFile(data.filePathAbsolute);
508
+ log("Retrieving client services:", data.filePathAbsolute);
509
+ const scriptInfo = nullThrows(service.getScriptInfo(data.filePathAbsolute), `Could not find script info for file: ${data.filePathAbsolute}`);
510
+ const program = nullThrows(nullThrows(service.getDefaultProjectForFile(scriptInfo.fileName, true), `Could not find default project for file: ${data.filePathAbsolute}`).getLanguageService(true).getProgram(), `Could not retrieve program for file: ${data.filePathAbsolute}`);
511
+ const sourceFile = nullThrows(program.getSourceFile(data.filePathAbsolute), `Could not retrieve source file for: ${data.filePathAbsolute}`);
512
+ const fileExtension = path.extname(data.filePathAbsolute);
513
+ if (typeScriptCoreSupportedExtensions.has(fileExtension)) return {
514
+ ...parseDirectivesFromTypeScriptFile(sourceFile),
515
+ about: data,
516
+ language: typescriptLanguage,
517
+ services: {
518
+ program,
519
+ sourceFile,
520
+ typeChecker: program.getTypeChecker()
521
+ },
522
+ [Symbol.dispose]() {
523
+ service.closeClientFile(data.filePathAbsolute);
524
+ }
525
+ };
526
+ if (languageState.volarCreateFile == null) throwUnknownLanguageExtension(data.filePathAbsolute);
527
+ return {
528
+ ...languageState.volarCreateFile(data, program, sourceFile),
529
+ [Symbol.dispose]() {
530
+ service.closeClientFile(data.filePathAbsolute);
531
+ }
532
+ };
533
+ }
534
+ return { createFile };
535
+ },
536
+ getFileCacheImpacts: getTypeScriptFileCacheImpacts,
537
+ getLanguageReports(file) {
538
+ if ("__volarServices" in file) return file.__volarServices.getLanguageReports();
539
+ return ts.getPreEmitDiagnostics(file.services.program, file.services.sourceFile).map(convertTypeScriptDiagnosticToLanguageReport);
540
+ },
541
+ orderFilePaths: orderTypeScriptFilePaths,
542
+ runFileVisitors(file, options, runtime) {
543
+ if (!runtime.visitors) return;
544
+ if ("__volarServices" in file) {
545
+ file.__volarServices.runVisitors(file, options, runtime);
546
+ return;
547
+ }
548
+ const { visitors } = runtime;
549
+ const visitorServices = {
550
+ options,
551
+ ...file.services
552
+ };
553
+ const visit = (node) => {
554
+ const key = NodeSyntaxKinds[node.kind];
555
+ visitors[key]?.(node, visitorServices);
556
+ node.forEachChild(visit);
557
+ visitors[`${key}:exit`]?.(node, visitorServices);
558
+ };
559
+ visit(file.services.sourceFile);
560
+ }
561
+ });
562
+ const typeScriptCoreSupportedExtensions = /* @__PURE__ */ new Set([
563
+ ".cjs",
564
+ ".cts",
565
+ ".d.cts",
566
+ ".d.mts",
567
+ ".d.ts",
568
+ ".js",
569
+ ".json",
570
+ ".jsx",
571
+ ".mjs",
572
+ ".mts",
573
+ ".ts",
574
+ ".tsx"
575
+ ]);
576
+ const fileExtToFlintPlugin = {
577
+ ".astro": "@flint.fyi/astro",
578
+ ".gjs": "@flint.fyi/ember",
579
+ ".gts": "@flint.fyi/ember",
580
+ ".mdx": "@flint.fyi/mdx",
581
+ ".svelte": "@flint.fyi/svelte",
582
+ ".vue": "@flint.fyi/vue"
583
+ };
584
+ function throwUnknownLanguageExtension(filename) {
585
+ const pluginName = fileExtToFlintPlugin[path.extname(filename)];
586
+ const message = pluginName ? `Did you install & import ${pluginName}?` : "Unknown extension.";
587
+ throw new Error(`Cannot process ${filename}. ${message}`);
588
+ }
589
+ //#endregion
590
+ //#region src/utils/forEachChild.ts
591
+ const forEachChild = ts.forEachChild;
592
+ //#endregion
593
+ //#region src/scope/identifierReferences.ts
594
+ function isNonReferenceIdentifier(identifier) {
595
+ if (isIdentifierDeclaration(identifier) || isTypeReferenceIdentifier(identifier)) return true;
596
+ const { parent } = identifier;
597
+ switch (parent.kind) {
598
+ case SyntaxKind.BindingElement:
599
+ case SyntaxKind.ImportSpecifier: return parent.propertyName === identifier;
600
+ case SyntaxKind.BreakStatement:
601
+ case SyntaxKind.ContinueStatement:
602
+ case SyntaxKind.LabeledStatement: return parent.label === identifier;
603
+ case SyntaxKind.JsxAttribute:
604
+ case SyntaxKind.MethodDeclaration:
605
+ case SyntaxKind.MethodSignature:
606
+ case SyntaxKind.PropertyAccessExpression:
607
+ case SyntaxKind.PropertyAssignment:
608
+ case SyntaxKind.PropertyDeclaration:
609
+ case SyntaxKind.PropertySignature: return parent.name === identifier;
610
+ }
611
+ return false;
612
+ }
613
+ function isWriteReference(identifier) {
614
+ const { parent } = identifier;
615
+ if (isAssignmentTarget(identifier)) return true;
616
+ switch (parent.kind) {
617
+ case SyntaxKind.PostfixUnaryExpression:
618
+ case SyntaxKind.PrefixUnaryExpression: return parent.operand === identifier && (parent.operator === SyntaxKind.PlusPlusToken || parent.operator === SyntaxKind.MinusMinusToken);
619
+ default: return false;
620
+ }
621
+ }
622
+ function isAssignmentTarget(identifier) {
623
+ let current = identifier;
624
+ while (true) {
625
+ const parent = current.parent;
626
+ switch (parent.kind) {
627
+ case SyntaxKind.ArrayLiteralExpression:
628
+ case SyntaxKind.ObjectLiteralExpression:
629
+ case SyntaxKind.ShorthandPropertyAssignment:
630
+ current = parent;
631
+ continue;
632
+ case SyntaxKind.PropertyAssignment:
633
+ if (parent.name === current) return false;
634
+ current = parent;
635
+ continue;
636
+ case SyntaxKind.BinaryExpression: return parent.left === current && tsutils.isAssignmentKind(parent.operatorToken.kind);
637
+ case SyntaxKind.ForInStatement:
638
+ case SyntaxKind.ForOfStatement: return parent.initializer === current;
639
+ }
640
+ return false;
641
+ }
642
+ }
643
+ function isIdentifierDeclaration(identifier) {
644
+ const { parent } = identifier;
645
+ switch (parent.kind) {
646
+ case SyntaxKind.BindingElement:
647
+ case SyntaxKind.Parameter:
648
+ case SyntaxKind.VariableDeclaration: return isIdentifierWithinParent(identifier, parent.name);
649
+ case SyntaxKind.ClassDeclaration:
650
+ case SyntaxKind.ClassExpression:
651
+ case SyntaxKind.FunctionDeclaration:
652
+ case SyntaxKind.FunctionExpression:
653
+ case SyntaxKind.InterfaceDeclaration:
654
+ case SyntaxKind.ModuleDeclaration:
655
+ case SyntaxKind.TypeAliasDeclaration: return parent.name === identifier;
656
+ case SyntaxKind.ImportClause:
657
+ case SyntaxKind.ImportSpecifier:
658
+ case SyntaxKind.NamespaceImport: return parent.name === identifier;
659
+ }
660
+ return false;
661
+ }
662
+ function isIdentifierWithinParent(identifier, parent) {
663
+ let current = identifier;
664
+ while (current !== parent) {
665
+ const next = current.parent;
666
+ if (!next) return false;
667
+ current = next;
668
+ }
669
+ return true;
670
+ }
671
+ function isTypeReferenceIdentifier(identifier) {
672
+ let current = identifier;
673
+ while (current.parent.kind === SyntaxKind.QualifiedName) current = current.parent;
674
+ const parent = current.parent;
675
+ if (parent.kind !== SyntaxKind.TypeReference) return false;
676
+ return isIdentifierWithinParent(identifier, parent.typeName);
677
+ }
678
+ //#endregion
679
+ //#region src/scope/scopes.ts
680
+ function createScope(block, upper) {
681
+ const variablesByName = /* @__PURE__ */ new Map();
682
+ const scope = {
683
+ block,
684
+ childScopes: [],
685
+ references: [],
686
+ upper,
687
+ variables: [],
688
+ variablesByName
689
+ };
690
+ Object.defineProperty(scope, "variablesByName", {
691
+ enumerable: false,
692
+ value: variablesByName
693
+ });
694
+ upper?.childScopes.push(scope);
695
+ return scope;
696
+ }
697
+ function getReferencesInScope(scope) {
698
+ const references = [...scope.references];
699
+ for (const childScope of scope.childScopes) if (!isFunctionLike(childScope.block)) references.push(...getReferencesInScope(childScope));
700
+ return references;
701
+ }
702
+ function getVariableDeclarationScope(node, scope) {
703
+ if (node.parent.kind === SyntaxKind.VariableDeclarationList && !(node.parent.flags & ts.NodeFlags.BlockScoped)) return findContainingVariableScope(scope);
704
+ return scope;
705
+ }
706
+ function isScopeBoundary(node) {
707
+ return isFunctionLike(node) || node.kind === SyntaxKind.CatchClause || node.kind === SyntaxKind.ClassExpression || node.kind === SyntaxKind.ForInStatement || node.kind === SyntaxKind.ForOfStatement || node.kind === SyntaxKind.ForStatement || isBlockScopeBoundary(node);
708
+ }
709
+ function resolveVariable(scope, name) {
710
+ let current = scope;
711
+ while (current) {
712
+ const variable = current.variablesByName.get(name);
713
+ if (variable) return variable;
714
+ current = current.upper;
715
+ }
716
+ }
717
+ function findContainingVariableScope(scope) {
718
+ let current = scope;
719
+ while (current.upper && !isVariableScopeBoundary(current.block)) current = current.upper;
720
+ return current;
721
+ }
722
+ function isBlockScopeBoundary(node) {
723
+ return node.kind === SyntaxKind.Block && node.parent.kind !== SyntaxKind.CatchClause && !isFunctionLike(node.parent);
724
+ }
725
+ function isFunctionLike(node) {
726
+ switch (node?.kind) {
727
+ case SyntaxKind.ArrowFunction:
728
+ case SyntaxKind.Constructor:
729
+ case SyntaxKind.FunctionDeclaration:
730
+ case SyntaxKind.FunctionExpression:
731
+ case SyntaxKind.GetAccessor:
732
+ case SyntaxKind.MethodDeclaration:
733
+ case SyntaxKind.SetAccessor: return true;
734
+ default: return false;
735
+ }
736
+ }
737
+ function isVariableScopeBoundary(node) {
738
+ return node.kind === SyntaxKind.SourceFile || isFunctionLike(node);
739
+ }
740
+ //#endregion
741
+ //#region src/scope/scopeManager.ts
742
+ const scopeManagers = new WeakCachedFactory(createScopeManager);
743
+ function getScopeManager(sourceFile) {
744
+ return scopeManagers.get(sourceFile);
745
+ }
746
+ function createScopeManager(sourceFile) {
747
+ const declarationVariablesByIdentifier = /* @__PURE__ */ new WeakMap();
748
+ const declaredVariablesByNode = /* @__PURE__ */ new WeakMap();
749
+ const nodeScopes = /* @__PURE__ */ new WeakMap();
750
+ const globalScope = createScope(sourceFile, void 0);
751
+ function addBindingName(scope, name, node, kind) {
752
+ if (name.kind === SyntaxKind.Identifier) return [addVariable(scope, name, node, kind)];
753
+ const variables = [];
754
+ for (const element of name.elements) if (element.kind !== SyntaxKind.OmittedExpression) variables.push(...addBindingName(scope, element.name, node, kind));
755
+ return variables;
756
+ }
757
+ function addImportVariables(scope, node) {
758
+ if (!node.importClause || node.importClause.isTypeOnly) return;
759
+ if (node.importClause.name) addVariable(scope, node.importClause.name, node, "import");
760
+ const { namedBindings } = node.importClause;
761
+ if (!namedBindings) return;
762
+ if (namedBindings.kind === SyntaxKind.NamespaceImport) {
763
+ addVariable(scope, namedBindings.name, node, "import");
764
+ return;
765
+ }
766
+ for (const element of namedBindings.elements) {
767
+ if (element.isTypeOnly) continue;
768
+ addVariable(scope, element.name, node, "import");
769
+ }
770
+ }
771
+ function addParameters(scope, node) {
772
+ for (const parameter of node.parameters) {
773
+ const variables = addBindingName(scope, parameter.name, node, "parameter");
774
+ declaredVariablesByNode.set(parameter, variables);
775
+ }
776
+ }
777
+ function addVariable(scope, identifier, node, kind) {
778
+ let variable = scope.variablesByName.get(identifier.text);
779
+ if (!variable) {
780
+ variable = {
781
+ declarations: [],
782
+ definitions: [],
783
+ name: identifier.text,
784
+ references: [],
785
+ scope
786
+ };
787
+ scope.variablesByName.set(identifier.text, variable);
788
+ scope.variables.push(variable);
789
+ }
790
+ variable.declarations.push(identifier);
791
+ variable.definitions.push({
792
+ identifier,
793
+ kind,
794
+ node
795
+ });
796
+ declarationVariablesByIdentifier.set(identifier, variable);
797
+ const declaredVariables = declaredVariablesByNode.get(node) ?? [];
798
+ declaredVariables.push(variable);
799
+ declaredVariablesByNode.set(node, declaredVariables);
800
+ return variable;
801
+ }
802
+ function collectDeclarations(node, scope) {
803
+ const nodeScope = node !== sourceFile && isScopeBoundary(node) ? createScope(node, scope) : scope;
804
+ nodeScopes.set(node, nodeScope);
805
+ switch (node.kind) {
806
+ case SyntaxKind.ArrowFunction:
807
+ case SyntaxKind.Constructor:
808
+ case SyntaxKind.GetAccessor:
809
+ case SyntaxKind.MethodDeclaration:
810
+ case SyntaxKind.SetAccessor:
811
+ addParameters(nodeScope, node);
812
+ break;
813
+ case SyntaxKind.CatchClause:
814
+ if (node.variableDeclaration) addBindingName(nodeScope, node.variableDeclaration.name, node, "catch");
815
+ break;
816
+ case SyntaxKind.ClassDeclaration:
817
+ if (node.name) addVariable(nodeScope, node.name, node, "class");
818
+ break;
819
+ case SyntaxKind.ClassExpression:
820
+ if (node.name) addVariable(nodeScope, node.name, node, "class");
821
+ break;
822
+ case SyntaxKind.FunctionDeclaration:
823
+ if (node.name) addVariable(scope, node.name, node, "function");
824
+ addParameters(nodeScope, node);
825
+ break;
826
+ case SyntaxKind.FunctionExpression:
827
+ if (node.name) addVariable(nodeScope, node.name, node, "function");
828
+ addParameters(nodeScope, node);
829
+ break;
830
+ case SyntaxKind.ImportDeclaration:
831
+ addImportVariables(nodeScope, node);
832
+ break;
833
+ case SyntaxKind.VariableDeclaration:
834
+ if (node.parent.kind === SyntaxKind.CatchClause) {
835
+ forEachChild(node, (child) => {
836
+ collectDeclarations(child, nodeScope);
837
+ });
838
+ return;
839
+ }
840
+ addBindingName(getVariableDeclarationScope(node, nodeScope), node.name, node, "variable");
841
+ break;
842
+ }
843
+ forEachChild(node, (child) => {
844
+ collectDeclarations(child, nodeScope);
845
+ });
846
+ }
847
+ function collectReferences(node) {
848
+ if (node.kind === SyntaxKind.Identifier && !isNonReferenceIdentifier(node)) {
849
+ const scope = nodeScopes.get(node) ?? globalScope;
850
+ const variable = resolveVariable(scope, node.text);
851
+ const reference = {
852
+ from: scope,
853
+ identifier: node,
854
+ isWrite: isWriteReference(node),
855
+ text: node.text,
856
+ variable
857
+ };
858
+ scope.references.push(reference);
859
+ variable?.references.push(reference);
860
+ }
861
+ forEachChild(node, collectReferences);
862
+ }
863
+ collectDeclarations(sourceFile, globalScope);
864
+ collectReferences(sourceFile);
865
+ return {
866
+ findVariable(identifier) {
867
+ const declarationVariable = declarationVariablesByIdentifier.get(identifier);
868
+ if (declarationVariable) return declarationVariable;
869
+ return resolveVariable(nodeScopes.get(identifier) ?? globalScope, identifier.text);
870
+ },
871
+ getDeclaredVariables(node) {
872
+ return declaredVariablesByNode.get(node) ?? [];
873
+ },
874
+ getReferencesInScope(node) {
875
+ return getReferencesInScope(nodeScopes.get(node) ?? globalScope);
876
+ },
877
+ getScope(node) {
878
+ return nodeScopes.get(node) ?? globalScope;
879
+ },
880
+ globalScope
881
+ };
882
+ }
883
+ //#endregion
884
+ //#region src/utils/createRuleTesterTSConfig.ts
885
+ function createRuleTesterTSConfig(defaultCompilerOptions) {
886
+ return {
887
+ "tsconfig.base.json": JSON.stringify({ compilerOptions: {
888
+ lib: ["esnext"],
889
+ moduleResolution: "bundler",
890
+ strict: true,
891
+ target: "esnext",
892
+ types: [],
893
+ ...defaultCompilerOptions
894
+ } }, null, 2),
895
+ "tsconfig.json": JSON.stringify({ extends: "./tsconfig.base.json" }, null, 2)
896
+ };
897
+ }
898
+ //#endregion
899
+ //#region src/utils/declarationIncludesGlobal.ts
900
+ function declarationIncludesGlobal(declaration, program) {
901
+ const sourceFile = declaration.getSourceFile();
902
+ return program.isSourceFileDefaultLibrary(sourceFile) || /\/lib\.[^/]*\.d\.ts$/.test(sourceFile.fileName);
903
+ }
904
+ //#endregion
905
+ //#region src/utils/declarationsIncludeGlobal.ts
906
+ function declarationsIncludeGlobal(declarations, program) {
907
+ return declarations.some((declaration) => declarationIncludesGlobal(declaration, program));
908
+ }
909
+ //#endregion
910
+ //#region src/utils/getDeclarationsIfGlobal.ts
911
+ function getDeclarationsIfGlobal(node, typeChecker, program) {
912
+ const declarations = typeChecker.getSymbolAtLocation(node)?.getDeclarations();
913
+ return !!declarations && declarationsIncludeGlobal(declarations, program) ? declarations : void 0;
914
+ }
915
+ //#endregion
916
+ //#region src/utils/getModifyingReferences.ts
917
+ /**
918
+ * Gets all references to a variable that modify it (assignments, increments, decrements).
919
+ * @returns An array of identifier nodes that modify the variable.
920
+ */
921
+ function getModifyingReferences(identifier, sourceFile) {
922
+ const variable = getScopeManager(sourceFile).findVariable(identifier);
923
+ if (!variable || variable.declarations.length > 1) return [];
924
+ return variable.references.filter((reference) => reference.isWrite).map((reference) => reference.identifier);
925
+ }
926
+ //#endregion
927
+ //#region src/utils/unwrapParenthesizedNode.ts
928
+ function unwrapParenthesizedNode(node) {
929
+ return node.kind === SyntaxKind.ParenthesizedExpression ? unwrapParenthesizedNode(node.expression) : node;
930
+ }
931
+ //#endregion
932
+ //#region src/utils/getStaticValue.ts
933
+ function getStaticNumberValue(node) {
934
+ const value = getStaticValue(node)?.value;
935
+ return typeof value === "number" ? value : void 0;
936
+ }
937
+ function getStaticStringValue(node) {
938
+ const value = getStaticValue(node)?.value;
939
+ return typeof value === "string" ? value : void 0;
940
+ }
941
+ function getStaticValue(node) {
942
+ const unwrapped = unwrapParenthesizedNode(node);
943
+ switch (unwrapped.kind) {
944
+ case SyntaxKind.AsExpression:
945
+ case SyntaxKind.NonNullExpression:
946
+ case SyntaxKind.SatisfiesExpression:
947
+ case SyntaxKind.TypeAssertionExpression: return getStaticValue(unwrapped.expression);
948
+ case SyntaxKind.BigIntLiteral: return { value: BigInt(unwrapped.text.slice(0, -1)) };
949
+ case SyntaxKind.FalseKeyword: return { value: false };
950
+ case SyntaxKind.NoSubstitutionTemplateLiteral: return { value: unwrapped.text };
951
+ case SyntaxKind.NullKeyword: return { value: null };
952
+ case SyntaxKind.NumericLiteral: return { value: Number(unwrapped.text) };
953
+ case SyntaxKind.PrefixUnaryExpression: return getPrefixUnaryStaticValue(unwrapped);
954
+ case SyntaxKind.StringLiteral: return { value: unwrapped.text };
955
+ case SyntaxKind.TrueKeyword: return { value: true };
956
+ }
957
+ }
958
+ function getPrefixUnaryStaticValue(node) {
959
+ const operand = getStaticValue(node.operand);
960
+ if (!operand) return;
961
+ switch (node.operator) {
962
+ case SyntaxKind.ExclamationToken: return { value: !operand.value };
963
+ case SyntaxKind.MinusToken:
964
+ if (typeof operand.value === "bigint" || typeof operand.value === "number") return { value: -operand.value };
965
+ return;
966
+ case SyntaxKind.PlusToken: return typeof operand.value === "number" ? { value: operand.value } : void 0;
967
+ case SyntaxKind.TildeToken: if (typeof operand.value === "bigint" || typeof operand.value === "number") return { value: ~operand.value };
968
+ }
969
+ }
970
+ //#endregion
971
+ //#region src/utils/hasSameTokens.ts
972
+ function hasSameTokens(nodeA, nodeB, sourceFile) {
973
+ const queueA = [unwrapParenthesizedNode(nodeA)];
974
+ const queueB = [unwrapParenthesizedNode(nodeB)];
975
+ while (true) {
976
+ const currentA = queueA.shift();
977
+ const currentB = queueB.shift();
978
+ if (!currentA || !currentB) break;
979
+ if (currentA.kind !== currentB.kind) return false;
980
+ if (ts.isTokenKind(currentA.kind)) {
981
+ if (!areSameToken(currentA, currentB, sourceFile)) return false;
982
+ continue;
983
+ }
984
+ const childrenA = currentA.getChildren(sourceFile);
985
+ const childrenB = currentB.getChildren(sourceFile);
986
+ if (childrenA.length !== childrenB.length) return false;
987
+ queueA.push(...childrenA);
988
+ queueB.push(...childrenB);
989
+ }
990
+ return queueA.length === queueB.length;
991
+ }
992
+ function areSameToken(nodeA, nodeB, sourceFile) {
993
+ if (ts.isIdentifier(nodeA) || ts.isPrivateIdentifier(nodeA) || ts.isNumericLiteral(nodeA) || ts.isBigIntLiteral(nodeA) || ts.isStringLiteral(nodeA) || ts.isNoSubstitutionTemplateLiteral(nodeA)) return nodeA.text === nodeB.text;
994
+ if (nodeA.kind === SyntaxKind.RegularExpressionLiteral) return sourceFile.text.slice(nodeA.getStart(sourceFile), nodeA.getEnd()) === sourceFile.text.slice(nodeB.getStart(sourceFile), nodeB.getEnd());
995
+ return true;
996
+ }
997
+ //#endregion
998
+ //#region src/utils/isBuiltinArrayMethod.ts
999
+ function isBuiltinArrayMethod(name, node, typeChecker) {
1000
+ return node.expression.kind === SyntaxKind.PropertyAccessExpression && node.expression.name.text === name && typeChecker.isArrayType(typeChecker.getTypeAtLocation(node.expression.expression)) && node.parent.kind !== SyntaxKind.ExpressionStatement;
1001
+ }
1002
+ //#endregion
1003
+ //#region src/utils/isFunction.ts
1004
+ function isFunction(node, typeChecker) {
1005
+ return !!typeChecker.getTypeAtLocation(node).getCallSignatures().length;
1006
+ }
1007
+ //#endregion
1008
+ //#region src/utils/isGlobalDeclaration.ts
1009
+ function isGlobalDeclaration(node, typeChecker, program) {
1010
+ return !!getDeclarationsIfGlobal(node, typeChecker, program);
1011
+ }
1012
+ //#endregion
1013
+ //#region src/utils/isGlobalDeclarationOfName.ts
1014
+ /**
1015
+ * TODO: Use a scope analyzer (#400).
1016
+ */
1017
+ function isGlobalDeclarationOfName(node, name, typeChecker, program) {
1018
+ const declarations = typeChecker.getSymbolAtLocation(node)?.getDeclarations();
1019
+ if (!declarations) return false;
1020
+ return declarations.every((declaration) => {
1021
+ if (isVariableDeclaration(declaration) && declaration.initializer && isIdentifier(declaration.initializer)) return isGlobalDeclarationOfName(declaration.initializer, name, typeChecker, program);
1022
+ if (isPropertySignature(declaration)) return isGlobalDeclarationOfName(declaration.parent, name, typeChecker, program);
1023
+ return isDeclarationOfName(declaration, name) && declarationIncludesGlobal(declaration, program);
1024
+ });
1025
+ }
1026
+ function isDeclarationOfName(node, name) {
1027
+ if (isClassDeclaration(node) || isFunctionDeclaration(node) || isInterfaceDeclaration(node) || isVariableDeclaration(node)) return node.name && isIdentifier(node.name) && node.name.text === name;
1028
+ return false;
1029
+ }
1030
+ //#endregion
1031
+ //#region src/utils/isGlobalVariable.ts
1032
+ /**
1033
+ * Checks if a node is a reference to a global variable (e.g., Object, undefined, NaN).
1034
+ * Global variables are those declared in TypeScript's lib files or are global identifiers
1035
+ * like undefined which have symbols but no user-defined declarations.
1036
+ * TODO: Use a scope manager (#400).
1037
+ */
1038
+ function isGlobalVariable(node, typeChecker, program) {
1039
+ const symbol = typeChecker.getSymbolAtLocation(node);
1040
+ if (!symbol) return false;
1041
+ const declarations = symbol.getDeclarations();
1042
+ if (!declarations?.length) return true;
1043
+ return declarationsIncludeGlobal(declarations, program);
1044
+ }
1045
+ //#endregion
1046
+ //#region src/utils/isInlineArrayCreation.ts
1047
+ const methodsReturningNewArray = /* @__PURE__ */ new Set([
1048
+ "concat",
1049
+ "entries",
1050
+ "filter",
1051
+ "flat",
1052
+ "flatMap",
1053
+ "from",
1054
+ "keys",
1055
+ "map",
1056
+ "of",
1057
+ "slice",
1058
+ "split",
1059
+ "values"
1060
+ ]);
1061
+ const objectStaticMethods = /* @__PURE__ */ new Set([
1062
+ "entries",
1063
+ "keys",
1064
+ "values"
1065
+ ]);
1066
+ /**
1067
+ * Checks if a node represents an inline array creation expression.
1068
+ * These are cases where a new array is created immediately before the method call,
1069
+ * so mutating methods like .sort() or .reverse() are safe to use.
1070
+ */
1071
+ function isInlineArrayCreation(node) {
1072
+ if (ts.isArrayLiteralExpression(node)) return true;
1073
+ if (ts.isParenthesizedExpression(node)) return isInlineArrayCreation(node.expression);
1074
+ if (ts.isCallExpression(node)) {
1075
+ if (ts.isPropertyAccessExpression(node.expression)) {
1076
+ const methodName = node.expression.name.text;
1077
+ if (ts.isIdentifier(node.expression.expression) && node.expression.expression.text === "Object" && objectStaticMethods.has(methodName)) return true;
1078
+ if (ts.isIdentifier(node.expression.expression) && node.expression.expression.text === "Array" && (methodName === "from" || methodName === "of")) return true;
1079
+ if (methodsReturningNewArray.has(methodName)) return true;
1080
+ }
1081
+ if (ts.isIdentifier(node.expression) && node.expression.text === "Array" && ts.isNewExpression(node.parent)) return true;
1082
+ }
1083
+ if (ts.isNewExpression(node) && ts.isIdentifier(node.expression) && node.expression.text === "Array") return true;
1084
+ return false;
1085
+ }
1086
+ //#endregion
1087
+ //#region src/utils/isStaticString.ts
1088
+ function isStaticString(node) {
1089
+ return node.kind === SyntaxKind.StringLiteral || node.kind === SyntaxKind.NoSubstitutionTemplateLiteral;
1090
+ }
1091
+ //#endregion
1092
+ //#region src/utils/isStringRawNoSubstitution.ts
1093
+ function isStringRawNoSubstitution(node) {
1094
+ if (node.kind !== SyntaxKind.TaggedTemplateExpression) return false;
1095
+ const tag = node.tag;
1096
+ return tag.kind === SyntaxKind.PropertyAccessExpression && tag.expression.kind === SyntaxKind.Identifier && tag.expression.text === "String" && tag.name.kind === SyntaxKind.Identifier && tag.name.text === "raw" && node.template.kind === SyntaxKind.NoSubstitutionTemplateLiteral;
1097
+ }
1098
+ //#endregion
1099
+ //#region src/utils/unwrapParentParenthesizedExpressions.ts
1100
+ function unwrapParentParenthesizedExpressions(node) {
1101
+ return node.parent.kind === SyntaxKind.ParenthesizedExpression ? unwrapParentParenthesizedExpressions(node.parent) : node.parent;
1102
+ }
1103
+ //#endregion
1104
+ export { NodeSyntaxKinds, convertTypeScriptDiagnosticToLanguageReport, createRuleTesterTSConfig, declarationIncludesGlobal, extractDirectivesFromTypeScriptFile, forEachChild, getDeclarationsIfGlobal, getModifyingReferences, getScopeManager, getStaticNumberValue, getStaticStringValue, getStaticValue, getTSNodeRange, hasSameTokens, isBuiltinArrayMethod, isFunction, isGlobalDeclaration, isGlobalDeclarationOfName, isGlobalVariable, isInlineArrayCreation, isStaticString, isStringRawNoSubstitution, setVolarCreateFile, throwUnknownLanguageExtension, typescriptLanguage, unwrapParentParenthesizedExpressions, unwrapParenthesizedNode };