Skip to content

Commit

Permalink
feat: Added support for TS 5.5
Browse files Browse the repository at this point in the history
* squash: Mostly fixed

* squash: Corrected ts-node issue (detail)

ts-node 'compile' was output *.ts transformers as cjs, even if they were esm code.

* build(ci): Updated scripts
  • Loading branch information
nonara authored Jun 3, 2024
1 parent 71000f4 commit 2c4954d
Show file tree
Hide file tree
Showing 19 changed files with 496 additions and 292 deletions.
3 changes: 2 additions & 1 deletion .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,8 @@ jobs:

strategy:
matrix:
node-version: [ 18.x, 20.x, 22.x ]
# NOTE - 22 is failing due to odd bug - maybe bug in node? Need to re-enable later
node-version: [ 18.x, 20.x ]

steps:
- name: Checkout
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/publish.yml
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ jobs:
- name: Setup Node.js 19.x to publish to npmjs.org
uses: actions/setup-node@v1
with:
node-version: '19.x'
node-version: '20.x'
registry-url: 'https://registry.npmjs.org'

- name: Install Packages
Expand Down
11 changes: 10 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,16 @@
"ts-node": "^10.9.1",
"ts-patch": "3.0.2",
"tsconfig-paths": "^4.2.0",
"typescript": "^5.3.2"
"typescript": "^5.4.5",
"ts-next": "npm:typescript@beta",
"ts-expose-internals" : "npm:[email protected]"
},
"workspaces": {
"nohoist": [
"jest",
"ts-jest",
"typescript"
]
},
"directories": {
"resources": "./dist/resources"
Expand Down
4 changes: 0 additions & 4 deletions projects/core/src/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -50,10 +50,6 @@ export const corePatchName = `<core>`;
export const modulePatchFilePath = path.resolve(appRoot, tspPackageJSON.directories.resources, 'module-patch.js');
export const dtsPatchFilePath = path.resolve(appRoot, tspPackageJSON.directories.resources, 'module-patch.d.ts');

// TODO - should do this in a better/dynamic way later
export const tsWrapperOpen = `var ts = (() => {`;
export const tsWrapperClose = `})();`;

export const execTscCmd = 'execTsc';

// endregion
Expand Down
9 changes: 7 additions & 2 deletions projects/core/src/module/module-source.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,10 @@ export interface ModuleSource {
fileFooter?: SourceSection;
usesTsNamespace: boolean;
getSections(): [ sectionName: SourceSection['sectionName'], section: SourceSection | undefined ][];
bodyWrapper?: {
start: string;
end: string;
}
}

// endregion
Expand All @@ -26,7 +30,7 @@ export interface ModuleSource {
export function getModuleSource(tsModule: TsModule): ModuleSource {
const moduleFile = tsModule.getUnpatchedModuleFile();

const { firstSourceFileStart, fileEnd, wrapperPos, bodyPos, sourceFileStarts } =
const { firstSourceFileStart, fileEnd, wrapperPos, bodyPos, sourceFileStarts, bodyWrapper } =
sliceModule(moduleFile, tsModule.package.version);

const fileHeaderEnd = wrapperPos?.start ?? firstSourceFileStart;
Expand All @@ -47,7 +51,8 @@ export function getModuleSource(tsModule: TsModule): ModuleSource {
...this.body.map((section, i) => [ `body`, section ] as [ SourceSection['sectionName'], SourceSection ]),
[ 'file-footer', this.fileFooter ],
];
}
},
bodyWrapper,
}
}

Expand Down
44 changes: 29 additions & 15 deletions projects/core/src/patch/patch-module.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,10 @@
import ts from 'typescript';
import fs from 'fs';
import {
defaultNodePrinterOptions, dtsPatchFilePath, execTscCmd, modulePatchFilePath, tsWrapperClose, tsWrapperOpen
} from '../config';
import { defaultNodePrinterOptions, dtsPatchFilePath, execTscCmd, modulePatchFilePath } from '../config';
import { getTsModule, TsModule } from '../module';
import {
addOriginalCreateProgramTransformer, createMergeStatementsTransformer, fixTsEarlyReturnTransformer,
hookTscExecTransformer, patchCreateProgramTransformer, patchEmitterTransformer
addOriginalCreateProgramTransformer, createMergeStatementsTransformer, createProgramExportFiles,
fixTsEarlyReturnTransformer, hookTscExecTransformer, patchCreateProgramTransformer, patchEmitterTransformer
} from './transformers';
import { SourceSection } from '../module/source-section';
import { PatchError } from '../system';
Expand Down Expand Up @@ -39,6 +37,7 @@ export function patchModule(tsModule: TsModule, skipDts: boolean = false): { js:
}

