@esportsplus/typescript 0.28.0 → 0.28.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.
@@ -1,5 +1,6 @@
1
1
  import { ts } from '../index.js';
2
2
  import imports from './imports.js';
3
+ const DIRECTORY_SEPARATOR = /\\/g;
3
4
  function applyImports(code, file, intents) {
4
5
  for (let i = 0, n = intents.length; i < n; i++) {
5
6
  let intent = intents[i];
@@ -43,6 +44,24 @@ function applyPrepend(code, file, prepend) {
43
44
  }
44
45
  return code.slice(0, position) + prepend.join('\n') + code.slice(position);
45
46
  }
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
+ }
46
65
  function hasPattern(code, patterns) {
47
66
  for (let i = 0, n = patterns.length; i < n; i++) {
48
67
  if (code.indexOf(patterns[i]) !== -1) {
@@ -117,34 +136,42 @@ const transform = (plugins, code, file, program, shared) => {
117
136
  if (plugins.length === 0) {
118
137
  return { changed: false, code, sourceFile: file };
119
138
  }
120
- let currentCode = code, currentFile = file;
139
+ let currentCode = code, currentFile = file, currentProgram = program, fileName = file.fileName, transformed = false;
121
140
  for (let i = 0, n = plugins.length; i < n; i++) {
122
141
  let plugin = plugins[i];
123
142
  if (plugin.patterns && !hasPattern(currentCode, plugin.patterns)) {
124
143
  continue;
125
144
  }
126
145
  let { imports, prepend, replacements } = plugin.transform({
127
- checker: program.getTypeChecker(),
146
+ checker: currentProgram.getTypeChecker(),
128
147
  code: currentCode,
129
- program,
148
+ program: currentProgram,
130
149
  shared,
131
150
  sourceFile: currentFile
132
151
  });
152
+ let pluginChanged = false;
133
153
  if (replacements?.length) {
134
154
  currentCode = applyIntents(currentCode, currentFile, replacements);
135
- currentFile = ts.createSourceFile(currentFile.fileName, currentCode, currentFile.languageVersion, true);
155
+ currentFile = ts.createSourceFile(fileName, currentCode, currentFile.languageVersion, true);
156
+ pluginChanged = true;
136
157
  }
137
158
  if (prepend?.length) {
138
159
  currentCode = applyPrepend(currentCode, currentFile, prepend);
139
- currentFile = ts.createSourceFile(currentFile.fileName, currentCode, currentFile.languageVersion, true);
160
+ currentFile = ts.createSourceFile(fileName, currentCode, currentFile.languageVersion, true);
161
+ pluginChanged = true;
140
162
  }
141
163
  if (imports?.length) {
142
164
  currentCode = applyImports(currentCode, currentFile, imports);
143
- currentFile = ts.createSourceFile(currentFile.fileName, currentCode, currentFile.languageVersion, true);
165
+ currentFile = ts.createSourceFile(fileName, currentCode, currentFile.languageVersion, true);
166
+ pluginChanged = true;
167
+ }
168
+ if (pluginChanged) {
169
+ transformed = true;
170
+ currentProgram = createUpdatedProgram(currentProgram, fileName, currentCode);
144
171
  }
145
172
  }
146
173
  return {
147
- changed: currentCode !== code,
174
+ changed: transformed,
148
175
  code: currentCode,
149
176
  sourceFile: currentFile
150
177
  };
@@ -46,7 +46,7 @@ const includes = (checker, node, pkg, symbolName) => {
46
46
  imports.set(pkg, names);
47
47
  }
48
48
  if (names.has(node.text)) {
49
- let symbol = checker?.getSymbolAtLocation(node);
49
+ let symbol = checker.getSymbolAtLocation(node);
50
50
  if (symbol) {
51
51
  let declarations = symbol.getDeclarations();
52
52
  if (declarations && declarations.length > 0) {
@@ -68,7 +68,7 @@ const includes = (checker, node, pkg, symbolName) => {
68
68
  }
69
69
  return true;
70
70
  }
71
- let symbol = checker?.getSymbolAtLocation(node);
71
+ let symbol = checker.getSymbolAtLocation(node);
72
72
  if (!symbol) {
73
73
  return false;
74
74
  }
package/package.json CHANGED
@@ -39,7 +39,7 @@
39
39
  },
40
40
  "type": "module",
41
41
  "types": "build/index.d.ts",
42
- "version": "0.28.0",
42
+ "version": "0.28.1",
43
43
  "scripts": {
44
44
  "build": "tsc && tsc-alias",
45
45
  "-": "-"
@@ -10,6 +10,9 @@ type CoordinatorResult = {
10
10
  };
11
11
 
12
12
 
13
+ const DIRECTORY_SEPARATOR = /\\/g;
14
+
15
+
13
16
  function applyImports(code: string, file: ts.SourceFile, intents: ImportIntent[]): string {
14
17
  for (let i = 0, n = intents.length; i < n; i++) {
15
18
  let intent = intents[i];
@@ -73,6 +76,51 @@ function applyPrepend(code: string, file: ts.SourceFile, prepend: string[]): str
73
76
  return code.slice(0, position) + prepend.join('\n') + code.slice(position);
74
77
  }
75
78
 
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
+
76
124
  function hasPattern(code: string, patterns: string[]): boolean {
77
125
  for (let i = 0, n = patterns.length; i < n; i++) {
78
126
  if (code.indexOf(patterns[i]) !== -1) {
@@ -174,7 +222,7 @@ function replaceReverse(code: string, replacements: Replacement[]): string {
174
222
 
175
223
  /**
176
224
  * Transform source through all plugins sequentially.
177
- * Each plugin receives fresh AST with accurate positions.
225
+ * Each plugin receives fresh AST and TypeChecker with accurate positions.
178
226
  */
179
227
  const transform = (
180
228
  plugins: Plugin[],
@@ -188,7 +236,10 @@ const transform = (
188
236
  }
189
237
 
190
238
  let currentCode = code,
191
- currentFile = file;
239
+ currentFile = file,
240
+ currentProgram = program,
241
+ fileName = file.fileName,
242
+ transformed = false;
192
243
 
193
244
  for (let i = 0, n = plugins.length; i < n; i++) {
194
245
  let plugin = plugins[i];
@@ -198,46 +249,57 @@ const transform = (
198
249
  }
199
250
 
200
251
  let { imports, prepend, replacements } = plugin.transform({
201
- checker: program.getTypeChecker(),
252
+ checker: currentProgram.getTypeChecker(),
202
253
  code: currentCode,
203
- program,
254
+ program: currentProgram,
204
255
  shared,
205
256
  sourceFile: currentFile
206
257
  });
207
258
 
259
+ let pluginChanged = false;
260
+
208
261
  if (replacements?.length) {
209
262
  currentCode = applyIntents(currentCode, currentFile, replacements);
210
263
  currentFile = ts.createSourceFile(
211
- currentFile.fileName,
264
+ fileName,
212
265
  currentCode,
213
266
  currentFile.languageVersion,
214
267
  true
215
268
  );
269
+ pluginChanged = true;
216
270
  }
217
271
 
218
272
  if (prepend?.length) {
219
273
  currentCode = applyPrepend(currentCode, currentFile, prepend);
220
274
  currentFile = ts.createSourceFile(
221
- currentFile.fileName,
275
+ fileName,
222
276
  currentCode,
223
277
  currentFile.languageVersion,
224
278
  true
225
279
  );
280
+ pluginChanged = true;
226
281
  }
227
282
 
228
283
  if (imports?.length) {
229
284
  currentCode = applyImports(currentCode, currentFile, imports);
230
285
  currentFile = ts.createSourceFile(
231
- currentFile.fileName,
286
+ fileName,
232
287
  currentCode,
233
288
  currentFile.languageVersion,
234
289
  true
235
290
  );
291
+ pluginChanged = true;
292
+ }
293
+
294
+ // Rebuild program with updated source so next plugin gets valid checker
295
+ if (pluginChanged) {
296
+ transformed = true;
297
+ currentProgram = createUpdatedProgram(currentProgram, fileName, currentCode);
236
298
  }
237
299
  }
238
300
 
239
301
  return {
240
- changed: currentCode !== code,
302
+ changed: transformed,
241
303
  code: currentCode,
242
304
  sourceFile: currentFile
243
305
  };
@@ -89,7 +89,7 @@ const includes = (checker: ts.TypeChecker, node: ts.Node, pkg: string, symbolNam
89
89
 
90
90
  // Fast path: direct import from package
91
91
  if (names.has(node.text)) {
92
- let symbol = checker?.getSymbolAtLocation(node);
92
+ let symbol = checker.getSymbolAtLocation(node);
93
93
 
94
94
  if (symbol) {
95
95
  let declarations = symbol.getDeclarations();
@@ -120,7 +120,7 @@ const includes = (checker: ts.TypeChecker, node: ts.Node, pkg: string, symbolNam
120
120
  }
121
121
 
122
122
  // Slow path: check for re-exports via aliased symbol
123
- let symbol = checker?.getSymbolAtLocation(node);
123
+ let symbol = checker.getSymbolAtLocation(node);
124
124
 
125
125
  if (!symbol) {
126
126
  return false;