Skip to content

Commit 1bebe75

Browse files
refactor: name three classes for what they do (#22361)
* refactor: name three classes for what they do `HoistContainerReferences` is a plugin, so it takes the `Plugin` suffix. `WebpackLogger` only restated its directory, so it becomes `Logger`. `LazyBarrelController` needed the file renamed instead: it holds one `WeakMap` of every module for the whole compilation, so it is not a lazy barrel — `LazyBarrelInfo` is — and `LazyBarrels` says so, matching `DependencyTemplates`. The field follows as `_lazyBarrels`. None registers a serializer or is exported by name. The three `No such label ... for WebpackLogger.timeX()` messages now say `Logger`, and `types.d.ts` declares `Logger` in place of `WebpackLogger`. * docs(agents): write down the naming rules for classes Two rules the codebase already follows but nothing stated: a class is named for what it does and carries its kind suffix, with the file taking that name; and a name never invents a role suffix, prefixes `Webpack` where the bare word is free, or repeats another class in `lib/`. * test(logging): cover the Logger timer error paths The three `No such label` throws and the aggregate paths had no test, so renaming `WebpackLogger` to `Logger` pulled uncovered lines into the diff.
1 parent 676fef7 commit 1bebe75

10 files changed

Lines changed: 209 additions & 129 deletions

File tree

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"webpack": patch
3+
---
4+
5+
Rename `HoistContainerReferences`, `LazyBarrelController` and `WebpackLogger`.

‎AGENTS.md‎

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -88,7 +88,7 @@ This is the canonical repository map. **When you add, rename or remove a top-lev
8888
- `lib/loaders/` — loader execution (vendored loader-runner): pitching/normal iteration and loader loading, plus `LoaderOptionsPlugin` and `LoaderTargetPlugin`, which feed the loader context, and `LoaderPlugin` with the `LoaderDependency`/`LoaderImportDependency` it builds a loader's own imports from.
8989
- `lib/logging/` — Logger API, console formatting, and `ProgressPlugin` (reports through it). `lib/ProgressPlugin` stays a re-export (webpack-stream requires it).
9090
- `lib/module/` — what a module is and what makes one: `Module`, `NormalModule`, the factories (`NormalModuleFactory`, `NullFactory`, `SelfModuleFactory`), base `Parser`/`Generator`, `CodeGenerationResults`, `ModuleProfile`, and the two constants files naming module and source types. A plugin's module subclass lives with the plugin (`ExternalModule` → `lib/externals/`, `CssModule` → `lib/css/`).
91-
- `lib/optimize/` — optimization plugins (`SplitChunksPlugin`, `ConcatenatedModule`, …), `CircularModulesPlugin` (flags import cycles), `LazyBarrel` (finds barrel files worth deferring) and the `PureExpressionDependency` only `InnerGraphPlugin` emits. The three data structures each of one plugin reads are here, not in `lib/util/`: `Queue` (`FlagDependencyExportsPlugin`), `TupleQueue` (`FlagDependencyUsagePlugin`) and `LazyBucketSortedSet` (`LimitChunkCountPlugin`). `ConcatenationScope` is scope hoisting's protocol: only `ConcatenatedModule` constructs one; any generator renders through it.
91+
- `lib/optimize/` — optimization plugins (`SplitChunksPlugin`, `ConcatenatedModule`, …), `CircularModulesPlugin` (flags import cycles), `LazyBarrels` (finds barrel files worth deferring) and the `PureExpressionDependency` only `InnerGraphPlugin` emits. The three data structures each of one plugin reads are here, not in `lib/util/`: `Queue` (`FlagDependencyExportsPlugin`), `TupleQueue` (`FlagDependencyUsagePlugin`) and `LazyBucketSortedSet` (`LimitChunkCountPlugin`). `ConcatenationScope` is scope hoisting's protocol: only `ConcatenatedModule` constructs one; any generator renders through it.
9292
- `lib/output/` — plugins shaping the set of files written to `output.path` (not the modules in them): `CopyPlugin` adds (`output.copy`), `CleanPlugin` prunes (`output.clean`), `BannerPlugin` rewrites an asset, `ManifestPlugin` describes the rest, and `SSRManifestPlugin` emits which client files each source module needs, so an SSR response can name the stylesheet of a route not yet requested. How names and formats are decided is `lib/template/`, `lib/library/`, `lib/devtool/`.
9393
- `lib/performance/` — asset/entrypoint size hints.
9494
- `lib/prefetch/` — two mechanisms sharing a word: runtime modules emitting `<link rel="prefetch">` for a chunk, and `PrefetchPlugin`/`AutomaticPrefetchPlugin`, which resolve a module eagerly at build time through the `PrefetchDependency` they own.
@@ -266,6 +266,10 @@ When a function's output type depends on its input, use a generic (`@template`)
266266

267267
Spell names out in full (functions, variables, parameters, properties): `insertHtmlElement` not `insHtmlEl`, `attributeCount` not `attrCnt`, `current` not `cur`, `element` not `el`. Exceptions: abbreviations webpack already uses pervasively (`ast`, `ns`, `id`, `url`, `css`, `js`, `dir`, `env`, `fs`) or spec-defined ones (`afe` — the HTML spec's active formatting elements), and throwaway loop indices (`i`, `j`, `k`). Otherwise write the full word.
268268

269+
**Name a class for what it does, and the file for the class.** The name says the idea, and ends in the suffix its kind uses — `*Plugin`, `*Dependency`, `*RuntimeModule`, `*Warning`, `*Error`, `*Module`, `*Factory`, `*Generator`, `*Parser`, `*Template` (546 of 668 PascalCase files in `lib/`). A file exporting one class takes that name; where the two disagree, rename whichever is wrong — often the file. Exempt: core nouns (`Compiler`, `Chunk`), helper and data-structure modules, and any module exporting several things.
270+
271+
**A name never** invents a role suffix (`lib/` has no `*Controller`, `*Manager`, `*Service` or `*Util` — a class that controls a thing is the thing), carries a `Webpack` prefix where the bare word is free, or repeats another class in `lib/`, since a stack trace and `types.d.ts` show the name alone.
272+
269273
### Path regexps and helpers live in one file
270274

271275
> [!REQUIRED]

‎lib/Compilation.js‎

Lines changed: 9 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ const CodeGenerationResults = require("./module/CodeGenerationResults");
3737
const Module = require("./module/Module");
3838
const ModuleProfile = require("./module/ModuleProfile");
3939
const { WEBPACK_MODULE_TYPE_RUNTIME } = require("./module/ModuleTypeConstants");
40-
const LazyBarrelController = require("./optimize/LazyBarrel");
40+
const LazyBarrels = require("./optimize/LazyBarrels");
4141
const RuntimeGlobals = require("./runtime/RuntimeGlobals");
4242
const Stats = require("./stats/Stats");
4343
const StatsFactory = require("./stats/StatsFactory");
@@ -1513,8 +1513,8 @@ BREAKING CHANGE: Asset processing hooks in Compilation has been merged into a si
15131513
this._unsafeCachePredicate =
15141514
typeof unsafeCache === "function" ? unsafeCache : () => true;
15151515

1516-
/** @type {LazyBarrelController} */
1517-
this._lazyBarrelController = new LazyBarrelController(this);
1516+
/** @type {LazyBarrels} */
1517+
this._lazyBarrels = new LazyBarrels(this);
15181518
}
15191519

15201520
getStats() {
@@ -1927,7 +1927,7 @@ BREAKING CHANGE: Asset processing hooks in Compilation has been merged into a si
19271927
);
19281928

19291929
// lazy barrel: defer re-export targets of side-effect-free modules
1930-
const hasLazyBarrel = this._lazyBarrelController.classify(module);
1930+
const hasLazyBarrel = this._lazyBarrels.classify(module);
19311931

19321932
/** @type {DependenciesBlock} */
19331933
let currentBlock;
@@ -2031,7 +2031,7 @@ BREAKING CHANGE: Asset processing hooks in Compilation has been merged into a si
20312031
dep,
20322032
unsafeCachedModule
20332033
);
2034-
this._lazyBarrelController.unlazyForDependency(
2034+
this._lazyBarrels.unlazyForDependency(
20352035
unsafeCachedModule,
20362036
dep,
20372037
sortedDependencies
@@ -2049,7 +2049,7 @@ BREAKING CHANGE: Asset processing hooks in Compilation has been merged into a si
20492049
dep,
20502050
cachedModule
20512051
);
2052-
this._lazyBarrelController.unlazyForDependency(
2052+
this._lazyBarrels.unlazyForDependency(
20532053
cachedModule,
20542054
dep,
20552055
sortedDependencies
@@ -2114,7 +2114,7 @@ BREAKING CHANGE: Asset processing hooks in Compilation has been merged into a si
21142114
dep,
21152115
cachedModule
21162116
); // a3
2117-
this._lazyBarrelController.unlazyForDependency(
2117+
this._lazyBarrels.unlazyForDependency(
21182118
cachedModule,
21192119
dep,
21202120
sortedDependencies
@@ -2659,10 +2659,7 @@ BREAKING CHANGE: Asset processing hooks in Compilation has been merged into a si
26592659
// lazy barrel: on the first call the module is unclassified, so this only
26602660
// records the ids and `processModuleDependencies` replays them. Later the
26612661
// module is built and never re-walked, so the targets are built here.
2662-
const unlazyItems = this._lazyBarrelController.request(
2663-
module,
2664-
dependencies
2665-
);
2662+
const unlazyItems = this._lazyBarrels.request(module, dependencies);
26662663

26672664
if (unlazyItems !== undefined) {
26682665
let inProgress = unlazyItems.length + 1; // +1 = the module's own dep processing
@@ -3620,7 +3617,7 @@ BREAKING CHANGE: Asset processing hooks in Compilation has been merged into a si
36203617
this.processDependenciesQueue.clear();
36213618
this.addModuleQueue.clear();
36223619
// lazy barrel only acts during make; release its bookkeeping now
3623-
this._lazyBarrelController.clear();
3620+
this._lazyBarrels.clear();
36243621
// unsafe-cache restore tables are only read during make; release them
36253622
// so they stop pinning restored modules for the rest of the compilation
36263623
this._restoredUnsafeCacheModuleEntries.clear();

‎lib/container/HoistContainerReferencesPlugin.js‎

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,12 +16,12 @@ const getModuleFederationCompilationHooks = require("./moduleFederationHooks");
1616
/** @import Dependency from "../graph/Dependency" */
1717
/** @import Module from "../module/Module" */
1818

19-
const PLUGIN_NAME = "HoistContainerReferences";
19+
const PLUGIN_NAME = "HoistContainerReferencesPlugin";
2020

2121
/**
2222
* This class is used to hoist container references in the code.
2323
*/
24-
class HoistContainerReferences {
24+
class HoistContainerReferencesPlugin {
2525
/**
2626
* Apply the plugin to the compiler.
2727
* @param {Compiler} compiler The webpack compiler instance.
@@ -240,4 +240,4 @@ function getAllReferencedModules(compilation, module, type, includeInitial) {
240240
return collectedModules;
241241
}
242242

243-
module.exports = HoistContainerReferences;
243+
module.exports = HoistContainerReferencesPlugin;

‎lib/container/ModuleFederationPlugin.js‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ const isValidExternalsType = require("../../schemas/plugins/container/ExternalsT
99
const SharePlugin = require("../sharing/SharePlugin");
1010
const ContainerPlugin = require("./ContainerPlugin");
1111
const ContainerReferencePlugin = require("./ContainerReferencePlugin");
12-
const HoistContainerReferences = require("./HoistContainerReferencesPlugin");
12+
const HoistContainerReferencesPlugin = require("./HoistContainerReferencesPlugin");
1313
const getCompilationHooks = require("./moduleFederationHooks");
1414

1515
/** @import Compiler from "../Compiler" */
@@ -399,7 +399,7 @@ class ModuleFederationPlugin {
399399
shareScope: options.shareScope
400400
}).apply(compiler);
401401
}
402-
new HoistContainerReferences().apply(compiler);
402+
new HoistContainerReferencesPlugin().apply(compiler);
403403
});
404404
}
405405
}

‎lib/logging/Logger.js‎

Lines changed: 7 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -38,11 +38,11 @@ const TIMERS_AGGREGATES_SYMBOL = Symbol("webpack logger aggregated times");
3838

3939
/** @typedef {EXPECTED_ANY[]} Args */
4040
/** @typedef {(type: LogTypeEnum, args?: Args) => void} LogFn */
41-
/** @typedef {(name: string | (() => string)) => WebpackLogger} GetChildLogger */
41+
/** @typedef {(name: string | (() => string)) => Logger} GetChildLogger */
4242

43-
class WebpackLogger {
43+
class Logger {
4444
/**
45-
* Creates an instance of WebpackLogger.
45+
* Creates an instance of Logger.
4646
* @param {LogFn} log log function
4747
* @param {GetChildLogger} getChildLogger function to create child logger
4848
*/
@@ -173,7 +173,7 @@ class WebpackLogger {
173173
timeLog(label) {
174174
const prev = this[TIMERS_SYMBOL] && this[TIMERS_SYMBOL].get(label);
175175
if (!prev) {
176-
throw new Error(`No such label '${label}' for WebpackLogger.timeLog()`);
176+
throw new Error(`No such label '${label}' for Logger.timeLog()`);
177177
}
178178
const time = process.hrtime(prev);
179179
this[LOG_SYMBOL](LogType.time, [label, ...time]);
@@ -186,7 +186,7 @@ class WebpackLogger {
186186
timeEnd(label) {
187187
const prev = this[TIMERS_SYMBOL] && this[TIMERS_SYMBOL].get(label);
188188
if (!prev) {
189-
throw new Error(`No such label '${label}' for WebpackLogger.timeEnd()`);
189+
throw new Error(`No such label '${label}' for Logger.timeEnd()`);
190190
}
191191
const time = process.hrtime(prev);
192192
/** @type {TimersMap} */
@@ -201,9 +201,7 @@ class WebpackLogger {
201201
timeAggregate(label) {
202202
const prev = this[TIMERS_SYMBOL] && this[TIMERS_SYMBOL].get(label);
203203
if (!prev) {
204-
throw new Error(
205-
`No such label '${label}' for WebpackLogger.timeAggregate()`
206-
);
204+
throw new Error(`No such label '${label}' for Logger.timeAggregate()`);
207205
}
208206
const time = process.hrtime(prev);
209207
/** @type {TimersMap} */
@@ -237,4 +235,4 @@ class WebpackLogger {
237235
}
238236
}
239237

240-
module.exports.Logger = WebpackLogger;
238+
module.exports.Logger = Logger;

‎lib/optimize/FlagDependencyExportsPlugin.js‎

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ const Queue = require("./Queue");
2121
*/
2222
/** @import ExportsInfo, { RestoreProvidedData } from "../graph/ExportsInfo" */
2323
/** @import { HashFunction } from "../../declarations/WebpackOptions" */
24-
/** @import LazyBarrelController from "./LazyBarrel" */
24+
/** @import LazyBarrels from "./LazyBarrels" */
2525
/** @import Module, { BuildInfo } from "../module/Module" */
2626

2727
const PLUGIN_NAME = "FlagDependencyExportsPlugin";
@@ -33,12 +33,12 @@ const PLUGIN_LOGGER_NAME = `webpack.${PLUGIN_NAME}`;
3333
* Cache key fragment for what changes provided exports without changing the
3434
* module's source: still-lazy barrel requests and DefinePlugin value versions.
3535
* @param {Module} module the module
36-
* @param {LazyBarrelController} lazyBarrelController the compilation's lazy barrel controller
36+
* @param {LazyBarrels} lazyBarrels the compilation's lazy barrels
3737
* @param {HashFunction} hashFunction hash function for long keys
3838
* @returns {string} etag for provided-exports mem/persistent cache, "" when neither applies
3939
*/
40-
const getProvidedExportsEtag = (module, lazyBarrelController, hashFunction) => {
41-
const lazyRequests = lazyBarrelController.getLazyRequests(module);
40+
const getProvidedExportsEtag = (module, lazyBarrels, hashFunction) => {
41+
const lazyRequests = lazyBarrels.getLazyRequests(module);
4242
const buildInfo = /** @type {BuildInfo | undefined} */ (module.buildInfo);
4343
const valueDependencies = buildInfo && buildInfo.valueDependencies;
4444
const hasValueDependencies =
@@ -80,7 +80,7 @@ class FlagDependencyExportsPlugin {
8080
compiler.hooks.compilation.tap(PLUGIN_NAME, (compilation) => {
8181
const moduleGraph = compilation.moduleGraph;
8282
const cache = compilation.getCache(PLUGIN_NAME);
83-
const lazyBarrelController = compilation._lazyBarrelController;
83+
const lazyBarrels = compilation._lazyBarrels;
8484
const hashFunction = compilation.outputOptions.hashFunction;
8585
compilation.hooks.finishModules.tapAsync(
8686
PLUGIN_NAME,
@@ -134,7 +134,7 @@ class FlagDependencyExportsPlugin {
134134
const memCacheEntry = memCache && memCache.get(this);
135135
const etag = getProvidedExportsEtag(
136136
module,
137-
lazyBarrelController,
137+
lazyBarrels,
138138
hashFunction
139139
);
140140
// validated by the still-lazy request keys: reference-change
@@ -534,7 +534,7 @@ class FlagDependencyExportsPlugin {
534534
moduleMemCaches && moduleMemCaches.get(module);
535535
const etag = getProvidedExportsEtag(
536536
module,
537-
lazyBarrelController,
537+
lazyBarrels,
538538
hashFunction
539539
);
540540
if (memCache) {
Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -328,7 +328,7 @@ class LazyBarrelInfo {
328328
/**
329329
* Owns the lazy barrel state of side-effect-free modules for a `Compilation`.
330330
*/
331-
class LazyBarrelController {
331+
class LazyBarrels {
332332
/**
333333
* @param {Compilation} compilation the owning compilation
334334
*/
@@ -599,4 +599,4 @@ class LazyBarrelController {
599599
}
600600
}
601601

602-
module.exports = LazyBarrelController;
602+
module.exports = LazyBarrels;

‎test/unitCases/Logger.unittest.js‎

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
"use strict";
2+
3+
const { Logger, LogType } = require("../../lib/logging/Logger");
4+
5+
/**
6+
* @returns {{ logger: Logger, entries: [string, EXPECTED_ANY[]][] }} a logger recording what it logged
7+
*/
8+
const createLogger = () => {
9+
/** @type {[string, EXPECTED_ANY[]][]} */
10+
const entries = [];
11+
const logger = new Logger(
12+
(type, args) => entries.push([type, args || []]),
13+
(name) => createLogger().logger
14+
);
15+
return { logger, entries };
16+
};
17+
18+
describe("Logger", () => {
19+
it("should log the elapsed time of a started label", () => {
20+
const { logger, entries } = createLogger();
21+
logger.time("build");
22+
logger.timeLog("build");
23+
logger.timeEnd("build");
24+
expect(entries).toHaveLength(2);
25+
for (const [type, args] of entries) {
26+
expect(type).toBe(LogType.time);
27+
expect(args[0]).toBe("build");
28+
expect(typeof args[1]).toBe("number");
29+
expect(typeof args[2]).toBe("number");
30+
}
31+
});
32+
33+
it("should throw for a label that was never started", () => {
34+
const { logger } = createLogger();
35+
expect(() => logger.timeLog("missing")).toThrow(
36+
"No such label 'missing' for Logger.timeLog()"
37+
);
38+
expect(() => logger.timeEnd("missing")).toThrow(
39+
"No such label 'missing' for Logger.timeEnd()"
40+
);
41+
expect(() => logger.timeAggregate("missing")).toThrow(
42+
"No such label 'missing' for Logger.timeAggregate()"
43+
);
44+
});
45+
46+
it("should throw once a label has been ended", () => {
47+
const { logger } = createLogger();
48+
logger.time("build");
49+
logger.timeEnd("build");
50+
expect(() => logger.timeEnd("build")).toThrow(
51+
"No such label 'build' for Logger.timeEnd()"
52+
);
53+
});
54+
55+
it("should sum the aggregated times of a label", () => {
56+
const { logger, entries } = createLogger();
57+
logger.time("hash");
58+
logger.timeAggregate("hash");
59+
logger.time("hash");
60+
logger.timeAggregate("hash");
61+
expect(entries).toHaveLength(0);
62+
logger.timeAggregateEnd("hash");
63+
expect(entries).toHaveLength(1);
64+
expect(entries[0][0]).toBe(LogType.time);
65+
expect(entries[0][1][0]).toBe("hash");
66+
});
67+
68+
it("should ignore ending an aggregate that was never collected", () => {
69+
const { logger, entries } = createLogger();
70+
logger.timeAggregateEnd("never");
71+
logger.time("other");
72+
logger.timeAggregate("other");
73+
logger.timeAggregateEnd("missing");
74+
expect(entries).toHaveLength(0);
75+
});
76+
});

0 commit comments

Comments
 (0)