const source = tsModule.getUnpatchedSource();
const { bodyWrapper } = source;

const printableBodyFooters: (SourceSection | string)[] = [];
const printableFooters: (SourceSection | string)[] = [];
Expand All @@ -64,11 +63,13 @@ export function patchModule(tsModule: TsModule, skipDts: boolean = false): { js:
source.body.unshift(...tsSource.body);

/* 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 ]);

printableBodyFooters.push(`return returnResult;`);
// NOTE - This exists up until TS 5.4, but isn't there for 5.5+
if (tsModule.majorVer <= 5 && tsModule.minorVer <= 4) {
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 ]);
printableBodyFooters.push(`return returnResult;`);
}
}

/* Patch Program */
Expand All @@ -77,9 +78,22 @@ export function patchModule(tsModule: TsModule, skipDts: boolean = false): { js:
programSection.transform([ patchCreateProgramTransformer ]);

/* 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 ]);
let createProgramAdded = false;
for (const fileName of createProgramExportFiles) {
// As of TS 5.5, we have to handle cases of multiple instances of the same file name. In this case, we need to
// handle both src/typescript/typescript.ts
const sections = source.body.filter(s => s.srcFileName === fileName);
for (const section of sections) {
try {
section.transform([ addOriginalCreateProgramTransformer ]);
createProgramAdded = true;
} catch (e) {
if (!(e instanceof PatchError)) throw e;
}
}
}

if (!createProgramAdded) throw new PatchError(`Could not find any of the createProgram export files`);

/* Patch emitter (for diagnostics tools) */
const emitterSection = source.body.find(s => s.srcFileName === 'src/compiler/watch.ts');
Expand Down Expand Up @@ -128,7 +142,7 @@ export function patchModule(tsModule: TsModule, skipDts: boolean = false): { js:

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

Expand All @@ -144,7 +158,7 @@ export function patchModule(tsModule: TsModule, skipDts: boolean = false): { js:
/* Body Wrapper Close */
if (shouldWrap) {
indentLevel = 0;
list.push([ `\n${tsWrapperClose}\n`, indentLevel ]);
if (bodyWrapper) list.push([ `\n${bodyWrapper.end}\n`, indentLevel ]);
}

/* File Footer */
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,21 @@
import ts from 'typescript';
import { PatchError } from '../../system';


/* ****************************************************************************************************************** */
// region: Config
/* ****************************************************************************************************************** */

export const createProgramExportFiles = [
/* TS < 5.4 */
'src/typescript/_namespaces/ts.ts',

/* TS >= 5.4 */
'src/server/_namespaces/ts.ts',
'src/typescript/typescript.ts'
]

// endregion


/* ****************************************************************************************************************** */
Expand All @@ -11,38 +28,39 @@ export function addOriginalCreateProgramTransformer(context: ts.TransformationCo
let patchSuccess = false;

return (sourceFile: ts.SourceFile) => {
if (sourceFile.fileName !== 'src/typescript/_namespaces/ts.ts')
if (!createProgramExportFiles.includes(sourceFile.fileName))
throw new Error('Wrong emitter file sent to transformer! This should be unreachable.');

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

if (!patchSuccess) throw new Error('Failed to patch typescript early return!');
if (!patchSuccess) throw new PatchError('Failed to patch typescript originalCreateProgram!');

return res;

function visitNodes(node: ts.Statement): ts.VisitResult<ts.Node> {
/* Handle: __export({ ... }) */
if (
ts.isExpressionStatement(node) &&
ts.isCallExpression(node.expression) &&
node.expression.expression.getText() === "__export"
node.expression.expression.getText() === '__export'
) {
const exportObjectLiteral = node.expression.arguments[1];
if (ts.isObjectLiteralExpression(exportObjectLiteral)) {
const originalCreateProgramProperty = factory.createPropertyAssignment(
"originalCreateProgram",
'originalCreateProgram',
factory.createArrowFunction(
undefined,
undefined,
[],
undefined,
factory.createToken(ts.SyntaxKind.EqualsGreaterThanToken),
factory.createIdentifier("originalCreateProgram")
factory.createIdentifier('originalCreateProgram')
)
);

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

const updatedNode = factory.updateExpressionStatement(
Expand All @@ -51,7 +69,7 @@ export function addOriginalCreateProgramTransformer(context: ts.TransformationCo
node.expression,
node.expression.expression,
undefined,
[node.expression.arguments[0], updatedExportObjectLiteral]
[ node.expression.arguments[0], updatedExportObjectLiteral ]
)
);

Expand All @@ -60,6 +78,49 @@ export function addOriginalCreateProgramTransformer(context: ts.TransformationCo
}
}

/* Handle: 1 && (module.exports = { ... }) (ts5.5+) */
if (
ts.isExpressionStatement(node) && ts.isBinaryExpression(node.expression) &&
node.expression.operatorToken.kind === ts.SyntaxKind.AmpersandAmpersandToken &&
ts.isParenthesizedExpression(node.expression.right) &&
ts.isBinaryExpression(node.expression.right.expression) &&
node.expression.right.expression.operatorToken.kind === ts.SyntaxKind.EqualsToken &&
ts.isPropertyAccessExpression(node.expression.right.expression.left) &&
node.expression.right.expression.left.expression.getText() === 'module' &&
node.expression.right.expression.left.name.getText() === 'exports' &&
ts.isObjectLiteralExpression(node.expression.right.expression.right)
) {
// Add originalCreateProgram to the object literal
const originalCreateProgramProperty = factory.createShorthandPropertyAssignment('originalCreateProgram');

const updatedObjectLiteral = factory.updateObjectLiteralExpression(
node.expression.right.expression.right,
[ ...node.expression.right.expression.right.properties, originalCreateProgramProperty ]
);

// Update the node
const updatedNode = factory.updateExpressionStatement(
node,
factory.updateBinaryExpression(
node.expression,
node.expression.left,
node.expression.operatorToken,
factory.updateParenthesizedExpression(
node.expression.right,
factory.updateBinaryExpression(
node.expression.right.expression,
node.expression.right.expression.left,
node.expression.right.expression.operatorToken,
updatedObjectLiteral
)
)
)
);

patchSuccess = true;
return updatedNode;
}

return node;
}
};
Expand Down
19 changes: 15 additions & 4 deletions projects/core/src/slice/module-slice.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import { ModuleFile } from '../module';
import { Position } from '../system';
import semver from 'semver';
import { sliceTs5 } from './ts5';
import { sliceTs54 } from './ts54';
import { sliceTs55 } from './ts55';


/* ****************************************************************************************************************** */
Expand All @@ -15,6 +16,10 @@ export interface ModuleSlice {
bodyPos: Position
fileEnd: number
sourceFileStarts: [ name: string, position: number ][]
bodyWrapper?: {
start: string;
end: string;
}
}

// endregion
Expand All @@ -25,12 +30,18 @@ export interface ModuleSlice {
/* ****************************************************************************************************************** */

export function sliceModule(moduleFile: ModuleFile, tsVersion: string) {
if (semver.lte(tsVersion, '5.0.0')) {
const baseVersion = semver.coerce(tsVersion, { includePrerelease: false });
if (!baseVersion) throw new Error(`Could not parse TS version: ${tsVersion}`);

if (semver.lt(baseVersion, '5.0.0')) {
throw new Error(`Cannot patch TS version <5`);
}

/* Handle 5+ */
return sliceTs5(moduleFile);
if (semver.lt(baseVersion, '5.5.0')) {
return sliceTs54(moduleFile);
}

return sliceTs55(moduleFile);
}

/** @internal */
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,10 @@ import { ModuleSlice } from './module-slice';
// region: Utils
/* ****************************************************************************************************************** */

export function sliceTs5(moduleFile: ModuleFile): ModuleSlice {
/**
* Slice 5.0 - 5.4
*/
export function sliceTs54(moduleFile: ModuleFile): ModuleSlice {
let firstSourceFileStart: number;
let wrapperStart: number | undefined;
let wrapperEnd: number | undefined;
Expand Down Expand Up @@ -65,7 +68,11 @@ export function sliceTs5(moduleFile: ModuleFile): ModuleSlice {
wrapperPos: wrapperStart != null ? { start: wrapperStart, end: wrapperEnd! } : undefined,
fileEnd: content.length,
bodyPos: { start: bodyStart, end: bodyEnd },
sourceFileStarts
sourceFileStarts,
bodyWrapper: {
start: 'var ts = (() => {',
end: '})();'
}
};
}

Expand Down
Loading

0 comments on commit 2c4954d

Please sign in to comment.