@esportsplus/typescript 0.28.1 → 0.28.3
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.
|
@@ -6,7 +6,11 @@ type CoordinatorResult = {
|
|
|
6
6
|
sourceFile: ts.SourceFile;
|
|
7
7
|
};
|
|
8
8
|
declare const _default: {
|
|
9
|
-
transform: (plugins: Plugin[], code: string, file: ts.SourceFile, program: ts.Program, shared: SharedContext) =>
|
|
9
|
+
transform: (plugins: Plugin[], code: string, file: ts.SourceFile, program: ts.Program, shared: SharedContext) => {
|
|
10
|
+
changed: boolean;
|
|
11
|
+
code: string;
|
|
12
|
+
sourceFile: ts.SourceFile;
|
|
13
|
+
};
|
|
10
14
|
};
|
|
11
15
|
export default _default;
|
|
12
16
|
export type { CoordinatorResult };
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import { ts } from '../index.js';
|
|
2
2
|
import imports from './imports.js';
|
|
3
|
-
const DIRECTORY_SEPARATOR = /\\/g;
|
|
4
3
|
function applyImports(code, file, intents) {
|
|
5
4
|
for (let i = 0, n = intents.length; i < n; i++) {
|
|
6
5
|
let intent = intents[i];
|
|
@@ -44,24 +43,6 @@ function applyPrepend(code, file, prepend) {
|
|
|
44
43
|
}
|
|
45
44
|
return code.slice(0, position) + prepend.join('\n') + code.slice(position);
|
|
46
45
|
}
|
|
47
|
-
function createUpdatedProgram(originalProgram, fileName, newCode) {
|
|
48
|
-
let options = originalProgram.getCompilerOptions(), originalHost = ts.createCompilerHost(options), originalGetSourceFile = originalHost.getSourceFile.bind(originalHost), originalReadFile = originalHost.readFile.bind(originalHost);
|
|
49
|
-
originalHost.getSourceFile = (name, languageVersion, onError, shouldCreateNewSourceFile) => {
|
|
50
|
-
if (name === fileName ||
|
|
51
|
-
name.replace(DIRECTORY_SEPARATOR, '/') === fileName.replace(DIRECTORY_SEPARATOR, '/')) {
|
|
52
|
-
return ts.createSourceFile(name, newCode, languageVersion, true);
|
|
53
|
-
}
|
|
54
|
-
return originalGetSourceFile(name, languageVersion, onError, shouldCreateNewSourceFile);
|
|
55
|
-
};
|
|
56
|
-
originalHost.readFile = (name) => {
|
|
57
|
-
if (name === fileName ||
|
|
58
|
-
name.replace(DIRECTORY_SEPARATOR, '/') === fileName.replace(DIRECTORY_SEPARATOR, '/')) {
|
|
59
|
-
return newCode;
|
|
60
|
-
}
|
|
61
|
-
return originalReadFile(name);
|
|
62
|
-
};
|
|
63
|
-
return ts.createProgram(originalProgram.getRootFileNames(), options, originalHost, originalProgram);
|
|
64
|
-
}
|
|
65
46
|
function hasPattern(code, patterns) {
|
|
66
47
|
for (let i = 0, n = patterns.length; i < n; i++) {
|
|
67
48
|
if (code.indexOf(patterns[i]) !== -1) {
|
|
@@ -136,42 +117,51 @@ const transform = (plugins, code, file, program, shared) => {
|
|
|
136
117
|
if (plugins.length === 0) {
|
|
137
118
|
return { changed: false, code, sourceFile: file };
|
|
138
119
|
}
|
|
139
|
-
let
|
|
120
|
+
let allImports = [], allPrepend = [], allReplacements = [], checker = program.getTypeChecker(), fileName = file.fileName, originalFile = file;
|
|
140
121
|
for (let i = 0, n = plugins.length; i < n; i++) {
|
|
141
122
|
let plugin = plugins[i];
|
|
142
|
-
if (plugin.patterns && !hasPattern(
|
|
123
|
+
if (plugin.patterns && !hasPattern(code, plugin.patterns)) {
|
|
143
124
|
continue;
|
|
144
125
|
}
|
|
145
126
|
let { imports, prepend, replacements } = plugin.transform({
|
|
146
|
-
checker
|
|
147
|
-
code
|
|
148
|
-
program
|
|
127
|
+
checker,
|
|
128
|
+
code,
|
|
129
|
+
program,
|
|
149
130
|
shared,
|
|
150
|
-
sourceFile:
|
|
131
|
+
sourceFile: originalFile
|
|
151
132
|
});
|
|
152
|
-
let pluginChanged = false;
|
|
153
133
|
if (replacements?.length) {
|
|
154
|
-
|
|
155
|
-
currentFile = ts.createSourceFile(fileName, currentCode, currentFile.languageVersion, true);
|
|
156
|
-
pluginChanged = true;
|
|
134
|
+
allReplacements.push(...replacements);
|
|
157
135
|
}
|
|
158
136
|
if (prepend?.length) {
|
|
159
|
-
|
|
160
|
-
currentFile = ts.createSourceFile(fileName, currentCode, currentFile.languageVersion, true);
|
|
161
|
-
pluginChanged = true;
|
|
137
|
+
allPrepend.push(...prepend);
|
|
162
138
|
}
|
|
163
139
|
if (imports?.length) {
|
|
164
|
-
|
|
165
|
-
currentFile = ts.createSourceFile(fileName, currentCode, currentFile.languageVersion, true);
|
|
166
|
-
pluginChanged = true;
|
|
140
|
+
allImports.push(...imports);
|
|
167
141
|
}
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
142
|
+
}
|
|
143
|
+
let currentCode = code, currentFile = originalFile;
|
|
144
|
+
if (allReplacements.length > 0) {
|
|
145
|
+
allReplacements.sort((a, b) => a.node.getStart(originalFile) - b.node.getStart(originalFile));
|
|
146
|
+
for (let i = 1, n = allReplacements.length; i < n; i++) {
|
|
147
|
+
let prev = allReplacements[i - 1], curr = allReplacements[i], prevEnd = prev.node.end, currStart = curr.node.getStart(originalFile);
|
|
148
|
+
if (currStart < prevEnd) {
|
|
149
|
+
console.warn(`[coordinator] Overlapping replacements detected at ${fileName}:`, `[${prev.node.getStart(originalFile)}-${prevEnd}] overlaps [${currStart}-${curr.node.end}]`);
|
|
150
|
+
}
|
|
171
151
|
}
|
|
152
|
+
currentCode = applyIntents(currentCode, currentFile, allReplacements);
|
|
153
|
+
currentFile = ts.createSourceFile(fileName, currentCode, currentFile.languageVersion, true);
|
|
154
|
+
}
|
|
155
|
+
if (allPrepend.length > 0) {
|
|
156
|
+
currentCode = applyPrepend(currentCode, currentFile, allPrepend);
|
|
157
|
+
currentFile = ts.createSourceFile(fileName, currentCode, currentFile.languageVersion, true);
|
|
158
|
+
}
|
|
159
|
+
if (allImports.length > 0) {
|
|
160
|
+
currentCode = applyImports(currentCode, currentFile, allImports);
|
|
161
|
+
currentFile = ts.createSourceFile(fileName, currentCode, currentFile.languageVersion, true);
|
|
172
162
|
}
|
|
173
163
|
return {
|
|
174
|
-
changed:
|
|
164
|
+
changed: currentCode !== code,
|
|
175
165
|
code: currentCode,
|
|
176
166
|
sourceFile: currentFile
|
|
177
167
|
};
|
|
@@ -3,6 +3,7 @@ import coordinator from '../coordinator.js';
|
|
|
3
3
|
import program from '../program.js';
|
|
4
4
|
const FILE_REGEX = /\.[tj]sx?$/;
|
|
5
5
|
const DIRECTORY_SEPARATOR_REGEX = /\\/g;
|
|
6
|
+
const LINE_ENDINGS_REGEX = /\r\n/g;
|
|
6
7
|
let contexts = new Map();
|
|
7
8
|
export default ({ name, onWatchChange, plugins }) => {
|
|
8
9
|
return ({ root } = {}) => {
|
|
@@ -18,7 +19,8 @@ export default ({ name, onWatchChange, plugins }) => {
|
|
|
18
19
|
}
|
|
19
20
|
try {
|
|
20
21
|
let prog = program.get(root || ''), sourceFile = prog.getSourceFile(id.replace(DIRECTORY_SEPARATOR_REGEX, '/')) || prog.getSourceFile(id);
|
|
21
|
-
if (!sourceFile ||
|
|
22
|
+
if (!sourceFile ||
|
|
23
|
+
sourceFile.getText().replace(LINE_ENDINGS_REGEX, '\n') !== code.replace(LINE_ENDINGS_REGEX, '\n')) {
|
|
22
24
|
sourceFile = ts.createSourceFile(id, code, ts.ScriptTarget.Latest, true);
|
|
23
25
|
}
|
|
24
26
|
let result = coordinator.transform(plugins, code, sourceFile, prog, contexts.get(root || '') ?? contexts.set(root || '', new Map()).get(root || ''));
|
package/package.json
CHANGED
|
@@ -10,9 +10,6 @@ type CoordinatorResult = {
|
|
|
10
10
|
};
|
|
11
11
|
|
|
12
12
|
|
|
13
|
-
const DIRECTORY_SEPARATOR = /\\/g;
|
|
14
|
-
|
|
15
|
-
|
|
16
13
|
function applyImports(code: string, file: ts.SourceFile, intents: ImportIntent[]): string {
|
|
17
14
|
for (let i = 0, n = intents.length; i < n; i++) {
|
|
18
15
|
let intent = intents[i];
|
|
@@ -76,51 +73,6 @@ function applyPrepend(code: string, file: ts.SourceFile, prepend: string[]): str
|
|
|
76
73
|
return code.slice(0, position) + prepend.join('\n') + code.slice(position);
|
|
77
74
|
}
|
|
78
75
|
|
|
79
|
-
function createUpdatedProgram(
|
|
80
|
-
originalProgram: ts.Program,
|
|
81
|
-
fileName: string,
|
|
82
|
-
newCode: string
|
|
83
|
-
): ts.Program {
|
|
84
|
-
let options = originalProgram.getCompilerOptions(),
|
|
85
|
-
originalHost = ts.createCompilerHost(options),
|
|
86
|
-
originalGetSourceFile = originalHost.getSourceFile.bind(originalHost),
|
|
87
|
-
originalReadFile = originalHost.readFile.bind(originalHost);
|
|
88
|
-
|
|
89
|
-
originalHost.getSourceFile = (
|
|
90
|
-
name: string,
|
|
91
|
-
languageVersion: ts.ScriptTarget,
|
|
92
|
-
onError?: (message: string) => void,
|
|
93
|
-
shouldCreateNewSourceFile?: boolean
|
|
94
|
-
): ts.SourceFile | undefined => {
|
|
95
|
-
if (
|
|
96
|
-
name === fileName ||
|
|
97
|
-
name.replace(DIRECTORY_SEPARATOR, '/') === fileName.replace(DIRECTORY_SEPARATOR, '/')
|
|
98
|
-
) {
|
|
99
|
-
return ts.createSourceFile(name, newCode, languageVersion, true);
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
return originalGetSourceFile(name, languageVersion, onError, shouldCreateNewSourceFile);
|
|
103
|
-
};
|
|
104
|
-
|
|
105
|
-
originalHost.readFile = (name: string): string | undefined => {
|
|
106
|
-
if (
|
|
107
|
-
name === fileName ||
|
|
108
|
-
name.replace(DIRECTORY_SEPARATOR, '/') === fileName.replace(DIRECTORY_SEPARATOR, '/')
|
|
109
|
-
) {
|
|
110
|
-
return newCode;
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
return originalReadFile(name);
|
|
114
|
-
};
|
|
115
|
-
|
|
116
|
-
return ts.createProgram(
|
|
117
|
-
originalProgram.getRootFileNames(),
|
|
118
|
-
options,
|
|
119
|
-
originalHost,
|
|
120
|
-
originalProgram
|
|
121
|
-
);
|
|
122
|
-
}
|
|
123
|
-
|
|
124
76
|
function hasPattern(code: string, patterns: string[]): boolean {
|
|
125
77
|
for (let i = 0, n = patterns.length; i < n; i++) {
|
|
126
78
|
if (code.indexOf(patterns[i]) !== -1) {
|
|
@@ -159,7 +111,6 @@ function modify(code: string, file: ts.SourceFile, pkg: string, options: ModifyO
|
|
|
159
111
|
}
|
|
160
112
|
|
|
161
113
|
let remove = options.remove ? new Set(options.remove) : null,
|
|
162
|
-
// Collect all non-removed specifiers from existing imports
|
|
163
114
|
specifiers = new Set<string>();
|
|
164
115
|
|
|
165
116
|
for (let i = 0, n = found.length; i < n; i++) {
|
|
@@ -176,7 +127,6 @@ function modify(code: string, file: ts.SourceFile, pkg: string, options: ModifyO
|
|
|
176
127
|
}
|
|
177
128
|
}
|
|
178
129
|
|
|
179
|
-
// Build replacement text - namespace import first, then named imports
|
|
180
130
|
let statements: string[] = [];
|
|
181
131
|
|
|
182
132
|
if (namespace) {
|
|
@@ -187,7 +137,6 @@ function modify(code: string, file: ts.SourceFile, pkg: string, options: ModifyO
|
|
|
187
137
|
statements.push(`import { ${[...specifiers].sort().join(', ')} } from '${pkg}';`);
|
|
188
138
|
}
|
|
189
139
|
|
|
190
|
-
// Build replacements - replace first import, remove others
|
|
191
140
|
let replacements: Replacement[] = [];
|
|
192
141
|
|
|
193
142
|
for (let i = 0, n = found.length; i < n; i++) {
|
|
@@ -220,86 +169,96 @@ function replaceReverse(code: string, replacements: Replacement[]): string {
|
|
|
220
169
|
};
|
|
221
170
|
|
|
222
171
|
|
|
223
|
-
/**
|
|
224
|
-
* Transform source through all plugins sequentially.
|
|
225
|
-
* Each plugin receives fresh AST and TypeChecker with accurate positions.
|
|
226
|
-
*/
|
|
227
172
|
const transform = (
|
|
228
173
|
plugins: Plugin[],
|
|
229
174
|
code: string,
|
|
230
175
|
file: ts.SourceFile,
|
|
231
176
|
program: ts.Program,
|
|
232
177
|
shared: SharedContext
|
|
233
|
-
)
|
|
178
|
+
) => {
|
|
234
179
|
if (plugins.length === 0) {
|
|
235
180
|
return { changed: false, code, sourceFile: file };
|
|
236
181
|
}
|
|
237
182
|
|
|
238
|
-
let
|
|
239
|
-
|
|
240
|
-
|
|
183
|
+
let allImports = [],
|
|
184
|
+
allPrepend = [],
|
|
185
|
+
allReplacements = [],
|
|
186
|
+
checker = program.getTypeChecker(),
|
|
241
187
|
fileName = file.fileName,
|
|
242
|
-
|
|
188
|
+
// Keep original source file for symbol resolution - checker operations
|
|
189
|
+
// require nodes from a file that's part of the program's module graph
|
|
190
|
+
originalFile = file;
|
|
243
191
|
|
|
192
|
+
// Phase 1: Collect intents from all plugins using original source file
|
|
193
|
+
// This ensures checker.getSymbolAtLocation() and getAliasedSymbol() work
|
|
194
|
+
// correctly for re-exports and module resolution
|
|
244
195
|
for (let i = 0, n = plugins.length; i < n; i++) {
|
|
245
196
|
let plugin = plugins[i];
|
|
246
197
|
|
|
247
|
-
if (plugin.patterns && !hasPattern(
|
|
198
|
+
if (plugin.patterns && !hasPattern(code, plugin.patterns)) {
|
|
248
199
|
continue;
|
|
249
200
|
}
|
|
250
201
|
|
|
251
202
|
let { imports, prepend, replacements } = plugin.transform({
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
let pluginChanged = false;
|
|
203
|
+
checker,
|
|
204
|
+
code,
|
|
205
|
+
program,
|
|
206
|
+
shared,
|
|
207
|
+
sourceFile: originalFile
|
|
208
|
+
});
|
|
260
209
|
|
|
261
210
|
if (replacements?.length) {
|
|
262
|
-
|
|
263
|
-
currentFile = ts.createSourceFile(
|
|
264
|
-
fileName,
|
|
265
|
-
currentCode,
|
|
266
|
-
currentFile.languageVersion,
|
|
267
|
-
true
|
|
268
|
-
);
|
|
269
|
-
pluginChanged = true;
|
|
211
|
+
allReplacements.push(...replacements);
|
|
270
212
|
}
|
|
271
213
|
|
|
272
214
|
if (prepend?.length) {
|
|
273
|
-
|
|
274
|
-
currentFile = ts.createSourceFile(
|
|
275
|
-
fileName,
|
|
276
|
-
currentCode,
|
|
277
|
-
currentFile.languageVersion,
|
|
278
|
-
true
|
|
279
|
-
);
|
|
280
|
-
pluginChanged = true;
|
|
215
|
+
allPrepend.push(...prepend);
|
|
281
216
|
}
|
|
282
217
|
|
|
283
218
|
if (imports?.length) {
|
|
284
|
-
|
|
285
|
-
currentFile = ts.createSourceFile(
|
|
286
|
-
fileName,
|
|
287
|
-
currentCode,
|
|
288
|
-
currentFile.languageVersion,
|
|
289
|
-
true
|
|
290
|
-
);
|
|
291
|
-
pluginChanged = true;
|
|
219
|
+
allImports.push(...imports);
|
|
292
220
|
}
|
|
221
|
+
}
|
|
293
222
|
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
223
|
+
// Phase 2: Apply all collected intents
|
|
224
|
+
let currentCode = code,
|
|
225
|
+
currentFile = originalFile;
|
|
226
|
+
|
|
227
|
+
if (allReplacements.length > 0) {
|
|
228
|
+
// Sort by start position to detect overlaps
|
|
229
|
+
allReplacements.sort((a, b) => a.node.getStart(originalFile) - b.node.getStart(originalFile));
|
|
230
|
+
|
|
231
|
+
// Check for overlapping replacements
|
|
232
|
+
for (let i = 1, n = allReplacements.length; i < n; i++) {
|
|
233
|
+
let prev = allReplacements[i - 1],
|
|
234
|
+
curr = allReplacements[i],
|
|
235
|
+
prevEnd = prev.node.end,
|
|
236
|
+
currStart = curr.node.getStart(originalFile);
|
|
237
|
+
|
|
238
|
+
if (currStart < prevEnd) {
|
|
239
|
+
console.warn(
|
|
240
|
+
`[coordinator] Overlapping replacements detected at ${fileName}:`,
|
|
241
|
+
`[${prev.node.getStart(originalFile)}-${prevEnd}] overlaps [${currStart}-${curr.node.end}]`
|
|
242
|
+
);
|
|
243
|
+
}
|
|
298
244
|
}
|
|
245
|
+
|
|
246
|
+
currentCode = applyIntents(currentCode, currentFile, allReplacements);
|
|
247
|
+
currentFile = ts.createSourceFile(fileName, currentCode, currentFile.languageVersion, true);
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
if (allPrepend.length > 0) {
|
|
251
|
+
currentCode = applyPrepend(currentCode, currentFile, allPrepend);
|
|
252
|
+
currentFile = ts.createSourceFile(fileName, currentCode, currentFile.languageVersion, true);
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
if (allImports.length > 0) {
|
|
256
|
+
currentCode = applyImports(currentCode, currentFile, allImports);
|
|
257
|
+
currentFile = ts.createSourceFile(fileName, currentCode, currentFile.languageVersion, true);
|
|
299
258
|
}
|
|
300
259
|
|
|
301
260
|
return {
|
|
302
|
-
changed:
|
|
261
|
+
changed: currentCode !== code,
|
|
303
262
|
code: currentCode,
|
|
304
263
|
sourceFile: currentFile
|
|
305
264
|
};
|
|
@@ -307,4 +266,4 @@ const transform = (
|
|
|
307
266
|
|
|
308
267
|
|
|
309
268
|
export default { transform };
|
|
310
|
-
export type { CoordinatorResult };
|
|
269
|
+
export type { CoordinatorResult };
|
|
@@ -24,6 +24,8 @@ const FILE_REGEX = /\.[tj]sx?$/;
|
|
|
24
24
|
|
|
25
25
|
const DIRECTORY_SEPARATOR_REGEX = /\\/g;
|
|
26
26
|
|
|
27
|
+
const LINE_ENDINGS_REGEX = /\r\n/g;
|
|
28
|
+
|
|
27
29
|
|
|
28
30
|
let contexts = new Map<string, SharedContext>();
|
|
29
31
|
|
|
@@ -45,7 +47,10 @@ export default ({ name, onWatchChange, plugins }: VitePluginOptions) => {
|
|
|
45
47
|
let prog = program.get(root || ''),
|
|
46
48
|
sourceFile = prog.getSourceFile(id.replace(DIRECTORY_SEPARATOR_REGEX, '/')) || prog.getSourceFile(id);
|
|
47
49
|
|
|
48
|
-
if (
|
|
50
|
+
if (
|
|
51
|
+
!sourceFile ||
|
|
52
|
+
sourceFile.getText().replace(LINE_ENDINGS_REGEX, '\n') !== code.replace(LINE_ENDINGS_REGEX, '\n')
|
|
53
|
+
) {
|
|
49
54
|
sourceFile = ts.createSourceFile(id, code, ts.ScriptTarget.Latest, true);
|
|
50
55
|
}
|
|
51
56
|
|