@esportsplus/typescript
Advanced tools
| import { ts } from '../index.js'; | ||
| import imports from './imports.js'; | ||
| const DIRECTORY_SEPARATOR = /\\/g; | ||
| function applyImports(code, file, intents) { | ||
@@ -46,2 +47,20 @@ for (let i = 0, n = intents.length; i < n; i++) { | ||
| } | ||
| function createUpdatedProgram(originalProgram, fileName, newCode) { | ||
| let options = originalProgram.getCompilerOptions(), originalHost = ts.createCompilerHost(options), originalGetSourceFile = originalHost.getSourceFile.bind(originalHost), originalReadFile = originalHost.readFile.bind(originalHost); | ||
| originalHost.getSourceFile = (name, languageVersion, onError, shouldCreateNewSourceFile) => { | ||
| if (name === fileName || | ||
| name.replace(DIRECTORY_SEPARATOR, '/') === fileName.replace(DIRECTORY_SEPARATOR, '/')) { | ||
| return ts.createSourceFile(name, newCode, languageVersion, true); | ||
| } | ||
| return originalGetSourceFile(name, languageVersion, onError, shouldCreateNewSourceFile); | ||
| }; | ||
| originalHost.readFile = (name) => { | ||
| if (name === fileName || | ||
| name.replace(DIRECTORY_SEPARATOR, '/') === fileName.replace(DIRECTORY_SEPARATOR, '/')) { | ||
| return newCode; | ||
| } | ||
| return originalReadFile(name); | ||
| }; | ||
| return ts.createProgram(originalProgram.getRootFileNames(), options, originalHost, originalProgram); | ||
| } | ||
| function hasPattern(code, patterns) { | ||
@@ -121,3 +140,3 @@ for (let i = 0, n = patterns.length; i < n; i++) { | ||
| } | ||
| let currentCode = code, currentFile = file; | ||
| let currentCode = code, currentFile = file, currentProgram = program, fileName = file.fileName, transformed = false; | ||
| for (let i = 0, n = plugins.length; i < n; i++) { | ||
@@ -129,23 +148,31 @@ let plugin = plugins[i]; | ||
| let { imports, prepend, replacements } = plugin.transform({ | ||
| checker: program.getTypeChecker(), | ||
| checker: currentProgram.getTypeChecker(), | ||
| code: currentCode, | ||
| program, | ||
| program: currentProgram, | ||
| shared, | ||
| sourceFile: currentFile | ||
| }); | ||
| let pluginChanged = false; | ||
| if (replacements?.length) { | ||
| currentCode = applyIntents(currentCode, currentFile, replacements); | ||
| currentFile = ts.createSourceFile(currentFile.fileName, currentCode, currentFile.languageVersion, true); | ||
| currentFile = ts.createSourceFile(fileName, currentCode, currentFile.languageVersion, true); | ||
| pluginChanged = true; | ||
| } | ||
| if (prepend?.length) { | ||
| currentCode = applyPrepend(currentCode, currentFile, prepend); | ||
| currentFile = ts.createSourceFile(currentFile.fileName, currentCode, currentFile.languageVersion, true); | ||
| currentFile = ts.createSourceFile(fileName, currentCode, currentFile.languageVersion, true); | ||
| pluginChanged = true; | ||
| } | ||
| if (imports?.length) { | ||
| currentCode = applyImports(currentCode, currentFile, imports); | ||
| currentFile = ts.createSourceFile(currentFile.fileName, currentCode, currentFile.languageVersion, true); | ||
| currentFile = ts.createSourceFile(fileName, currentCode, currentFile.languageVersion, true); | ||
| pluginChanged = true; | ||
| } | ||
| if (pluginChanged) { | ||
| transformed = true; | ||
| currentProgram = createUpdatedProgram(currentProgram, fileName, currentCode); | ||
| } | ||
| } | ||
| return { | ||
| changed: currentCode !== code, | ||
| changed: transformed, | ||
| code: currentCode, | ||
@@ -152,0 +179,0 @@ sourceFile: currentFile |
@@ -49,3 +49,3 @@ import { ts } from '../index.js'; | ||
| if (names.has(node.text)) { | ||
| let symbol = checker?.getSymbolAtLocation(node); | ||
| let symbol = checker.getSymbolAtLocation(node); | ||
| if (symbol) { | ||
@@ -72,3 +72,3 @@ let declarations = symbol.getDeclarations(); | ||
| } | ||
| let symbol = checker?.getSymbolAtLocation(node); | ||
| let symbol = checker.getSymbolAtLocation(node); | ||
| if (!symbol) { | ||
@@ -75,0 +75,0 @@ return false; |
+1
-1
@@ -42,3 +42,3 @@ { | ||
| "types": "build/index.d.ts", | ||
| "version": "0.28.0", | ||
| "version": "0.28.1", | ||
| "scripts": { | ||
@@ -45,0 +45,0 @@ "build": "tsc && tsc-alias", |
@@ -13,2 +13,5 @@ import type { ImportIntent, Plugin, Replacement, ReplacementIntent, SharedContext } from './types'; | ||
| const DIRECTORY_SEPARATOR = /\\/g; | ||
| function applyImports(code: string, file: ts.SourceFile, intents: ImportIntent[]): string { | ||
@@ -77,2 +80,47 @@ for (let i = 0, n = intents.length; i < n; i++) { | ||
| function createUpdatedProgram( | ||
| originalProgram: ts.Program, | ||
| fileName: string, | ||
| newCode: string | ||
| ): ts.Program { | ||
| let options = originalProgram.getCompilerOptions(), | ||
| originalHost = ts.createCompilerHost(options), | ||
| originalGetSourceFile = originalHost.getSourceFile.bind(originalHost), | ||
| originalReadFile = originalHost.readFile.bind(originalHost); | ||
| originalHost.getSourceFile = ( | ||
| name: string, | ||
| languageVersion: ts.ScriptTarget, | ||
| onError?: (message: string) => void, | ||
| shouldCreateNewSourceFile?: boolean | ||
| ): ts.SourceFile | undefined => { | ||
| if ( | ||
| name === fileName || | ||
| name.replace(DIRECTORY_SEPARATOR, '/') === fileName.replace(DIRECTORY_SEPARATOR, '/') | ||
| ) { | ||
| return ts.createSourceFile(name, newCode, languageVersion, true); | ||
| } | ||
| return originalGetSourceFile(name, languageVersion, onError, shouldCreateNewSourceFile); | ||
| }; | ||
| originalHost.readFile = (name: string): string | undefined => { | ||
| if ( | ||
| name === fileName || | ||
| name.replace(DIRECTORY_SEPARATOR, '/') === fileName.replace(DIRECTORY_SEPARATOR, '/') | ||
| ) { | ||
| return newCode; | ||
| } | ||
| return originalReadFile(name); | ||
| }; | ||
| return ts.createProgram( | ||
| originalProgram.getRootFileNames(), | ||
| options, | ||
| originalHost, | ||
| originalProgram | ||
| ); | ||
| } | ||
| function hasPattern(code: string, patterns: string[]): boolean { | ||
@@ -179,3 +227,3 @@ for (let i = 0, n = patterns.length; i < n; i++) { | ||
| * Transform source through all plugins sequentially. | ||
| * Each plugin receives fresh AST with accurate positions. | ||
| * Each plugin receives fresh AST and TypeChecker with accurate positions. | ||
| */ | ||
@@ -194,3 +242,6 @@ const transform = ( | ||
| let currentCode = code, | ||
| currentFile = file; | ||
| currentFile = file, | ||
| currentProgram = program, | ||
| fileName = file.fileName, | ||
| transformed = false; | ||
@@ -205,5 +256,5 @@ for (let i = 0, n = plugins.length; i < n; i++) { | ||
| let { imports, prepend, replacements } = plugin.transform({ | ||
| checker: program.getTypeChecker(), | ||
| checker: currentProgram.getTypeChecker(), | ||
| code: currentCode, | ||
| program, | ||
| program: currentProgram, | ||
| shared, | ||
@@ -213,6 +264,8 @@ sourceFile: currentFile | ||
| let pluginChanged = false; | ||
| if (replacements?.length) { | ||
| currentCode = applyIntents(currentCode, currentFile, replacements); | ||
| currentFile = ts.createSourceFile( | ||
| currentFile.fileName, | ||
| fileName, | ||
| currentCode, | ||
@@ -222,2 +275,3 @@ currentFile.languageVersion, | ||
| ); | ||
| pluginChanged = true; | ||
| } | ||
@@ -228,3 +282,3 @@ | ||
| currentFile = ts.createSourceFile( | ||
| currentFile.fileName, | ||
| fileName, | ||
| currentCode, | ||
@@ -234,2 +288,3 @@ currentFile.languageVersion, | ||
| ); | ||
| pluginChanged = true; | ||
| } | ||
@@ -240,3 +295,3 @@ | ||
| currentFile = ts.createSourceFile( | ||
| currentFile.fileName, | ||
| fileName, | ||
| currentCode, | ||
@@ -246,7 +301,14 @@ currentFile.languageVersion, | ||
| ); | ||
| pluginChanged = true; | ||
| } | ||
| // Rebuild program with updated source so next plugin gets valid checker | ||
| if (pluginChanged) { | ||
| transformed = true; | ||
| currentProgram = createUpdatedProgram(currentProgram, fileName, currentCode); | ||
| } | ||
| } | ||
| return { | ||
| changed: currentCode !== code, | ||
| changed: transformed, | ||
| code: currentCode, | ||
@@ -253,0 +315,0 @@ sourceFile: currentFile |
@@ -92,3 +92,3 @@ import { ts } from '~/index'; | ||
| if (names.has(node.text)) { | ||
| let symbol = checker?.getSymbolAtLocation(node); | ||
| let symbol = checker.getSymbolAtLocation(node); | ||
@@ -124,3 +124,3 @@ if (symbol) { | ||
| // Slow path: check for re-exports via aliased symbol | ||
| let symbol = checker?.getSymbolAtLocation(node); | ||
| let symbol = checker.getSymbolAtLocation(node); | ||
@@ -127,0 +127,0 @@ if (!symbol) { |
62121
5.78%1580
5.26%