diff --git a/scripts/build/config.mjs b/scripts/build/config.mjs index 4c55bd896bef..e06d5bbbc48a 100644 --- a/scripts/build/config.mjs +++ b/scripts/build/config.mjs @@ -2,6 +2,7 @@ import path from "node:path"; import { createRequire } from "node:module"; import createEsmUtils from "esm-utils"; import { PROJECT_ROOT } from "../utils/index.mjs"; +import modifyTypescriptModule from "./modify-typescript-module.mjs"; const { require } = createEsmUtils(import.meta); @@ -101,62 +102,8 @@ const parsers = [ }, { module: require.resolve("typescript"), - process(text) { - return text.replace( - /(?<=\n)(?\s+)function tryGetNodePerformanceHooks\(\) {.*?\n\k}(?=\n)/s, - "function tryGetNodePerformanceHooks() {}" - ); - }, + process: modifyTypescriptModule, }, - - ...Object.entries({ - // `typescript/lib/typescript.js` expose extra global objects - // `TypeScript`, `toolsVersion`, `globalThis` - 'typeof process === "undefined" || process.browser': "false", - 'typeof globalThis === "object"': "true", - - "_fs.realpathSync.native": - "_fs.realpathSync && _fs.realpathSync.native", - // Remove useless `ts.sys` - "ts.sys = ": "ts.sys = undefined && ", - - // Remove useless language service - "ts.realizeDiagnostics = ": "ts.realizeDiagnostics = undefined && ", - "ts.TypeScriptServicesFactory = ": - "ts.TypeScriptServicesFactory = undefined && ", - "var ShimBase = ": "var ShimBase = undefined && ", - "var TypeScriptServicesFactory = ": - "var TypeScriptServicesFactory = undefined && ", - "var LanguageServiceShimObject = ": - "var LanguageServiceShimObject = undefined && ", - "var CoreServicesShimHostAdapter = ": - "var CoreServicesShimHostAdapter = undefined && ", - "var LanguageServiceShimHostAdapter = ": - "var LanguageServiceShimHostAdapter = undefined && ", - "var ScriptSnapshotShimAdapter = ": - "var ScriptSnapshotShimAdapter = undefined && ", - "var ClassifierShimObject = ": - "var ClassifierShimObject = undefined && ", - "var CoreServicesShimObject = ": - "var CoreServicesShimObject = undefined && ", - "function simpleForwardCall(": "0 && function simpleForwardCall(", - "function forwardJSONCall(": "0 && function forwardJSONCall(", - "function forwardCall(": "0 && function forwardCall(", - "function realizeDiagnostics(": "0 && function realizeDiagnostics(", - "function realizeDiagnostic(": "0 && function realizeDiagnostic(", - "function convertClassifications(": - "0 && function convertClassifications(", - - // Dynamic `require()`s - "ts.sys && ts.sys.require": "false", - "require(etwModulePath)": "undefined", - 'require("source-map-support").install()': "", - "require(modulePath)": "undefined", - }).map(([find, replacement]) => ({ - module: require.resolve("typescript"), - find, - replacement, - })), { module: require.resolve("debug/src/browser.js"), path: require.resolve("./shims/debug.cjs"), diff --git a/scripts/build/modify-typescript-module.mjs b/scripts/build/modify-typescript-module.mjs new file mode 100644 index 000000000000..da1b4760729c --- /dev/null +++ b/scripts/build/modify-typescript-module.mjs @@ -0,0 +1,484 @@ +import path from "node:path"; +import escapeStringRegexp from "escape-string-regexp"; +import { outdent } from "outdent"; +import { writeFile, PROJECT_ROOT } from "../utils/index.mjs"; + +/* +Root submodule in `typescript.js` are bundled like + +```js +var ts; +(function (ts) { + // Submodule +})(ts || (ts = {})); +``` +*/ + +const SUBMODULE_START = escapeStringRegexp("var ts;\n(function (ts) {"); +const SUBMODULE_END = escapeStringRegexp("})(ts || (ts = {}));"); + +function getSubmodules(text, testFunction) { + const regexp = new RegExp( + [ + "(?<=\n)", + `(?${SUBMODULE_START})`, + "(?=\n)", + "(?.*?)", + "(?<=\n)", + `(?${SUBMODULE_END})`, + "(?=\n)", + ].join(""), + "gsu" + ); + + return [...text.matchAll(regexp)] + .filter((match) => testFunction(match.groups.text)) + .map((match) => ({ + start: match.index, + end: match.index + match[0].length, + ...match.groups, + })); +} + +function removeSubmodule(text, testFunction) { + return replaceSubmodule(text, testFunction, ""); +} + +function replaceSubmodule(text, testFunction, replacement) { + const modules = getSubmodules(text, testFunction); + if (modules.length !== 1) { + return text; + // TODO: Enable this check when merge to `next` branch + // throw Object.assign( + // new Error( + // `Expect exactly one submodule to be found, got ${modules.length} submodules.` + // ), + // { modules } + // ); + } + + const [{ start, end, before, after }] = modules; + if (replacement) { + replacement = before + "\n" + replacement + "\n" + after; + } + + return text.slice(0, start) + replacement + text.slice(end); +} + +function removeMultipleSubmodules(text, testFunction) { + let modules = getSubmodules(text, testFunction); + + if (modules.length < 2) { + throw new Error("Expect more than one submodules to be found"); + } + + for (; modules.length > 0; modules = getSubmodules(text, testFunction)) { + const [{ start, end }] = modules; + text = text.slice(0, start) + text.slice(end); + } + + return text; +} + +function replaceAlignedCode(text, { start, end, replacement = "" }) { + const regexp = new RegExp( + [ + "(?<=\n)", + "(?\\s*)", + escapeStringRegexp(start), + ".*?", + "(?<=\n)", + "\\k", + escapeStringRegexp(end), + "(?=\n)", + ].join(""), + "gsu" + ); + + return text.replaceAll(regexp, replacement); +} + +function modifyTypescriptModule(text) { + // Code after `globalThis` shim are useless + const positionOfGlobalThisShim = text.indexOf( + "// We polyfill `globalThis` here so re can reliably patch the global scope" + ); + if (positionOfGlobalThisShim === -1) { + throw new Error("Unexpected source."); + } + text = text.slice(0, positionOfGlobalThisShim) + "module.exports = ts;"; + + // File system + text = removeSubmodule(text, (text) => + text.includes("ts.generateDjb2Hash = generateDjb2Hash;") + ); + + // Language service + text = removeSubmodule(text, (text) => + text.includes("ts.TypeScriptServicesFactory = TypeScriptServicesFactory;") + ); + + // `ts.Version` + text = removeSubmodule(text, (text) => + text.includes("ts.Version = Version;") + ); + + // `ts.transform` + text = removeSubmodule(text, (text) => + text.includes("ts.transform = transform;") + ); + + // `ts.BreakpointResolver` + text = removeSubmodule(text, (text) => + text.trimStart().startsWith("var BreakpointResolver;") + ); + + // `ts.textChanges` + text = removeSubmodule(text, (text) => + text.trimStart().startsWith("var textChanges;") + ); + + // `ts.preProcessFile` + text = removeSubmodule(text, (text) => + text.includes("ts.preProcessFile = preProcessFile;") + ); + + // `ts.Rename` + text = removeSubmodule(text, (text) => + text.trimStart().startsWith("var Rename;") + ); + + // `ts.SmartSelectionRange` + text = removeSubmodule(text, (text) => + text.trimStart().startsWith("var SmartSelectionRange;") + ); + + // `ts.SignatureHelp` + text = removeSubmodule(text, (text) => + text.trimStart().startsWith("var SignatureHelp;") + ); + + // `ts.InlayHints` + text = removeSubmodule(text, (text) => + text.trimStart().startsWith("var InlayHints;") + ); + + // Sourcemap + text = removeSubmodule(text, (text) => + text.includes("ts.getSourceMapper = getSourceMapper;") + ); + text = removeSubmodule(text, (text) => + text.includes("ts.createSourceMapGenerator = createSourceMapGenerator;") + ); + + // Suggestion + text = removeSubmodule(text, (text) => + text.includes( + "ts.computeSuggestionDiagnostics = computeSuggestionDiagnostics;" + ) + ); + + // Tracing + text = removeSubmodule(text, (text) => + text.includes("ts.startTracing = tracingEnabled.startTracing;") + ); + + // Diagnostics + text = removeSubmodule(text, (text) => + text.includes("ts.createProgramHost = createProgramHost;") + ); + + // `ts.transformTypeScript` + text = removeSubmodule(text, (text) => + text.includes("ts.transformTypeScript = transformTypeScript;") + ); + + // `ts.createRuntimeTypeSerializer` + text = removeSubmodule(text, (text) => + text.includes( + "ts.createRuntimeTypeSerializer = createRuntimeTypeSerializer;" + ) + ); + + // Transform + // `ts.transformLegacyDecorators` + text = removeSubmodule(text, (text) => + text.includes("ts.transformLegacyDecorators = transformLegacyDecorators;") + ); + // `ts.transformES5` + text = removeSubmodule(text, (text) => + text.includes("ts.transformES5 = transformES5;") + ); + // `ts.transformES2015` + text = removeSubmodule(text, (text) => + text.includes("ts.transformES2015 = transformES2015;") + ); + // `ts.transformES2016` + text = removeSubmodule(text, (text) => + text.includes("ts.transformES2016 = transformES2016;") + ); + // `ts.transformES2017` & `ts.createSuperAccessVariableStatement` + text = removeSubmodule( + text, + (text) => + text.includes("ts.transformES2017 = transformES2017;") && + text.includes( + "ts.createSuperAccessVariableStatement = createSuperAccessVariableStatement;" + ) + ); + // `ts.transformES2018` + text = removeSubmodule(text, (text) => + text.includes("ts.transformES2018 = transformES2018;") + ); + // `ts.transformES2019` + text = removeSubmodule(text, (text) => + text.includes("ts.transformES2019 = transformES2019;") + ); + // `ts.transformES2020` + text = removeSubmodule(text, (text) => + text.includes("ts.transformES2020 = transformES2020;") + ); + // `ts.transformES2021` + text = removeSubmodule(text, (text) => + text.includes("ts.transformES2021 = transformES2021;") + ); + // `ts.transformESNext` + text = removeSubmodule(text, (text) => + text.includes("ts.transformESNext = transformESNext;") + ); + // `ts.transformJsx` + text = removeSubmodule(text, (text) => + text.includes("ts.transformJsx = transformJsx;") + ); + // `ts.transformGenerators` + text = removeSubmodule(text, (text) => + text.includes("ts.transformGenerators = transformGenerators;") + ); + // `ts.transformModule` + text = removeSubmodule(text, (text) => + text.includes("ts.transformModule = transformModule;") + ); + // `ts.transformSystemModule` + text = removeSubmodule(text, (text) => + text.includes("ts.transformSystemModule = transformSystemModule;") + ); + // `ts.transformECMAScriptModule` + text = removeSubmodule(text, (text) => + text.includes("ts.transformECMAScriptModule = transformECMAScriptModule;") + ); + // `ts.transformNodeModule` + text = removeSubmodule(text, (text) => + text.includes("ts.transformNodeModule = transformNodeModule;") + ); + // `ts.transformClassFields` + text = removeSubmodule(text, (text) => + text.includes("ts.transformClassFields = transformClassFields;") + ); + // `ts.transformDeclarations` + text = removeSubmodule(text, (text) => + text.includes("ts.transformDeclarations = transformDeclarations;") + ); + // `ts.transformNodes` and more + text = removeSubmodule(text, (text) => + text.includes("ts.transformNodes = transformNodes;") + ); + + // `ts.server` + text = removeSubmodule(text, (text) => text.includes("(ts.server = {})")); + + // `ts.JsTyping` + text = removeSubmodule(text, (text) => text.includes("(ts.JsTyping = {})")); + + // `ts.ClassificationType` + text = removeSubmodule(text, (text) => + text.includes("(ts.ClassificationType = {})") + ); + + // Build + text = removeSubmodule(text, (text) => + text.includes("ts.createSolutionBuilder = createSolutionBuilder;") + ); + text = removeSubmodule(text, (text) => + text.includes("ts.parseBuildCommand = parseBuildCommand;") + ); + text = removeSubmodule(text, (text) => + text.includes("ts.createBuilderProgram = createBuilderProgram;") + ); + text = removeSubmodule(text, (text) => + text.includes( + "ts.createSemanticDiagnosticsBuilderProgram = createSemanticDiagnosticsBuilderProgram;" + ) + ); + text = removeSubmodule(text, (text) => + text.includes("ts.createResolutionCache = createResolutionCache;") + ); + text = removeSubmodule(text, (text) => + text.includes("ts.createWatchCompilerHost = createWatchCompilerHost;") + ); + text = removeSubmodule(text, (text) => + text.includes( + "ts.resolveConfigFileProjectName = resolveConfigFileProjectName;" + ) + ); + text = removeSubmodule(text, (text) => + text.includes("ts.getBuildInfo = getBuildInfo;") + ); + + // Compile + text = removeSubmodule(text, (text) => + text.includes("ts.createCompilerHost = createCompilerHost;") + ); + text = removeSubmodule(text, (text) => + text.includes("(ts.BuilderState = {})") + ); + text = removeSubmodule(text, (text) => + text.includes("ts.transpile = transpile;") + ); + + // Watch + text = removeSubmodule(text, (text) => + text.includes("ts.getWatchFactory = getWatchFactory;") + ); + + // `ts.canProduceDiagnostics`, `ts.createGetSymbolAccessibilityDiagnosticForNode`, and `ts.createGetSymbolAccessibilityDiagnosticForNode` + text = removeSubmodule(text, (text) => + text.includes("ts.canProduceDiagnostics = canProduceDiagnostics;") + ); + + // `ts.moduleSpecifiers` + text = removeSubmodule(text, (text) => + text.includes("(ts.moduleSpecifiers = {})") + ); + + // `ts.trace` + text = removeSubmodule(text, (text) => text.includes("ts.trace = trace;")); + + // `ts.createTypeChecker` + text = removeSubmodule(text, (text) => + text.includes("ts.createTypeChecker = createTypeChecker;") + ); + + // `ts.DocumentHighlights` + text = removeSubmodule(text, (text) => + text.includes("(ts.DocumentHighlights = {})") + ); + + // `ts.createDocumentRegistry` + text = removeSubmodule(text, (text) => + text.includes("ts.createDocumentRegistry = createDocumentRegistry;") + ); + + // `ts.CallHierarchy` + text = removeSubmodule(text, (text) => + text.includes("(ts.CallHierarchy = {})") + ); + + // `ts.flattenDestructuringAssignment` and `ts.flattenDestructuringBinding` + text = removeSubmodule( + text, + (text) => + text.includes( + "ts.flattenDestructuringAssignment = flattenDestructuringAssignment" + ) && + text.includes( + "ts.flattenDestructuringBinding = flattenDestructuringBinding" + ) + ); + + // `ts.processTaggedTemplateExpression` + text = removeSubmodule(text, (text) => + text.includes( + "ts.processTaggedTemplateExpression = processTaggedTemplateExpression" + ) + ); + + // Editor + text = removeSubmodule(text, (text) => + text.includes("ts.getEditsForFileRename = getEditsForFileRename;") + ); + text = removeSubmodule(text, (text) => + text.includes("(ts.GoToDefinition = {})") + ); + text = removeSubmodule(text, (text) => text.includes("(ts.JsDoc = {})")); + text = removeSubmodule(text, (text) => text.includes("(ts.NavigateTo = {})")); + text = removeSubmodule(text, (text) => + text.includes("(ts.NavigationBar = {})") + ); + text = removeSubmodule(text, (text) => + text.includes("(ts.OrganizeImports = {})") + ); + text = removeSubmodule(text, (text) => + text.includes("(ts.OutliningElementsCollector = {})") + ); + text = removeSubmodule(text, (text) => + text.includes("ts.createPatternMatcher = createPatternMatcher;") + ); + text = removeSubmodule(text, (text) => + text.includes("(ts.SymbolDisplay = {})") + ); + + // `ts.refactor` (multiple) + text = removeMultipleSubmodules(text, (text) => + text.trimStart().startsWith("var refactor;") + ); + + // `ts.codefix` (multiple) + text = removeMultipleSubmodules(text, (text) => + text.trimStart().startsWith("var codefix;") + ); + + // `ts.formatting` (multiple) + text = removeMultipleSubmodules(text, (text) => + text.trimStart().startsWith("var formatting;") + ); + + // `ts.Completions` (multiple) + text = removeMultipleSubmodules(text, (text) => + text.trimStart().startsWith("var Completions;") + ); + + // `ts.FindAllReferences` (multiple) + text = removeMultipleSubmodules(text, (text) => + text.trimStart().startsWith("var FindAllReferences;") + ); + + // Performance + text = replaceSubmodule( + text, + (text) => + text.includes( + "ts.tryGetNativePerformanceHooks = tryGetNativePerformanceHooks;" + ), + outdent` + ts.tryGetNativePerformanceHooks = () => {}; + ts.timestamp = Date.now; + ` + ); + + for (const [find, replacement] of Object.entries({ + // yarn pnp + "process.versions.pnp": "undefined", + + // Dynamic `require()`s + "ts.sys && ts.sys.require": "false", + "require(etwModulePath)": "undefined", + })) { + text = text.replaceAll(find, replacement); + } + + text = replaceAlignedCode(text, { + start: "var debugObjectHost = (function () {", + end: "})();", + }); + + return text; +} + +// Save modified code to `{PROJECT_ROOT}/.tmp/modified-typescript.js` for debug +const saveOutputToDisk = (process) => (text) => { + const result = process(text); + writeFile(path.join(PROJECT_ROOT, ".tmp/modified-typescript.js"), result); + return result; +}; + +export default saveOutputToDisk(modifyTypescriptModule);