Files
test-repo/scripts/build/modify-typescript-module.mjs
T

485 lines
14 KiB
JavaScript

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)",
`(?<before>${SUBMODULE_START})`,
"(?=\n)",
"(?<text>.*?)",
"(?<=\n)",
`(?<after>${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)",
"(?<indentString>\\s*)",
escapeStringRegexp(start),
".*?",
"(?<=\n)",
"\\k<indentString>",
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);