Improve typescript bundle speed (#13433)

This commit is contained in:
fisker Cheung authored and GitHub committed 2022-09-22 19:11:19 +08:00
1 parent b0d9387b95
commit 8764a3d378
3 files changed
+265 -244

No files matched your search

+1
View File
@@ -128,6 +128,7 @@
"jest-snapshot-serializer-ansi": "1.0.0",
"jest-snapshot-serializer-raw": "1.2.0",
"jest-watch-typeahead": "1.0.0",
"magic-string": "0.26.4",
"node-actionlint": "1.2.2",
"node-fetch": "3.2.10",
"npm-run-all": "4.1.5",
+257 -244
View File
@@ -1,6 +1,7 @@
import path from "node:path";
import escapeStringRegexp from "escape-string-regexp";
import { outdent } from "outdent";
import MagicString from "magic-string";
import { writeFile, PROJECT_ROOT } from "../utils/index.mjs";
/*
@@ -17,7 +18,7 @@ var ts;
const SUBMODULE_START = escapeStringRegexp("var ts;\n(function (ts) {");
const SUBMODULE_END = escapeStringRegexp("})(ts || (ts = {}));");
function getSubmodules(text, testFunction) {
function getSubmodules(text) {
const regexp = new RegExp(
[
"(?<=\n)",
@@ -31,74 +32,108 @@ function getSubmodules(text, testFunction) {
"gsu"
);
return [...text.matchAll(regexp)]
.filter((match) => testFunction(match.groups.text))
.map((match) => ({
start: match.index,
end: match.index + match[0].length,
...match.groups,
}));
return [...text.matchAll(regexp)].map((match) => ({
start: match.index,
end: match.index + match[0].length,
...match.groups,
}));
}
function removeSubmodule(text, testFunction) {
return replaceSubmodule(text, testFunction, "");
}
class TypeScriptModuleSource {
#source;
#modules;
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 }
// );
constructor(text) {
this.#source = new MagicString(text);
this.#modules = getSubmodules(text);
}
const [{ start, end, before, after }] = modules;
if (replacement) {
replacement = before + "\n" + replacement + "\n" + after;
removeSubmodule(testFunction) {
return this.replaceSubmodule(testFunction, "");
}
return text.slice(0, start) + replacement + text.slice(end);
}
replaceSubmodule(testFunction, replacement) {
const modules = this.#modules.filter(({ text }) => testFunction(text));
if (modules.length !== 1) {
return this;
function removeMultipleSubmodules(text, testFunction) {
let modules = getSubmodules(text, testFunction);
// 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 }
// );
}
if (modules.length < 2) {
throw new Error("Expect more than one submodules to be found");
const [{ start, end, before, after }] = modules;
if (!replacement) {
this.#source.remove(start, end);
} else {
this.#source.overwrite(
start,
end,
before + "\n" + replacement + "\n" + after
);
}
return this;
}
for (; modules.length > 0; modules = getSubmodules(text, testFunction)) {
const [{ start, end }] = modules;
text = text.slice(0, start) + text.slice(end);
removeMultipleSubmodules(testFunction) {
const modules = this.#modules.filter(({ text }) => testFunction(text));
if (modules.length < 2) {
throw new Error("Expect more than one submodules to be found");
}
for (const { start, end } of modules) {
this.#source.remove(start, end);
}
return this;
}
return text;
}
replaceAlignedCode({ start, end, replacement = "" }) {
const regexp = new RegExp(
[
"(?<=\n)",
"(?<indentString>\\s*)",
escapeStringRegexp(start),
".*?",
"(?<=\n)",
"\\k<indentString>",
escapeStringRegexp(end),
"(?=\n)",
].join(""),
"gsu"
);
function replaceAlignedCode(text, { start, end, replacement = "" }) {
const regexp = new RegExp(
[
"(?<=\n)",
"(?<indentString>\\s*)",
escapeStringRegexp(start),
".*?",
"(?<=\n)",
"\\k<indentString>",
escapeStringRegexp(end),
"(?=\n)",
].join(""),
"gsu"
);
this.#source.replaceAll(regexp, replacement);
return this;
}
return text.replaceAll(regexp, replacement);
remove(...args) {
this.#source.remove(...args);
return this;
}
append(...args) {
this.#source.append(...args);
return this;
}
replaceAll(...args) {
this.#source.replaceAll(...args);
return this;
}
toString() {
return this.#source.toString();
}
}
function modifyTypescriptModule(text) {
const source = new TypeScriptModuleSource(text);
// Code after `globalThis` shim are useless
const positionOfGlobalThisShim = text.indexOf(
"// We polyfill `globalThis` here so re can reliably patch the global scope"
@@ -106,276 +141,262 @@ function modifyTypescriptModule(text) {
if (positionOfGlobalThisShim === -1) {
throw new Error("Unexpected source.");
}
text = text.slice(0, positionOfGlobalThisShim) + "module.exports = ts;";
source.remove(positionOfGlobalThisShim, text.length);
source.append("module.exports = ts;");
// File system
text = removeSubmodule(text, (text) =>
source.removeSubmodule((text) =>
text.includes("ts.generateDjb2Hash = generateDjb2Hash;")
);
// Language service
text = removeSubmodule(text, (text) =>
source.removeSubmodule((text) =>
text.includes("ts.TypeScriptServicesFactory = TypeScriptServicesFactory;")
);
// `ts.Version`
text = removeSubmodule(text, (text) =>
text.includes("ts.Version = Version;")
);
source.removeSubmodule((text) => text.includes("ts.Version = Version;"));
// `ts.transform`
text = removeSubmodule(text, (text) =>
text.includes("ts.transform = transform;")
);
source.removeSubmodule((text) => text.includes("ts.transform = transform;"));
// `ts.BreakpointResolver`
text = removeSubmodule(text, (text) =>
source.removeSubmodule((text) =>
text.trimStart().startsWith("var BreakpointResolver;")
);
// `ts.textChanges`
text = removeSubmodule(text, (text) =>
source.removeSubmodule((text) =>
text.trimStart().startsWith("var textChanges;")
);
// `ts.preProcessFile`
text = removeSubmodule(text, (text) =>
source.removeSubmodule((text) =>
text.includes("ts.preProcessFile = preProcessFile;")
);
// `ts.Rename`
text = removeSubmodule(text, (text) =>
text.trimStart().startsWith("var Rename;")
);
source.removeSubmodule((text) => text.trimStart().startsWith("var Rename;"));
// `ts.SmartSelectionRange`
text = removeSubmodule(text, (text) =>
source.removeSubmodule((text) =>
text.trimStart().startsWith("var SmartSelectionRange;")
);
// `ts.SignatureHelp`
text = removeSubmodule(text, (text) =>
source.removeSubmodule((text) =>
text.trimStart().startsWith("var SignatureHelp;")
);
// `ts.InlayHints`
text = removeSubmodule(text, (text) =>
source.removeSubmodule((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;")
);
source
.removeSubmodule((text) =>
text.includes("ts.getSourceMapper = getSourceMapper;")
)
.removeSubmodule((text) =>
text.includes("ts.createSourceMapGenerator = createSourceMapGenerator;")
);
// Suggestion
text = removeSubmodule(text, (text) =>
source.removeSubmodule((text) =>
text.includes(
"ts.computeSuggestionDiagnostics = computeSuggestionDiagnostics;"
)
);
// Tracing
text = removeSubmodule(text, (text) =>
source.removeSubmodule((text) =>
text.includes("ts.startTracing = tracingEnabled.startTracing;")
);
// Diagnostics
text = removeSubmodule(text, (text) =>
source.removeSubmodule((text) =>
text.includes("ts.createProgramHost = createProgramHost;")
);
// `ts.transformTypeScript`
text = removeSubmodule(text, (text) =>
source.removeSubmodule((text) =>
text.includes("ts.transformTypeScript = transformTypeScript;")
);
// `ts.createRuntimeTypeSerializer`
text = removeSubmodule(text, (text) =>
source.removeSubmodule((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;")
);
source
// `ts.transformLegacyDecorators`
.removeSubmodule((text) =>
text.includes("ts.transformLegacyDecorators = transformLegacyDecorators;")
)
// `ts.transformES5`
.removeSubmodule((text) => text.includes("ts.transformES5 = transformES5;"))
// `ts.transformES2015`
.removeSubmodule((text) =>
text.includes("ts.transformES2015 = transformES2015;")
)
// `ts.transformES2016`
.removeSubmodule((text) =>
text.includes("ts.transformES2016 = transformES2016;")
)
// `ts.transformES2017` & `ts.createSuperAccessVariableStatement`
.removeSubmodule(
(text) =>
text.includes("ts.transformES2017 = transformES2017;") &&
text.includes(
"ts.createSuperAccessVariableStatement = createSuperAccessVariableStatement;"
)
)
// `ts.transformES2018`
.removeSubmodule((text) =>
text.includes("ts.transformES2018 = transformES2018;")
)
// `ts.transformES2019`
.removeSubmodule((text) =>
text.includes("ts.transformES2019 = transformES2019;")
)
// `ts.transformES2020`
.removeSubmodule((text) =>
text.includes("ts.transformES2020 = transformES2020;")
)
// `ts.transformES2021`
.removeSubmodule((text) =>
text.includes("ts.transformES2021 = transformES2021;")
)
// `ts.transformESNext`
.removeSubmodule((text) =>
text.includes("ts.transformESNext = transformESNext;")
)
// `ts.transformJsx`
.removeSubmodule((text) => text.includes("ts.transformJsx = transformJsx;"))
// `ts.transformGenerators`
.removeSubmodule((text) =>
text.includes("ts.transformGenerators = transformGenerators;")
)
// `ts.transformModule`
.removeSubmodule((text) =>
text.includes("ts.transformModule = transformModule;")
)
// `ts.transformSystemModule`
.removeSubmodule((text) =>
text.includes("ts.transformSystemModule = transformSystemModule;")
)
// `ts.transformECMAScriptModule`
.removeSubmodule((text) =>
text.includes("ts.transformECMAScriptModule = transformECMAScriptModule;")
)
// `ts.transformNodeModule`
.removeSubmodule((text) =>
text.includes("ts.transformNodeModule = transformNodeModule;")
)
// `ts.transformClassFields`
.removeSubmodule((text) =>
text.includes("ts.transformClassFields = transformClassFields;")
)
// `ts.transformDeclarations`
.removeSubmodule((text) =>
text.includes("ts.transformDeclarations = transformDeclarations;")
);
// `ts.transformNodes` and more
text = removeSubmodule(text, (text) =>
source.removeSubmodule((text) =>
text.includes("ts.transformNodes = transformNodes;")
);
// `ts.server`
text = removeSubmodule(text, (text) => text.includes("(ts.server = {})"));
source.removeSubmodule((text) => text.includes("(ts.server = {})"));
// `ts.JsTyping`
text = removeSubmodule(text, (text) => text.includes("(ts.JsTyping = {})"));
source.removeSubmodule((text) => text.includes("(ts.JsTyping = {})"));
// `ts.ClassificationType`
text = removeSubmodule(text, (text) =>
source.removeSubmodule((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;"
source
.removeSubmodule((text) =>
text.includes("ts.createSolutionBuilder = createSolutionBuilder;")
)
);
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;"
.removeSubmodule((text) =>
text.includes("ts.parseBuildCommand = parseBuildCommand;")
)
);
text = removeSubmodule(text, (text) =>
text.includes("ts.getBuildInfo = getBuildInfo;")
);
.removeSubmodule((text) =>
text.includes("ts.createBuilderProgram = createBuilderProgram;")
)
.removeSubmodule((text) =>
text.includes(
"ts.createSemanticDiagnosticsBuilderProgram = createSemanticDiagnosticsBuilderProgram;"
)
)
.removeSubmodule((text) =>
text.includes("ts.createResolutionCache = createResolutionCache;")
)
.removeSubmodule((text) =>
text.includes("ts.createWatchCompilerHost = createWatchCompilerHost;")
)
.removeSubmodule((text) =>
text.includes(
"ts.resolveConfigFileProjectName = resolveConfigFileProjectName;"
)
)
.removeSubmodule((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;")
);
source
.removeSubmodule((text) =>
text.includes("ts.createCompilerHost = createCompilerHost;")
)
.removeSubmodule((text) => text.includes("(ts.BuilderState = {})"))
.removeSubmodule((text) => text.includes("ts.transpile = transpile;"));
// Watch
text = removeSubmodule(text, (text) =>
source.removeSubmodule((text) =>
text.includes("ts.getWatchFactory = getWatchFactory;")
);
// `ts.canProduceDiagnostics`, `ts.createGetSymbolAccessibilityDiagnosticForNode`, and `ts.createGetSymbolAccessibilityDiagnosticForNode`
text = removeSubmodule(text, (text) =>
source.removeSubmodule((text) =>
text.includes("ts.canProduceDiagnostics = canProduceDiagnostics;")
);
// `ts.moduleSpecifiers`
text = removeSubmodule(text, (text) =>
text.includes("(ts.moduleSpecifiers = {})")
);
source.removeSubmodule((text) => text.includes("(ts.moduleSpecifiers = {})"));
// `ts.trace`
text = removeSubmodule(text, (text) => text.includes("ts.trace = trace;"));
source.removeSubmodule((text) => text.includes("ts.trace = trace;"));
// `ts.createTypeChecker`
text = removeSubmodule(text, (text) =>
source.removeSubmodule((text) =>
text.includes("ts.createTypeChecker = createTypeChecker;")
);
// `ts.DocumentHighlights`
text = removeSubmodule(text, (text) =>
source.removeSubmodule((text) =>
text.includes("(ts.DocumentHighlights = {})")
);
// `ts.createDocumentRegistry`
text = removeSubmodule(text, (text) =>
source.removeSubmodule((text) =>
text.includes("ts.createDocumentRegistry = createDocumentRegistry;")
);
// `ts.CallHierarchy`
text = removeSubmodule(text, (text) =>
text.includes("(ts.CallHierarchy = {})")
);
source.removeSubmodule((text) => text.includes("(ts.CallHierarchy = {})"));
// `ts.flattenDestructuringAssignment` and `ts.flattenDestructuringBinding`
text = removeSubmodule(
text,
source.removeSubmodule(
(text) =>
text.includes(
"ts.flattenDestructuringAssignment = flattenDestructuringAssignment"
@@ -386,65 +407,57 @@ function modifyTypescriptModule(text) {
);
// `ts.processTaggedTemplateExpression`
text = removeSubmodule(text, (text) =>
source.removeSubmodule((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 = {})")
);
source
.removeSubmodule((text) =>
text.includes("ts.getEditsForFileRename = getEditsForFileRename;")
)
.removeSubmodule((text) => text.includes("(ts.GoToDefinition = {})"))
.removeSubmodule((text) => text.includes("(ts.JsDoc = {})"))
.removeSubmodule((text) => text.includes("(ts.NavigateTo = {})"))
.removeSubmodule((text) => text.includes("(ts.NavigationBar = {})"))
.removeSubmodule((text) => text.includes("(ts.OrganizeImports = {})"))
.removeSubmodule((text) =>
text.includes("(ts.OutliningElementsCollector = {})")
)
.removeSubmodule((text) =>
text.includes("ts.createPatternMatcher = createPatternMatcher;")
)
.removeSubmodule((text) => text.includes("(ts.SymbolDisplay = {})"));
// `ts.refactor` (multiple)
text = removeMultipleSubmodules(text, (text) =>
source.removeMultipleSubmodules((text) =>
text.trimStart().startsWith("var refactor;")
);
// `ts.codefix` (multiple)
text = removeMultipleSubmodules(text, (text) =>
source.removeMultipleSubmodules((text) =>
text.trimStart().startsWith("var codefix;")
);
// `ts.formatting` (multiple)
text = removeMultipleSubmodules(text, (text) =>
source.removeMultipleSubmodules((text) =>
text.trimStart().startsWith("var formatting;")
);
// `ts.Completions` (multiple)
text = removeMultipleSubmodules(text, (text) =>
source.removeMultipleSubmodules((text) =>
text.trimStart().startsWith("var Completions;")
);
// `ts.FindAllReferences` (multiple)
text = removeMultipleSubmodules(text, (text) =>
source.removeMultipleSubmodules((text) =>
text.trimStart().startsWith("var FindAllReferences;")
);
// Performance
text = replaceSubmodule(
text,
source.replaceSubmodule(
(text) =>
text.includes(
"ts.tryGetNativePerformanceHooks = tryGetNativePerformanceHooks;"
@@ -463,15 +476,15 @@ function modifyTypescriptModule(text) {
"ts.sys && ts.sys.require": "false",
"require(etwModulePath)": "undefined",
})) {
text = text.replaceAll(find, replacement);
source.replaceAll(find, replacement);
}
text = replaceAlignedCode(text, {
source.replaceAlignedCode({
start: "var debugObjectHost = (function () {",
end: "})();",
});
return text;
return source.toString();
}
// Save modified code to `{PROJECT_ROOT}/.tmp/modified-typescript.js` for debug
+7
View File
@@ -4885,6 +4885,13 @@ magic-string@0.26.1:
dependencies:
sourcemap-codec "^1.4.8"
magic-string@0.26.4:
version "0.26.4"
resolved "https://registry.yarnpkg.com/magic-string/-/magic-string-0.26.4.tgz#3d057d3d0234c3b179aa3f421b33fe5d8a4044a8"
integrity sha512-e5uXtVJ22aEpK9u1+eQf0fSxHeqwyV19K+uGnlROCxUhzwRip9tBsaMViK/0vC3viyPd5Gtucp3UmEp/Q2cPTQ==
dependencies:
sourcemap-codec "^1.4.8"
magic-string@^0.25.3:
version "0.25.7"
resolved "https://registry.yarnpkg.com/magic-string/-/magic-string-0.25.7.tgz#3f497d6fd34c669c6798dcb821f2ef31f5445051"