Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Use transformer to remove require calls and nodeSystem from typescript.js #3352

Closed
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
163 changes: 101 additions & 62 deletions build/importTypescript.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
import path = require('path');
import fs = require('fs');
import child_process = require('child_process');
import ts = require('typescript');
import { REPO_ROOT } from './utils';

const generatedNote = `//
Expand Down Expand Up @@ -35,74 +36,118 @@ const TYPESCRIPT_LIB_DESTINATION = path.join(REPO_ROOT, 'src/language/typescript
export const typescriptVersion = "${typeScriptDependencyVersion}";\n`
);

let tsServices = fs
.readFileSync(path.join(TYPESCRIPT_LIB_SOURCE, 'typescriptServices.js'))
.toString();
let tsServices = fs.readFileSync(path.join(TYPESCRIPT_LIB_SOURCE, 'typescript.js')).toString();

// Ensure we never run into the node system...
// (this also removes require calls that trick webpack into shimming those modules...)
tsServices = tsServices.replace(
/\n ts\.sys =([^]*)\n \}\)\(\);/m,
`\n // MONACOCHANGE\n ts.sys = undefined;\n // END MONACOCHANGE`
);
const transformed = ts.transpileModule(tsServices, {
compilerOptions: {
target: ts.ScriptTarget.Latest,
removeComments: false
},
transformers: {
after: [
(context) => {
function isRequireCall(node: ts.Node): node is ts.CallExpression {
return (ts as any).isRequireCall(node);
}

return (sourceFile) => {
return ts.visitEachChild(
sourceFile,
function visit(node) {
// Replace require calls with functions that return undefined.
if (isRequireCall(node)) {
return context.factory.updateCallExpression(
node,
context.factory.createParenthesizedExpression(
context.factory.createFunctionExpression(
/*modifiers*/ undefined,
/*asteriskToken*/ undefined,
context.factory.createIdentifier('MONACOCHANGE_require'),
/*typeParameters*/ undefined,
/*parameters*/ undefined,
/*type*/ undefined,
context.factory.createBlock([])
)
),
node.typeArguments,
node.arguments
);
}

// Ensure we never run into the node system...
if (ts.isFunctionDeclaration(node) && node.name?.escapedText === 'getNodeSystem') {
const returnStatement = context.factory.createReturnStatement(
context.factory.createIdentifier('undefined')
);
ts.addSyntheticLeadingComment(
returnStatement,
ts.SyntaxKind.MultiLineCommentTrivia,
' MONACOCHANGE '
);
ts.addSyntheticTrailingComment(
returnStatement,
ts.SyntaxKind.MultiLineCommentTrivia,
' END MONACOCHANGE '
);

const block = context.factory.createBlock([returnStatement]);

// Don't call the deprecated method when the new one is available.
if (parseFloat(ts.versionMajorMinor) < 4.8) {
return context.factory.updateFunctionDeclaration(
node,
node.decorators,
node.modifiers,
node.asteriskToken,
node.name,
node.typeParameters,
node.parameters,
node.type,
block
);
} else {
return (context.factory.updateFunctionDeclaration as any)(
node,
node.modifiers,
node.asteriskToken,
node.name,
node.typeParameters,
node.parameters,
node.type,
block
);
}
}
return ts.visitEachChild(node, visit, context);
},
context
);
};
}
]
}
});

tsServices = transformed.outputText;

// Eliminate more require() calls...
tsServices = tsServices.replace(
/^( +)etwModule = require\(.*$/m,
'$1// MONACOCHANGE\n$1etwModule = undefined;\n$1// END MONACOCHANGE'
);
tsServices = tsServices.replace(
/^( +)var result = ts\.sys\.require\(.*$/m,
'$1// MONACOCHANGE\n$1var result = undefined;\n$1// END MONACOCHANGE'
);
tsServices = tsServices.replace(
/^( +)fs = require\("fs"\);$/m,
'$1// MONACOCHANGE\n$1fs = undefined;\n$1// END MONACOCHANGE'
);
tsServices = tsServices.replace(
/^( +)debugger;$/m,
/^( +)debugger;$/gm,
'$1// MONACOCHANGE\n$1// debugger;\n$1// END MONACOCHANGE'
);
tsServices = tsServices.replace(
/= require\("perf_hooks"\)/m,
'/* MONACOCHANGE */= {}/* END MONACOCHANGE */'
/typeof require === "function"/g,
'/* MONACOCHANGE */ false /* END MONACOCHANGE */'
);
tsServices = tsServices.replace(
/typeof require === "function"/m,
'/* MONACOCHANGE */false/* END MONACOCHANGE */'
/typeof require !== "undefined"/g,
'/* MONACOCHANGE */ false /* END MONACOCHANGE */'
);

tsServices = tsServices.replace(
/module.exports = ts;/m,
/module.exports = ts;/g,
'/* MONACOCHANGE */ /*module.exports = ts;*/ /* END MONACOCHANGE */'
);

// Flag any new require calls (outside comments) so they can be corrected preemptively.
// To avoid missing cases (or using an even more complex regex), temporarily remove comments
// about require() and then check for lines actually calling require().
// \/[*/] matches the start of a comment (single or multi-line).
// ^\s+\*[^/] matches (presumably) a later line of a multi-line comment.
const tsServicesNoCommentedRequire = tsServices.replace(
/(\/[*/]|^\s+\*[^/]).*\brequire\(.*/gm,
''
);
const linesWithRequire = tsServicesNoCommentedRequire.match(/^.*?\brequire\(.*$/gm);

// Allow error messages to include references to require() in their strings
const runtimeRequires =
linesWithRequire &&
linesWithRequire.filter((l) => !l.includes(': diag(') && !l.includes('ts.DiagnosticCategory'));

if (runtimeRequires && runtimeRequires.length && linesWithRequire) {
console.error(
'Found new require() calls on the following lines. These should be removed to avoid breaking webpack builds.\n'
);
console.error(
runtimeRequires.map((r) => `${r} (${tsServicesNoCommentedRequire.indexOf(r)})`).join('\n')
);
process.exit(1);
}

const tsServices_amd =
generatedNote +
tsServices +
Expand Down Expand Up @@ -149,14 +194,8 @@ export var typescript = ts;
stripSourceMaps(tsServices_esm)
);

let dtsServices = fs
.readFileSync(path.join(TYPESCRIPT_LIB_SOURCE, 'typescriptServices.d.ts'))
.toString();
dtsServices += `
// MONACOCHANGE
export = ts;
// END MONACOCHANGE
`;
let dtsServices = fs.readFileSync(path.join(TYPESCRIPT_LIB_SOURCE, 'typescript.d.ts')).toString();

fs.writeFileSync(
path.join(TYPESCRIPT_LIB_DESTINATION, 'typescriptServices.d.ts'),
generatedNote + dtsServices
Expand Down
Loading