Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 21 additions & 15 deletions projects/core/src/patch/patch-module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import { SourceSection } from '../module/source-section';
import { PatchError } from '../system';
import { readFileWithLock } from '../utils';
import { PatchDetail } from './patch-detail';
import { patchSourceFile } from './transformers/sourcefile-parse';


/* ****************************************************************************************************************** */
Expand Down Expand Up @@ -66,36 +67,41 @@ export function patchModule(tsModule: TsModule, skipDts: boolean = false): { js:
/* Fix early return */
const typescriptSection = source.body.find(s => s.srcFileName === 'src/typescript/typescript.ts');
if (!typescriptSection) throw new PatchError(`Could not find Typescript source section`);
typescriptSection.transform([ fixTsEarlyReturnTransformer ]);
typescriptSection.transform([fixTsEarlyReturnTransformer]);

printableBodyFooters.push(`return returnResult;`);
}

/* Patch Program */
const programSection = source.body.find(s => s.srcFileName === 'src/compiler/program.ts');
if (!programSection) throw new PatchError(`Could not find Program source section`);
programSection.transform([ patchCreateProgramTransformer ]);
programSection.transform([patchCreateProgramTransformer, patchSourceFile]);

/* Add originalCreateProgram to exports */
const namespacesTsSection = source.body.find(s => s.srcFileName === 'src/typescript/_namespaces/ts.ts');
if (!namespacesTsSection) throw new PatchError(`Could not find NamespacesTs source section`);
namespacesTsSection.transform([ addOriginalCreateProgramTransformer ]);
namespacesTsSection.transform([addOriginalCreateProgramTransformer]);

/* Patch emitter (for diagnostics tools) */
const emitterSection = source.body.find(s => s.srcFileName === 'src/compiler/watch.ts');
if (!emitterSection) throw new PatchError(`Could not find Emitter source section`);
emitterSection.transform([ patchEmitterTransformer ]);
emitterSection.transform([patchEmitterTransformer]);

/* Move executeCommandLine outside of closure */
if (tsModule.moduleName === 'tsc.js') {
const tscSection = source.body.find(s => s.srcFileName === 'src/tsc/tsc.ts');
if (!tscSection) throw new PatchError(`Could not find Tsc source section`);

tscSection.transform([ hookTscExecTransformer ]);
tscSection.transform([hookTscExecTransformer]);

printableFooters.push(`tsp.${execTscCmd}();`);
}

/* patch getSourceFile for incremental parser */
const parserSection = source.body.find(s => s.srcFileName === 'src/compiler/parser.ts');
if (!parserSection) throw new PatchError(`Could not find parser source section`);
parserSection.transform([patchSourceFile]);

/* Print the module */
const printedJs = printModule();

