@esportsplus/typescript 0.29.6 → 0.31.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 (129) hide show
  1. package/README.md +97 -3
  2. package/bin/tsc-alias +1 -1
  3. package/bin/tsc-lsp +3 -0
  4. package/build/cli/diagnostics.d.ts +8 -0
  5. package/build/cli/diagnostics.js +95 -0
  6. package/build/cli/tsc.d.ts +12 -2
  7. package/build/cli/tsc.js +343 -52
  8. package/build/compiler/ast.d.ts +7 -5
  9. package/build/compiler/ast.js +6 -6
  10. package/build/compiler/code.d.ts +5 -4
  11. package/build/compiler/code.js +12 -1
  12. package/build/compiler/coordinator.d.ts +16 -7
  13. package/build/compiler/coordinator.js +75 -41
  14. package/build/compiler/imports.d.ts +8 -3
  15. package/build/compiler/imports.js +34 -24
  16. package/build/compiler/index.d.ts +3 -0
  17. package/build/compiler/index.js +1 -0
  18. package/build/compiler/language-service.d.ts +36 -6
  19. package/build/compiler/language-service.js +252 -58
  20. package/build/compiler/plugins/index.d.ts +4 -2
  21. package/build/compiler/plugins/tsc.d.ts +1 -1
  22. package/build/compiler/plugins/vite.d.ts +6 -3
  23. package/build/compiler/plugins/vite.js +15 -8
  24. package/build/compiler/sourcemap.d.ts +52 -0
  25. package/build/compiler/sourcemap.js +265 -0
  26. package/build/compiler/types.d.ts +7 -6
  27. package/build/compiler/uid.d.ts +5 -2
  28. package/build/compiler/uid.js +33 -4
  29. package/build/index.d.ts +1 -1
  30. package/build/index.js +1 -1
  31. package/build/jsonc.d.ts +2 -0
  32. package/build/jsonc.js +85 -0
  33. package/build/lsp/bin.d.ts +1 -0
  34. package/build/lsp/bin.js +2 -0
  35. package/build/lsp/diagnostics.d.ts +10 -0
  36. package/build/lsp/diagnostics.js +69 -0
  37. package/build/lsp/index.d.ts +2 -0
  38. package/build/lsp/index.js +2 -0
  39. package/build/lsp/server.d.ts +4 -0
  40. package/build/lsp/server.js +130 -0
  41. package/build/lsp/workspace.d.ts +14 -0
  42. package/build/lsp/workspace.js +46 -0
  43. package/build/probe/adapter.d.ts +14 -0
  44. package/build/probe/adapter.js +42 -0
  45. package/build/probe/async/channel.d.ts +4 -0
  46. package/build/probe/async/channel.js +560 -0
  47. package/build/probe/async/value.d.ts +9 -0
  48. package/build/probe/async/value.js +16 -0
  49. package/build/probe/channels.d.ts +4 -0
  50. package/build/probe/channels.js +16 -0
  51. package/build/probe/exceptions/channel.d.ts +5 -0
  52. package/build/probe/exceptions/channel.js +657 -0
  53. package/build/probe/exceptions/jsdoc.d.ts +8 -0
  54. package/build/probe/exceptions/jsdoc.js +34 -0
  55. package/build/probe/exceptions/value.d.ts +39 -0
  56. package/build/probe/exceptions/value.js +158 -0
  57. package/build/probe/kernel/analyze.d.ts +8 -0
  58. package/build/probe/kernel/analyze.js +68 -0
  59. package/build/probe/kernel/ast.d.ts +11 -0
  60. package/build/probe/kernel/ast.js +49 -0
  61. package/build/probe/kernel/config.d.ts +5 -0
  62. package/build/probe/kernel/config.js +209 -0
  63. package/build/probe/kernel/fixpoint.d.ts +6 -0
  64. package/build/probe/kernel/fixpoint.js +206 -0
  65. package/build/probe/kernel/format.d.ts +4 -0
  66. package/build/probe/kernel/format.js +32 -0
  67. package/build/probe/kernel/graph.d.ts +4 -0
  68. package/build/probe/kernel/graph.js +507 -0
  69. package/build/probe/kernel/ids.d.ts +8 -0
  70. package/build/probe/kernel/ids.js +66 -0
  71. package/build/probe/kernel/program.d.ts +8 -0
  72. package/build/probe/kernel/program.js +13 -0
  73. package/build/probe/kernel/types.d.ts +134 -0
  74. package/build/probe/kernel/types.js +1 -0
  75. package/build/probe/overlay/base/async.jsonc +13 -0
  76. package/build/probe/overlay/base/exceptions.jsonc +54 -0
  77. package/build/probe/overlay/base/resources.jsonc +57 -0
  78. package/build/probe/overlay/load.d.ts +18 -0
  79. package/build/probe/overlay/load.js +302 -0
  80. package/build/probe/overlay/presets/express.jsonc +25 -0
  81. package/build/probe/overlay/presets/node.jsonc +17 -0
  82. package/build/probe/resources/channel.d.ts +4 -0
  83. package/build/probe/resources/channel.js +798 -0
  84. package/build/probe/resources/value.d.ts +9 -0
  85. package/build/probe/resources/value.js +36 -0
  86. package/build/ts.d.ts +3 -0
  87. package/build/ts.js +3 -0
  88. package/build/tsconfig.d.ts +2 -0
  89. package/build/tsconfig.js +150 -0
  90. package/package.json +29 -9
  91. package/tsconfig.base.json +19 -0
  92. package/tsconfig.dev.json +13 -0
  93. package/tsconfig.package.json +4 -1
  94. package/.claude/skills/code-audit/registry-typescript.json +0 -326
  95. package/.editorconfig +0 -9
  96. package/.gitattributes +0 -2
  97. package/.github/dependabot.yml +0 -25
  98. package/.github/workflows/bump.yml +0 -27
  99. package/.github/workflows/dependabot.yml +0 -58
  100. package/.github/workflows/publish.yml +0 -42
  101. package/.github/workflows/templates/bump.yml +0 -9
  102. package/.github/workflows/templates/dependabot.yml +0 -12
  103. package/.github/workflows/templates/publish.yml +0 -16
  104. package/pnpm-workspace.yaml +0 -6
  105. package/src/cli/tsc.ts +0 -235
  106. package/src/compiler/ast.ts +0 -60
  107. package/src/compiler/code.ts +0 -27
  108. package/src/compiler/coordinator.ts +0 -284
  109. package/src/compiler/imports.ts +0 -185
  110. package/src/compiler/index.ts +0 -7
  111. package/src/compiler/language-service.ts +0 -121
  112. package/src/compiler/plugins/index.ts +0 -5
  113. package/src/compiler/plugins/tsc.ts +0 -6
  114. package/src/compiler/plugins/vite.ts +0 -91
  115. package/src/compiler/types.ts +0 -83
  116. package/src/compiler/uid.ts +0 -10
  117. package/src/constants.ts +0 -4
  118. package/src/index.ts +0 -1
  119. package/tests/cli/tsc.test.ts +0 -155
  120. package/tests/compiler/ast.test.ts +0 -131
  121. package/tests/compiler/code.test.ts +0 -73
  122. package/tests/compiler/coordinator.bench.ts +0 -101
  123. package/tests/compiler/coordinator.test.ts +0 -872
  124. package/tests/compiler/imports.test.ts +0 -167
  125. package/tests/compiler/language-service.test.ts +0 -90
  126. package/tests/compiler/plugins.test.ts +0 -172
  127. package/tests/compiler/uid.test.ts +0 -70
  128. package/tsconfig.json +0 -3
  129. package/vitest.config.ts +0 -14
