@lzwme/feps-webpack-plugin
Version:
This plug-in is used for function execution performance statistics. It calculates the execution time by injecting statistical code and finds slow functions.
131 lines (115 loc) • 4.05 kB
JavaScript
/*
* @Author: lzw
* @Date: 2021-12-13 18:45:53
* @LastEditors: lzw
* @LastEditTime: 2022-05-19 16:11:13
* @Description:
*/
// @ts-check
/** @typedef {import("estree").Program} Program */
/** @typedef {import("estree").Node} AnyNode */
/** @typedef {import("webpack/lib/javascript/JavascriptParser")} JavascriptParser */
/** @typedef {import("webpack-sources").ReplaceSource} ReplaceSource */
/** @typedef {import("webpack/lib/Dependency")} Dependency */
/** @typedef {import("webpack/lib/DependencyTemplate").DependencyTemplateContext} DependencyTemplateContext */
/**
* @typedef {{
* debug?: boolean;
* disabled?: boolean;
* timeLimit?: number;
* logger?: string; // 'console.warn'
* rootDir?: string;
* minFnCodeLength?: number;
* include?: (string | RegExp)[] | ((p: string) => boolean);
* exclude?: (string | RegExp)[] | ((p: string) => boolean);
* excludeNodeType?: AnyNode['type'][];
*}} FepsOptions
*/
const PLUGIN_NAME = 'FEPSPlugin';
exports.PLUGIN_NAME = PLUGIN_NAME;
function getMarkTimeCode(timeLimit = 50, logger = 'console.warn', filepath = '') {
return `\nconst markTime = (() => {
let lastLabel = null;
const skipMap = new Map();
const markTimeMap = new Map();
return (label) => {
try {
if (typeof ${logger} !== 'function') return;
} catch(e) {
if (typeof console) console.error(e);
return;
}
const skip = skipMap.get(label);
if (skip) return;
const start = markTimeMap.get(label);
if (start) {
markTimeMap.delete(label);
if (lastLabel === label) {
skipMap.set(label, true);
} else {
const cost = Date.now() - start;
if (cost > ${timeLimit}) {
${logger}('[Performance]', cost + 'ms', '${filepath}', new Error().stack.replace(/\\((\\S+?)\\)/g, '').replace(/at file\\S+/g, '').replace(/\\s+at\\s+/g, ' <- ').replace('Error <- ', '').trim());
}
}
lastLabel = label;
} else {
markTimeMap.set(label, Date.now());
}
}
})();\n`;
}
exports.getMarkTimeCode = getMarkTimeCode;
/**
* @param {JavascriptParser} parser
* @param {FepsOptions} options
* @param {(isMatch: boolean, ast: Program, relativePath: string) => void} callback
*/
function astHandler(parser, options, callback) {
parser.hooks.program.tap(PLUGIN_NAME, (ast, _comments) => {
if (!parser.state || !parser.state.module) return;
let isMatch = true;
/** @type string */
let relativePath = parser.state.module.resource.replace(options.rootDir, '');
if (process.platform === 'win32') relativePath = relativePath.replace(/\\/g, '/');
if (typeof options.exclude === 'function') {
isMatch = options.exclude(relativePath);
} else if (options.exclude.length) {
const isExMatch = options.exclude.some(d => {
if (typeof d === 'string') return relativePath.includes(d);
if (d instanceof RegExp) return d.test(relativePath);
});
isMatch = !isExMatch;
}
if (isMatch) {
if (typeof options.include === 'function') {
isMatch = options.include(relativePath);
} else if (options.include.length) {
isMatch = options.include.some(d => {
if (typeof d === 'string') return relativePath.includes(d);
if (d instanceof RegExp) return d.test(relativePath);
});
}
}
callback(isMatch, ast, relativePath);
});
}
exports.astHandler = astHandler;
exports.getDefaultOptions = () => {
/** @type {FepsOptions} */
const options = {
debug: !!process.env.DEBUG,
rootDir: process.cwd(),
timeLimit: 50,
minFnCodeLength: 300,
include: [/(jsx?|tsx?)$/],
exclude: ['node_modules'],
excludeNodeType: [],
logger: 'console.warn',
disabled: false,
};
return options;
};
exports.getColors = () => {
return require('console-log-colors').color;
}