Expand All @@ -122,32 +128,32 @@ export function patchModule(tsModule: TsModule, skipDts: boolean = false): { js:
let indentLevel = 0;

/* File Header */
list.push([ source.fileHeader, indentLevel ]);
list.push([source.fileHeader, indentLevel]);

/* Body Wrapper Open */
if (shouldWrap) {
list.push([ `\n${tsWrapperOpen}\n`, indentLevel ]);
list.push([`\n${tsWrapperOpen}\n`, indentLevel]);
indentLevel = 2;
}

/* Body Header*/
list.push([ source.bodyHeader, indentLevel ]);
list.push([source.bodyHeader, indentLevel]);

/* Body */
source.body.forEach(section => list.push([ section, indentLevel ]));
source.body.forEach(section => list.push([section, indentLevel]));

/* Body Footers */
printableBodyFooters.forEach(f => list.push([ f, indentLevel ]));
printableBodyFooters.forEach(f => list.push([f, indentLevel]));

/* Body Wrapper Close */
if (shouldWrap) {
indentLevel = 0;
list.push([ `\n${tsWrapperClose}\n`, indentLevel ]);
list.push([`\n${tsWrapperClose}\n`, indentLevel]);
}

/* File Footer */
list.push([ source.fileFooter, indentLevel ]);
printableFooters.forEach(f => list.push([ f, indentLevel ]));
list.push([source.fileFooter, indentLevel]);
printableFooters.forEach(f => list.push([f, indentLevel]));

return list;
}
Expand All @@ -156,7 +162,7 @@ export function patchModule(tsModule: TsModule, skipDts: boolean = false): { js:
const printer = ts.createPrinter(defaultNodePrinterOptions);
let outputStr = ``;

for (const [ item, indentLevel ] of getPrintList()) {
for (const [item, indentLevel] of getPrintList()) {
let printed: string;
let addedIndent: number | undefined;
if (item === undefined) continue;
Expand Down Expand Up @@ -190,7 +196,7 @@ export function patchModule(tsModule: TsModule, skipDts: boolean = false): { js:
const addedSourceFile = addedSection.getSourceFile();

const transformer = createMergeStatementsTransformer(baseSourceFile, addedSourceFile);
baseSection.transform([ transformer ]);
baseSection.transform([transformer]);
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,18 @@ export function addOriginalCreateProgramTransformer(context: ts.TransformationCo
) {
const exportObjectLiteral = node.expression.arguments[1];
if (ts.isObjectLiteralExpression(exportObjectLiteral)) {
const originalParseSourceFile = factory.createPropertyAssignment(
"originalParseSourceFile",
factory.createArrowFunction(
undefined,
undefined,
[],
undefined,
factory.createToken(ts.SyntaxKind.EqualsGreaterThanToken),
factory.createIdentifier("originalParseSourceFile")
)
);

const originalCreateProgramProperty = factory.createPropertyAssignment(
"originalCreateProgram",
factory.createArrowFunction(
Expand All @@ -42,7 +54,7 @@ export function addOriginalCreateProgramTransformer(context: ts.TransformationCo

const updatedExportObjectLiteral = factory.updateObjectLiteralExpression(
exportObjectLiteral,
[...exportObjectLiteral.properties, originalCreateProgramProperty]
[...exportObjectLiteral.properties, originalCreateProgramProperty, originalParseSourceFile]
);

const updatedNode = factory.updateExpressionStatement(
Expand Down
111 changes: 111 additions & 0 deletions projects/core/src/patch/transformers/sourcefile-parse.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
import ts from 'typescript';


export function patchSourceFile(context: ts.TransformationContext) {
const { factory } = context;


return (sourceFile: ts.SourceFile) => {

const getCompilerOptions = factory.createIdentifier('getCompilerOptions');
const res = factory.updateSourceFile(sourceFile, ts.visitNodes(sourceFile.statements, visitNodes) as unknown as ts.Statement[]);

return res;

function visitNodes(node: ts.Node): ts.VisitResult<ts.Node> {
if (ts.isFunctionDeclaration(node) && node.name && (
node.name.escapedText === 'createSourceFile' || node.name.escapedText === 'updateSourceFile2' || node.name.escapedText === 'updateSourceFile'
)) {
const newParams = factory.createNodeArray([
...node.parameters,
factory.createParameterDeclaration(
undefined,
undefined,
getCompilerOptions,
undefined,
undefined,
)])

return ts.visitEachChild(factory.updateFunctionDeclaration(
node,
node.modifiers,
node.asteriskToken,
node.name,
node.typeParameters,
newParams,
node.type,
node.body
), visitNodes, context);
}

if (ts.isCallExpression(node) && ((
ts.isIdentifier(node.expression) &&
node.expression.escapedText === 'createSourceFile'
))) {
return factory.updateCallExpression(node, node.expression, node.typeArguments,
factory.createNodeArray([...node.arguments, factory.createNull(), getCompilerOptions])
);
}

if (
ts.isCallExpression(node) && ((
ts.isIdentifier(node.expression) &&
node.expression.escapedText === 'parseSourceFile'
) || (
ts.isPropertyAccessExpression(node.expression) &&
ts.isIdentifier(node.expression.expression) &&
node.expression.expression.escapedText === 'Parser' &&
node.expression.name.escapedText === "parseSourceFile"
))) {
return factory.updateCallExpression(node, node.expression, node.typeArguments,
factory.createNodeArray([...node.arguments, getCompilerOptions])
);
}
if (ts.isFunctionDeclaration(node) && node.name && node.name.escapedText === 'parseSourceFile') {
const originalParseSourceFileId = factory.createIdentifier('originalParseSourceFile');
const originalParseSourceFile = factory.updateFunctionDeclaration(
node,
node.modifiers,
node.asteriskToken,
originalParseSourceFileId,
node.typeParameters,
node.parameters,
node.type,
node.body
);
const globalAsignment = factory.createAssignment(
factory.createPropertyAccessExpression(factory.createIdentifier("globalThis"), originalParseSourceFileId),
originalParseSourceFileId
)

const newParseSourceFile = factory.createFunctionDeclaration(
undefined,
undefined,
'parseSourceFile',
undefined,
[],
undefined,
factory.createBlock([
factory.createReturnStatement(
factory.createCallExpression(
factory.createPropertyAccessExpression(
factory.createIdentifier('tsp'),
factory.createIdentifier('parseSourceFile')
),
undefined,
[factory.createSpreadElement(factory.createIdentifier('arguments'))]
)
),
])
);

return [newParseSourceFile, originalParseSourceFile, globalAsignment]
}

return ts.visitEachChild(node, visitNodes, context);
}

};
}

// endregion
18 changes: 9 additions & 9 deletions projects/patch/src/ts/create-program.ts
Original file line number Diff line number Diff line change
@@ -1,16 +1,16 @@
namespace tsp {
const activeProgramTransformers = new Set<string>();
export const activeProgramTransformers = new Set<string>();
const { dirname } = require('path');

/* ********************************************************* */
// region: Helpers
/* ********************************************************* */

function getProjectDir(compilerOptions: tsShim.CompilerOptions) {
export function getProjectDir(compilerOptions: tsShim.CompilerOptions) {
return compilerOptions.configFilePath && dirname(compilerOptions.configFilePath);
}

function getProjectConfig(compilerOptions: tsShim.CompilerOptions, rootFileNames: ReadonlyArray<string>) {
export function getProjectConfig(compilerOptions: tsShim.CompilerOptions, rootFileNames: ReadonlyArray<string>) {
let configFilePath = compilerOptions.configFilePath;
let projectDir = getProjectDir(compilerOptions);

Expand All @@ -37,7 +37,7 @@ namespace tsp {
return tsShim.parseJsonConfigFileContent(result.config, tsShim.sys, projectDir, undefined, configFileNamePath);
}

function preparePluginsFromCompilerOptions(plugins: any): PluginConfig[] {
export function preparePluginsFromCompilerOptions(plugins: any): PluginConfig[] {
if (!plugins) return [];

// Old transformers system
Expand Down Expand Up @@ -83,16 +83,16 @@ namespace tsp {

/* Get Config */
const projectConfig = getProjectConfig(options, rootNames);
if ([ 'tsc', 'tsserver', 'tsserverlibrary' ].includes(tsp.currentLibrary)) {
if (['tsc', 'tsserver', 'tsserverlibrary'].includes(tsp.currentLibrary)) {
options = projectConfig.compilerOptions;
if (createOpts) createOpts.options = options;
}

/* Invoke TS createProgram */
let program: tsShim.Program & { originalEmit?: tsShim.Program['emit'] } =
createOpts ?
tsShim.originalCreateProgram(createOpts) :
tsShim.originalCreateProgram(rootNames, options, host, oldProgram, configFileParsingDiagnostics);
createOpts ? // @ts-ignore
tsShim.originalCreateProgram(createOpts) : // @ts-ignore
tsShim.originalCreateProgram(rootNames, options, host, oldProgram, configFileParsingDiagnostics);

/* Prepare Plugins */
const plugins = preparePluginsFromCompilerOptions(options.plugins);
Expand All @@ -102,7 +102,7 @@ namespace tsp {
const programTransformers = pluginCreator.getProgramTransformers();

/* Transform Program */
for (const [ transformerKey, [ programTransformer, config ] ] of programTransformers) {
for (const [transformerKey, [programTransformer, config]] of programTransformers) {
if (activeProgramTransformers.has(transformerKey)) continue;
activeProgramTransformers.add(transformerKey);

Expand Down
2 changes: 2 additions & 0 deletions projects/patch/src/ts/shim.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,8 @@ namespace tsp {
export type CompilerHost = import('typescript').CompilerHost;
export type Diagnostic = import('typescript').Diagnostic;
export type SourceFile = import('typescript').SourceFile;
export type ScriptKind = import('typescript').ScriptKind;
export type ScriptTarget = import('typescript').ScriptTarget;
export type WriteFileCallback = import('typescript').WriteFileCallback;
export type CancellationToken = import('typescript').CancellationToken;
export type CustomTransformers = import('typescript').CustomTransformers;
Expand Down
38 changes: 38 additions & 0 deletions projects/patch/src/ts/source-file.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@

namespace tsp {

/* ********************************************************* *
* Patched parseSourceFile()
* ********************************************************* */

export function parseSourceFile(
fileName: string,
sourceText: string,
languageVersion: tsShim.ScriptTarget,
syntaxCursor: never | undefined,
setParentNodes = false,
scriptKind: tsShim.ScriptKind | undefined,
setExternalModuleIndicatorOverride: ((file: tsShim.SourceFile) => void) | undefined,
getCompilerOptions: () => tsShim.CompilerOptions
): tsShim.SourceFile {
const options = getCompilerOptions();
const projectDir = getProjectDir(options)
/* Get Config */

/* Invoke TS createProgram */
// @ts-ignore
let file: tsShim.SourceFile = tsShim.originalParseSourceFile(fileName, sourceText, languageVersion, syntaxCursor, setParentNodes, scriptKind, setExternalModuleIndicatorOverride)

// /* Prepare Plugins */
const plugins = preparePluginsFromCompilerOptions(options.plugins);
const pluginCreator = new PluginCreator(plugins, projectDir ?? process.cwd());

/* Prevent recursion in Program transformers */
const transformers = pluginCreator.createTransformers({ program: new Error("Program not available for single source file") as never })

const transformed = tsShim.transform(file, transformers.before, options);
transformed.dispose();

return transformed.transformed[0]
}
}