package/README.md CHANGED
@@ -18,6 +18,7 @@ Extends the TypeScript compiler with a plugin architecture for custom AST transf
18
18
  - Vite plugin for dev/build integration
19
19
  - CLI wrapper for `tsc` with automatic plugin detection
20
20
  - Language service caching for incremental compilation
21
+ - `analyze` throw-safety analysis on the `tsc` passthrough and over LSP
21
22
 
22
23
  ## Usage
23
24
 
@@ -72,13 +73,90 @@ export default defineConfig({
72
73
  tsc
73
74
  ```
74
75
 
75
- The CLI detects plugins in `tsconfig.json` `compilerOptions.plugins`, loads them, runs coordinated compilation, and automatically calls `tsc-alias` afterward.
76
+ The CLI detects plugins in `tsconfig.json` `compilerOptions.plugins`, loads them, runs coordinated compilation, and automatically calls `tsc-alias` afterward. The package installs `tsc`/`tsc-alias` bins (also under the unambiguous `esportsplus-tsc`/`esportsplus-tsc-alias` names).
77
+
78
+ ## analyze
79
+
80
+ `analyze` is a static analyzer for effects TypeScript's types leave implicit. It runs a per-function summary fixpoint over the call graph — so a finding points at the *consumer*, the call site where a caller should be careful — and is organised into **channels**, each checking one class of effect:
81
+
82
+ - **`exceptions`** — calls that may throw with no `catch` on the path to a handler boundary, `@throws` under-declaration, and `catch` rethrows that drop the caught error's `cause`. Carries the throw origin as related information.
83
+ - **`resources`** — acquired resources (timers, event listeners, file handles, sockets, `Disposable`s, …) that can leak: not released, transferred, or `using`-bound on every path — including throwing paths, which it derives from the `exceptions` channel's summaries.
84
+ - **`async`** — unbounded `Promise.all(…)`-style fan-out, orphaned promises whose rejections go unhandled, and awaited cancellable calls that drop an `AbortSignal` the function holds.
85
+
86
+ Only `exceptions` is on by default; enable the others per project. It rides the `tsc` passthrough (build/CI), and ships an LSP server so editors can render the same findings.
87
+
88
+ ### Enable
89
+
90
+ Add an `analyze` entry to `compilerOptions.plugins`. Analysis covers every file the tsconfig includes (ignoring excludes); no config beyond the entry is required.
91
+
92
+ ```jsonc
93
+ {
94
+ "compilerOptions": {
95
+ "plugins": [
96
+ {
97
+ "name": "ts-probe",
98
+ // Editor squiggle color; "warn" opts down. CLI/build ignore this.
99
+ "severity": "error",
100
+ // Fail the tsc/build run when there are findings.
101
+ "failOnFindings": true,
102
+ // Per-channel config. `enabled` and `dispatch` are recognised on
103
+ // every channel; other keys are that channel's own options.
104
+ "channels": {
105
+ "exceptions": {
106
+ "enabled": true,
107
+ // "consumers" (default): uncaught calls only.
108
+ // "cross-module": only when the throwing callee is in another package.
109
+ // "all": throws AND uncaught calls.
110
+ "report": "consumers",
111
+ // Flag `throw`s inside `catch` that drop the caught error's cause.
112
+ "errorCause": true
113
+ },
114
+ "resources": {
115
+ "enabled": true,
116
+ // Untrackable handling when a resource escapes local analysis:
117
+ // "optimist" assumes transfer (silent), "pessimist" reports it.
118
+ "dispatch": "optimist",
119
+ // Calls that take ownership of a passed resource argument.
120
+ "ownership": [{ "callee": "registerCleanup", "params": [0] }]
121
+ },
122
+ "async": {
123
+ "enabled": true,
124
+ "fanOut": "warn", // "off" | "warn" | "error"
125
+ "fanOutAllowLiteralUpTo": 16, // inline array/tuple size that is fine
126
+ "poolFunctions": ["p-limit", "p-map"] // sanctioned concurrency wrappers
127
+ }
128
+ },
129
+ // Model third-party throw behavior: "node", "express".
130
+ "presets": ["node"]
131
+ }
132
+ ]
133
+ }
134
+ }
135
+ ```
136
+
137
+ ### CLI / build
138
+
139
+ The `tsc` passthrough runs analyze after a successful compile and prints findings to stderr. With `failOnFindings: true`, a run with findings exits non-zero — drop it into CI as a gate.
140
+
141
+ ```bash
142
+ tsc # compiles, resolves aliases, then reports analyze findings
143
+ ```
144
+
145
+ ### Editor (LSP)
146
+
147
+ The package ships a standalone language server (`esportsplus-tsc-lsp` bin, or the `@esportsplus/typescript/lsp` export) that publishes analyze findings over LSP. A client spawns it beside the native TypeScript server and merges both diagnostic streams; `severity` drives the squiggle color. Analysis runs against saved files on open and save. Beyond diagnostics it serves **hovers** (the finding plus its origin→boundary chain) and **quick-fixes** — `void`/`await` an orphaned promise, forward an `AbortSignal`, or fix a leaked handle by converting it to `using` or wrapping the region in `try/finally`.
148
+
149
+ ```typescript
150
+ import { startServer } from '@esportsplus/typescript/lsp';
151
+
152
+ startServer(); // stdio LSP server
153
+ ```
76
154
 
77
155
  ## API
78
156
 
79
157
  ### `@esportsplus/typescript`
80
158
 
81
- Re-exports the TypeScript compiler API (`ts`).
159
+ No exports. Import the TypeScript compiler API directly from `typescript/unstable/*` (see [TypeScript 7 migration](#typescript-7-migration)).
82
160
 
83
161
  ### `@esportsplus/typescript/compiler`
84
162
 
@@ -90,7 +168,14 @@ Re-exports the TypeScript compiler API (`ts`).
90
168
  | `imports` | Import detection and modification (WeakMap cached) |
91
169
  | `plugin` | Built-in plugins (`tsc`, `vite`) |
92
170
  | `uid` | Unique identifier generation |
93
- | `languageService` | Cached TypeScript language service |
171
+
172
+ ### `@esportsplus/typescript/lsp`
173
+
174
+ | Export | Description |
175
+ |---|---|
176
+ | `startServer` | Start the stdio analyze LSP server |
177
+ | `createServer` | Wire the server onto an existing JSON-RPC connection |
178
+ | `AnalyzeWorkspace` | Long-lived native session that re-runs the analysis |
94
179
 
95
180
  ### Types
96
181
 
@@ -137,6 +222,15 @@ Importable base tsconfig files:
137
222
  { "extends": "@esportsplus/typescript/tsconfig.package.json" }
138
223
  ```
139
224
 
225
+ ## TypeScript 7 migration
226
+
227
+ This package targets `typescript@^7`, shipped as a regular `dependencies` entry. Breaking changes for consumers:
228
+
229
+ - **Root export removed.** `@esportsplus/typescript` no longer re-exports `ts`. Import the compiler API directly from the `typescript/unstable/*` subpaths you need — `typescript/unstable/ast`, `typescript/unstable/ast/is`, `typescript/unstable/sync`, `typescript/unstable/fs`, etc.
230
+ - **Plugin-facing types.** `TransformContext` and `ReplacementIntent` now carry `typescript/unstable/ast` + `typescript/unstable/sync` identities instead of the classic `ts` namespace types.
231
+ - **`coordinator.transform` signature.** Takes the `{ checker, program }` project pair instead of a single `ts.Program`.
232
+ - **`imports.includes` signature.** Takes the API `Checker` type, and symbol declarations are represented as NodeHandles rather than `ts.Node`.
233
+
140
234
  ## Scripts
141
235
 
142
236
  ```bash
package/bin/tsc-alias CHANGED
@@ -1,3 +1,3 @@
1
1
  #!/usr/bin/env node
2
2
 
3
- (await import ('@esportsplus/cli-passthrough')).default('tsc-alias');
3
+ (await import ('@esportsplus/cli-passthrough')).default('tsc-alias');
package/bin/tsc-lsp ADDED
@@ -0,0 +1,3 @@
1
+ #!/usr/bin/env node
2
+
3
+ import '../build/lsp/bin.js';
@@ -0,0 +1,8 @@
1
+ import { type Diagnostic } from 'typescript/unstable/sync';
2
+ import type { PositionMapping } from '../compiler/sourcemap.js';
3
+ declare const flatten: (diagnostic: Diagnostic, indent?: number) => string;
4
+ declare const format: (diagnostics: readonly Diagnostic[], root: string, transformed?: Map<string, {
5
+ code: string;
6
+ mapping: PositionMapping;
7
+ }>) => string;
8
+ export { flatten, format };
@@ -0,0 +1,95 @@
1
+ import { readFileSync } from 'fs';
2
+ import { computeLineStarts } from 'typescript/unstable/ast/scanner';
3
+ import { DiagnosticCategory } from 'typescript/unstable/sync';
4
+ import path from 'path';
5
+ import { resolveOffset } from '../compiler/sourcemap.js';
6
+ const ANSI_BLUE = '\x1b[94m';
7
+ const ANSI_CYAN = '\x1b[96m';
8
+ const ANSI_GREY = '\x1b[90m';
9
+ const ANSI_RED = '\x1b[91m';
10
+ const ANSI_RESET = '\x1b[0m';
11
+ const ANSI_YELLOW = '\x1b[93m';
12
+ const BACKSLASH_REGEX = /\\/g;
13
+ const TRAILING_NEWLINE_REGEX = /\r?\n$/;
14
+ function categoryColor(category) {
15
+ switch (category) {
16
+ case DiagnosticCategory.Error:
17
+ return ANSI_RED;
18
+ case DiagnosticCategory.Suggestion:
19
+ return ANSI_BLUE;
20
+ case DiagnosticCategory.Warning:
21
+ return ANSI_YELLOW;
22
+ default:
23
+ return ANSI_GREY;
24
+ }
25
+ }
26
+ function categoryLabel(category) {
27
+ switch (category) {
28
+ case DiagnosticCategory.Error:
29
+ return 'error';
30
+ case DiagnosticCategory.Suggestion:
31
+ return 'suggestion';
32
+ case DiagnosticCategory.Warning:
33
+ return 'warning';
34
+ default:
35
+ return 'message';
36
+ }
37
+ }
38
+ function formatOne(diagnostic, root, sources, transformed) {
39
+ let category = categoryLabel(diagnostic.category), code = `${ANSI_GREY}TS${diagnostic.code}${ANSI_RESET}`, color = categoryColor(diagnostic.category), message = flatten(diagnostic);
40
+ if (diagnostic.fileName === undefined) {
41
+ return `${color}${category}${ANSI_RESET} ${code}: ${message}`;
42
+ }
43
+ let location = `${ANSI_CYAN}${path.relative(root, diagnostic.fileName).replace(BACKSLASH_REGEX, '/')}${ANSI_RESET}`, entry = transformed?.get(diagnostic.fileName.replace(BACKSLASH_REGEX, '/')), end = entry ? resolveOffset(entry.mapping, diagnostic.end) : diagnostic.end, pos = entry ? resolveOffset(entry.mapping, diagnostic.pos) : diagnostic.pos, sourceInfo = sources.get(diagnostic.fileName) ?? readSourceInfo(diagnostic.fileName);
44
+ sources.set(diagnostic.fileName, sourceInfo);
45
+ const text = sourceInfo.text;
46
+ if (text === undefined) {
47
+ return `${location} - ${color}${category}${ANSI_RESET} ${code}: ${message}`;
48
+ }
49
+ let lineStarts = sourceInfo.lineStarts, line = lineOfPosition(lineStarts, pos), character = pos - lineStarts[line], lineEnd = line + 1 < lineStarts.length ? lineStarts[line + 1] : text.length, source = text.slice(lineStarts[line], lineEnd).replace(TRAILING_NEWLINE_REGEX, ''), width = Math.max(1, Math.min(end, lineEnd) - pos), underline = `${' '.repeat(character)}${color}${'~'.repeat(width)}${ANSI_RESET}`, header = `${location}:${ANSI_YELLOW}${line + 1}${ANSI_RESET}:${ANSI_YELLOW}${character + 1}${ANSI_RESET} - ${color}${category}${ANSI_RESET} ${code}: ${message}`;
50
+ return `${header}\n\n${source}\n${underline}`;
51
+ }
52
+ function lineOfPosition(lineStarts, position) {
53
+ let high = lineStarts.length - 1, low = 0;
54
+ while (low <= high) {
55
+ let middle = (low + high) >> 1;
56
+ if (lineStarts[middle] <= position) {
57
+ low = middle + 1;
58
+ }
59
+ else {
60
+ high = middle - 1;
61
+ }
62
+ }
63
+ return high < 0 ? 0 : high;
64
+ }
65
+ function readSourceInfo(fileName) {
66
+ try {
67
+ const text = readFileSync(fileName, 'utf8');
68
+ return { lineStarts: computeLineStarts(text), text };
69
+ }
70
+ catch {
71
+ return { lineStarts: [], text: undefined };
72
+ }
73
+ }
74
+ const flatten = (diagnostic, indent = 0) => {
75
+ let result = '';
76
+ if (indent > 0) {
77
+ result += `\n${' '.repeat(indent)}`;
78
+ }
79
+ result += diagnostic.text;
80
+ if (diagnostic.messageChain !== undefined) {
81
+ for (let i = 0, n = diagnostic.messageChain.length; i < n; i++) {
82
+ result += flatten(diagnostic.messageChain[i], indent + 1);
83
+ }
84
+ }
85
+ return result;
86
+ };
87
+ const format = (diagnostics, root, transformed) => {
88
+ let parts = [];
89
+ const sources = new Map();
90
+ for (let i = 0, n = diagnostics.length; i < n; i++) {
91
+ parts.push(formatOne(diagnostics[i], root, sources, transformed));
92
+ }
93
+ return parts.join('\n\n');
94
+ };
95
+ export { flatten, format };
@@ -1,10 +1,20 @@
1
1
  import type { Plugin } from '../compiler/types.js';
2
+ import { API } from 'typescript/unstable/sync';
3
+ import { stripJsonc } from '../jsonc.js';
2
4
  type PluginConfig = {
3
5
  transform: string;
4
6
  };
5
- declare function build(config: object, tsconfig: string, pluginConfigs: PluginConfig[]): Promise<void>;
7
+ declare function build(tsconfig: string, pluginConfigs: PluginConfig[], instance?: API, noEmit?: boolean): Promise<void>;
8
+ declare function classifyFlags(args: string[]): {
9
+ informational: boolean;
10
+ noEmit: boolean;
11
+ watch: boolean;
12
+ };
6
13
  declare function isPlugin(value: unknown): value is Plugin;
7
14
  declare function loadPlugins(configs: PluginConfig[], root: string): Promise<Plugin[]>;
15
+ declare function main(): void;
8
16
  declare function normalizePath(fileName: string): string;
17
+ declare function projectPath(args: string[]): string | null;
18
+ declare function resolvePluginConfigs(tsconfig: string): PluginConfig[];
9
19
  declare function runTscAlias(args: string[]): Promise<number>;
10
- export { build, isPlugin, loadPlugins, normalizePath, runTscAlias };
20
+ export { build, classifyFlags, isPlugin, loadPlugins, main, normalizePath, projectPath, resolvePluginConfigs, runTscAlias, stripJsonc };