@esportsplus/typescript 0.32.0 → 0.33.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 (68) hide show
  1. package/README.md +34 -49
  2. package/build/cli/tsc.d.ts +3 -2
  3. package/build/cli/tsc.js +15 -97
  4. package/build/{probe → guard}/adapter.d.ts +1 -2
  5. package/build/{probe → guard}/adapter.js +1 -4
  6. package/build/{probe → guard}/async/channel.d.ts +1 -1
  7. package/build/{probe → guard}/async/channel.js +11 -191
  8. package/build/{probe → guard}/async/value.d.ts +1 -2
  9. package/build/{probe → guard}/async/value.js +1 -4
  10. package/build/guard/channels.d.ts +4 -0
  11. package/build/guard/channels.js +13 -0
  12. package/build/{probe → guard}/exceptions/channel.d.ts +3 -2
  13. package/build/{probe → guard}/exceptions/channel.js +167 -107
  14. package/build/guard/exceptions/derive.d.ts +49 -0
  15. package/build/guard/exceptions/derive.js +478 -0
  16. package/build/guard/exceptions/resolve-js.d.ts +2 -0
  17. package/build/guard/exceptions/resolve-js.js +26 -0
  18. package/build/{probe → guard}/exceptions/value.d.ts +3 -2
  19. package/build/{probe → guard}/exceptions/value.js +12 -4
  20. package/build/{probe → guard}/kernel/analyze.d.ts +2 -1
  21. package/build/guard/kernel/analyze.js +84 -0
  22. package/build/guard/kernel/ast.d.ts +13 -0
  23. package/build/{probe → guard}/kernel/ast.js +22 -15
  24. package/build/guard/kernel/config.js +93 -0
  25. package/build/guard/kernel/fixpoint.d.ts +6 -0
  26. package/build/{probe → guard}/kernel/fixpoint.js +8 -48
  27. package/build/guard/kernel/graph.d.ts +4 -0
  28. package/build/guard/kernel/graph.js +268 -0
  29. package/build/{probe → guard}/kernel/ids.d.ts +2 -3
  30. package/build/{probe → guard}/kernel/ids.js +1 -6
  31. package/build/{probe → guard}/kernel/types.d.ts +3 -32
  32. package/build/{probe → guard}/overlay/base/async.jsonc +1 -1
  33. package/build/{probe → guard}/overlay/base/exceptions.jsonc +16 -3
  34. package/build/{probe → guard}/overlay/base/resources.jsonc +7 -8
  35. package/build/{probe → guard}/overlay/load.d.ts +3 -7
  36. package/build/{probe → guard}/overlay/load.js +37 -120
  37. package/build/{probe → guard}/resources/channel.d.ts +1 -1
  38. package/build/{probe → guard}/resources/channel.js +81 -159
  39. package/build/{probe → guard}/resources/value.d.ts +1 -2
  40. package/build/{probe → guard}/resources/value.js +1 -14
  41. package/build/lsp/diagnostics.d.ts +2 -2
  42. package/build/lsp/diagnostics.js +2 -2
  43. package/build/lsp/server.js +41 -3
  44. package/build/lsp/workspace.d.ts +6 -3
  45. package/build/lsp/workspace.js +19 -5
  46. package/build/tsconfig.d.ts +2 -1
  47. package/build/tsconfig.js +44 -102
  48. package/package.json +2 -2
  49. package/tsconfig.base.json +12 -19
  50. package/tsconfig.package.json +6 -1
  51. package/build/probe/channels.d.ts +0 -4
  52. package/build/probe/channels.js +0 -16
  53. package/build/probe/kernel/analyze.js +0 -68
  54. package/build/probe/kernel/ast.d.ts +0 -11
  55. package/build/probe/kernel/config.js +0 -209
  56. package/build/probe/kernel/fixpoint.d.ts +0 -6
  57. package/build/probe/kernel/graph.d.ts +0 -4
  58. package/build/probe/kernel/graph.js +0 -507
  59. package/build/probe/overlay/presets/express.jsonc +0 -25
  60. package/build/probe/overlay/presets/node.jsonc +0 -17
  61. /package/build/{probe → guard}/exceptions/jsdoc.d.ts +0 -0
  62. /package/build/{probe → guard}/exceptions/jsdoc.js +0 -0
  63. /package/build/{probe → guard}/kernel/config.d.ts +0 -0
  64. /package/build/{probe → guard}/kernel/format.d.ts +0 -0
  65. /package/build/{probe → guard}/kernel/format.js +0 -0
  66. /package/build/{probe → guard}/kernel/program.d.ts +0 -0
  67. /package/build/{probe → guard}/kernel/program.js +0 -0
  68. /package/build/{probe → guard}/kernel/types.js +0 -0
