@esportsplus/typescript 0.29.5 → 0.31.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.
- package/README.md +11 -3
- package/bin/tsc-alias +1 -1
- package/build/cli/diagnostics.d.ts +4 -0
- package/build/cli/diagnostics.js +90 -0
- package/build/cli/tsc.d.ts +10 -2
- package/build/cli/tsc.js +352 -51
- package/build/compiler/ast.d.ts +7 -5
- package/build/compiler/ast.js +6 -6
- package/build/compiler/code.d.ts +5 -4
- package/build/compiler/code.js +12 -1
- package/build/compiler/coordinator.d.ts +17 -7
- package/build/compiler/coordinator.js +56 -32
- package/build/compiler/imports.d.ts +7 -3
- package/build/compiler/imports.js +30 -24
- package/build/compiler/index.d.ts +3 -0
- package/build/compiler/index.js +1 -0
- package/build/compiler/language-service.d.ts +24 -5
- package/build/compiler/language-service.js +215 -53
- package/build/compiler/plugins/index.d.ts +4 -2
- package/build/compiler/plugins/tsc.d.ts +1 -1
- package/build/compiler/plugins/vite.d.ts +6 -3
- package/build/compiler/plugins/vite.js +12 -7
- package/build/compiler/sourcemap.d.ts +50 -0
- package/build/compiler/sourcemap.js +247 -0
- package/build/compiler/types.d.ts +7 -6
- package/build/compiler/uid.d.ts +5 -2
- package/build/compiler/uid.js +33 -4
- package/build/index.d.ts +1 -1
- package/build/index.js +1 -1
- package/build/ts.d.ts +3 -0
- package/build/ts.js +3 -0
- package/package.json +22 -8
- package/tsconfig.dev.json +13 -0
- package/tsconfig.package.json +4 -1
- package/.claude/skills/code-audit/registry-typescript.json +0 -326
- package/.editorconfig +0 -9
- package/.gitattributes +0 -2
- package/.github/dependabot.yml +0 -25
- package/.github/workflows/bump.yml +0 -27
- package/.github/workflows/dependabot.yml +0 -58
- package/.github/workflows/publish.yml +0 -42
- package/.github/workflows/templates/bump.yml +0 -9
- package/.github/workflows/templates/dependabot.yml +0 -12
- package/.github/workflows/templates/publish.yml +0 -16
- package/src/cli/tsc.ts +0 -235
- package/src/compiler/ast.ts +0 -60
- package/src/compiler/code.ts +0 -27
- package/src/compiler/coordinator.ts +0 -279
- package/src/compiler/imports.ts +0 -175
- package/src/compiler/index.ts +0 -7
- package/src/compiler/language-service.ts +0 -121
- package/src/compiler/plugins/index.ts +0 -5
- package/src/compiler/plugins/tsc.ts +0 -6
- package/src/compiler/plugins/vite.ts +0 -91
- package/src/compiler/types.ts +0 -83
- package/src/compiler/uid.ts +0 -10
- package/src/constants.ts +0 -4
- package/src/index.ts +0 -1
- package/tests/cli/tsc.test.ts +0 -155
- package/tests/compiler/ast.test.ts +0 -131
- package/tests/compiler/code.test.ts +0 -73
- package/tests/compiler/coordinator.bench.ts +0 -101
- package/tests/compiler/coordinator.test.ts +0 -855
- package/tests/compiler/imports.test.ts +0 -167
- package/tests/compiler/language-service.test.ts +0 -90
- package/tests/compiler/plugins.test.ts +0 -172
- package/tests/compiler/uid.test.ts +0 -70
- package/tsconfig.json +0 -3
- package/vitest.config.ts +0 -14
package/README.md
CHANGED
|
@@ -72,13 +72,13 @@ export default defineConfig({
|
|
|
72
72
|
tsc
|
|
73
73
|
```
|
|
74
74
|
|
|
75
|
-
The CLI detects plugins in `tsconfig.json` `compilerOptions.plugins`, loads them, runs coordinated compilation, and automatically calls `tsc-alias` afterward.
|
|
75
|
+
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).
|
|
76
76
|
|
|
77
77
|
## API
|
|
78
78
|
|
|
79
79
|
### `@esportsplus/typescript`
|
|
80
80
|
|
|
81
|
-
|
|
81
|
+
No exports. Import the TypeScript compiler API directly from `typescript/unstable/*` (see [TypeScript 7 migration](#typescript-7-migration)).
|
|
82
82
|
|
|
83
83
|
### `@esportsplus/typescript/compiler`
|
|
84
84
|
|
|
@@ -90,7 +90,6 @@ Re-exports the TypeScript compiler API (`ts`).
|
|
|
90
90
|
| `imports` | Import detection and modification (WeakMap cached) |
|
|
91
91
|
| `plugin` | Built-in plugins (`tsc`, `vite`) |
|
|
92
92
|
| `uid` | Unique identifier generation |
|
|
93
|
-
| `languageService` | Cached TypeScript language service |
|
|
94
93
|
|
|
95
94
|
### Types
|
|
96
95
|
|
|
@@ -137,6 +136,15 @@ Importable base tsconfig files:
|
|
|
137
136
|
{ "extends": "@esportsplus/typescript/tsconfig.package.json" }
|
|
138
137
|
```
|
|
139
138
|
|
|
139
|
+
## TypeScript 7 migration
|
|
140
|
+
|
|
141
|
+
This package targets `typescript@^7`, shipped as a regular `dependencies` entry. Breaking changes for consumers:
|
|
142
|
+
|
|
143
|
+
- **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.
|
|
144
|
+
- **Plugin-facing types.** `TransformContext` and `ReplacementIntent` now carry `typescript/unstable/ast` + `typescript/unstable/sync` identities instead of the classic `ts` namespace types.
|
|
145
|
+
- **`coordinator.transform` signature.** Takes the `{ checker, program }` project pair instead of a single `ts.Program`.
|
|
146
|
+
- **`imports.includes` signature.** Takes the API `Checker` type, and symbol declarations are represented as NodeHandles rather than `ts.Node`.
|
|
147
|
+
|
|
140
148
|
## Scripts
|
|
141
149
|
|
|
142
150
|
```bash
|
package/bin/tsc-alias
CHANGED
|
@@ -0,0 +1,90 @@
|
|
|
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
|
+
const ANSI_BLUE = '\x1b[94m';
|
|
6
|
+
const ANSI_CYAN = '\x1b[96m';
|
|
7
|
+
const ANSI_GREY = '\x1b[90m';
|
|
8
|
+
const ANSI_RED = '\x1b[91m';
|
|
9
|
+
const ANSI_RESET = '\x1b[0m';
|
|
10
|
+
const ANSI_YELLOW = '\x1b[93m';
|
|
11
|
+
const BACKSLASH_REGEX = /\\/g;
|
|
12
|
+
const TRAILING_NEWLINE_REGEX = /\r?\n$/;
|
|
13
|
+
function categoryColor(category) {
|
|
14
|
+
switch (category) {
|
|
15
|
+
case DiagnosticCategory.Error:
|
|
16
|
+
return ANSI_RED;
|
|
17
|
+
case DiagnosticCategory.Suggestion:
|
|
18
|
+
return ANSI_BLUE;
|
|
19
|
+
case DiagnosticCategory.Warning:
|
|
20
|
+
return ANSI_YELLOW;
|
|
21
|
+
default:
|
|
22
|
+
return ANSI_GREY;
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
function categoryLabel(category) {
|
|
26
|
+
switch (category) {
|
|
27
|
+
case DiagnosticCategory.Error:
|
|
28
|
+
return 'error';
|
|
29
|
+
case DiagnosticCategory.Suggestion:
|
|
30
|
+
return 'suggestion';
|
|
31
|
+
case DiagnosticCategory.Warning:
|
|
32
|
+
return 'warning';
|
|
33
|
+
default:
|
|
34
|
+
return 'message';
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
function formatOne(diagnostic, root) {
|
|
38
|
+
let category = categoryLabel(diagnostic.category), code = `${ANSI_GREY}TS${diagnostic.code}${ANSI_RESET}`, color = categoryColor(diagnostic.category), message = flatten(diagnostic);
|
|
39
|
+
if (diagnostic.fileName === undefined) {
|
|
40
|
+
return `${color}${category}${ANSI_RESET} ${code}: ${message}`;
|
|
41
|
+
}
|
|
42
|
+
let location = `${ANSI_CYAN}${path.relative(root, diagnostic.fileName).replace(BACKSLASH_REGEX, '/')}${ANSI_RESET}`, text = readSource(diagnostic.fileName);
|
|
43
|
+
if (text === undefined) {
|
|
44
|
+
return `${location} - ${color}${category}${ANSI_RESET} ${code}: ${message}`;
|
|
45
|
+
}
|
|
46
|
+
let lineStarts = computeLineStarts(text), line = lineOfPosition(lineStarts, diagnostic.pos), character = diagnostic.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(diagnostic.end, lineEnd) - diagnostic.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}`;
|
|
47
|
+
return `${header}\n\n${source}\n${underline}`;
|
|
48
|
+
}
|
|
49
|
+
function lineOfPosition(lineStarts, position) {
|
|
50
|
+
let high = lineStarts.length - 1, low = 0;
|
|
51
|
+
while (low <= high) {
|
|
52
|
+
let middle = (low + high) >> 1;
|
|
53
|
+
if (lineStarts[middle] <= position) {
|
|
54
|
+
low = middle + 1;
|
|
55
|
+
}
|
|
56
|
+
else {
|
|
57
|
+
high = middle - 1;
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
return high < 0 ? 0 : high;
|
|
61
|
+
}
|
|
62
|
+
function readSource(fileName) {
|
|
63
|
+
try {
|
|
64
|
+
return readFileSync(fileName, 'utf8');
|
|
65
|
+
}
|
|
66
|
+
catch {
|
|
67
|
+
return undefined;
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
const flatten = (diagnostic, indent = 0) => {
|
|
71
|
+
let result = '';
|
|
72
|
+
if (indent > 0) {
|
|
73
|
+
result += `\n${' '.repeat(indent)}`;
|
|
74
|
+
}
|
|
75
|
+
result += diagnostic.text;
|
|
76
|
+
if (diagnostic.messageChain !== undefined) {
|
|
77
|
+
for (let i = 0, n = diagnostic.messageChain.length; i < n; i++) {
|
|
78
|
+
result += flatten(diagnostic.messageChain[i], indent + 1);
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
return result;
|
|
82
|
+
};
|
|
83
|
+
const format = (diagnostics, root) => {
|
|
84
|
+
let parts = [];
|
|
85
|
+
for (let i = 0, n = diagnostics.length; i < n; i++) {
|
|
86
|
+
parts.push(formatOne(diagnostics[i], root));
|
|
87
|
+
}
|
|
88
|
+
return parts.join('\n\n');
|
|
89
|
+
};
|
|
90
|
+
export { flatten, format };
|
package/build/cli/tsc.d.ts
CHANGED
|
@@ -1,10 +1,18 @@
|
|
|
1
1
|
import type { Plugin } from '../compiler/types.js';
|
|
2
|
+
import { API } from 'typescript/unstable/sync';
|
|
2
3
|
type PluginConfig = {
|
|
3
4
|
transform: string;
|
|
4
5
|
};
|
|
5
|
-
declare function build(
|
|
6
|
+
declare function build(tsconfig: string, pluginConfigs: PluginConfig[], instance?: API, noEmit?: boolean): Promise<void>;
|
|
7
|
+
declare function classifyFlags(args: string[]): {
|
|
8
|
+
informational: boolean;
|
|
9
|
+
noEmit: boolean;
|
|
10
|
+
watch: boolean;
|
|
11
|
+
};
|
|
6
12
|
declare function isPlugin(value: unknown): value is Plugin;
|
|
7
13
|
declare function loadPlugins(configs: PluginConfig[], root: string): Promise<Plugin[]>;
|
|
14
|
+
declare function main(): void;
|
|
8
15
|
declare function normalizePath(fileName: string): string;
|
|
16
|
+
declare function resolvePluginConfigs(tsconfig: string): PluginConfig[];
|
|
9
17
|
declare function runTscAlias(args: string[]): Promise<number>;
|
|
10
|
-
export { build, isPlugin, loadPlugins, normalizePath, runTscAlias };
|
|
18
|
+
export { build, classifyFlags, isPlugin, loadPlugins, main, normalizePath, resolvePluginConfigs, runTscAlias };
|
package/build/cli/tsc.js
CHANGED
|
@@ -1,59 +1,221 @@
|
|
|
1
|
+
import { API, DiagnosticCategory } from 'typescript/unstable/sync';
|
|
1
2
|
import { createRequire } from 'module';
|
|
3
|
+
import { format } from './diagnostics.js';
|
|
2
4
|
import { PACKAGE_NAME } from '../constants.js';
|
|
3
5
|
import { pathToFileURL } from 'url';
|
|
4
6
|
import { spawn } from 'child_process';
|
|
5
7
|
import coordinator from '../compiler/coordinator.js';
|
|
8
|
+
import fs from 'fs';
|
|
9
|
+
import languageService from '../compiler/language-service.js';
|
|
6
10
|
import path from 'path';
|
|
7
|
-
import
|
|
11
|
+
import sourcemap from '../compiler/sourcemap.js';
|
|
8
12
|
const BACKSLASH_REGEX = /\\/g;
|
|
13
|
+
const INFORMATIONAL_FLAGS = new Set(['--help', '--init', '--showConfig', '--version', '-h', '-v']);
|
|
14
|
+
const NO_EMIT_FLAGS = new Set(['--noEmit', '-noEmit']);
|
|
15
|
+
const WATCH_FLAGS = new Set(['--watch', '-w']);
|
|
9
16
|
let require = createRequire(import.meta.url), skipFlags = new Set(['--help', '--init', '--noEmit', '--showConfig', '--version', '-h', '-noEmit', '-v']);
|
|
10
|
-
async function build(
|
|
11
|
-
let root = path.dirname(path.resolve(tsconfig)),
|
|
12
|
-
if (
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
17
|
+
async function build(tsconfig, pluginConfigs, instance, noEmit = false) {
|
|
18
|
+
let root = path.dirname(path.resolve(tsconfig)), owned = instance === undefined, api = instance ?? new API({ cwd: root }), snapshot = api.updateSnapshot({ openProjects: [tsconfig] }), project = snapshot.getProject(tsconfig);
|
|
19
|
+
if (!project) {
|
|
20
|
+
teardown(snapshot, api, root, owned);
|
|
21
|
+
throw new Error(`${PACKAGE_NAME}: project not found for ${tsconfig}`);
|
|
22
|
+
}
|
|
23
|
+
let configDiagnostics = project.program.getConfigFileParsingDiagnostics();
|
|
24
|
+
if (configDiagnostics.some((diagnostic) => diagnostic.category === DiagnosticCategory.Error)) {
|
|
25
|
+
console.error(format(configDiagnostics, root));
|
|
26
|
+
teardown(snapshot, api, root, owned);
|
|
27
|
+
process.exit(1);
|
|
28
|
+
}
|
|
29
|
+
let { fileNames, options } = api.parseConfigFile(tsconfig), plugins, shared = new Map(), transformedFiles = new Map();
|
|
30
|
+
try {
|
|
31
|
+
plugins = await loadPlugins(pluginConfigs, root);
|
|
32
|
+
}
|
|
33
|
+
catch (error) {
|
|
34
|
+
console.error(error instanceof Error ? error.message : String(error));
|
|
35
|
+
teardown(snapshot, api, root, owned);
|
|
16
36
|
process.exit(1);
|
|
17
37
|
}
|
|
18
|
-
let
|
|
19
|
-
|
|
20
|
-
for (let i = 0, n = parsed.fileNames.length; i < n; i++) {
|
|
21
|
-
let fileName = parsed.fileNames[i], sourceFile = program.getSourceFile(fileName);
|
|
38
|
+
for (let i = 0, n = fileNames.length; i < n; i++) {
|
|
39
|
+
let fileName = fileNames[i], sourceFile = project.program.getSourceFile(fileName);
|
|
22
40
|
if (!sourceFile) {
|
|
23
41
|
continue;
|
|
24
42
|
}
|
|
25
|
-
let result = coordinator.transform(plugins, sourceFile.getFullText(), sourceFile, program, root, shared);
|
|
43
|
+
let result = coordinator.transform(plugins, sourceFile.getFullText(), sourceFile, { checker: project.checker, program: project.program }, root, shared);
|
|
26
44
|
if (result.changed) {
|
|
27
|
-
transformedFiles.set(normalizePath(fileName), result.code);
|
|
45
|
+
transformedFiles.set(normalizePath(fileName), { code: result.code, mapping: result.map });
|
|
28
46
|
}
|
|
29
47
|
}
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
let transformed = transformedFiles.get(normalizePath(fileName));
|
|
34
|
-
if (transformed) {
|
|
35
|
-
return ts.createSourceFile(fileName, transformed, languageVersion, true);
|
|
36
|
-
}
|
|
37
|
-
return originalGetSourceFile(fileName, languageVersion, onError, shouldCreateNewSourceFile);
|
|
38
|
-
};
|
|
39
|
-
customHost.readFile = (fileName) => {
|
|
40
|
-
return transformedFiles.get(normalizePath(fileName)) ?? originalReadFile(fileName);
|
|
41
|
-
};
|
|
42
|
-
program = ts.createProgram(parsed.fileNames, parsed.options, customHost);
|
|
48
|
+
let program = project.program;
|
|
49
|
+
for (let [fileName, entry] of transformedFiles) {
|
|
50
|
+
program = languageService.update(root, fileName, entry.code).program;
|
|
43
51
|
}
|
|
44
|
-
let
|
|
45
|
-
|
|
52
|
+
let diagnostics = [
|
|
53
|
+
...program.getConfigFileParsingDiagnostics(),
|
|
54
|
+
...program.getSyntacticDiagnostics(),
|
|
55
|
+
...program.getBindDiagnostics(),
|
|
56
|
+
...program.getSemanticDiagnostics(),
|
|
57
|
+
...program.getGlobalDiagnostics(),
|
|
58
|
+
...program.getProgramDiagnostics()
|
|
59
|
+
];
|
|
46
60
|
if (diagnostics.length > 0) {
|
|
47
|
-
console.error(
|
|
48
|
-
getCanonicalFileName: (fileName) => fileName,
|
|
49
|
-
getCurrentDirectory: () => root,
|
|
50
|
-
getNewLine: () => '\n'
|
|
51
|
-
}));
|
|
61
|
+
console.error(format(diagnostics, root));
|
|
52
62
|
}
|
|
53
|
-
if (
|
|
63
|
+
if (diagnostics.some((diagnostic) => diagnostic.category === DiagnosticCategory.Error)) {
|
|
64
|
+
teardown(snapshot, api, root, owned);
|
|
54
65
|
process.exit(1);
|
|
55
66
|
}
|
|
56
|
-
|
|
67
|
+
if (noEmit) {
|
|
68
|
+
teardown(snapshot, api, root, owned);
|
|
69
|
+
process.exit(0);
|
|
70
|
+
}
|
|
71
|
+
let code = await emit(tsconfig, fileNames, transformedFiles, root, options);
|
|
72
|
+
teardown(snapshot, api, root, owned);
|
|
73
|
+
if (code !== 0) {
|
|
74
|
+
process.exit(code);
|
|
75
|
+
}
|
|
76
|
+
return runTscAlias(process.argv.slice(2)).then((exitCode) => process.exit(exitCode));
|
|
77
|
+
}
|
|
78
|
+
function classifyFlags(args) {
|
|
79
|
+
let informational = false, noEmit = false, watch = false;
|
|
80
|
+
for (let i = 0, n = args.length; i < n; i++) {
|
|
81
|
+
let arg = args[i];
|
|
82
|
+
if (INFORMATIONAL_FLAGS.has(arg)) {
|
|
83
|
+
informational = true;
|
|
84
|
+
}
|
|
85
|
+
else if (NO_EMIT_FLAGS.has(arg)) {
|
|
86
|
+
noEmit = true;
|
|
87
|
+
}
|
|
88
|
+
else if (WATCH_FLAGS.has(arg)) {
|
|
89
|
+
watch = true;
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
return { informational, noEmit, watch };
|
|
93
|
+
}
|
|
94
|
+
function collectMaps(dir, results) {
|
|
95
|
+
for (let entry of fs.readdirSync(dir, { withFileTypes: true })) {
|
|
96
|
+
let full = path.join(dir, entry.name);
|
|
97
|
+
if (entry.isDirectory()) {
|
|
98
|
+
collectMaps(full, results);
|
|
99
|
+
}
|
|
100
|
+
else if (entry.name.endsWith('.js.map')) {
|
|
101
|
+
results.push(full);
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
function composeSourceMaps(mirrorOut, mirror, root, transformedFiles) {
|
|
106
|
+
let maps = [];
|
|
107
|
+
collectMaps(mirrorOut, maps);
|
|
108
|
+
for (let i = 0, n = maps.length; i < n; i++) {
|
|
109
|
+
let mapPath = maps[i], raw;
|
|
110
|
+
try {
|
|
111
|
+
raw = JSON.parse(fs.readFileSync(mapPath, 'utf8'));
|
|
112
|
+
}
|
|
113
|
+
catch {
|
|
114
|
+
continue;
|
|
115
|
+
}
|
|
116
|
+
if (!raw.sources || raw.sources.length === 0 || typeof raw.sources[0] !== 'string' || typeof raw.mappings !== 'string') {
|
|
117
|
+
continue;
|
|
118
|
+
}
|
|
119
|
+
let mirrorSource = path.resolve(path.dirname(mapPath), raw.sources[0]), relative = path.relative(mirror, mirrorSource), realSource = normalizePath(path.join(root, relative)), entry = transformedFiles.get(realSource);
|
|
120
|
+
if (!entry) {
|
|
121
|
+
continue;
|
|
122
|
+
}
|
|
123
|
+
let composed = sourcemap.composeEmittedMap({ mappings: raw.mappings, names: raw.names ?? [], sources: raw.sources, version: 3 }, entry.mapping, entry.code, fs.readFileSync(path.join(root, relative), 'utf8'));
|
|
124
|
+
if (raw.file !== undefined) {
|
|
125
|
+
composed.file = raw.file;
|
|
126
|
+
}
|
|
127
|
+
if (raw.sourceRoot !== undefined) {
|
|
128
|
+
composed.sourceRoot = raw.sourceRoot;
|
|
129
|
+
}
|
|
130
|
+
fs.writeFileSync(mapPath, JSON.stringify(composed));
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
async function emit(tsconfig, fileNames, transformedFiles, root, options) {
|
|
134
|
+
let tscJs = path.join(path.dirname(require.resolve('typescript/package.json')), 'lib', 'tsc.js');
|
|
135
|
+
if (transformedFiles.size === 0) {
|
|
136
|
+
return spawnTsc(tscJs, ['-p', tsconfig]);
|
|
137
|
+
}
|
|
138
|
+
let mirror = fs.mkdtempSync(path.join(root, '.esportsplus-tsc-'));
|
|
139
|
+
try {
|
|
140
|
+
let declaration = options.declaration === true, files = [], mirrorDecl = path.join(mirror, '__decl'), mirrorOut = path.join(mirror, '__emit'), realOutDir = typeof options.outDir === 'string' ? path.resolve(options.outDir) : root, realDeclDir = declaration
|
|
141
|
+
? (typeof options.declarationDir === 'string' ? path.resolve(options.declarationDir) : realOutDir)
|
|
142
|
+
: undefined, realRootDir = typeof options.rootDir === 'string' ? path.resolve(options.rootDir) : undefined, separateDecl = realDeclDir !== undefined && normalizePath(realDeclDir) !== normalizePath(realOutDir);
|
|
143
|
+
for (let i = 0, n = fileNames.length; i < n; i++) {
|
|
144
|
+
let source = path.resolve(fileNames[i]), relative = path.relative(root, source);
|
|
145
|
+
if (relative.startsWith('..')) {
|
|
146
|
+
throw new Error(`${PACKAGE_NAME}: source ${normalizePath(source)} resolves outside the project root ${normalizePath(root)}; monorepo-external sources are not supported by the transform emit path`);
|
|
147
|
+
}
|
|
148
|
+
let target = path.join(mirror, relative), transformed = transformedFiles.get(normalizePath(fileNames[i]));
|
|
149
|
+
fs.mkdirSync(path.dirname(target), { recursive: true });
|
|
150
|
+
if (transformed === undefined) {
|
|
151
|
+
fs.copyFileSync(source, target);
|
|
152
|
+
}
|
|
153
|
+
else {
|
|
154
|
+
fs.writeFileSync(target, transformed.code);
|
|
155
|
+
}
|
|
156
|
+
files.push(normalizePath(target));
|
|
157
|
+
}
|
|
158
|
+
let compilerOptions = {
|
|
159
|
+
outDir: normalizePath(mirrorOut)
|
|
160
|
+
};
|
|
161
|
+
if (declaration) {
|
|
162
|
+
compilerOptions.declarationDir = normalizePath(separateDecl ? mirrorDecl : mirrorOut);
|
|
163
|
+
}
|
|
164
|
+
if (realRootDir !== undefined) {
|
|
165
|
+
compilerOptions.rootDir = normalizePath(path.join(mirror, path.relative(root, realRootDir)));
|
|
166
|
+
}
|
|
167
|
+
fs.writeFileSync(path.join(mirror, 'tsconfig.json'), JSON.stringify({
|
|
168
|
+
compilerOptions,
|
|
169
|
+
exclude: [],
|
|
170
|
+
extends: normalizePath(tsconfig),
|
|
171
|
+
files,
|
|
172
|
+
include: []
|
|
173
|
+
}));
|
|
174
|
+
let code = await spawnTsc(tscJs, ['-p', path.join(mirror, 'tsconfig.json')]);
|
|
175
|
+
if (code === 0) {
|
|
176
|
+
if (fs.existsSync(mirrorOut)) {
|
|
177
|
+
composeSourceMaps(mirrorOut, mirror, root, transformedFiles);
|
|
178
|
+
fs.cpSync(mirrorOut, realOutDir, { force: true, recursive: true });
|
|
179
|
+
}
|
|
180
|
+
if (separateDecl && realDeclDir !== undefined && fs.existsSync(mirrorDecl)) {
|
|
181
|
+
fs.cpSync(mirrorDecl, realDeclDir, { force: true, recursive: true });
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
return code;
|
|
185
|
+
}
|
|
186
|
+
finally {
|
|
187
|
+
fs.rmSync(mirror, { force: true, recursive: true });
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
function extendsTarget(specifier, fromDir) {
|
|
191
|
+
if (typeof specifier !== 'string') {
|
|
192
|
+
return null;
|
|
193
|
+
}
|
|
194
|
+
if (specifier.startsWith('.')) {
|
|
195
|
+
let resolved = path.resolve(fromDir, specifier);
|
|
196
|
+
if (fs.existsSync(resolved) && fs.statSync(resolved).isFile()) {
|
|
197
|
+
return resolved;
|
|
198
|
+
}
|
|
199
|
+
if (fs.existsSync(resolved + '.json')) {
|
|
200
|
+
return resolved + '.json';
|
|
201
|
+
}
|
|
202
|
+
let nested = path.join(resolved, 'tsconfig.json');
|
|
203
|
+
if (fs.existsSync(nested)) {
|
|
204
|
+
return nested;
|
|
205
|
+
}
|
|
206
|
+
return null;
|
|
207
|
+
}
|
|
208
|
+
try {
|
|
209
|
+
return require.resolve(specifier, { paths: [fromDir] });
|
|
210
|
+
}
|
|
211
|
+
catch {
|
|
212
|
+
try {
|
|
213
|
+
return require.resolve(specifier + '/tsconfig.json', { paths: [fromDir] });
|
|
214
|
+
}
|
|
215
|
+
catch {
|
|
216
|
+
return null;
|
|
217
|
+
}
|
|
218
|
+
}
|
|
57
219
|
}
|
|
58
220
|
function isPlugin(value) {
|
|
59
221
|
return typeof value === 'object' && value !== null && 'transform' in value && typeof value.transform === 'function';
|
|
@@ -75,18 +237,15 @@ async function loadPlugins(configs, root) {
|
|
|
75
237
|
}
|
|
76
238
|
if (Array.isArray(plugin)) {
|
|
77
239
|
for (let j = 0, m = plugin.length; j < m; j++) {
|
|
78
|
-
if (isPlugin(plugin[j])) {
|
|
79
|
-
|
|
80
|
-
}
|
|
81
|
-
else {
|
|
82
|
-
console.error(`${PACKAGE_NAME}: plugin ${config.transform}[${j}] uses an invalid plugin format`);
|
|
240
|
+
if (!isPlugin(plugin[j])) {
|
|
241
|
+
throw new Error(`${PACKAGE_NAME}: plugin ${config.transform}[${j}] uses an invalid plugin format, expected { transform: Function }`);
|
|
83
242
|
}
|
|
243
|
+
plugins.push(plugin[j]);
|
|
84
244
|
}
|
|
85
245
|
return;
|
|
86
246
|
}
|
|
87
247
|
if (!isPlugin(plugin)) {
|
|
88
|
-
|
|
89
|
-
return;
|
|
248
|
+
throw new Error(`${PACKAGE_NAME}: plugin ${config.transform} uses an invalid plugin format, expected { transform: Function } or Plugin[]`);
|
|
90
249
|
}
|
|
91
250
|
plugins.push(plugin);
|
|
92
251
|
}));
|
|
@@ -95,20 +254,25 @@ async function loadPlugins(configs, root) {
|
|
|
95
254
|
return plugins;
|
|
96
255
|
}
|
|
97
256
|
function main() {
|
|
98
|
-
let tsconfig =
|
|
257
|
+
let tsconfig = languageService.findConfig(process.cwd());
|
|
99
258
|
if (!tsconfig) {
|
|
100
259
|
return passthrough();
|
|
101
260
|
}
|
|
102
|
-
let
|
|
103
|
-
if (
|
|
261
|
+
let pluginConfigs = resolvePluginConfigs(tsconfig);
|
|
262
|
+
if (pluginConfigs.length === 0) {
|
|
104
263
|
return passthrough();
|
|
105
264
|
}
|
|
106
|
-
let
|
|
107
|
-
if (
|
|
265
|
+
let flags = classifyFlags(process.argv.slice(2));
|
|
266
|
+
if (flags.informational) {
|
|
108
267
|
return passthrough();
|
|
109
268
|
}
|
|
269
|
+
if (flags.watch) {
|
|
270
|
+
console.error(`${PACKAGE_NAME}: --watch is not supported on the transformer plugin path; run a one-shot build or use real tsc directly`);
|
|
271
|
+
process.exit(1);
|
|
272
|
+
return;
|
|
273
|
+
}
|
|
110
274
|
console.log(`${PACKAGE_NAME}: found ${pluginConfigs.length} transformer plugin(s), using coordinated build...`);
|
|
111
|
-
build(
|
|
275
|
+
build(tsconfig, pluginConfigs, undefined, flags.noEmit).catch((err) => {
|
|
112
276
|
console.error(`${PACKAGE_NAME}: ${err}`);
|
|
113
277
|
process.exit(1);
|
|
114
278
|
});
|
|
@@ -117,8 +281,8 @@ function normalizePath(fileName) {
|
|
|
117
281
|
return path.resolve(fileName).replace(BACKSLASH_REGEX, '/');
|
|
118
282
|
}
|
|
119
283
|
function passthrough() {
|
|
120
|
-
let args = process.argv.slice(2);
|
|
121
|
-
spawn(process.execPath, [
|
|
284
|
+
let args = process.argv.slice(2), tsDir = path.dirname(require.resolve('typescript/package.json'));
|
|
285
|
+
spawn(process.execPath, [path.join(tsDir, 'lib', 'tsc.js'), ...args], { stdio: 'inherit' })
|
|
122
286
|
.on('exit', async (code) => {
|
|
123
287
|
if (code === 0) {
|
|
124
288
|
code = await runTscAlias(args);
|
|
@@ -126,6 +290,44 @@ function passthrough() {
|
|
|
126
290
|
process.exit(code ?? 0);
|
|
127
291
|
});
|
|
128
292
|
}
|
|
293
|
+
function readPlugins(tsconfig, seen) {
|
|
294
|
+
let id = path.resolve(tsconfig);
|
|
295
|
+
if (seen.has(id)) {
|
|
296
|
+
return undefined;
|
|
297
|
+
}
|
|
298
|
+
seen.add(id);
|
|
299
|
+
let config;
|
|
300
|
+
try {
|
|
301
|
+
config = JSON.parse(stripJsonc(fs.readFileSync(id, 'utf8')));
|
|
302
|
+
}
|
|
303
|
+
catch {
|
|
304
|
+
return undefined;
|
|
305
|
+
}
|
|
306
|
+
let plugins;
|
|
307
|
+
if (config?.extends !== undefined) {
|
|
308
|
+
let bases = Array.isArray(config.extends) ? config.extends : [config.extends];
|
|
309
|
+
for (let i = 0, n = bases.length; i < n; i++) {
|
|
310
|
+
let target = extendsTarget(bases[i], path.dirname(id));
|
|
311
|
+
if (target) {
|
|
312
|
+
let inherited = readPlugins(target, seen);
|
|
313
|
+
if (inherited !== undefined) {
|
|
314
|
+
plugins = inherited;
|
|
315
|
+
}
|
|
316
|
+
}
|
|
317
|
+
}
|
|
318
|
+
}
|
|
319
|
+
if (Array.isArray(config?.compilerOptions?.plugins)) {
|
|
320
|
+
plugins = config.compilerOptions.plugins;
|
|
321
|
+
}
|
|
322
|
+
return plugins;
|
|
323
|
+
}
|
|
324
|
+
function resolvePluginConfigs(tsconfig) {
|
|
325
|
+
let plugins = readPlugins(tsconfig, new Set());
|
|
326
|
+
if (!Array.isArray(plugins)) {
|
|
327
|
+
return [];
|
|
328
|
+
}
|
|
329
|
+
return plugins.filter((p) => typeof p === 'object' && p !== null && 'transform' in p);
|
|
330
|
+
}
|
|
129
331
|
function runTscAlias(args) {
|
|
130
332
|
for (let i = 0, n = args.length; i < n; i++) {
|
|
131
333
|
if (skipFlags.has(args[i])) {
|
|
@@ -137,7 +339,106 @@ function runTscAlias(args) {
|
|
|
137
339
|
child.on('exit', (code) => resolve(code ?? 0));
|
|
138
340
|
});
|
|
139
341
|
}
|
|
342
|
+
function spawnTsc(tscJs, args) {
|
|
343
|
+
return new Promise((resolve) => {
|
|
344
|
+
let child = spawn(process.execPath, [tscJs, ...args], { stdio: 'inherit' });
|
|
345
|
+
child.on('exit', (code) => resolve(code ?? 0));
|
|
346
|
+
});
|
|
347
|
+
}
|
|
348
|
+
function stripJsonc(text) {
|
|
349
|
+
let escaped = false, inBlockComment = false, inLineComment = false, inString = false, stripped = '';
|
|
350
|
+
for (let i = 0, n = text.length; i < n; i++) {
|
|
351
|
+
let char = text[i], next = text[i + 1];
|
|
352
|
+
if (inLineComment) {
|
|
353
|
+
if (char === '\n') {
|
|
354
|
+
inLineComment = false;
|
|
355
|
+
stripped += char;
|
|
356
|
+
}
|
|
357
|
+
continue;
|
|
358
|
+
}
|
|
359
|
+
if (inBlockComment) {
|
|
360
|
+
if (char === '*' && next === '/') {
|
|
361
|
+
inBlockComment = false;
|
|
362
|
+
i++;
|
|
363
|
+
}
|
|
364
|
+
continue;
|
|
365
|
+
}
|
|
366
|
+
if (inString) {
|
|
367
|
+
stripped += char;
|
|
368
|
+
if (escaped) {
|
|
369
|
+
escaped = false;
|
|
370
|
+
}
|
|
371
|
+
else if (char === '\\') {
|
|
372
|
+
escaped = true;
|
|
373
|
+
}
|
|
374
|
+
else if (char === '"') {
|
|
375
|
+
inString = false;
|
|
376
|
+
}
|
|
377
|
+
continue;
|
|
378
|
+
}
|
|
379
|
+
if (char === '"') {
|
|
380
|
+
inString = true;
|
|
381
|
+
stripped += char;
|
|
382
|
+
continue;
|
|
383
|
+
}
|
|
384
|
+
if (char === '/' && next === '/') {
|
|
385
|
+
inLineComment = true;
|
|
386
|
+
i++;
|
|
387
|
+
continue;
|
|
388
|
+
}
|
|
389
|
+
if (char === '/' && next === '*') {
|
|
390
|
+
inBlockComment = true;
|
|
391
|
+
i++;
|
|
392
|
+
continue;
|
|
393
|
+
}
|
|
394
|
+
stripped += char;
|
|
395
|
+
}
|
|
396
|
+
escaped = false;
|
|
397
|
+
inString = false;
|
|
398
|
+
let result = '';
|
|
399
|
+
for (let i = 0, n = stripped.length; i < n; i++) {
|
|
400
|
+
let char = stripped[i];
|
|
401
|
+
if (inString) {
|
|
402
|
+
result += char;
|
|
403
|
+
if (escaped) {
|
|
404
|
+
escaped = false;
|
|
405
|
+
}
|
|
406
|
+
else if (char === '\\') {
|
|
407
|
+
escaped = true;
|
|
408
|
+
}
|
|
409
|
+
else if (char === '"') {
|
|
410
|
+
inString = false;
|
|
411
|
+
}
|
|
412
|
+
continue;
|
|
413
|
+
}
|
|
414
|
+
if (char === '"') {
|
|
415
|
+
inString = true;
|
|
416
|
+
result += char;
|
|
417
|
+
continue;
|
|
418
|
+
}
|
|
419
|
+
if (char === ',') {
|
|
420
|
+
let j = i + 1;
|
|
421
|
+
while (j < n && (stripped[j] === ' ' || stripped[j] === '\n' || stripped[j] === '\r' || stripped[j] === '\t')) {
|
|
422
|
+
j++;
|
|
423
|
+
}
|
|
424
|
+
if (stripped[j] === ']' || stripped[j] === '}') {
|
|
425
|
+
continue;
|
|
426
|
+
}
|
|
427
|
+
}
|
|
428
|
+
result += char;
|
|
429
|
+
}
|
|
430
|
+
return result;
|
|
431
|
+
}
|
|
432
|
+
function teardown(snapshot, api, root, owned) {
|
|
433
|
+
if (!snapshot.isDisposed()) {
|
|
434
|
+
snapshot.dispose();
|
|
435
|
+
}
|
|
436
|
+
if (owned) {
|
|
437
|
+
api.close();
|
|
438
|
+
}
|
|
439
|
+
languageService.dispose(root);
|
|
440
|
+
}
|
|
140
441
|
if (process.env.VITEST === undefined) {
|
|
141
442
|
main();
|
|
142
443
|
}
|
|
143
|
-
export { build, isPlugin, loadPlugins, normalizePath, runTscAlias };
|
|
444
|
+
export { build, classifyFlags, isPlugin, loadPlugins, main, normalizePath, resolvePluginConfigs, runTscAlias };
|
package/build/compiler/ast.d.ts
CHANGED
|
@@ -1,14 +1,16 @@
|
|
|
1
1
|
import type { Range } from './types.js';
|
|
2
|
-
import {
|
|
2
|
+
import type { Expression, Node } from 'typescript/unstable/ast';
|
|
3
|
+
declare const inRange: (ranges: Range[], start: number, end: number) => boolean;
|
|
4
|
+
declare const test: (node: Node, fn: (n: Node) => boolean) => boolean;
|
|
3
5
|
declare const _default: {
|
|
4
6
|
expression: {
|
|
5
|
-
name: (node:
|
|
7
|
+
name: (node: Expression) => string | null;
|
|
6
8
|
};
|
|
7
|
-
inRange:
|
|
9
|
+
inRange: typeof inRange;
|
|
8
10
|
property: {
|
|
9
|
-
path: (node:
|
|
11
|
+
path: (node: Expression) => string | null;
|
|
10
12
|
};
|
|
11
|
-
test:
|
|
13
|
+
test: typeof test;
|
|
12
14
|
};
|
|
13
15
|
export default _default;
|
|
14
16
|
export type { Range };
|