@nuxt/webpack-builder 3.14.159 → 3.15.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
@@ -109,7 +109,7 @@ Follow the docs to [Set Up Your Local Development Environment](https://nuxt.com/
109
109
  ## <a name="follow-us">🔗 Follow Us</a>
110
110
 
111
111
  <p valign="center">
112
- <a href="https://go.nuxt.com/discord"><img width="20px" src="https://github.com/nuxt/nuxt/tree/main/.github/assets/discord.svg" alt="Discord"></a>&nbsp;&nbsp;<a href="https://go.nuxt.com/x"><img width="20px" src="https://github.com/nuxt/nuxt/tree/main/.github/assets/twitter.svg" alt="Twitter"></a>&nbsp;&nbsp;<a href="https://go.nuxt.com/github"><img width="20px" src="https://github.com/nuxt/nuxt/tree/main/.github/assets/github.svg" alt="GitHub"></a>
112
+ <a href="https://go.nuxt.com/discord"><img width="20px" src="https://github.com/nuxt/nuxt/tree/main/.github/assets/discord.svg" alt="Discord"></a>&nbsp;&nbsp;<a href="https://go.nuxt.com/x"><img width="20px" src="https://github.com/nuxt/nuxt/tree/main/.github/assets/twitter.svg" alt="Twitter"></a>&nbsp;&nbsp;<a href="https://go.nuxt.com/github"><img width="20px" src="https://github.com/nuxt/nuxt/tree/main/.github/assets/github.svg" alt="GitHub"></a>&nbsp;&nbsp;<a href="https://go.nuxt.com/bluesky"><img width="20px" src="https://github.com/nuxt/nuxt/tree/main/.github/assets/bluesky.svg" alt="Bluesky"></a>
113
113
  </p>
114
114
 
115
115
  ## <a name="license">⚖️ License</a>
package/builder.mjs CHANGED
@@ -1,3 +1,4 @@
1
1
  export const builder = 'webpack'
2
2
  export { default as webpack } from 'webpack'
3
3
  export { default as MiniCssExtractPlugin } from 'mini-css-extract-plugin'
4
+ export { default as WebpackBarPlugin } from 'webpackbar'
package/dist/index.mjs CHANGED
@@ -3,17 +3,12 @@ import { fromNodeMiddleware, defineEventHandler } from 'h3';
3
3
  import webpackDevMiddleware from 'webpack-dev-middleware';
4
4
  import webpackHotMiddleware from 'webpack-hot-middleware';
5
5
  import { defu } from 'defu';
6
- import { parseURL, parseQuery, joinURL } from 'ufo';
6
+ import { joinURL } from 'ufo';
7
7
  import { logger, useNuxt } from '@nuxt/kit';
8
- import { pathToFileURL } from 'node:url';
9
8
  import { createUnplugin } from 'unplugin';
10
- import { isAbsolute, relative, join, resolve, normalize, dirname } from 'pathe';
11
- import { walk } from 'estree-walker';
12
9
  import MagicString from 'magic-string';
13
- import { hash } from 'ohash';
14
- import escapeRE from 'escape-string-regexp';
15
- import { findStaticImports, parseStaticImport } from 'mlly';
16
- import { webpack, builder, MiniCssExtractPlugin } from '#builder';
10
+ import { webpack, WebpackBarPlugin, builder, MiniCssExtractPlugin } from '#builder';
11
+ import { join, resolve, normalize, dirname, isAbsolute } from 'pathe';
17
12
  import { createFsFromVolume, Volume } from 'memfs';
18
13
  import querystring from 'node:querystring';
19
14
  import { BundleAnalyzerPlugin } from 'webpack-bundle-analyzer';
@@ -21,8 +16,8 @@ import ForkTSCheckerWebpackPlugin from 'fork-ts-checker-webpack-plugin';
21
16
  import { env, nodeless } from 'unenv';
22
17
  import { cloneDeep } from 'lodash-es';
23
18
  import TimeFixPlugin from 'time-fix-plugin';
24
- import WebpackBar from 'webpackbar';
25
19
  import FriendlyErrorsWebpackPlugin from '@nuxt/friendly-errors-webpack-plugin';
20
+ import escapeRegExp from 'escape-string-regexp';
26
21
  import { isTest } from 'std-env';
27
22
  import { EsbuildPlugin } from 'esbuild-loader';
28
23
  import CssMinimizerPlugin from 'css-minimizer-webpack-plugin';
@@ -31,246 +26,7 @@ import { createJiti } from 'jiti';
31
26
  import VueLoaderPlugin from 'vue-loader/dist/pluginWebpack5.js';
32
27
  import { mkdir, writeFile } from 'node:fs/promises';
33
28
  import { normalizeWebpackManifest } from 'vue-bundle-renderer';
34
-
35
- function matchWithStringOrRegex(value, matcher) {
36
- if (typeof matcher === "string") {
37
- return value === matcher;
38
- } else if (matcher instanceof RegExp) {
39
- return matcher.test(value);
40
- }
41
- return false;
42
- }
43
-
44
- var __defProp$4 = Object.defineProperty;
45
- var __defNormalProp$4 = (obj, key, value) => key in obj ? __defProp$4(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
46
- var __publicField$4 = (obj, key, value) => {
47
- __defNormalProp$4(obj, typeof key !== "symbol" ? key + "" : key, value);
48
- return value;
49
- };
50
- const stringTypes = ["Literal", "TemplateLiteral"];
51
- const NUXT_LIB_RE = /node_modules\/(?:nuxt|nuxt3|nuxt-nightly)\//;
52
- const SUPPORTED_EXT_RE = /\.(?:m?[jt]sx?|vue)/;
53
- const SCRIPT_RE = /(?<=<script[^>]*>)[\s\S]*?(?=<\/script>)/i;
54
- const composableKeysPlugin = createUnplugin((options) => {
55
- const composableMeta = {};
56
- const composableLengths = /* @__PURE__ */ new Set();
57
- const keyedFunctions = /* @__PURE__ */ new Set();
58
- for (const { name, ...meta } of options.composables) {
59
- composableMeta[name] = meta;
60
- keyedFunctions.add(name);
61
- composableLengths.add(meta.argumentLength);
62
- }
63
- const maxLength = Math.max(...composableLengths);
64
- const KEYED_FUNCTIONS_RE = new RegExp(`\\b(${[...keyedFunctions].map((f) => escapeRE(f)).join("|")})\\b`);
65
- return {
66
- name: "nuxt:composable-keys",
67
- enforce: "post",
68
- transformInclude(id) {
69
- const { pathname, search } = parseURL(decodeURIComponent(pathToFileURL(id).href));
70
- return !NUXT_LIB_RE.test(pathname) && SUPPORTED_EXT_RE.test(pathname) && parseQuery(search).type !== "style" && !parseQuery(search).macro;
71
- },
72
- transform(code, id) {
73
- if (!KEYED_FUNCTIONS_RE.test(code)) {
74
- return;
75
- }
76
- const { 0: script = code, index: codeIndex = 0 } = code.match(SCRIPT_RE) || { index: 0, 0: code };
77
- const s = new MagicString(code);
78
- let imports;
79
- let count = 0;
80
- const relativeID = isAbsolute(id) ? relative(options.rootDir, id) : id;
81
- const { pathname: relativePathname } = parseURL(relativeID);
82
- const ast = this.parse(script, {
83
- sourceType: "module",
84
- ecmaVersion: "latest"
85
- });
86
- let scopeTracker = new ScopeTracker();
87
- const varCollector = new ScopedVarsCollector();
88
- walk(ast, {
89
- enter(_node) {
90
- if (_node.type === "BlockStatement") {
91
- scopeTracker.enterScope();
92
- varCollector.refresh(scopeTracker.curScopeKey);
93
- } else if (_node.type === "FunctionDeclaration" && _node.id) {
94
- varCollector.addVar(_node.id.name);
95
- } else if (_node.type === "VariableDeclarator") {
96
- varCollector.collect(_node.id);
97
- }
98
- },
99
- leave(_node) {
100
- if (_node.type === "BlockStatement") {
101
- scopeTracker.leaveScope();
102
- varCollector.refresh(scopeTracker.curScopeKey);
103
- }
104
- }
105
- });
106
- scopeTracker = new ScopeTracker();
107
- walk(ast, {
108
- enter(_node) {
109
- if (_node.type === "BlockStatement") {
110
- scopeTracker.enterScope();
111
- }
112
- if (_node.type !== "CallExpression" || _node.callee.type !== "Identifier") {
113
- return;
114
- }
115
- const node = _node;
116
- const name = "name" in node.callee && node.callee.name;
117
- if (!name || !keyedFunctions.has(name) || node.arguments.length >= maxLength) {
118
- return;
119
- }
120
- imports = imports || detectImportNames(script, composableMeta);
121
- if (imports.has(name)) {
122
- return;
123
- }
124
- const meta = composableMeta[name];
125
- if (varCollector.hasVar(scopeTracker.curScopeKey, name)) {
126
- let skip = true;
127
- if (meta.source) {
128
- skip = !matchWithStringOrRegex(relativePathname, meta.source);
129
- }
130
- if (skip) {
131
- return;
132
- }
133
- }
134
- if (node.arguments.length >= meta.argumentLength) {
135
- return;
136
- }
137
- switch (name) {
138
- case "useState":
139
- if (stringTypes.includes(node.arguments[0]?.type)) {
140
- return;
141
- }
142
- break;
143
- case "useFetch":
144
- case "useLazyFetch":
145
- if (stringTypes.includes(node.arguments[1]?.type)) {
146
- return;
147
- }
148
- break;
149
- case "useAsyncData":
150
- case "useLazyAsyncData":
151
- if (stringTypes.includes(node.arguments[0]?.type) || stringTypes.includes(node.arguments[node.arguments.length - 1]?.type)) {
152
- return;
153
- }
154
- break;
155
- }
156
- const newCode = code.slice(codeIndex + node.start, codeIndex + node.end - 1).trim();
157
- const endsWithComma = newCode[newCode.length - 1] === ",";
158
- s.appendLeft(
159
- codeIndex + node.end - 1,
160
- (node.arguments.length && !endsWithComma ? ", " : "") + "'$" + hash(`${relativeID}-${++count}`) + "'"
161
- );
162
- },
163
- leave(_node) {
164
- if (_node.type === "BlockStatement") {
165
- scopeTracker.leaveScope();
166
- }
167
- }
168
- });
169
- if (s.hasChanged()) {
170
- return {
171
- code: s.toString(),
172
- map: options.sourcemap ? s.generateMap({ hires: true }) : void 0
173
- };
174
- }
175
- }
176
- };
177
- });
178
- class ScopeTracker {
179
- constructor() {
180
- // the top of the stack is not a part of current key, it is used for next level
181
- __publicField$4(this, "scopeIndexStack");
182
- __publicField$4(this, "curScopeKey");
183
- this.scopeIndexStack = [0];
184
- this.curScopeKey = "";
185
- }
186
- getKey() {
187
- return this.scopeIndexStack.slice(0, -1).join("-");
188
- }
189
- enterScope() {
190
- this.scopeIndexStack.push(0);
191
- this.curScopeKey = this.getKey();
192
- }
193
- leaveScope() {
194
- this.scopeIndexStack.pop();
195
- this.curScopeKey = this.getKey();
196
- this.scopeIndexStack[this.scopeIndexStack.length - 1]++;
197
- }
198
- }
199
- class ScopedVarsCollector {
200
- constructor() {
201
- __publicField$4(this, "curScopeKey");
202
- __publicField$4(this, "all");
203
- this.all = /* @__PURE__ */ new Map();
204
- this.curScopeKey = "";
205
- }
206
- refresh(scopeKey) {
207
- this.curScopeKey = scopeKey;
208
- }
209
- addVar(name) {
210
- let vars = this.all.get(this.curScopeKey);
211
- if (!vars) {
212
- vars = /* @__PURE__ */ new Set();
213
- this.all.set(this.curScopeKey, vars);
214
- }
215
- vars.add(name);
216
- }
217
- hasVar(scopeKey, name) {
218
- const indices = scopeKey.split("-").map(Number);
219
- for (let i = indices.length; i >= 0; i--) {
220
- if (this.all.get(indices.slice(0, i).join("-"))?.has(name)) {
221
- return true;
222
- }
223
- }
224
- return false;
225
- }
226
- collect(n) {
227
- const t = n.type;
228
- if (t === "Identifier") {
229
- this.addVar(n.name);
230
- } else if (t === "RestElement") {
231
- this.collect(n.argument);
232
- } else if (t === "AssignmentPattern") {
233
- this.collect(n.left);
234
- } else if (t === "ArrayPattern") {
235
- n.elements.forEach((e) => e && this.collect(e));
236
- } else if (t === "ObjectPattern") {
237
- n.properties.forEach((p) => {
238
- if (p.type === "RestElement") {
239
- this.collect(p);
240
- } else {
241
- this.collect(p.value);
242
- }
243
- });
244
- }
245
- }
246
- }
247
- const NUXT_IMPORT_RE = /nuxt|#app|#imports/;
248
- function detectImportNames(code, composableMeta) {
249
- const names = /* @__PURE__ */ new Set();
250
- function addName(name, specifier) {
251
- const source = composableMeta[name]?.source;
252
- if (source && matchWithStringOrRegex(specifier, source)) {
253
- return;
254
- }
255
- names.add(name);
256
- }
257
- for (const i of findStaticImports(code)) {
258
- if (NUXT_IMPORT_RE.test(i.specifier)) {
259
- continue;
260
- }
261
- const { namedImports = {}, defaultImport, namespacedImport } = parseStaticImport(i);
262
- for (const name in namedImports) {
263
- addName(namedImports[name], i.specifier);
264
- }
265
- if (defaultImport) {
266
- addName(defaultImport, i.specifier);
267
- }
268
- if (namespacedImport) {
269
- addName(namespacedImport, i.specifier);
270
- }
271
- }
272
- return names;
273
- }
29
+ import { hash } from 'ohash';
274
30
 
275
31
  const defaults = {
276
32
  globalPublicPath: "__webpack_public_path__",
@@ -298,16 +54,9 @@ ${options.globalPublicPath} = buildAssetsURL();
298
54
  };
299
55
  });