@@ -0,0 +1,478 @@
1
+ import { dirname } from 'node:path';
2
+ import * as NodeFS from 'node:fs';
3
+ import * as ts from '../adapter.js';
4
+ import { API } from 'typescript/unstable/sync';
5
+ import { bodyOf, hasModifier, isAwaited, unwrap } from '../kernel/ast.js';
6
+ import { isFunctionLike } from '../kernel/ids.js';
7
+ import { resolveBackingJs } from './resolve-js.js';
8
+ import { ORIGIN_CAP, originOf } from './value.js';
9
+ const GLOBAL_ERROR_CLASSES = new Set([
10
+ 'AggregateError',
11
+ 'Error',
12
+ 'EvalError',
13
+ 'RangeError',
14
+ 'ReferenceError',
15
+ 'SyntaxError',
16
+ 'TypeError',
17
+ 'URIError',
18
+ ]);
19
+ const MAX_AVG_LINE = 300;
20
+ const MAX_FILE_BYTES = 512 * 1024;
21
+ const MAX_DEPTH = 6;
22
+ function createThrowIndex() {
23
+ return {
24
+ api: null,
25
+ contents: new Map(),
26
+ nextProject: 0,
27
+ parsed: new Map(),
28
+ resolved: new Map(),
29
+ snapshots: [],
30
+ summaries: new Map(),
31
+ };
32
+ }
33
+ function disposeThrowIndex(index) {
34
+ for (let snapshot of index.snapshots) {
35
+ try {
36
+ if (!snapshot.isDisposed()) {
37
+ snapshot.dispose();
38
+ }
39
+ }
40
+ catch {
41
+ }
42
+ }
43
+ try {
44
+ index.api?.close();
45
+ }
46
+ catch {
47
+ }
48
+ index.api = null;
49
+ index.contents.clear();
50
+ index.parsed.clear();
51
+ index.resolved.clear();
52
+ index.snapshots.length = 0;
53
+ index.summaries.clear();
54
+ }
55
+ function isMinified(text) {
56
+ let lines = 1;
57
+ for (let i = 0, n = text.length; i < n; i++) {
58
+ if (text[i] === '\n') {
59
+ lines += 1;
60
+ }
61
+ }
62
+ return text.length / lines > MAX_AVG_LINE;
63
+ }
64
+ function normalize(fileName) {
65
+ return fileName.replace(/\\/g, '/');
66
+ }
67
+ function overlayFileSystem(contents) {
68
+ return {
69
+ fileExists: (fileName) => contents.has(normalize(fileName)) || undefined,
70
+ getAccessibleEntries: (directoryName) => {
71
+ let directory = normalize(directoryName), files = [];
72
+ for (let fileName of contents.keys()) {
73
+ let slash = fileName.lastIndexOf('/');
74
+ if (fileName.slice(0, slash) === directory) {
75
+ files.push(fileName.slice(slash + 1));
76
+ }
77
+ }
78
+ return files.length > 0 ? { directories: [], files } : undefined;
79
+ },
80
+ readFile: (fileName) => contents.get(normalize(fileName)),
81
+ };
82
+ }
83
+ function parseText(index, text) {
84
+ try {
85
+ if (!index.api) {
86
+ index.api = new API({ cwd: '/guard-scan', fs: overlayFileSystem(index.contents) });
87
+ }
88
+ let directory = `/guard-scan/${index.nextProject++}`, config = `${directory}/tsconfig.json`, file = `${directory}/scan.js`;
89
+ index.contents.set(file, text);
90
+ index.contents.set(config, JSON.stringify({
91
+ compilerOptions: {
92
+ allowJs: true,
93
+ checkJs: false,
94
+ module: 'commonjs',
95
+ noEmit: true,
96
+ target: 'esnext',
97
+ },
98
+ files: ['scan.js'],
99
+ }));
100
+ let snapshot = index.api.updateSnapshot({ openProjects: [config] });
101
+ index.snapshots.push(snapshot);
102
+ return (snapshot
103
+ .getProject(config)
104
+ ?.program.getSourceFile(file) ?? null);
105
+ }
106
+ catch {
107
+ return null;
108
+ }
109
+ }
110
+ function parseFile(index, realpath) {
111
+ let stat;
112
+ try {
113
+ stat = NodeFS.statSync(realpath);
114
+ }
115
+ catch {
116
+ return undefined;
117
+ }
118
+ const cached = index.parsed.get(realpath);
119
+ if (cached && cached.mtimeMs === stat.mtimeMs && cached.size === stat.size) {
120
+ return cached;
121
+ }
122
+ if (cached) {
123
+ index.summaries.clear();
124
+ }
125
+ let text = '';
126
+ try {
127
+ text = stat.size <= MAX_FILE_BYTES ? NodeFS.readFileSync(realpath, 'utf8') : '';
128
+ }
129
+ catch {
130
+ text = '';
131
+ }
132
+ const sourceFile = text.length > 0 && !isMinified(text) ? parseText(index, text) : null;
133
+ const parsed = {
134
+ fileIndex: sourceFile ? indexFile(sourceFile) : null,
135
+ mtimeMs: stat.mtimeMs,
136
+ size: stat.size,
137
+ sourceFile,
138
+ };
139
+ index.parsed.set(realpath, parsed);
140
+ return parsed;
141
+ }
142
+ function classifyThrow(expr) {
143
+ if (!expr) {
144
+ return 'Error';
145
+ }
146
+ const value = unwrap(expr);
147
+ if (ts.isNewExpression(value) &&
148
+ ts.isIdentifier(value.expression) &&
149
+ GLOBAL_ERROR_CLASSES.has(value.expression.text)) {
150
+ return value.expression.text;
151
+ }
152
+ return 'Error';
153
+ }
154
+ function initializerFn(node) {
155
+ const initializer = node.initializer;
156
+ return initializer && isFunctionLike(initializer) ? initializer : undefined;
157
+ }
158
+ function requireSpecifier(expr) {
159
+ if (!expr || !ts.isCallExpression(expr) || !ts.isIdentifier(expr.expression) || expr.expression.text !== 'require') {
160
+ return undefined;
161
+ }
162
+ const arg = expr.arguments[0];
163
+ return arg && ts.isStringLiteral(arg) ? arg.text : undefined;
164
+ }
165
+ function targetOf(expr, fns, classes) {
166
+ const value = unwrap(expr);
167
+ if (isFunctionLike(value)) {
168
+ return value;
169
+ }
170
+ if (ts.isIdentifier(value)) {
171
+ return fns.get(value.text) ?? classes.get(value.text);
172
+ }
173
+ return undefined;
174
+ }
175
+ function collectDeclarations(sourceFile, classes, fns, imports, namespaces) {
176
+ for (const statement of sourceFile.statements) {
177
+ if (ts.isFunctionDeclaration(statement) && statement.name) {
178
+ fns.set(statement.name.text, statement);
179
+ }
180
+ else if (ts.isClassDeclaration(statement) && statement.name) {
181
+ classes.set(statement.name.text, statement);
182
+ }
183
+ else if (ts.isVariableStatement(statement)) {
184
+ for (const declaration of statement.declarationList.declarations) {
185
+ const fn = initializerFn(declaration);
186
+ if (fn && ts.isIdentifier(declaration.name)) {
187
+ fns.set(declaration.name.text, fn);
188
+ }
189
+ const specifier = requireSpecifier(declaration.initializer);
190
+ if (specifier && ts.isIdentifier(declaration.name)) {
191
+ namespaces.set(declaration.name.text, specifier);
192
+ }
193
+ else if (specifier && ts.isObjectBindingPattern(declaration.name)) {
194
+ for (const element of declaration.name.elements) {
195
+ if (ts.isBindingElement(element) && element.name && ts.isIdentifier(element.name)) {
196
+ const property = element.propertyName;
197
+ imports.set(element.name.text, {
198
+ exportName: property && ts.isIdentifier(property) ? property.text : element.name.text,
199
+ specifier,
200
+ });
201
+ }
202
+ }
203
+ }
204
+ }
205
+ }
206
+ else if (ts.isImportDeclaration(statement) && ts.isStringLiteral(statement.moduleSpecifier) && statement.importClause) {
207
+ const specifier = statement.moduleSpecifier.text;
208
+ const clause = statement.importClause;
209
+ if (clause.name) {
210
+ imports.set(clause.name.text, { exportName: 'default', specifier });
211
+ }
212
+ if (clause.namedBindings && ts.isNamespaceImport(clause.namedBindings)) {
213
+ namespaces.set(clause.namedBindings.name.text, specifier);
214
+ }
215
+ else if (clause.namedBindings && ts.isNamedImports(clause.namedBindings)) {
216
+ for (const element of clause.namedBindings.elements) {
217
+ imports.set(element.name.text, { exportName: (element.propertyName ?? element.name).text, specifier });
218
+ }
219
+ }
220
+ }
221
+ }
222
+ }
223
+ function collectExports(sourceFile, classes, fns, exportsByName, reexports) {
224
+ let reexportAll;
225
+ const add = (name, expr) => {
226
+ const target = targetOf(expr, fns, classes);
227
+ if (target) {
228
+ exportsByName.set(name, target);
229
+ }
230
+ };
231
+ for (const statement of sourceFile.statements) {
232
+ const exported = hasModifier(statement, ts.SyntaxKind.ExportKeyword);
233
+ const defaultExport = hasModifier(statement, ts.SyntaxKind.DefaultKeyword);
234
+ if (ts.isFunctionDeclaration(statement) && exported) {
235
+ exportsByName.set(defaultExport ? 'default' : statement.name?.text ?? 'default', statement);
236
+ }
237
+ else if (ts.isClassDeclaration(statement) && exported) {
238
+ exportsByName.set(defaultExport ? 'default' : statement.name?.text ?? 'default', statement);
239
+ }
240
+ else if (ts.isVariableStatement(statement) && exported) {
241
+ for (const declaration of statement.declarationList.declarations) {
242
+ if (ts.isIdentifier(declaration.name)) {
243
+ add(declaration.name.text, (declaration.initializer ?? declaration.name));
244
+ }
245
+ }
246
+ }
247
+ else if (ts.isExportAssignment(statement) && !statement.isExportEquals) {
248
+ add('default', statement.expression);
249
+ }
250
+ else if (ts.isExportDeclaration(statement) && statement.moduleSpecifier && ts.isStringLiteral(statement.moduleSpecifier)) {
251
+ const specifier = statement.moduleSpecifier.text;
252
+ if (!statement.exportClause) {
253
+ reexportAll = specifier;
254
+ }
255
+ else if (ts.isNamedExports(statement.exportClause)) {
256
+ for (const element of statement.exportClause.elements) {
257
+ reexports.set(element.name.text, { exportName: (element.propertyName ?? element.name).text, specifier });
258
+ }
259
+ }
260
+ }
261
+ else if (ts.isExpressionStatement(statement) && ts.isBinaryExpression(statement.expression) && statement.expression.operatorToken.kind === ts.SyntaxKind.EqualsToken) {
262
+ reexportAll = collectCjsExport(statement.expression, add) ?? reexportAll;
263
+ }
264
+ }
265
+ return reexportAll;
266
+ }
267
+ function collectCjsExport(assign, add) {
268
+ const left = assign.left;
269
+ if (!ts.isPropertyAccessExpression(left)) {
270
+ return undefined;
271
+ }
272
+ if (ts.isIdentifier(left.expression) && left.expression.text === 'exports') {
273
+ add(left.name.text, assign.right);
274
+ return undefined;
275
+ }
276
+ if (ts.isPropertyAccessExpression(left.expression) && ts.isIdentifier(left.expression.expression) && left.expression.expression.text === 'module' && left.expression.name.text === 'exports') {
277
+ add(left.name.text, assign.right);
278
+ return undefined;
279
+ }
280
+ if (!ts.isIdentifier(left.expression) || left.expression.text !== 'module' || left.name.text !== 'exports') {
281
+ return undefined;
282
+ }
283
+ const required = requireSpecifier(assign.right);
284
+ if (required) {
285
+ return required;
286
+ }
287
+ const value = unwrap(assign.right);
288
+ if (!ts.isObjectLiteralExpression(value)) {
289
+ add('default', assign.right);
290
+ return undefined;
291
+ }
292
+ for (const property of value.properties) {
293
+ if (ts.isPropertyAssignment(property) && (ts.isIdentifier(property.name) || ts.isStringLiteral(property.name))) {
294
+ add(property.name.text, property.initializer);
295
+ }
296
+ else if (ts.isShorthandPropertyAssignment(property) && ts.isIdentifier(property.name)) {
297
+ add(property.name.text, property.name);
298
+ }
299
+ }
300
+ return undefined;
301
+ }
302
+ function indexFile(sourceFile) {
303
+ const classes = new Map();
304
+ const exportsByName = new Map();
305
+ const fns = new Map();
306
+ const imports = new Map();
307
+ const namespaces = new Map();
308
+ const reexports = new Map();
309
+ collectDeclarations(sourceFile, classes, fns, imports, namespaces);
310
+ const reexportAll = collectExports(sourceFile, classes, fns, exportsByName, reexports);
311
+ return { classes, exportsByName, fns, imports, namespaces, reexportAll, reexports };
312
+ }
313
+ function locateExport(index, realpath, exportName, seen = new Set()) {
314
+ const key = `${realpath}\0${exportName}`;
315
+ if (seen.has(key)) {
316
+ return undefined;
317
+ }
318
+ const parsed = parseFile(index, realpath);
319
+ if (!parsed?.sourceFile || !parsed.fileIndex) {
320
+ return undefined;
321
+ }
322
+ const target = parsed.fileIndex.exportsByName.get(exportName);
323
+ if (target) {
324
+ return { fileIndex: parsed.fileIndex, realpath, sourceFile: parsed.sourceFile, target };
325
+ }
326
+ const redirect = parsed.fileIndex.reexports.get(exportName) ??
327
+ (parsed.fileIndex.reexportAll ? { exportName, specifier: parsed.fileIndex.reexportAll } : undefined);
328
+ if (!redirect) {
329
+ return undefined;
330
+ }
331
+ const next = resolveBackingJs(realpath, redirect.specifier);
332
+ return next ? locateExport(index, next, redirect.exportName, new Set(seen).add(key)) : undefined;
333
+ }
334
+ function findMethod(sourceFile, classNode, memberName) {
335
+ const members = classNode.members ?? [];
336
+ for (const member of members) {
337
+ if (ts.isMethodDeclaration(member) && ts.isIdentifier(member.name) && member.name.text === memberName) {
338
+ return member;
339
+ }
340
+ }
341
+ const name = classNode.name;
342
+ if (!name || !ts.isIdentifier(name)) {
343
+ return undefined;
344
+ }
345
+ for (const statement of sourceFile.statements) {
346
+ if (!ts.isExpressionStatement(statement) || !ts.isBinaryExpression(statement.expression) || statement.expression.operatorToken.kind !== ts.SyntaxKind.EqualsToken) {
347
+ continue;
348
+ }
349
+ const left = statement.expression.left;
350
+ if (ts.isPropertyAccessExpression(left) &&
351
+ ts.isPropertyAccessExpression(left.expression) &&
352
+ ts.isIdentifier(left.expression.expression) &&
353
+ left.expression.expression.text === name.text &&
354
+ left.expression.name.text === 'prototype' &&
355
+ left.name.text === memberName) {
356
+ const target = unwrap(statement.expression.right);
357
+ if (isFunctionLike(target)) {
358
+ return target;
359
+ }
360
+ }
361
+ }
362
+ return undefined;
363
+ }
364
+ function importedTarget(index, realpath, fileIndex, call) {
365
+ if (ts.isIdentifier(call.expression)) {
366
+ const binding = fileIndex.imports.get(call.expression.text);
367
+ if (!binding) {
368
+ return undefined;
369
+ }
370
+ const next = resolveBackingJs(realpath, binding.specifier);
371
+ return next ? locateExport(index, next, binding.exportName) : undefined;
372
+ }
373
+ if (ts.isPropertyAccessExpression(call.expression) && ts.isIdentifier(call.expression.expression)) {
374
+ const specifier = fileIndex.namespaces.get(call.expression.expression.text);
375
+ if (!specifier) {
376
+ return undefined;
377
+ }
378
+ const next = resolveBackingJs(realpath, specifier);
379
+ return next ? locateExport(index, next, call.expression.name.text) : undefined;
380
+ }
381
+ return undefined;
382
+ }
383
+ function mergeClasses(into, from) {
384
+ for (const [name, origins] of from) {
385
+ const existing = into.get(name);
386
+ if (!existing) {
387
+ into.set(name, origins.slice(0, ORIGIN_CAP));
388
+ continue;
389
+ }
390
+ for (const origin of origins) {
391
+ if (existing.length < ORIGIN_CAP && !existing.some((candidate) => candidate.pos === origin.pos && candidate.fileName === origin.fileName)) {
392
+ existing.push(origin);
393
+ }
394
+ }
395
+ }
396
+ }
397
+ function computeEscaping(index, realpath, sourceFile, fileIndex, functionNode, depth, chain) {
398
+ const classes = new Map();
399
+ const body = bodyOf(functionNode);
400
+ if (!body || depth <= 0 || chain.has(functionNode)) {
401
+ return classes;
402
+ }
403
+ const nextChain = new Set(chain).add(functionNode);
404
+ const visit = (node) => {
405
+ if (node !== functionNode && isFunctionLike(node)) {
406
+ return;
407
+ }
408
+ if (ts.isThrowStatement(node)) {
409
+ const name = classifyThrow(node.expression);
410
+ const origins = classes.get(name) ?? [];
411
+ if (origins.length < ORIGIN_CAP) {
412
+ origins.push(originOf(node, sourceFile, realpath));
413
+ }
414
+ classes.set(name, origins);
415
+ }
416
+ else if (ts.isCallExpression(node)) {
417
+ let target = ts.isIdentifier(node.expression) ? fileIndex.fns.get(node.expression.text) : undefined;
418
+ let targetPath = realpath;
419
+ let targetSource = sourceFile;
420
+ let targetIndex = fileIndex;
421
+ if (!target) {
422
+ const imported = importedTarget(index, realpath, fileIndex, node);
423
+ if (imported) {
424
+ target = imported.target;
425
+ targetPath = imported.realpath;
426
+ targetSource = imported.sourceFile;
427
+ targetIndex = imported.fileIndex;
428
+ }
429
+ }
430
+ if (target && !(hasModifier(target, ts.SyntaxKind.AsyncKeyword) && !isAwaited(node))) {
431
+ mergeClasses(classes, computeEscaping(index, targetPath, targetSource, targetIndex, target, depth - 1, nextChain));
432
+ }
433
+ }
434
+ ts.forEachChild(node, visit);
435
+ };
436
+ ts.forEachChild(body, visit);
437
+ return classes;
438
+ }
439
+ function computeSummary(index, realpath, exportName, memberName) {
440
+ const located = locateExport(index, realpath, exportName);
441
+ if (!located) {
442
+ return null;
443
+ }
444
+ let target = located.target;
445
+ if (memberName) {
446
+ const method = findMethod(located.sourceFile, target, memberName);
447
+ if (!method) {
448
+ return null;
449
+ }
450
+ target = method;
451
+ }
452
+ return {
453
+ async: hasModifier(target, ts.SyntaxKind.AsyncKeyword),
454
+ classes: computeEscaping(index, located.realpath, located.sourceFile, located.fileIndex, target, MAX_DEPTH, new Set()),
455
+ };
456
+ }
457
+ function deriveThrows(index, params) {
458
+ let resolution = `${dirname(params.importerFileName)}\0${params.specifier}`, realpath = index.resolved.get(resolution);
459
+ if (!index.resolved.has(resolution)) {
460
+ realpath = resolveBackingJs(params.importerFileName, params.specifier);
461
+ index.resolved.set(resolution, realpath);
462
+ }
463
+ if (!realpath) {
464
+ return undefined;
465
+ }
466
+ parseFile(index, realpath);
467
+ const key = `${realpath}\0${params.exportName}\0${params.memberName ?? ''}`;
468
+ let cached = index.summaries.get(key);
469
+ if (!index.summaries.has(key)) {
470
+ cached = computeSummary(index, realpath, params.exportName, params.memberName);
471
+ index.summaries.set(key, cached);
472
+ }
473
+ if (!cached || (cached.async && !params.awaited) || cached.classes.size === 0) {
474
+ return undefined;
475
+ }
476
+ return { classes: cached.classes };
477
+ }
478
+ export { createThrowIndex, deriveThrows, disposeThrowIndex };
@@ -0,0 +1,2 @@
1
+ declare function resolveBackingJs(importerFileName: string, specifier: string): string | undefined;
2
+ export { resolveBackingJs };
@@ -0,0 +1,26 @@
1
+ import * as NodeFS from 'node:fs';
2
+ import * as NodePath from 'node:path';
3
+ import { createRequire, isBuiltin } from 'node:module';
4
+ const SCANNABLE = new Set(['.cjs', '.js', '.mjs']);
5
+ function resolveBackingJs(importerFileName, specifier) {
6
+ if (isBuiltin(specifier)) {
7
+ return undefined;
8
+ }
9
+ let resolved;
10
+ try {
11
+ resolved = createRequire(importerFileName).resolve(specifier);
12
+ }
13
+ catch {
14
+ return undefined;
15
+ }
16
+ if (!SCANNABLE.has(NodePath.extname(resolved).toLowerCase())) {
17
+ return undefined;
18
+ }
19
+ try {
20
+ return NodeFS.realpathSync(resolved);
21
+ }
22
+ catch {
23
+ return resolved;
24
+ }
25
+ }
26
+ export { resolveBackingJs };
@@ -13,6 +13,7 @@ type ExceptionsValue = {
13
13
  readonly origins: ReadonlyMap<string, ReadonlyArray<ExceptionOrigin>>;
14
14
  };
