time-analytics-webpack-plugin
Advanced tools
Comparing version
@@ -7,2 +7,3 @@ import chalk, { Chalk } from 'chalk'; | ||
import { PACKAGE_NAME } from './const'; | ||
import { getLoaderName } from './loaderHelper'; | ||
import { assert, fail } from './utils'; | ||
@@ -39,3 +40,2 @@ | ||
loaderPath: string; | ||
loaderName: string; | ||
time: number; | ||
@@ -125,2 +125,3 @@ /** | ||
dangerTimeLimit: number; | ||
groupLoaderByPath: boolean; | ||
/** | ||
@@ -173,3 +174,3 @@ * TODO: should we remove this option? Feels like we should not collect the info at all. Do this by give loader options. | ||
const messages3 = outputLoaderInfos(this.loaderData, option); | ||
const content = [...messages1, ...messages2, ...messages3].join(EOL); | ||
const content = [headerText, ...messages1, ...messages2, ...messages3].join(EOL); | ||
if (option.filePath) { | ||
@@ -222,2 +223,6 @@ const outputFileAbsolutePath = resolve(option.filePath); | ||
const headerText = '┌── time-analytics-webpack-plugin'; | ||
const sectionStartPrefix = '├── '; | ||
const nextLinePrefix = '│ '; | ||
function outputMetaInfo(data: WebpackMetaEventInfo[], option: OutputOption) { | ||
@@ -233,3 +238,3 @@ // validate | ||
const compileTotalTime = compilerDoneEvents[0].time - compilerCompileEvents[0].time; | ||
messages.push(`Webpack compile takes ${prettyTime(compileTotalTime, option)}`); | ||
messages.push(`${nextLinePrefix}Webpack compile takes ${prettyTime(compileTotalTime, option)}`); | ||
return messages; | ||
@@ -243,3 +248,3 @@ } | ||
messages.push(`${chalk.blue(chalk.bold('Plugins'))}`); | ||
messages.push(`${sectionStartPrefix}${chalk.blue(chalk.bold('Plugins'))}`); | ||
let allPluginTime = 0; | ||
@@ -259,8 +264,7 @@ const nameGrouppedPlugin = groupBy(prop('pluginName'), data); | ||
allPluginTime += currentPluginTotalTime; | ||
messages.push(`Plugin ${chalk.bold(pluginName)} takes ${prettyTime(currentPluginTotalTime, option)}`); | ||
messages.push(`${nextLinePrefix}Plugin ${chalk.bold(pluginName)} takes ${prettyTime(currentPluginTotalTime, option)}`); | ||
}); | ||
messages.push(`All plugins take ${prettyTime(allPluginTime, option)}`); | ||
messages.push(`${nextLinePrefix}All plugins take ${prettyTime(allPluginTime, option)}`); | ||
return messages; | ||
} | ||
function outputLoaderInfos(data: LoaderEventInfo[], option: OutputOption) { | ||
@@ -271,8 +275,12 @@ assert(isArraySortBy(['time'], data), 'loader event info should be sorted by time.'); | ||
messages.push(`${chalk.blue(chalk.bold('Loaders'))}`); | ||
const loaderIdSet = new Set<string>(); | ||
let isDuplicatedLodaerIdOutputed = false; | ||
messages.push(`${sectionStartPrefix}${chalk.blue(chalk.bold('Loaders'))}`); | ||
let allLoaderTime = 0; | ||
const nameGrouppedLoader = groupBy(prop('loaderName'), data); | ||
Object.entries(nameGrouppedLoader).forEach(([loaderName, dataA]) => { | ||
if (option.ignoredLoaders.includes(loaderName)) { | ||
messages.push(`Loader ${chalk.bold(loaderName)} is ignored.`); | ||
const nameGrouppedLoader = groupBy(prop('loaderPath'), data); | ||
Object.entries(nameGrouppedLoader).forEach(([loaderPath, dataA]) => { | ||
const loaderName = getLoaderName(loaderPath); | ||
if (option.ignoredLoaders.includes(loaderPath)) { | ||
messages.push(`${nextLinePrefix}Loader ${chalk.bold(loaderPath)} is ignored.`); | ||
return; | ||
@@ -286,3 +294,3 @@ } | ||
&& dataB[1].eventType === LoaderEventType.end | ||
, `each laoder execution should be collected info for start and end, once and only once. But for ${loaderName}, there is an error, why?`); | ||
, `each laoder execution should be collected info for start and end, once and only once. But for ${loaderPath}, there is an error, why?`); | ||
const tapTime = dataB[1].time - dataB[0].time; | ||
@@ -292,5 +300,13 @@ currentLoaderTotalTime += tapTime; | ||
allLoaderTime += currentLoaderTotalTime; | ||
messages.push(`Loader ${chalk.bold(loaderName)} takes ${prettyTime(currentLoaderTotalTime, option)}`); | ||
const loaderId = option.groupLoaderByPath ? loaderPath : loaderName; | ||
if (loaderIdSet.has(loaderId)) { | ||
isDuplicatedLodaerIdOutputed = true; | ||
} | ||
loaderIdSet.add(loaderId); | ||
messages.push(`${nextLinePrefix}Loader ${chalk.bold(loaderId)} takes ${prettyTime(currentLoaderTotalTime, option)}`); | ||
}); | ||
messages.push(`All loaders take ${prettyTime(allLoaderTime, option)}`); | ||
if (isDuplicatedLodaerIdOutputed) { | ||
messages.push(`${nextLinePrefix}There are many differnt loaders that have same assumed name. Consider use "loader.groupedByAbsolutePath" option to show the full path of loaders.`); | ||
} | ||
messages.push(`${nextLinePrefix}All loaders take ${prettyTime(allLoaderTime, option)}`); | ||
return messages; | ||
@@ -297,0 +313,0 @@ } |
@@ -13,2 +13,3 @@ "use strict"; | ||
const const_1 = require("./const"); | ||
const loaderHelper_1 = require("./loaderHelper"); | ||
const utils_1 = require("./utils"); | ||
@@ -85,3 +86,3 @@ var AnalyzeInfoKind; | ||
const messages3 = outputLoaderInfos(this.loaderData, option); | ||
const content = [...messages1, ...messages2, ...messages3].join(os_1.EOL); | ||
const content = [headerText, ...messages1, ...messages2, ...messages3].join(os_1.EOL); | ||
if (option.filePath) { | ||
@@ -128,2 +129,5 @@ const outputFileAbsolutePath = (0, path_1.resolve)(option.filePath); | ||
} | ||
const headerText = '┌── time-analytics-webpack-plugin'; | ||
const sectionStartPrefix = '├── '; | ||
const nextLinePrefix = '│ '; | ||
function outputMetaInfo(data, option) { | ||
@@ -138,3 +142,3 @@ // validate | ||
const compileTotalTime = compilerDoneEvents[0].time - compilerCompileEvents[0].time; | ||
messages.push(`Webpack compile takes ${prettyTime(compileTotalTime, option)}`); | ||
messages.push(`${nextLinePrefix}Webpack compile takes ${prettyTime(compileTotalTime, option)}`); | ||
return messages; | ||
@@ -145,3 +149,3 @@ } | ||
const messages = []; | ||
messages.push(`${chalk_1.default.blue(chalk_1.default.bold('Plugins'))}`); | ||
messages.push(`${sectionStartPrefix}${chalk_1.default.blue(chalk_1.default.bold('Plugins'))}`); | ||
let allPluginTime = 0; | ||
@@ -160,5 +164,5 @@ const nameGrouppedPlugin = (0, ramda_1.groupBy)((0, ramda_1.prop)('pluginName'), data); | ||
allPluginTime += currentPluginTotalTime; | ||
messages.push(`Plugin ${chalk_1.default.bold(pluginName)} takes ${prettyTime(currentPluginTotalTime, option)}`); | ||
messages.push(`${nextLinePrefix}Plugin ${chalk_1.default.bold(pluginName)} takes ${prettyTime(currentPluginTotalTime, option)}`); | ||
}); | ||
messages.push(`All plugins take ${prettyTime(allPluginTime, option)}`); | ||
messages.push(`${nextLinePrefix}All plugins take ${prettyTime(allPluginTime, option)}`); | ||
return messages; | ||
@@ -169,8 +173,11 @@ } | ||
const messages = []; | ||
messages.push(`${chalk_1.default.blue(chalk_1.default.bold('Loaders'))}`); | ||
const loaderIdSet = new Set(); | ||
let isDuplicatedLodaerIdOutputed = false; | ||
messages.push(`${sectionStartPrefix}${chalk_1.default.blue(chalk_1.default.bold('Loaders'))}`); | ||
let allLoaderTime = 0; | ||
const nameGrouppedLoader = (0, ramda_1.groupBy)((0, ramda_1.prop)('loaderName'), data); | ||
Object.entries(nameGrouppedLoader).forEach(([loaderName, dataA]) => { | ||
if (option.ignoredLoaders.includes(loaderName)) { | ||
messages.push(`Loader ${chalk_1.default.bold(loaderName)} is ignored.`); | ||
const nameGrouppedLoader = (0, ramda_1.groupBy)((0, ramda_1.prop)('loaderPath'), data); | ||
Object.entries(nameGrouppedLoader).forEach(([loaderPath, dataA]) => { | ||
const loaderName = (0, loaderHelper_1.getLoaderName)(loaderPath); | ||
if (option.ignoredLoaders.includes(loaderPath)) { | ||
messages.push(`${nextLinePrefix}Loader ${chalk_1.default.bold(loaderPath)} is ignored.`); | ||
return; | ||
@@ -183,3 +190,3 @@ } | ||
&& dataB[0].eventType === LoaderEventType.start | ||
&& dataB[1].eventType === LoaderEventType.end, `each laoder execution should be collected info for start and end, once and only once. But for ${loaderName}, there is an error, why?`); | ||
&& dataB[1].eventType === LoaderEventType.end, `each laoder execution should be collected info for start and end, once and only once. But for ${loaderPath}, there is an error, why?`); | ||
const tapTime = dataB[1].time - dataB[0].time; | ||
@@ -189,5 +196,13 @@ currentLoaderTotalTime += tapTime; | ||
allLoaderTime += currentLoaderTotalTime; | ||
messages.push(`Loader ${chalk_1.default.bold(loaderName)} takes ${prettyTime(currentLoaderTotalTime, option)}`); | ||
const loaderId = option.groupLoaderByPath ? loaderPath : loaderName; | ||
if (loaderIdSet.has(loaderId)) { | ||
isDuplicatedLodaerIdOutputed = true; | ||
} | ||
loaderIdSet.add(loaderId); | ||
messages.push(`${nextLinePrefix}Loader ${chalk_1.default.bold(loaderId)} takes ${prettyTime(currentLoaderTotalTime, option)}`); | ||
}); | ||
messages.push(`All loaders take ${prettyTime(allLoaderTime, option)}`); | ||
if (isDuplicatedLodaerIdOutputed) { | ||
messages.push(`${nextLinePrefix}There are many differnt loaders that have same assumed name. Consider use "loader.groupedByAbsolutePath" option to show the full path of loaders.`); | ||
} | ||
messages.push(`${nextLinePrefix}All loaders take ${prettyTime(allLoaderTime, option)}`); | ||
return messages; | ||
@@ -194,0 +209,0 @@ } |
@@ -60,14 +60,2 @@ "use strict"; | ||
} | ||
function getLoaderName(path) { | ||
// get the folder name after the last "node_moduels" | ||
// otherwise, the whole path | ||
const canonicalPath = path.replace(/\\/g, '/'); | ||
const targetString = '/node_modules/'; | ||
const index = canonicalPath.lastIndexOf(targetString); | ||
if (index === -1) | ||
return canonicalPath; | ||
const sub = canonicalPath.substring(index + targetString.length); | ||
const loaderName = sub.substring(0, sub.indexOf('/')); | ||
return loaderName; | ||
} | ||
const loader = function timeAnalyticHackLoader(source) { | ||
@@ -93,3 +81,2 @@ // console.log('Time analytics plugin: normal loader is executed'); | ||
hackWrapLoaderModule(loaderPaths, function wrapLoaderModuleCallback(loaderModule, path) { | ||
const loaderName = getLoaderName(path); | ||
const wrapLoaderFunc = (originLoader, loaderType) => { | ||
@@ -111,3 +98,2 @@ // return originLoader; | ||
callId: uuid, | ||
loaderName, | ||
kind: analyzer_1.AnalyzeInfoKind.loader, | ||
@@ -129,3 +115,2 @@ eventType: analyzer_1.LoaderEventType.end, | ||
callId: uuid, | ||
loaderName, | ||
kind: analyzer_1.AnalyzeInfoKind.loader, | ||
@@ -152,3 +137,2 @@ eventType: analyzer_1.LoaderEventType.start, | ||
callId: uuid, | ||
loaderName, | ||
kind: analyzer_1.AnalyzeInfoKind.loader, | ||
@@ -155,0 +139,0 @@ eventType: analyzer_1.LoaderEventType.end, |
"use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
exports.normalizeRules = void 0; | ||
exports.normalizeRules = exports.getLoaderName = void 0; | ||
const const_1 = require("./const"); | ||
const utils_1 = require("./utils"); | ||
/** | ||
* get the folder name after the last "node_moduels" | ||
* otherwise, the whole path | ||
*/ | ||
function getLoaderName(path) { | ||
const canonicalPath = path.replace(/\\/g, '/'); | ||
const targetString = '/node_modules/'; | ||
const index = canonicalPath.lastIndexOf(targetString); | ||
if (index === -1) | ||
return canonicalPath; | ||
const sub = canonicalPath.substring(index + targetString.length); | ||
const loaderName = sub.substring(0, sub.indexOf('/')); | ||
return loaderName; | ||
} | ||
exports.getLoaderName = getLoaderName; | ||
function normalizeRuleCore(rule) { | ||
@@ -7,0 +22,0 @@ if (rule.loader) { |
"use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
const crypto_1 = require("crypto"); | ||
const const_1 = require("../const"); | ||
const utils_1 = require("../utils"); | ||
const WeakMapIdObject_1 = require("./WeakMapIdObject"); | ||
/** | ||
* The object which will be used as the key of WeakMap for compilation/compiler. | ||
* | ||
* Need this, because WeakMap only accepts key as object. | ||
*/ | ||
class WebpackWeakMapId { | ||
constructor() { | ||
this.id = (0, crypto_1.randomUUID)(); | ||
} | ||
} | ||
(0, utils_1.assert)(WeakMap, 'WeakMap must be existed.'); | ||
@@ -28,3 +38,3 @@ const originSet = WeakMap.prototype.set; | ||
if (!key[const_1.WEBPACK_WEAK_MAP_ID_KEY]) { | ||
key[const_1.WEBPACK_WEAK_MAP_ID_KEY] = new WeakMapIdObject_1.WebpackWeakMapId(); | ||
key[const_1.WEBPACK_WEAK_MAP_ID_KEY] = new WebpackWeakMapId(); | ||
} | ||
@@ -31,0 +41,0 @@ finalKey = key[const_1.WEBPACK_WEAK_MAP_ID_KEY]; |
@@ -57,2 +57,3 @@ "use strict"; | ||
ignoredLoaders: this.option?.loader?.exclude ?? [], | ||
groupLoaderByPath: this.option?.loader?.groupedByAbsolutePath ?? false, | ||
}); | ||
@@ -59,0 +60,0 @@ }); |
@@ -64,14 +64,2 @@ /* eslint-disable prefer-rest-params */ | ||
function getLoaderName(path: string) { | ||
// get the folder name after the last "node_moduels" | ||
// otherwise, the whole path | ||
const canonicalPath = path.replace(/\\/g, '/'); | ||
const targetString = '/node_modules/'; | ||
const index = canonicalPath.lastIndexOf(targetString); | ||
if (index === -1) return canonicalPath; | ||
const sub = canonicalPath.substring(index + targetString.length); | ||
const loaderName = sub.substring(0, sub.indexOf('/')); | ||
return loaderName; | ||
} | ||
const loader: LoaderDefinition = function timeAnalyticHackLoader(source) { | ||
@@ -99,3 +87,2 @@ // console.log('Time analytics plugin: normal loader is executed'); | ||
hackWrapLoaderModule(loaderPaths, function wrapLoaderModuleCallback(loaderModule, path) { | ||
const loaderName = getLoaderName(path); | ||
const wrapLoaderFunc = (originLoader: LoaderDefinitionFunction | PitchLoaderDefinitionFunction, loaderType: LoaderType) => { | ||
@@ -121,3 +108,2 @@ // return originLoader; | ||
callId: uuid, | ||
loaderName, | ||
kind: AnalyzeInfoKind.loader, | ||
@@ -140,3 +126,2 @@ eventType: LoaderEventType.end, | ||
callId: uuid, | ||
loaderName, | ||
kind: AnalyzeInfoKind.loader, | ||
@@ -165,3 +150,2 @@ eventType: LoaderEventType.start, | ||
callId: uuid, | ||
loaderName, | ||
kind: AnalyzeInfoKind.loader, | ||
@@ -168,0 +152,0 @@ eventType: LoaderEventType.end, |
import type { RuleSetRule } from 'webpack'; | ||
import { PACKAGE_NAME } from './const'; | ||
import { fail } from './utils'; | ||
/** | ||
* get the folder name after the last "node_moduels" | ||
* otherwise, the whole path | ||
*/ | ||
export function getLoaderName(path: string) { | ||
const canonicalPath = path.replace(/\\/g, '/'); | ||
const targetString = '/node_modules/'; | ||
const index = canonicalPath.lastIndexOf(targetString); | ||
if (index === -1) return canonicalPath; | ||
const sub = canonicalPath.substring(index + targetString.length); | ||
const loaderName = sub.substring(0, sub.indexOf('/')); | ||
return loaderName; | ||
} | ||
@@ -5,0 +18,0 @@ function normalizeRuleCore(rule: RuleSetRule) { |
{ | ||
"name": "time-analytics-webpack-plugin", | ||
"version": "0.1.15", | ||
"version": "0.1.16", | ||
"description": "analytize the time of each part of webpack", | ||
@@ -5,0 +5,0 @@ "main": "./dist/index.js", |
@@ -0,5 +1,14 @@ | ||
import { randomUUID } from 'crypto'; | ||
import { WEBPACK_WEAK_MAP_ID_KEY } from '../const'; | ||
import { assert } from '../utils'; | ||
import { WebpackWeakMapId } from './WeakMapIdObject'; | ||
/** | ||
* The object which will be used as the key of WeakMap for compilation/compiler. | ||
* | ||
* Need this, because WeakMap only accepts key as object. | ||
*/ | ||
class WebpackWeakMapId { | ||
private id = randomUUID(); | ||
} | ||
assert(WeakMap, 'WeakMap must be existed.'); | ||
@@ -6,0 +15,0 @@ |
@@ -5,6 +5,4 @@ import type { Compiler, Configuration, ModuleOptions, RuleSetRule } from 'webpack'; | ||
import { normalizeRules } from './loaderHelper'; | ||
import { assert, assertNever, ConsoleHelper, fail, now } from './utils'; | ||
import { ConsoleHelper, fail, now } from './utils'; | ||
import './sideEffects/hackWeakMap'; | ||
import { WEBPACK_WEAK_MAP_ID_KEY } from './const'; | ||
import { WebpackWeakMapId } from './sideEffects/WeakMapIdObject'; | ||
@@ -67,8 +65,16 @@ export declare class WebpackPlugin { | ||
/** | ||
* If true, output the absolute path of the loader | ||
* If true, output the absolute path of the loader. | ||
* | ||
* By default, the plugin displays loader time by a assumed loader name | ||
* | ||
* Like `babel-loader takes xxx ms.` | ||
* | ||
* The assumption is the loader's name is the first name after the last `node_modules` in the path. | ||
* | ||
* However, sometimes, it's not correct, like the loader's package is `@foo/loader1` then the assumed name is "@foo", | ||
* or some framework like `next` will move the loader to some strange place. | ||
* | ||
* @default false | ||
* @NotImplementYet | ||
*/ | ||
displayAbsolutePath?: boolean; | ||
groupedByAbsolutePath?: boolean; | ||
/** | ||
@@ -152,2 +158,3 @@ * If true, display the most time consumed resource's info | ||
ignoredLoaders: this.option?.loader?.exclude ?? [], | ||
groupLoaderByPath: this.option?.loader?.groupedByAbsolutePath ?? false, | ||
}); | ||
@@ -154,0 +161,0 @@ }); |
@@ -27,3 +27,2 @@ export declare enum AnalyzeInfoKind { | ||
loaderPath: string; | ||
loaderName: string; | ||
time: number; | ||
@@ -106,2 +105,3 @@ /** | ||
dangerTimeLimit: number; | ||
groupLoaderByPath: boolean; | ||
/** | ||
@@ -108,0 +108,0 @@ * TODO: should we remove this option? Feels like we should not collect the info at all. Do this by give loader options. |
import type { RuleSetRule } from 'webpack'; | ||
/** | ||
* get the folder name after the last "node_moduels" | ||
* otherwise, the whole path | ||
*/ | ||
export declare function getLoaderName(path: string): string; | ||
export declare function normalizeRules(rules: RuleSetRule[] | undefined): RuleSetRule[] | undefined; |
@@ -56,8 +56,16 @@ import type { Compiler, Configuration } from 'webpack'; | ||
/** | ||
* If true, output the absolute path of the loader | ||
* If true, output the absolute path of the loader. | ||
* | ||
* By default, the plugin displays loader time by a assumed loader name | ||
* | ||
* Like `babel-loader takes xxx ms.` | ||
* | ||
* The assumption is the loader's name is the first name after the last `node_modules` in the path. | ||
* | ||
* However, sometimes, it's not correct, like the loader's package is `@foo/loader1` then the assumed name is "@foo", | ||
* or some framework like `next` will move the loader to some strange place. | ||
* | ||
* @default false | ||
* @NotImplementYet | ||
*/ | ||
displayAbsolutePath?: boolean; | ||
groupedByAbsolutePath?: boolean; | ||
/** | ||
@@ -64,0 +72,0 @@ * If true, display the most time consumed resource's info |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
No README
QualityPackage does not have a README. This may indicate a failed publish or a low quality package.
Found 1 instance in 1 package
156788
5.66%2751
2.15%0
-100%95
Infinity%