300
56
 
301
- var __defProp$3 = Object.defineProperty;
302
- var __defNormalProp$3 = (obj, key, value) => key in obj ? __defProp$3(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
303
- var __publicField$3 = (obj, key, value) => {
304
- __defNormalProp$3(obj, key + "" , value);
305
- return value;
306
- };
307
57
  const pluginName = "ChunkErrorPlugin";
308
58
  class ChunkErrorPlugin {
309
- constructor() {
310
- __publicField$3(this, "script", `
59
+ script = `
311
60
  if (typeof ${webpack.RuntimeGlobals.require} !== "undefined") {
312
61
  var _ensureChunk = ${webpack.RuntimeGlobals.ensureChunk};
313
62
  ${webpack.RuntimeGlobals.ensureChunk} = function (chunkId) {
@@ -318,8 +67,7 @@ if (typeof ${webpack.RuntimeGlobals.require} !== "undefined") {
318
67
  throw error
319
68
  });
320
69
  };
321
- };`);
322
- }
70
+ };`;
323
71
  apply(compiler) {
324
72
  compiler.hooks.thisCompilation.tap(
325
73
  pluginName,
@@ -419,15 +167,9 @@ function assets(ctx) {
419
167
  );
420
168
  }
421
169
 
422
- var __defProp$2 = Object.defineProperty;
423
- var __defNormalProp$2 = (obj, key, value) => key in obj ? __defProp$2(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
424
- var __publicField$2 = (obj, key, value) => {
425
- __defNormalProp$2(obj, key + "" , value);
426
- return value;
427
- };
428
170
  class WarningIgnorePlugin {
171
+ filter;
429
172
  constructor(filter) {
430
- __publicField$2(this, "filter");
431
173
  this.filter = filter;
432
174
  }
433
175
  apply(compiler) {
@@ -495,7 +237,7 @@ function basePlugins(ctx) {
495
237
  server: "orange",
496
238
  modern: "blue"
497
239
  };
498
- ctx.config.plugins.push(new WebpackBar({
240
+ ctx.config.plugins.push(new WebpackBarPlugin({
499
241
  name: ctx.name,
500
242
  color: colors[ctx.name],
501
243
  reporters: ["stats"],
@@ -508,18 +250,18 @@ function basePlugins(ctx) {
508
250
  ctx.nuxt.callHook(`${builder}:change`, shortPath);
509
251
  }
510
252
  },
511
- done: ({ state }) => {
512
- if (state.hasErrors) {
253
+ done: (_, { stats }) => {
254
+ if (stats.hasErrors()) {
513
255
  ctx.nuxt.callHook(`${builder}:error`);
514
256
  } else {
515
- logger.success(`${state.name} ${state.message}`);
257
+ logger.success(`Finished building ${stats.compilation.name ?? "Nuxt app"}`);
516
258
  }
517
259
  },
518
260
  allDone: () => {
519
261
  ctx.nuxt.callHook(`${builder}:done`);
520
262
  },
521
- progress({ statesArray }) {
522
- ctx.nuxt.callHook(`${builder}:progress`, statesArray);
263
+ progress: ({ webpackbar }) => {
264
+ ctx.nuxt.callHook(`${builder}:progress`, webpackbar.statesArray);
523
265
  }
524
266
  }
525
267
  }
@@ -566,7 +308,7 @@ function baseTranspile(ctx) {
566
308
  }
567
309
  }
568
310
  if (typeof pattern === "string") {
569
- transpile.push(new RegExp(escapeRE(normalize(pattern))));
311
+ transpile.push(new RegExp(escapeRegExp(normalize(pattern))));
570
312
  } else if (pattern instanceof RegExp) {
571
313
  transpile.push(pattern);
572
314
  }
@@ -855,15 +597,9 @@ const isCSSRegExp = /\.css(?:\?[^.]+)?$/;
855
597
  const isCSS = (file) => isCSSRegExp.test(file);
856
598
  const isHotUpdate = (file) => file.includes("hot-update");
857
599
 
858
- var __defProp$1 = Object.defineProperty;
859
- var __defNormalProp$1 = (obj, key, value) => key in obj ? __defProp$1(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
860
- var __publicField$1 = (obj, key, value) => {
861
- __defNormalProp$1(obj, key + "" , value);
862
- return value;
863
- };
864
600
  class VueSSRClientPlugin {
601
+ options;
865
602
  constructor(options) {
866
- __publicField$1(this, "options");
867
603
  this.options = Object.assign({
868
604
  filename: null
869
605
  }, options);
@@ -897,7 +633,7 @@ class VueSSRClientPlugin {
897
633
  }
898
634
  if (isFileJS) {
899
635
  const componentHash = hash(chunkNames.join("|"));
900
- const map = assetsMapping[componentHash] || (assetsMapping[componentHash] = []);
636
+ const map = assetsMapping[componentHash] ||= [];
901
637
  map.push(file);
902
638
  }
903
639
  }
@@ -963,16 +699,10 @@ class VueSSRClientPlugin {
963
699
  }
964
700
  }
965
701
 
966
- var __defProp = Object.defineProperty;
967
- var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
968
- var __publicField = (obj, key, value) => {
969
- __defNormalProp(obj, key + "" , value);
970
- return value;
971
- };
972
702
  const JS_MAP_RE = /\.js\.map$/;
973
703
  class VueSSRServerPlugin {
704
+ options;
974
705
  constructor(options = {}) {
975
- __publicField(this, "options");
976
706
  this.options = Object.assign({
977
707
  filename: null
978
708
  }, options);
@@ -1242,7 +972,7 @@ function serverStandalone(ctx) {
1242
972
  resolve(ctx.nuxt.options.rootDir, ctx.nuxt.options.dir.shared)
1243
973
  ];
1244
974
  if (!ctx.nuxt.options.dev) {
1245
- external.push("#internal/nuxt/paths");
975
+ external.push("#internal/nuxt/paths", "#app-manifest");
1246
976
  }
1247
977
  if (!Array.isArray(ctx.config.externals)) {
1248
978
  return;
@@ -1291,11 +1021,6 @@ const bundle = async (nuxt) => {
1291
1021
  if (config.name === "client" && nuxt.options.experimental.emitRouteChunkError && nuxt.options.builder !== "@nuxt/rspack-builder") {
1292
1022
  config.plugins.push(new ChunkErrorPlugin());
1293
1023
  }
1294
- config.plugins.push(composableKeysPlugin.webpack({
1295
- sourcemap: !!nuxt.options.sourcemap[config.name],
1296
- rootDir: nuxt.options.rootDir,
1297
- composables: nuxt.options.optimization.keyedComposables
1298
- }));
1299
1024
  }
1300
1025
  await nuxt.callHook(`${builder}:configResolved`, webpackConfigs);
1301
1026
  const compilers = webpackConfigs.map((config) => {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nuxt/webpack-builder",
3
- "version": "3.14.159",
3
+ "version": "3.15.0",
4
4
  "repository": {
5
5
  "type": "git",
6
6
  "url": "git+https://github.com/nuxt/nuxt.git",
@@ -34,31 +34,28 @@
34
34
  "defu": "^6.1.4",
35
35
  "esbuild-loader": "^4.2.2",
36
36
  "escape-string-regexp": "^5.0.0",
37
- "estree-walker": "^3.0.3",
38
37
  "file-loader": "^6.2.0",
39
38
  "fork-ts-checker-webpack-plugin": "^9.0.2",
40
39
  "h3": "^1.13.0",
41
- "hash-sum": "^2.0.0",
42
- "jiti": "^2.4.0",
40
+ "jiti": "^2.4.2",
43
41
  "lodash-es": "4.17.21",
44
- "magic-string": "^0.30.12",
45
- "memfs": "^4.14.0",
42
+ "magic-string": "^0.30.17",
43
+ "memfs": "^4.14.1",
46
44
  "mini-css-extract-plugin": "^2.9.2",
47
- "mlly": "^1.7.2",
48
45
  "ohash": "^1.1.4",
49
46
  "pathe": "^1.1.2",
50
47
  "pify": "^6.1.0",
51
- "postcss": "^8.4.47",
48
+ "postcss": "^8.4.49",
52
49
  "postcss-import": "^16.1.0",
53
50
  "postcss-import-resolver": "^2.0.0",
54
51
  "postcss-loader": "^8.1.1",
55
52
  "postcss-url": "^10.1.3",
56
53
  "pug-plain-loader": "^1.1.0",
57
- "std-env": "^3.7.0",
54
+ "std-env": "^3.8.0",
58
55
  "time-fix-plugin": "^2.0.7",
59
56
  "ufo": "^1.5.4",
60
57
  "unenv": "^1.10.0",
61
- "unplugin": "^1.15.0",
58
+ "unplugin": "^2.1.0",
62
59
  "url-loader": "^4.1.1",
63
60
  "vue-bundle-renderer": "^2.1.1",
64
61
  "vue-loader": "^17.4.2",
@@ -66,25 +63,26 @@
66
63
  "webpack-bundle-analyzer": "^4.10.2",
67
64
  "webpack-dev-middleware": "^7.4.2",
68
65
  "webpack-hot-middleware": "^2.26.1",
69
- "webpackbar": "^6.0.1",
70
- "@nuxt/kit": "3.14.159"
66
+ "webpackbar": "^7.0.0",
67
+ "@nuxt/kit": "3.15.0"
71
68
  },
72
69
  "devDependencies": {
73
- "@rspack/core": "1.0.14",
74
- "@types/hash-sum": "1.0.2",
70
+ "@rspack/core": "1.1.8",
75
71
  "@types/lodash-es": "4.17.12",
76
72
  "@types/pify": "5.0.4",
77
73
  "@types/webpack-bundle-analyzer": "4.7.0",
78
74
  "@types/webpack-hot-middleware": "2.25.9",
79
75
  "unbuild": "latest",
80
- "vue": "3.5.12",
81
- "@nuxt/schema": "3.14.159"
76
+ "vue": "3.5.13",
77
+ "@nuxt/schema": "3.15.0"
82
78
  },
83
79
  "peerDependencies": {
84
80
  "vue": "^3.3.4"
85
81
  },
86
82
  "engines": {
87
- "node": "^14.18.0 || >=16.10.0"
83
+ "node": "^18.20.5 || ^20.9.0 || >=22.0.0"
88
84
  },
89
- "scripts": {}
85
+ "scripts": {
86
+ "test:attw": "attw --pack"
87
+ }
90
88
  }