15
15
  declare const TOP_KEY = "\0top";
16
+ declare const ORIGIN_CAP = 4;
16
17
  declare function bottom(): ExceptionsValue;
17
18
  declare function top(): ExceptionsValue;
18
19
  declare function single(key: string, display: string): ExceptionsValue;
@@ -24,7 +25,7 @@ declare function widen(prev: ExceptionsValue, next: ExceptionsValue, round: numb
24
25
  declare function subtract(a: ExceptionsValue, keys: ReadonlySet<string>): ExceptionsValue;
25
26
  declare function isEmpty(v: ExceptionsValue): boolean;
26
27
  declare function render(v: ExceptionsValue): string;
27
- declare function constituentsOf(type: ts.Type): ReadonlyArray<ts.Type>;
28
+ declare function originOf(node: ts.Node, sourceFile: ts.SourceFile, fileName?: string): ExceptionOrigin;
28
29
  declare function refOfType(checker: ts.TypeChecker, t: ts.Type): {
29
30
  top: true;
30
31
  } | {
@@ -36,4 +37,4 @@ declare function typeRef(checker: ts.TypeChecker, type: ts.Type): ReadonlyArray<
36
37
  key: string;
37
38
  display: string;
38
39
  }>;
39
- export { TOP_KEY, bottom, constituentsOf, equals, isEmpty, join, originsOf, refOfType, render, single, subtract, top, type ExceptionOrigin, type ExceptionsValue, typeRef, widen, withOrigin };
40
+ export { ORIGIN_CAP, TOP_KEY, bottom, equals, isEmpty, join, originOf, originsOf, refOfType, render, single, subtract, top, type ExceptionOrigin, type ExceptionsValue, typeRef, widen, withOrigin };
@@ -1,4 +1,6 @@
1
1
  import * as ts from '../adapter.js';
2
+ import { constituents } from '../kernel/ast.js';
3
+ import { locationOf } from '../kernel/ids.js';
2
4
  const TOP_KEY = '\u0000top';
3
5
  const TOP_DISPLAY = 'an unknown error';
4
6
  const WIDEN_CAP = 8;
@@ -122,8 +124,14 @@ function render(v) {
122
124
  return TOP_DISPLAY;
123
125
  return Array.from(v.types.values()).sort().join(' | ');
124
126
  }
125
- function constituentsOf(type) {
126
- return ts.unionTypes(type) ?? [type];
127
+ function originOf(node, sourceFile, fileName = sourceFile.fileName) {
128
+ const location = locationOf(node, sourceFile);
129
+ const text = node.getText(sourceFile).replace(/\s+/g, ' ').trim();
130
+ return {
131
+ ...location,
132
+ fileName,
133
+ text: text.length > 120 ? `${text.slice(0, 117)}…` : text,
134
+ };
127
135
  }
128
136
  function basename(p) {
129
137
  const i = Math.max(p.lastIndexOf('/'), p.lastIndexOf('\\'));
@@ -146,7 +154,7 @@ function refOfType(checker, t) {
146
154
  }
147
155
  function typeRef(checker, type) {
148
156
  const out = [];
149
- for (const t of constituentsOf(type)) {
157
+ for (const t of constituents(type)) {
150
158
  const r = refOfType(checker, t);
151
159
  if (r.top)
152
160
  out.push({ key: TOP_KEY, display: TOP_DISPLAY });
@@ -155,4 +163,4 @@ function typeRef(checker, type) {
155
163
  }
156
164
  return out;
157
165
  }
158
- export { TOP_KEY, bottom, constituentsOf, equals, isEmpty, join, originsOf, refOfType, render, single, subtract, top, typeRef, widen, withOrigin };
166
+ export { ORIGIN_CAP, TOP_KEY, bottom, equals, isEmpty, join, originOf, originsOf, refOfType, render, single, subtract, top, typeRef, widen, withOrigin };
@@ -1,8 +1,9 @@
1
1
  import * as ts from '../adapter.js';
2
2
  import type { Diagnostic, AnalyzeConfig } from './types.js';
3
+ import type { ThrowIndex } from '../exceptions/derive.js';
3
4
  type AnalyzeResult = {
4
5
  readonly diagnostics: ReadonlyArray<Diagnostic>;
5
6
  };
6
- declare function analyzeProgram(program: ts.Program, checker: ts.TypeChecker, config: AnalyzeConfig): AnalyzeResult;
7
+ declare function analyzeProgram(program: ts.Program, checker: ts.TypeChecker, config: AnalyzeConfig, throwIndex?: ThrowIndex): AnalyzeResult;
7
8
  declare function analyze(config: AnalyzeConfig): AnalyzeResult;
8
9
  export { analyze, analyzeProgram, type AnalyzeResult };
@@ -0,0 +1,84 @@
1
+ import { channelFor } from '../channels.js';
2
+ import { buildCallGraph } from './graph.js';
3
+ import { buildProgram } from './program.js';
4
+ import { createThrowIndex, disposeThrowIndex } from '../exceptions/derive.js';
5
+ import { runChannel } from './fixpoint.js';
6
+ import { loadOverlays } from '../overlay/load.js';
7
+ function analyzeProgram(program, checker, config, throwIndex) {
8
+ const index = throwIndex ?? createThrowIndex();
9
+ try {
10
+ return runAnalysis(program, checker, config, index);
11
+ }
12
+ finally {
13
+ if (!throwIndex) {
14
+ disposeThrowIndex(index);
15
+ }
16
+ }
17
+ }
18
+ function runAnalysis(program, checker, config, throwIndex) {
19
+ const overlays = loadOverlays();
20
+ const graph = buildCallGraph(program, checker, overlays);
21
+ const analysis = { checker, graph };
22
+ const built = new Map();
23
+ const instance = (name) => {
24
+ const cached = built.get(name);
25
+ if (cached) {
26
+ return cached;
27
+ }
28
+ const channelConfig = config.channels[name];
29
+ const channel = channelConfig
30
+ ? channelFor(name, throwIndex)
31
+ : undefined;
32
+ if (!channelConfig || !channel) {
33
+ return undefined;
34
+ }
35
+ const entry = [channelConfig, channel];
36
+ built.set(name, entry);
37
+ return entry;
38
+ };
39
+ const enabled = new Set();
40
+ for (const [name, channelConfig] of Object.entries(config.channels)) {
41
+ if (channelConfig.severity !== 'off') {
42
+ enabled.add(name);
43
+ }
44
+ }
45
+ const runnable = new Set();
46
+ const include = (name, stack) => {
47
+ if (runnable.has(name) || stack.has(name)) {
48
+ return;
49
+ }
50
+ const inst = instance(name);
51
+ if (!inst) {
52
+ return;
53
+ }
54
+ const next = new Set(stack).add(name);
55
+ for (const dep of inst[1].dependsOn ?? []) {
56
+ include(dep, next);
57
+ }
58
+ runnable.add(name);
59
+ };
60
+ for (const name of enabled) {
61
+ include(name, new Set());
62
+ }
63
+ const diagnostics = [];
64
+ const peers = new Map();
65
+ for (const name of runnable) {
66
+ const [channelConfig, channel] = instance(name);
67
+ const result = runChannel(analysis, channel, channelConfig.options, peers);
68
+ peers.set(name, result.store);
69
+ if (enabled.has(name)) {
70
+ diagnostics.push(...result.diagnostics);
71
+ }
72
+ }
73
+ return { diagnostics };
74
+ }
75
+ function analyze(config) {
76
+ const built = buildProgram(config.tsconfigPath);
77
+ try {
78
+ return analyzeProgram(built.program, built.checker, config);
79
+ }
80
+ finally {
81
+ built.dispose();
82
+ }
83
+ }
84
+ export { analyze, analyzeProgram };
@@ -0,0 +1,13 @@
1
+ import * as ts from '../adapter.js';
2
+ import type { FunctionLike } from './types.js';
3
+ type UnwrapOptions = {
4
+ assertions?: boolean;
5
+ awaits?: boolean;
6
+ };
7
+ declare const bodyOf: (node: ts.Node) => ts.Node | undefined;
8
+ declare const constituents: (type: ts.Type) => ReadonlyArray<ts.Type>;
9
+ declare const hasModifier: (node: ts.Node, kind: ts.SyntaxKind) => boolean;
10
+ declare const isAwaited: (call: ts.CallExpression) => boolean;
11
+ declare const paramSymbols: (checker: ts.TypeChecker, fn: FunctionLike) => Map<ts.Symbol, number>;
12
+ declare const unwrap: (expr: ts.Expression, options?: UnwrapOptions) => ts.Expression;
13
+ export { bodyOf, constituents, hasModifier, isAwaited, paramSymbols, unwrap };