mirror of
https://github.com/keymanapp/keyman.git
synced 2026-08-12 11:55:32 +00:00
refactor(web): refactored unused helper detection aspect
This commit is contained in:
parent
b66c173fc2
commit
8deddfbab2
1 changed files with 122 additions and 104 deletions
|
|
@ -29,103 +29,87 @@ if(process.argv.length > 2) {
|
|||
}
|
||||
}
|
||||
|
||||
// Component #1: Detect all `tslib` helpers we actually want to use.
|
||||
const tslibHelperNames = [
|
||||
"__extends",
|
||||
"__assign",
|
||||
"__rest",
|
||||
"__decorate",
|
||||
"__param",
|
||||
"__metadata",
|
||||
"__awaiter",
|
||||
"__generator",
|
||||
"__exportStar",
|
||||
"__createBinding",
|
||||
"__values",
|
||||
"__read",
|
||||
"__spread",
|
||||
"__spreadArrays",
|
||||
"__await",
|
||||
"__asyncGenerator",
|
||||
"__asyncDelegator",
|
||||
"__asyncValues",
|
||||
"__makeTemplateObject",
|
||||
"__importStar",
|
||||
"__importDefault",
|
||||
"__classPrivateFieldGet",
|
||||
"__classPrivateFieldSet"
|
||||
];
|
||||
async function determineNeededDowncompileHelpers(config, log) {
|
||||
// Component #1: Detect all `tslib` helpers we actually want to use.
|
||||
const tslibHelperNames = [
|
||||
"__extends",
|
||||
"__assign",
|
||||
"__rest",
|
||||
"__decorate",
|
||||
"__param",
|
||||
"__metadata",
|
||||
"__awaiter",
|
||||
"__generator",
|
||||
"__exportStar",
|
||||
"__createBinding",
|
||||
"__values",
|
||||
"__read",
|
||||
"__spread",
|
||||
"__spreadArrays",
|
||||
"__await",
|
||||
"__asyncGenerator",
|
||||
"__asyncDelegator",
|
||||
"__asyncValues",
|
||||
"__makeTemplateObject",
|
||||
"__importStar",
|
||||
"__importDefault",
|
||||
"__classPrivateFieldGet",
|
||||
"__classPrivateFieldSet"
|
||||
];
|
||||
|
||||
const detectedHelpers = [];
|
||||
const detectedHelpers = [];
|
||||
|
||||
let tslibHelperDetectionPlugin = {
|
||||
name: 'tslib helper use detection',
|
||||
setup(build) {
|
||||
build.onLoad({filter: /\.js$/}, async (args) => {
|
||||
//
|
||||
if(/tslib.js$/.test(args.path)) {
|
||||
return;
|
||||
}
|
||||
|
||||
let source = await fs.promises.readFile(args.path, 'utf8');
|
||||
|
||||
for(let helper of tslibHelperNames) {
|
||||
if(source.indexOf(helper) > -1 && !detectedHelpers.find((entry) => entry == helper)) {
|
||||
detectedHelpers.push(helper);
|
||||
let tslibHelperDetectionPlugin = {
|
||||
name: 'tslib helper use detection',
|
||||
setup(build) {
|
||||
build.onLoad({filter: /\.js$/}, async (args) => {
|
||||
//
|
||||
if(/tslib.js$/.test(args.path)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
return;
|
||||
});
|
||||
}
|
||||
}
|
||||
//
|
||||
let source = await fs.promises.readFile(args.path, 'utf8');
|
||||
|
||||
// Component #2: when we've actually determined which ones are safe to remove, this plugin
|
||||
// can remove their code.
|
||||
let tslibForcedTreeshakingPlugin = {
|
||||
name: 'tslib helpers - forced treeshaking',
|
||||
setup(build) {
|
||||
build.onLoad({filter: /tslib.js$/}, async (args) => {
|
||||
let source = await fs.promises.readFile(args.path, 'utf8');
|
||||
|
||||
// TODO: transformations to eliminate the stuff we don't want.
|
||||
for(let unusedHelper of unusedHelpers) {
|
||||
// Removes the 'exporter' line used to actually export it from the tslib source.
|
||||
source = source.replace(`exporter\(\"${unusedHelper}\", ${unusedHelper}\);`, '');
|
||||
|
||||
// Removes the actual helper function definition - obviously, the biggest filesize savings to be had here.
|
||||
let definitionStart = source.indexOf(`${unusedHelper} = function`);
|
||||
if(definitionStart == -1) {
|
||||
console.error("tslib has likely been updated recently; could not erase definition for helper " + unusedHelper);
|
||||
continue;
|
||||
}
|
||||
let scopeDepth = 0;
|
||||
let i = definitionStart;
|
||||
let char = source.charAt(i);
|
||||
while(char != '}' || --scopeDepth != 0) {
|
||||
if(char == '{') {
|
||||
scopeDepth++;
|
||||
for(let helper of tslibHelperNames) {
|
||||
if(source.indexOf(helper) > -1 && !detectedHelpers.find((entry) => entry == helper)) {
|
||||
detectedHelpers.push(helper);
|
||||
}
|
||||
i++;
|
||||
char = source.charAt(i);
|
||||
}
|
||||
i++; // we want to erase it, too.
|
||||
|
||||
source = source.replace(source.substring(definitionStart, i), '');
|
||||
|
||||
// The top-level var declaration is auto-removed by esbuild when no references to it remain.
|
||||
|
||||
}
|
||||
|
||||
return {
|
||||
contents: source,
|
||||
loader: 'js'
|
||||
};
|
||||
});
|
||||
return;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// tslib tree-shake phase 1 - detecting which helpers are safe to remove.
|
||||
await esbuild.build({
|
||||
...config,
|
||||
plugins: [ tslibHelperDetectionPlugin, ...config.plugins ],
|
||||
write: false
|
||||
});
|
||||
|
||||
// At this point, we can determine what's unused.
|
||||
detectedHelpers.sort();
|
||||
|
||||
const unusedHelpers = [];
|
||||
tslibHelperNames.forEach((entry) => {
|
||||
if(!detectedHelpers.find((detected) => detected == entry)) {
|
||||
unusedHelpers.push(entry);
|
||||
}
|
||||
});
|
||||
|
||||
if(log) {
|
||||
// Logs on the tree-shaking decisions
|
||||
console.log("Detected helpers from tslib: ");
|
||||
console.log();
|
||||
|
||||
console.log();
|
||||
console.log("Unused helpers: ");
|
||||
console.log(unusedHelpers);
|
||||
}
|
||||
|
||||
return unusedHelpers;
|
||||
}
|
||||
//
|
||||
|
||||
/*
|
||||
* Refer to https://github.com/microsoft/TypeScript/issues/13721#issuecomment-307259227 -
|
||||
|
|
@ -160,32 +144,66 @@ const commonConfig = {
|
|||
'index': '../../../build/app/browser/obj/debug-main.js',
|
||||
},
|
||||
outfile: '../../../build/app/browser/debug/keymanweb.js',
|
||||
plugins: [ tslibForcedTreeshakingPlugin, es5ClassAnnotationAsPurePlugin ],
|
||||
plugins: [ es5ClassAnnotationAsPurePlugin ],
|
||||
target: "es5",
|
||||
treeShaking: true,
|
||||
tsconfig: './tsconfig.json'
|
||||
};
|
||||
|
||||
// tslib tree-shake phase 1 - detecting which helpers are safe to remove.
|
||||
await esbuild.build({
|
||||
...commonConfig,
|
||||
plugins: [ tslibHelperDetectionPlugin ],
|
||||
write: false
|
||||
});
|
||||
const unusedHelpers = await determineNeededDowncompileHelpers(commonConfig);
|
||||
|
||||
// Logs on the tree-shaking decisions
|
||||
console.log("Detected helpers from tslib: ");
|
||||
console.log(detectedHelpers.sort());
|
||||
// Component #2: when we've actually determined which ones are safe to remove, this plugin
|
||||
// can remove their code.
|
||||
let tslibForcedTreeshakingPlugin = {
|
||||
name: 'tslib helpers - forced treeshaking',
|
||||
setup(build) {
|
||||
build.onLoad({filter: /tslib.js$/}, async (args) => {
|
||||
let source = await fs.promises.readFile(args.path, 'utf8');
|
||||
|
||||
console.log();
|
||||
console.log("Unused helpers: ");
|
||||
const unusedHelpers = [];
|
||||
tslibHelperNames.forEach((entry) => {
|
||||
if(!detectedHelpers.find((detected) => detected == entry)) {
|
||||
unusedHelpers.push(entry);
|
||||
// TODO: transformations to eliminate the stuff we don't want.
|
||||
for(let unusedHelper of unusedHelpers) {
|
||||
// Removes the 'exporter' line used to actually export it from the tslib source.
|
||||
source = source.replace(`exporter\(\"${unusedHelper}\", ${unusedHelper}\);`, '');
|
||||
|
||||
// Removes the actual helper function definition - obviously, the biggest filesize savings to be had here.
|
||||
let definitionStart = source.indexOf(`${unusedHelper} = function`);
|
||||
if(definitionStart == -1) {
|
||||
if(unusedHelper == '__createBinding') {
|
||||
console.warn("Currently unable to force-treeshake the __createBinding tslib helper");
|
||||
} else {
|
||||
console.error("tslib has likely been updated recently; could not force-treeshake tslib helper " + unusedHelper);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
let scopeDepth = 0;
|
||||
let i = definitionStart;
|
||||
let char = source.charAt(i);
|
||||
while(char != '}' || --scopeDepth != 0) {
|
||||
if(char == '{') {
|
||||
scopeDepth++;
|
||||
}
|
||||
i++;
|
||||
char = source.charAt(i);
|
||||
}
|
||||
i++; // we want to erase it, too.
|
||||
|
||||
source = source.replace(source.substring(definitionStart, i), '');
|
||||
|
||||
// The top-level var declaration is auto-removed by esbuild when no references to it remain.
|
||||
|
||||
}
|
||||
|
||||
return {
|
||||
contents: source,
|
||||
loader: 'js'
|
||||
};
|
||||
});
|
||||
}
|
||||
});
|
||||
console.log(unusedHelpers);
|
||||
}
|
||||
|
||||
commonConfig.plugins = [tslibForcedTreeshakingPlugin, ...commonConfig.plugins];
|
||||
|
||||
// From here, the builds are configured to do phase 2 from the preprocessing data done 'til now.
|
||||
await esbuild.build(commonConfig);
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue