webpack-bundle-analyzer 5.2.0 → 5.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -24,13 +24,11 @@ yarn add -D webpack-bundle-analyzer
24
24
  <h2 align="center">Usage (as a plugin)</h2>
25
25
 
26
26
  ```js
27
- const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin;
27
+ const { BundleAnalyzerPlugin } = require("webpack-bundle-analyzer");
28
28
 
29
29
  module.exports = {
30
- plugins: [
31
- new BundleAnalyzerPlugin()
32
- ]
33
- }
30
+ plugins: [new BundleAnalyzerPlugin()],
31
+ };
34
32
  ```
35
33
 
36
34
  It will create an interactive treemap visualization of the contents of all your bundles.
@@ -39,7 +37,7 @@ It will create an interactive treemap visualization of the contents of all your
39
37
 
40
38
  This module will help you:
41
39
 
42
- 1. Realize what's *really* inside your bundle
40
+ 1. Realize what's _really_ inside your bundle
43
41
  2. Find out what modules make up the most of its size
44
42
  3. Find modules that got there by mistake
45
43
  4. Optimize it!
@@ -49,26 +47,28 @@ And it also shows their gzipped, Brotli, or Zstandard sizes!
49
47
 
50
48
  <h2 align="center">Options (for plugin)</h2>
51
49
 
50
+ <!-- eslint-skip -->
51
+
52
52
  ```js
53
53
  new BundleAnalyzerPlugin(options?: object)
54
54
  ```
55
55
 
56
- |Name|Type|Description|
57
- |:--:|:--:|:----------|
58
- |**`analyzerMode`**|One of: `server`, `static`, `json`, `disabled`|Default: `server`. In `server` mode analyzer will start HTTP server to show bundle report. In `static` mode single HTML file with bundle report will be generated. In `json` mode single JSON file with bundle report will be generated. In `disabled` mode you can use this plugin to just generate Webpack Stats JSON file by setting `generateStatsFile` to `true`. |
59
- |**`analyzerHost`**|`{String}`|Default: `127.0.0.1`. Host that will be used in `server` mode to start HTTP server.|
60
- |**`analyzerPort`**|`{Number}` or `auto`|Default: `8888`. Port that will be used in `server` mode to start HTTP server. If `analyzerPort` is `auto`, the operating system will assign an arbitrary unused port |
61
- |**`analyzerUrl`**|`{Function}` called with `{ listenHost: string, listenHost: string, boundAddress: server.address}`. [server.address comes from Node.js](https://nodejs.org/api/net.html#serveraddress)| Default: `http://${listenHost}:${boundAddress.port}`. The URL printed to console with server mode.|
62
- |**`reportFilename`**|`{String}`|Default: `report.html`. Path to bundle report file that will be generated in `static` mode. It can be either an absolute path or a path relative to a bundle output directory (which is output.path in webpack config).|
63
- |**`reportTitle`**|`{String\|function}`|Default: function that returns pretty printed current date and time. Content of the HTML `title` element; or a function of the form `() => string` that provides the content.|
64
- |**`defaultSizes`**|One of: `stat`, `parsed`, `gzip`, `brotli`|Default: `parsed`. Module sizes to show in report by default. [Size definitions](#size-definitions) section describes what these values mean.|
65
- |**`compressionAlgorithm`**|One of: `gzip`, `brotli`, `zstd`|Default: `gzip`. Compression type used to calculate the compressed module sizes.|
66
- |**`openAnalyzer`**|`{Boolean}`|Default: `true`. Automatically open report in default browser.|
67
- |**`generateStatsFile`**|`{Boolean}`|Default: `false`. If `true`, webpack stats JSON file will be generated in bundle output directory|
68
- |**`statsFilename`**|`{String}`|Default: `stats.json`. Name of webpack stats JSON file that will be generated if `generateStatsFile` is `true`. It can be either an absolute path or a path relative to a bundle output directory (which is output.path in webpack config).|
69
- |**`statsOptions`**|`null` or `{Object}`|Default: `null`. Options for `stats.toJson()` method. For example you can exclude sources of your modules from stats file with `source: false` option. [See more options here](https://webpack.js.org/configuration/stats/). |
70
- |**`excludeAssets`**|`{null\|pattern\|pattern[]}` where `pattern` equals to `{String\|RegExp\|function}`|Default: `null`. Patterns that will be used to match against asset names to exclude them from the report. If pattern is a string it will be converted to RegExp via `new RegExp(str)`. If pattern is a function it should have the following signature `(assetName: string) => boolean` and should return `true` to *exclude* matching asset. If multiple patterns are provided asset should match at least one of them to be excluded. |
71
- |**`logLevel`**|One of: `info`, `warn`, `error`, `silent`|Default: `info`. Used to control how much details the plugin outputs.|
56
+ | Name | Type | Description |
57
+ | :------------------------: | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: | :-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
58
+ | **`analyzerMode`** | One of: `server`, `static`, `json`, `disabled` | Default: `server`. In `server` mode analyzer will start HTTP server to show bundle report. In `static` mode single HTML file with bundle report will be generated. In `json` mode single JSON file with bundle report will be generated. In `disabled` mode you can use this plugin to just generate Webpack Stats JSON file by setting `generateStatsFile` to `true`. |
59
+ | **`analyzerHost`** | `{String}` | Default: `127.0.0.1`. Host that will be used in `server` mode to start HTTP server. |
60
+ | **`analyzerPort`** | `{Number}` or `auto` | Default: `8888`. Port that will be used in `server` mode to start HTTP server. If `analyzerPort` is `auto`, the operating system will assign an arbitrary unused port |
61
+ | **`analyzerUrl`** | `{Function}` called with `{ listenHost: string, listenHost: string, boundAddress: server.address}`. [server.address comes from Node.js](https://nodejs.org/api/net.html#serveraddress) | Default: `http://${listenHost}:${boundAddress.port}`. The URL printed to console with server mode. |
62
+ | **`reportFilename`** | `{String}` | Default: `report.html`. Path to bundle report file that will be generated in `static` mode. It can be either an absolute path or a path relative to a bundle output directory (which is output.path in webpack config). |
63
+ | **`reportTitle`** | `{String\|function}` | Default: function that returns pretty printed current date and time. Content of the HTML `title` element; or a function of the form `() => string` that provides the content. |
64
+ | **`defaultSizes`** | One of: `stat`, `parsed`, `gzip`, `brotli` | Default: `parsed`. Module sizes to show in report by default. [Size definitions](#size-definitions) section describes what these values mean. |
65
+ | **`compressionAlgorithm`** | One of: `gzip`, `brotli`, `zstd` | Default: `gzip`. Compression type used to calculate the compressed module sizes. |
66
+ | **`openAnalyzer`** | `{Boolean}` | Default: `true`. Automatically open report in default browser. |
67
+ | **`generateStatsFile`** | `{Boolean}` | Default: `false`. If `true`, webpack stats JSON file will be generated in bundle output directory |
68
+ | **`statsFilename`** | `{String}` | Default: `stats.json`. Name of webpack stats JSON file that will be generated if `generateStatsFile` is `true`. It can be either an absolute path or a path relative to a bundle output directory (which is output.path in webpack config). |
69
+ | **`statsOptions`** | `null` or `{Object}` | Default: `null`. Options for `stats.toJson()` method. For example you can exclude sources of your modules from stats file with `source: false` option. [See more options here](https://webpack.js.org/configuration/stats/). |
70
+ | **`excludeAssets`** | `{null\|pattern\|pattern[]}` where `pattern` equals to `{String\|RegExp\|function}` | Default: `null`. Patterns that will be used to match against asset names to exclude them from the report. If pattern is a string it will be converted to RegExp via `new RegExp(str)`. If pattern is a function it should have the following signature `(assetName: string) => boolean` and should return `true` to _exclude_ matching asset. If multiple patterns are provided asset should match at least one of them to be excluded. |
71
+ | **`logLevel`** | One of: `info`, `warn`, `error`, `silent` | Default: `info`. Used to control how much details the plugin outputs. |
72
72
 
73
73
  <h2 align="center">Usage (as a CLI utility)</h2>
74
74
 
@@ -174,19 +174,21 @@ The Sidebar Menu can be opened by clicking the `>` button at the top left of the
174
174
 
175
175
  The Chunk Context Menu can be opened by right-clicking or `Ctrl`-clicking on a specific chunk in the report. It provides the following options:
176
176
 
177
- * **Hide chunk:** Hides the selected chunk
178
- * **Hide all other chunks:** Hides all chunks besides the selected one
179
- * **Show all chunks:** Un-hides any hidden chunks, returning the report to its initial, unfiltered view
177
+ - **Hide chunk:** Hides the selected chunk
178
+ - **Hide all other chunks:** Hides all chunks besides the selected one
179
+ - **Show all chunks:** Un-hides any hidden chunks, returning the report to its initial, unfiltered view
180
180
 
181
181
  <h2 align="center">Troubleshooting</h2>
182
182
 
183
183
  ### I don't see `gzip` or `parsed` sizes, it only shows `stat` size
184
184
 
185
185
  It happens when `webpack-bundle-analyzer` analyzes files that don't actually exist in your file system, for example when you work with `webpack-dev-server` that keeps all the files in RAM. If you use `webpack-bundle-analyzer` as a plugin you won't get any errors, however if you run it via CLI you get the error message in terminal:
186
+
186
187
  ```
187
188
  Error parsing bundle asset "your_bundle_name.bundle.js": no such file
188
189
  No bundles were parsed. Analyzer will show only original module sizes from stats file.
189
190
  ```
191
+
190
192
  To get more information about it you can read [issue #147](https://github.com/webpack/webpack-bundle-analyzer/issues/147).
191
193
 
192
194
  <h2 align="center">Other tools</h2>
@@ -214,16 +216,12 @@ To get more information about it you can read [issue #147](https://github.com/we
214
216
  <tbody>
215
217
  </table>
216
218
 
217
-
218
219
  [npm]: https://img.shields.io/npm/v/webpack-bundle-analyzer.svg
219
220
  [npm-url]: https://npmjs.com/package/webpack-bundle-analyzer
220
-
221
221
  [node]: https://img.shields.io/node/v/webpack-bundle-analyzer.svg
222
222
  [node-url]: https://nodejs.org
223
-
224
223
  [tests]: https://github.com/webpack/webpack-bundle-analyzer/actions/workflows/main.yml/badge.svg
225
224
  [tests-url]: https://github.com/webpack/webpack-bundle-analyzer/actions/workflows/main.yml
226
-
227
225
  [downloads]: https://img.shields.io/npm/dt/webpack-bundle-analyzer.svg
228
226
  [downloads-url]: https://npmjs.com/package/webpack-bundle-analyzer
229
227
 
@@ -1,58 +1,120 @@
1
1
  "use strict";
2
2
 
3
- const fs = require('fs');
4
- const path = require('path');
3
+ const fs = require("node:fs");
4
+ const path = require("node:path");
5
5
  const {
6
6
  bold
7
- } = require('picocolors');
8
- const Logger = require('./Logger');
9
- const viewer = require('./viewer');
10
- const utils = require('./utils');
7
+ } = require("picocolors");
8
+ const Logger = require("./Logger");
11
9
  const {
12
10
  writeStats
13
- } = require('./statsUtils');
11
+ } = require("./statsUtils");
12
+ const utils = require("./utils");
13
+ const viewer = require("./viewer");
14
+
15
+ /** @typedef {import("net").AddressInfo} AddressInfo */
16
+ /** @typedef {import("webpack").Compiler} Compiler */
17
+ /** @typedef {import("webpack").OutputFileSystem} OutputFileSystem */
18
+ /** @typedef {import("webpack").Stats} Stats */
19
+ /** @typedef {import("webpack").StatsOptions} StatsOptions */
20
+ /** @typedef {import("webpack").StatsAsset} StatsAsset */
21
+ /** @typedef {import("webpack").StatsCompilation} StatsCompilation */
22
+ /** @typedef {import("./sizeUtils").Algorithm} CompressionAlgorithm */
23
+ /** @typedef {import("./Logger").Level} LogLever */
24
+ /** @typedef {import("./viewer").ViewerServerObj} ViewerServerObj */
25
+
26
+ /** @typedef {string | boolean | StatsOptions} PluginStatsOptions */
27
+
28
+ // eslint-disable-next-line jsdoc/reject-any-type
29
+ /** @typedef {any} EXPECTED_ANY */
30
+
31
+ /** @typedef {"static" | "json" | "server" | "disabled"} Mode */
32
+ /** @typedef {string | RegExp | ((asset: string) => void)} Pattern */
33
+ /** @typedef {null | Pattern | Pattern[]} ExcludeAssets */
34
+ /** @typedef {"stat" | "parsed" | "gzip" | "brotli" | "zstd"} Sizes */
35
+ /** @typedef {string | (() => string)} ReportTitle */
36
+ /** @typedef {(options: { listenHost: string, listenPort: number, boundAddress: string | AddressInfo | null }) => string} AnalyzerUrl */
37
+
38
+ /**
39
+ * @typedef {object} Options
40
+ * @property {Mode=} analyzerMode analyzer mode
41
+ * @property {string=} analyzerHost analyzer host
42
+ * @property {"auto" | number=} analyzerPort analyzer port
43
+ * @property {CompressionAlgorithm=} compressionAlgorithm compression algorithm
44
+ * @property {string | null=} reportFilename report filename
45
+ * @property {ReportTitle=} reportTitle report title
46
+ * @property {Sizes=} defaultSizes default sizes
47
+ * @property {boolean=} openAnalyzer open analyzer
48
+ * @property {boolean=} generateStatsFile generate stats file
49
+ * @property {string=} statsFilename stats filename
50
+ * @property {PluginStatsOptions=} statsOptions stats options
51
+ * @property {ExcludeAssets=} excludeAssets exclude assets
52
+ * @property {LogLever=} logLevel exclude assets
53
+ * @property {boolean=} startAnalyzer start analyzer
54
+ * @property {AnalyzerUrl=} analyzerUrl start analyzer
55
+ */
56
+
14
57
  class BundleAnalyzerPlugin {
58
+ /**
59
+ * @param {Options=} opts options
60
+ */
15
61
  constructor(opts = {}) {
62
+ /** @type {Required<Omit<Options, "analyzerPort" | "statsOptions">> & { analyzerPort: number, statsOptions: undefined | PluginStatsOptions }} */
16
63
  this.opts = {
17
- analyzerMode: 'server',
18
- analyzerHost: '127.0.0.1',
19
- compressionAlgorithm: 'gzip',
64
+ analyzerMode: "server",
65
+ analyzerHost: "127.0.0.1",
66
+ compressionAlgorithm: "gzip",
20
67
  reportFilename: null,
21
68
  reportTitle: utils.defaultTitle,
22
- defaultSizes: 'parsed',
69
+ defaultSizes: "parsed",
23
70
  openAnalyzer: true,
24
71
  generateStatsFile: false,
25
- statsFilename: 'stats.json',
26
- statsOptions: null,
72
+ statsFilename: "stats.json",
73
+ statsOptions: undefined,
27
74
  excludeAssets: null,
28
- logLevel: 'info',
29
- // deprecated
75
+ logLevel: "info",
76
+ // TODO deprecated
30
77
  startAnalyzer: true,
31
78
  analyzerUrl: utils.defaultAnalyzerUrl,
32
79
  ...opts,
33
- analyzerPort: 'analyzerPort' in opts ? opts.analyzerPort === 'auto' ? 0 : opts.analyzerPort : 8888
80
+ analyzerPort: opts.analyzerPort === "auto" ? 0 : opts.analyzerPort ?? 8888
34
81
  };
82
+
83
+ /** @type {Compiler | null} */
84
+ this.compiler = null;
85
+ /** @type {Promise<ViewerServerObj> | null} */
35
86
  this.server = null;
36
87
  this.logger = new Logger(this.opts.logLevel);
37
88
  }
89
+
90
+ /**
91
+ * @param {Compiler} compiler compiler
92
+ */
38
93
  apply(compiler) {
39
94
  this.compiler = compiler;
95
+
96
+ /**
97
+ * @param {Stats} stats stats
98
+ * @param {(err?: Error) => void} callback callback
99
+ */
40
100
  const done = (stats, callback) => {
41
- callback = callback || (() => {});
101
+ callback ||= () => {};
102
+
103
+ /** @type {(() => Promise<void>)[]} */
42
104
  const actions = [];
43
105
  if (this.opts.generateStatsFile) {
44
106
  actions.push(() => this.generateStatsFile(stats.toJson(this.opts.statsOptions)));
45
107
  }
46
108
 
47
109
  // Handling deprecated `startAnalyzer` flag
48
- if (this.opts.analyzerMode === 'server' && !this.opts.startAnalyzer) {
49
- this.opts.analyzerMode = 'disabled';
110
+ if (this.opts.analyzerMode === "server" && !this.opts.startAnalyzer) {
111
+ this.opts.analyzerMode = "disabled";
50
112
  }
51
- if (this.opts.analyzerMode === 'server') {
113
+ if (this.opts.analyzerMode === "server") {
52
114
  actions.push(() => this.startAnalyzerServer(stats.toJson()));
53
- } else if (this.opts.analyzerMode === 'static') {
115
+ } else if (this.opts.analyzerMode === "static") {
54
116
  actions.push(() => this.generateStaticReport(stats.toJson()));
55
- } else if (this.opts.analyzerMode === 'json') {
117
+ } else if (this.opts.analyzerMode === "json") {
56
118
  actions.push(() => this.generateJSONReport(stats.toJson()));
57
119
  }
58
120
  if (actions.length) {
@@ -61,8 +123,8 @@ class BundleAnalyzerPlugin {
61
123
  try {
62
124
  await Promise.all(actions.map(action => action()));
63
125
  callback();
64
- } catch (e) {
65
- callback(e);
126
+ } catch (err) {
127
+ callback(/** @type {Error} */err);
66
128
  }
67
129
  });
68
130
  } else {
@@ -70,23 +132,35 @@ class BundleAnalyzerPlugin {
70
132
  }
71
133
  };
72
134
  if (compiler.hooks) {
73
- compiler.hooks.done.tapAsync('webpack-bundle-analyzer', done);
135
+ compiler.hooks.done.tapAsync("webpack-bundle-analyzer", done);
74
136
  } else {
75
- compiler.plugin('done', done);
137
+ // @ts-expect-error old webpack@4 API
138
+ compiler.plugin("done", done);
76
139
  }
77
140
  }
141
+
142
+ /**
143
+ * @param {StatsCompilation} stats stats
144
+ * @returns {Promise<void>}
145
+ */
78
146
  async generateStatsFile(stats) {
79
- const statsFilepath = path.resolve(this.compiler.outputPath, this.opts.statsFilename);
147
+ const statsFilepath = path.resolve(/** @type {Compiler} */
148
+ this.compiler.outputPath, this.opts.statsFilename);
80
149
  await fs.promises.mkdir(path.dirname(statsFilepath), {
81
150
  recursive: true
82
151
  });
83
152
  try {
84
153
  await writeStats(stats, statsFilepath);
85
- this.logger.info(`${bold('Webpack Bundle Analyzer')} saved stats file to ${bold(statsFilepath)}`);
154
+ this.logger.info(`${bold("Webpack Bundle Analyzer")} saved stats file to ${bold(statsFilepath)}`);
86
155
  } catch (error) {
87
- this.logger.error(`${bold('Webpack Bundle Analyzer')} error saving stats file to ${bold(statsFilepath)}: ${error}`);
156
+ this.logger.error(`${bold("Webpack Bundle Analyzer")} error saving stats file to ${bold(statsFilepath)}: ${error}`);
88
157
  }
89
158
  }
159
+
160
+ /**
161
+ * @param {StatsCompilation} stats stats
162
+ * @returns {Promise<void>}
163
+ */
90
164
  async startAnalyzerServer(stats) {
91
165
  if (this.server) {
92
166
  (await this.server).updateChartData(stats);
@@ -105,19 +179,31 @@ class BundleAnalyzerPlugin {
105
179
  });
106
180
  }
107
181
  }
182
+
183
+ /**
184
+ * @param {StatsCompilation} stats stats
185
+ * @returns {Promise<void>}
186
+ */
108
187
  async generateJSONReport(stats) {
109
188
  await viewer.generateJSONReport(stats, {
110
- reportFilename: path.resolve(this.compiler.outputPath, this.opts.reportFilename || 'report.json'),
189
+ reportFilename: path.resolve(/** @type {Compiler} */
190
+ this.compiler.outputPath, this.opts.reportFilename || "report.json"),
111
191
  compressionAlgorithm: this.opts.compressionAlgorithm,
112
192
  bundleDir: this.getBundleDirFromCompiler(),
113
193
  logger: this.logger,
114
194
  excludeAssets: this.opts.excludeAssets
115
195
  });
116
196
  }
197
+
198
+ /**
199
+ * @param {StatsCompilation} stats stats
200
+ * @returns {Promise<void>}
201
+ */
117
202
  async generateStaticReport(stats) {
118
203
  await viewer.generateReport(stats, {
119
204
  openBrowser: this.opts.openAnalyzer,
120
- reportFilename: path.resolve(this.compiler.outputPath, this.opts.reportFilename || 'report.html'),
205
+ reportFilename: path.resolve(/** @type {Compiler} */
206
+ this.compiler.outputPath, this.opts.reportFilename || "report.html"),
121
207
  reportTitle: this.opts.reportTitle,
122
208
  compressionAlgorithm: this.opts.compressionAlgorithm,
123
209
  bundleDir: this.getBundleDirFromCompiler(),
@@ -127,18 +213,20 @@ class BundleAnalyzerPlugin {
127
213
  });
128
214
  }
129
215
  getBundleDirFromCompiler() {
130
- if (typeof this.compiler.outputFileSystem.constructor === 'undefined') {
131
- return this.compiler.outputPath;
216
+ const outputFileSystemConstructor = /** @type {OutputFileSystem} */
217
+ (/** @type {Compiler} */this.compiler.outputFileSystem).constructor;
218
+ if (typeof outputFileSystemConstructor === "undefined") {
219
+ return /** @type {Compiler} */this.compiler.outputPath;
132
220
  }
133
- switch (this.compiler.outputFileSystem.constructor.name) {
134
- case 'MemoryFileSystem':
221
+ switch (outputFileSystemConstructor.name) {
222
+ case "MemoryFileSystem":
135
223
  return null;
136
224
  // Detect AsyncMFS used by Nuxt 2.5 that replaces webpack's MFS during development
137
225
  // Related: #274
138
- case 'AsyncMFS':
226
+ case "AsyncMFS":
139
227
  return null;
140
228
  default:
141
- return this.compiler.outputPath;
229
+ return /** @type {Compiler} */this.compiler.outputPath;
142
230
  }
143
231
  }
144
232
  }
package/lib/Logger.js CHANGED
@@ -1,31 +1,89 @@
1
1
  "use strict";
2
2
 
3
- const LEVELS = ['debug', 'info', 'warn', 'error', 'silent'];
4
- const LEVEL_TO_CONSOLE_METHOD = new Map([['debug', 'log'], ['info', 'log'], ['warn', 'log']]);
3
+ /** @typedef {import("./BundleAnalyzerPlugin").EXPECTED_ANY} EXPECTED_ANY */
4
+
5
+ /** @typedef {"debug" | "info" | "warn" | "error" | "silent"} Level */
6
+
7
+ /** @type {Level[]} */
8
+ const LEVELS = ["debug", "info", "warn", "error", "silent"];
9
+
10
+ /** @type {Map<Level, string>} */
11
+ const LEVEL_TO_CONSOLE_METHOD = new Map([["debug", "log"], ["info", "log"], ["warn", "log"]]);
5
12
  class Logger {
13
+ /** @type {Level[]} */
6
14
  static levels = LEVELS;
7
- static defaultLevel = 'info';
15
+
16
+ /** @type {Level} */
17
+ static defaultLevel = "info";
18
+
19
+ /**
20
+ * @param {Level=} level level
21
+ */
8
22
  constructor(level = Logger.defaultLevel) {
23
+ /** @type {Set<Level>} */
9
24
  this.activeLevels = new Set();
10
25
  this.setLogLevel(level);
11
26
  }
27
+
28
+ /**
29
+ * @param {Level} level level
30
+ */
12
31
  setLogLevel(level) {
13
32
  const levelIndex = LEVELS.indexOf(level);
14
- if (levelIndex === -1) throw new Error(`Invalid log level "${level}". Use one of these: ${LEVELS.join(', ')}`);
33
+ if (levelIndex === -1) {
34
+ throw new Error(`Invalid log level "${level}". Use one of these: ${LEVELS.join(", ")}`);
35
+ }
15
36
  this.activeLevels.clear();
16
37
  for (const [i, level] of LEVELS.entries()) {
17
38
  if (i >= levelIndex) this.activeLevels.add(level);
18
39
  }
19
40
  }
41
+
42
+ /**
43
+ * @template {EXPECTED_ANY[]} T
44
+ * @param {T} args args
45
+ */
46
+ debug(...args) {
47
+ if (!this.activeLevels.has("debug")) return;
48
+ this._log("debug", ...args);
49
+ }
50
+
51
+ /**
52
+ * @template {EXPECTED_ANY[]} T
53
+ * @param {T} args args
54
+ */
55
+ info(...args) {
56
+ if (!this.activeLevels.has("info")) return;
57
+ this._log("info", ...args);
58
+ }
59
+
60
+ /**
61
+ * @template {EXPECTED_ANY[]} T
62
+ * @param {T} args args
63
+ */
64
+ error(...args) {
65
+ if (!this.activeLevels.has("error")) return;
66
+ this._log("error", ...args);
67
+ }
68
+
69
+ /**
70
+ * @template {EXPECTED_ANY[]} T
71
+ * @param {T} args args
72
+ */
73
+ warn(...args) {
74
+ if (!this.activeLevels.has("warn")) return;
75
+ this._log("warn", ...args);
76
+ }
77
+
78
+ /**
79
+ * @template {EXPECTED_ANY[]} T
80
+ * @param {Level} level level
81
+ * @param {T} args args
82
+ */
20
83
  _log(level, ...args) {
21
- console[LEVEL_TO_CONSOLE_METHOD.get(level) || level](...args);
84
+ // eslint-disable-next-line no-console
85
+ console[(/** @type {Exclude<Level, "silent">} */
86
+ LEVEL_TO_CONSOLE_METHOD.get(level) || level)](...args);
22
87
  }
23
88
  }
24
- ;
25
- LEVELS.forEach(level => {
26
- if (level === 'silent') return;
27
- Logger.prototype[level] = function (...args) {
28
- if (this.activeLevels.has(level)) this._log(level, ...args);
29
- };
30
- });
31
89
  module.exports = Logger;