@depup/nuxt 4.3.0-depup.0 → 4.4.2-depup.1

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.
Files changed (59) hide show
  1. package/README.md +24 -107
  2. package/dist/app/components/nuxt-announcer.d.ts +26 -0
  3. package/dist/app/components/nuxt-announcer.js +59 -0
  4. package/dist/app/components/nuxt-island.js +7 -1
  5. package/dist/app/components/nuxt-layout.js +44 -21
  6. package/dist/app/components/nuxt-link.js +29 -15
  7. package/dist/app/components/nuxt-root.vue +1 -1
  8. package/dist/app/components/nuxt-route-announcer.js +11 -2
  9. package/dist/app/components/nuxt-time.vue +13 -2
  10. package/dist/app/components/test-component-wrapper.js +10 -2
  11. package/dist/app/components/utils.d.ts +7 -1
  12. package/dist/app/components/utils.js +18 -0
  13. package/dist/app/components/welcome.vue +1 -1
  14. package/dist/app/composables/announcer.d.ts +23 -0
  15. package/dist/app/composables/announcer.js +47 -0
  16. package/dist/app/composables/asyncData.d.ts +18 -36
  17. package/dist/app/composables/asyncData.js +214 -186
  18. package/dist/app/composables/chunk.js +1 -2
  19. package/dist/app/composables/cookie.d.ts +14 -0
  20. package/dist/app/composables/cookie.js +66 -17
  21. package/dist/app/composables/error.d.ts +2 -2
  22. package/dist/app/composables/error.js +11 -1
  23. package/dist/app/composables/fetch.d.ts +11 -16
  24. package/dist/app/composables/fetch.js +79 -76
  25. package/dist/app/composables/index.d.ts +2 -0
  26. package/dist/app/composables/index.js +1 -0
  27. package/dist/app/composables/manifest.d.ts +1 -1
  28. package/dist/app/composables/manifest.js +1 -4
  29. package/dist/app/composables/pages.d.ts +2 -0
  30. package/dist/app/composables/pages.js +1 -0
  31. package/dist/app/composables/payload.js +14 -5
  32. package/dist/app/composables/preload.js +1 -1
  33. package/dist/app/composables/route-announcer.d.ts +2 -2
  34. package/dist/app/composables/route-announcer.js +6 -6
  35. package/dist/app/composables/router.d.ts +7 -0
  36. package/dist/app/composables/router.js +8 -3
  37. package/dist/app/composables/ssr.d.ts +1 -1
  38. package/dist/app/composables/ssr.js +1 -1
  39. package/dist/app/composables/state.d.ts +11 -1
  40. package/dist/app/composables/state.js +11 -2
  41. package/dist/app/composables/url.d.ts +1 -1
  42. package/dist/app/composables/url.js +1 -1
  43. package/dist/app/config.d.ts +1 -2
  44. package/dist/app/index.d.ts +2 -2
  45. package/dist/app/index.js +1 -1
  46. package/dist/app/nuxt.d.ts +46 -31
  47. package/dist/app/nuxt.js +6 -2
  48. package/dist/app/plugins/restore-state.client.js +1 -2
  49. package/dist/app/plugins/revive-payload.client.js +9 -3
  50. package/dist/app/plugins/router.js +2 -2
  51. package/dist/app/plugins/view-transitions.client.js +39 -4
  52. package/dist/compiler/runtime/index.d.ts +14 -0
  53. package/dist/compiler/runtime/index.js +14 -0
  54. package/dist/index.mjs +2592 -1365
  55. package/dist/pages/runtime/page.js +11 -21
  56. package/dist/pages/runtime/plugins/router.js +20 -8
  57. package/dist/pages/runtime/router.options.js +12 -6
  58. package/meta.js +2 -1
  59. package/package.json +77 -37
package/dist/index.mjs CHANGED
@@ -1,12 +1,12 @@
1
1
  import process from 'node:process';
2
2
  import fs, { statSync, promises, existsSync, readdirSync, mkdirSync, writeFileSync } from 'node:fs';
3
- import { mkdir, readFile, readdir, writeFile, rm, stat, unlink, open } from 'node:fs/promises';
3
+ import { mkdir, readFile, readdir, writeFile, stat, unlink, open, rm } from 'node:fs/promises';
4
4
  import { randomUUID } from 'node:crypto';
5
5
  import { AsyncLocalStorage } from 'node:async_hooks';
6
- import { dirname, resolve, normalize, basename, extname, relative, isAbsolute, join, parse as parse$1 } from 'pathe';
6
+ import { dirname, resolve, normalize, basename, extname, relative, join, isAbsolute, parse as parse$1 } from 'pathe';
7
7
  import { createHooks, createDebugger } from 'hookable';
8
8
  import ignore from 'ignore';
9
- import { useLogger, tryUseNuxt, useNuxt, directoryToURL, getLayerDirectories, resolveFiles, resolvePath, defineNuxtModule, findPath, addPlugin, addTemplate, addTypeTemplate, addComponent, useNitro, addBuildPlugin, isIgnored, resolveAlias as resolveAlias$1, addPluginTemplate, addImportsSources, addVitePlugin, createIsIgnored, updateTemplates, tryResolveModule, normalizeModuleTranspilePath, importModule, createResolver, runWithNuxtContext, nuxtCtx, loadNuxtConfig, installModules, resolveIgnorePatterns, addRouteMiddleware, resolveModuleWithOptions, normalizeTemplate, normalizePlugin } from '@nuxt/kit';
9
+ import { useLogger, tryUseNuxt, useNuxt, directoryToURL, getLayerDirectories, resolveFiles, resolvePath, defineNuxtModule, findPath, addPlugin, addTemplate, addTypeTemplate, addComponent, isIgnored, useNitro, addBuildPlugin, resolveAlias as resolveAlias$1, addPluginTemplate, addImportsSources, addVitePlugin, createIsIgnored, updateTemplates, tryResolveModule, normalizeModuleTranspilePath, importModule, createResolver, runWithNuxtContext, nuxtCtx, loadNuxtConfig, installModules, resolveIgnorePatterns, addRouteMiddleware, resolveModuleWithOptions, normalizeTemplate, normalizePlugin } from '@nuxt/kit';
10
10
  import { resolvePackageJSON, readPackage, readPackageJSON } from 'pkg-types';
11
11
  import { hash, isEqual, serialize } from 'ohash';
12
12
  import { consola } from 'consola';
@@ -14,24 +14,25 @@ import onChange from 'on-change';
14
14
  import { colors } from 'consola/utils';
15
15
  import { resolveCompatibilityDatesFromEnv, formatDate } from 'compatx';
16
16
  import escapeRE from 'escape-string-regexp';
17
- import { withTrailingSlash as withTrailingSlash$1, parseURL, parseQuery, joinURL, withLeadingSlash, withoutLeadingSlash } from 'ufo';
17
+ import { withTrailingSlash as withTrailingSlash$1, joinURL, withoutLeadingSlash } from 'ufo';
18
18
  import { ImpoundPlugin } from 'impound';
19
19
  import { defu } from 'defu';
20
20
  import { satisfies, coerce } from 'semver';
21
21
  import { isCI, provider, hasTTY } from 'std-env';
22
- import { genArrayFromRaw, genSafeVariableName, genImport, genDynamicImport, genObjectFromRawEntries, genString, genDynamicTypeImport, genExport } from 'knitwork';
22
+ import { genArrayFromRaw, genSafeVariableName, genImport, genDynamicImport, genObjectFromRawEntries, genString, genObjectKey, genInlineTypeImport, genDynamicTypeImport, genExport } from 'knitwork';
23
23
  import { resolveModulePath } from 'exsolve';
24
24
  import { addDependency } from 'nypm';
25
25
  import { reverseResolveAlias, filename, resolveAlias } from 'pathe/utils';
26
- import { createRoutesContext } from 'unplugin-vue-router';
27
- import { resolveOptions } from 'unplugin-vue-router/options';
26
+ import { createRoutesContext, resolveOptions } from 'vue-router/unplugin';
28
27
  import { createRouter, addRoute, findAllRoutes } from 'rou3';
29
- import { fileURLToPath, pathToFileURL } from 'node:url';
28
+ import { fileURLToPath } from 'node:url';
29
+ import picomatch from 'picomatch';
30
30
  import { runInNewContext } from 'node:vm';
31
31
  import { klona } from 'klona';
32
32
  import { parseAndWalk, ScopeTracker, walk, isBindingIdentifier, getUndeclaredIdentifiersInFunction } from 'oxc-walker';
33
33
  import { parseSync } from 'oxc-parser';
34
34
  import { transformSync } from 'oxc-transform';
35
+ import { compileParsePath, buildTree, removeFile, addFile, toVueRouter4 } from 'unrouting';
35
36
  import { splitByCase, kebabCase, pascalCase, camelCase } from 'scule';
36
37
  import { createUnplugin } from 'unplugin';
37
38
  import { findStaticImports, findExports, parseStaticImport, parseNodeModulePath, lookupNodeModuleSubpath } from 'mlly';
@@ -41,17 +42,16 @@ import { defineUnimportPreset, createUnimport, toExports, toTypeDeclarationFile,
41
42
  import { glob } from 'tinyglobby';
42
43
  import { parse, walk as walk$1, ELEMENT_NODE } from 'ultrahtml';
43
44
  import { isObject } from '@vue/shared';
44
- import { parseQuery as parseQuery$1 } from 'vue-router';
45
+ import { resolve as resolve$1 } from 'node:path';
46
+ import { parseTar, createTar } from 'nanotar';
45
47
  import { createTransformer } from 'unctx/transform';
48
+ import { performance } from 'node:perf_hooks';
46
49
  import { watch as watch$1 } from 'chokidar';
47
50
  import { debounce } from 'perfect-debounce';
48
51
  import { resolveSchema, generateTypes } from 'untyped';
49
52
  import untypedPlugin from 'untyped/babel-plugin';
50
53
  import { createJiti } from 'jiti';
51
54
  import { minifySync } from 'oxc-minify';
52
- import { performance } from 'node:perf_hooks';
53
- import { resolve as resolve$1 } from 'node:path';
54
- import { parseTar, createTar } from 'nanotar';
55
55
 
56
56
  function toArray(value) {
57
57
  return Array.isArray(value) ? value : [value];
@@ -66,6 +66,10 @@ function isDirectorySync(path) {
66
66
  return false;
67
67
  }
68
68
  }
69
+ const LEADING_DOT_RE = /^\.+/g;
70
+ function normalizeExtension(input) {
71
+ return input.replace(LEADING_DOT_RE, "");
72
+ }
69
73
  function stripExtension(path) {
70
74
  return path.replace(/\.[^./\\]+$/, "");
71
75
  }
@@ -73,6 +77,10 @@ function isWhitespace(char) {
73
77
  const c = typeof char === "string" ? char.charCodeAt(0) : char;
74
78
  return c === 32 || c === 9 || c === 10 || c === 13 || c === 12;
75
79
  }
80
+ const JS_EXT_RE = /^[^?]*\.(?:[jt]sx?|[cm][jt]s)(?:$|\?)/;
81
+ const NUXT_LIB_RE = /^[^?]*node_modules\/(?:nuxt|nuxt3|nuxt-nightly|@nuxt)\//;
82
+ const STYLE_QUERY_RE$1 = /[?&]type=style/;
83
+ const MACRO_QUERY_RE$2 = /[?&]macro(?:=|&|$)/;
76
84
  const DECLARATION_EXTENSIONS = ["d.ts", "d.mts", "d.cts", "d.vue.ts", "d.vue.mts", "d.vue.cts"];
77
85
  const logger = useLogger("nuxt");
78
86
  function resolveToAlias(path, nuxt = tryUseNuxt()) {
@@ -192,34 +200,51 @@ function resolveComponentNameSegments(fileName, prefixParts) {
192
200
  return [...componentNameParts, ...fileNameParts];
193
201
  }
194
202
 
203
+ function parseModuleId(id) {
204
+ const qIndex = id.indexOf("?");
205
+ if (qIndex === -1) {
206
+ return { pathname: id, search: "" };
207
+ }
208
+ return { pathname: id.slice(0, qIndex), search: id.slice(qIndex) };
209
+ }
210
+ const NUXT_COMPONENT_RE = /[?&]nuxt_component=/;
211
+ const MACRO_RE$1 = /[?&]macro=/;
212
+ const VUE_QUERY_RE = /[?&]vue(?:&|$)/;
213
+ const SETUP_QUERY_RE = /[?&]setup(?:=|&|$)/;
214
+ const TYPE_QUERY_RE = /[?&]type=([^&]*)/;
195
215
  function isVue(id, opts = {}) {
196
- const { search } = parseURL(decodeURIComponent(pathToFileURL(id).href));
216
+ const { search } = parseModuleId(id);
197
217
  if (id.endsWith(".vue") && !search) {
198
218
  return true;
199
219
  }
200
220
  if (!search) {
201
221
  return false;
202
222
  }
203
- const query = parseQuery(search);
204
- if (query.nuxt_component) {
223
+ if (NUXT_COMPONENT_RE.test(search)) {
205
224
  return false;
206
225
  }
207
- if (query.macro && (search === "?macro=true" || !opts.type || opts.type.includes("script"))) {
226
+ if (MACRO_RE$1.test(search) && (search === "?macro=true" || !opts.type || opts.type.includes("script"))) {
208
227
  return true;
209
228
  }
210
- const type = "setup" in query ? "script" : query.type;
211
- if (!("vue" in query) || opts.type && !opts.type.includes(type)) {
229
+ if (!VUE_QUERY_RE.test(search)) {
212
230
  return false;
213
231
  }
232
+ if (opts.type) {
233
+ const type = SETUP_QUERY_RE.test(search) ? "script" : TYPE_QUERY_RE.exec(search)?.[1];
234
+ if (!type || !opts.type.includes(type)) {
235
+ return false;
236
+ }
237
+ }
214
238
  return true;
215
239
  }
216
240
  const JS_RE$1 = /\.(?:[cm]?j|t)sx?$/;
241
+ const VUE_ID_RE = /\.vue(?:\?|$)/;
217
242
  function isJS(id) {
218
- const { pathname } = parseURL(decodeURIComponent(pathToFileURL(id).href));
243
+ const { pathname } = parseModuleId(id);
219
244
  return JS_RE$1.test(pathname);
220
245
  }
221
246
  function getLoader(id) {
222
- const { pathname } = parseURL(decodeURIComponent(pathToFileURL(id).href));
247
+ const { pathname } = parseModuleId(id);
223
248
  const ext = extname(pathname);
224
249
  if (ext === ".vue") {
225
250
  return "vue";
@@ -249,30 +274,69 @@ const QUOTE_RE = /["']/g;
249
274
  const EXTENSION_RE = /\b\.\w+$/g;
250
275
  const SX_RE = /\.[tj]sx$/;
251
276
 
252
- const SegmentTokenType = {
253
- static: "static",
254
- dynamic: "dynamic",
255
- optional: "optional",
256
- catchall: "catchall",
257
- group: "group"
258
- };
259
- const SegmentParserState = {
260
- initial: "initial",
261
- ...SegmentTokenType
262
- };
263
- const enUSComparator = new Intl.Collator("en-US");
264
- async function resolvePagesRoutes(pattern, nuxt = useNuxt()) {
277
+ function createPagesContext(options = {}) {
278
+ const modes = options.shouldUseServerComponents ? ["server", "client"] : ["client"];
279
+ const treeOptions = {
280
+ roots: options.roots,
281
+ modes,
282
+ warn: (msg) => logger.warn(msg)
283
+ };
284
+ const emitOptions = {
285
+ onDuplicateRouteName: (_name, file, existingFile) => {
286
+ logger.warn(`Route name generated for \`${file}\` is the same as \`${existingFile}\`. You may wish to set a custom name using \`definePageMeta\` within the page file.`);
287
+ },
288
+ attrs: { mode: modes }
289
+ };
290
+ const compiledParse = compileParsePath(treeOptions);
291
+ let tree = buildTree([], treeOptions);
292
+ const trackedFiles = /* @__PURE__ */ new Set();
293
+ return {
294
+ emit() {
295
+ return toVueRouter4(tree, emitOptions);
296
+ },
297
+ addFile(filePath, priority = 0) {
298
+ addFile(tree, { path: filePath, priority }, compiledParse);
299
+ trackedFiles.add(filePath);
300
+ },
301
+ removeFile(filePath) {
302
+ const removed = removeFile(tree, filePath);
303
+ if (removed) {
304
+ trackedFiles.delete(filePath);
305
+ }
306
+ return removed;
307
+ },
308
+ rebuild(files) {
309
+ tree = buildTree(files, treeOptions);
310
+ trackedFiles.clear();
311
+ for (const f of files) {
312
+ trackedFiles.add(f.path);
313
+ }
314
+ },
315
+ trackedFiles
316
+ };
317
+ }
318
+ async function resolvePagesRoutes(pattern, nuxt = useNuxt(), ctx) {
265
319
  const pagesDirs = getLayerDirectories(nuxt).map((d) => d.appPages);
266
- const scannedFiles = [];
267
- for (const dir of pagesDirs) {
320
+ const inputFiles = [];
321
+ for (let priority = 0; priority < pagesDirs.length; priority++) {
322
+ const dir = pagesDirs[priority];
268
323
  const files = await resolveFiles(dir, pattern);
269
- scannedFiles.push(...files.map((file) => ({ relativePath: relative(dir, file), absolutePath: file })));
324
+ for (const file of files) {
325
+ inputFiles.push({ path: file, priority });
326
+ }
270
327
  }
271
- scannedFiles.sort((a, b) => enUSComparator.compare(a.relativePath, b.relativePath));
272
- const allRoutes = generateRoutesFromFiles(uniqueBy(scannedFiles, "relativePath"), {
273
- shouldUseServerComponents: !!nuxt.options.experimental.componentIslands
274
- });
275
- const pages = uniqueBy(allRoutes, "path");
328
+ let pages;
329
+ if (ctx) {
330
+ ctx.rebuild(inputFiles);
331
+ pages = ctx.emit();
332
+ } else {
333
+ const oneShot = createPagesContext({ roots: pagesDirs, shouldUseServerComponents: !!nuxt.options.experimental.componentIslands });
334
+ oneShot.rebuild(inputFiles);
335
+ pages = oneShot.emit();
336
+ }
337
+ return augmentAndResolve(pages, ctx?.trackedFiles ?? new Set(inputFiles.map((f) => f.path)), nuxt);
338
+ }
339
+ async function augmentAndResolve(pages, trackedFiles, nuxt = useNuxt()) {
276
340
  const shouldAugment = nuxt.options.experimental.scanPageMeta || nuxt.options.experimental.typedPages;
277
341
  if (shouldAugment === false) {
278
342
  await nuxt.callHook("pages:extend", pages);
@@ -284,7 +348,7 @@ async function resolvePagesRoutes(pattern, nuxt = useNuxt()) {
284
348
  "middleware",
285
349
  ...extraPageMetaExtractionKeys
286
350
  ]),
287
- fullyResolvedPaths: new Set(scannedFiles.map((file) => file.absolutePath))
351
+ fullyResolvedPaths: trackedFiles
288
352
  };
289
353
  if (shouldAugment === "after-resolve") {
290
354
  await nuxt.callHook("pages:extend", pages);
@@ -298,68 +362,11 @@ async function resolvePagesRoutes(pattern, nuxt = useNuxt()) {
298
362
  await nuxt.callHook("pages:resolved", pages);
299
363
  return pages;
300
364
  }
301
- const INDEX_PAGE_RE = /\/index$/;
302
- function generateRoutesFromFiles(files, options = {}) {
303
- if (!files.length) {
304
- return [];
305
- }
306
- const routes = [];
307
- const sortedFiles = [...files].sort((a, b) => a.relativePath.length - b.relativePath.length);
308
- for (const file of sortedFiles) {
309
- const segments = file.relativePath.replace(new RegExp(`${escapeRE(extname(file.relativePath))}$`), "").split("/");
310
- const route = {
311
- name: "",
312
- path: "",
313
- file: file.absolutePath,
314
- children: []
315
- };
316
- let parent = routes;
317
- const routeGroups = [];
318
- const lastSegment = segments[segments.length - 1];
319
- if (lastSegment.endsWith(".server")) {
320
- segments[segments.length - 1] = lastSegment.replace(".server", "");
321
- if (options.shouldUseServerComponents) {
322
- route.mode = "server";
323
- }
324
- } else if (lastSegment.endsWith(".client")) {
325
- segments[segments.length - 1] = lastSegment.replace(".client", "");
326
- route.mode = "client";
327
- }
328
- for (let i = 0; i < segments.length; i++) {
329
- const segment = segments[i];
330
- const tokens = parseSegment(segment, file.absolutePath);
331
- if (tokens.every((token) => token.type === SegmentTokenType.group)) {
332
- const groupNames = tokens.map((t) => t.value);
333
- routeGroups.push(...groupNames);
334
- continue;
335
- }
336
- const segmentName = tokens.map(({ value, type }) => type === SegmentTokenType.group ? "" : value).join("");
337
- route.name += (route.name && "/") + segmentName;
338
- const routePath = getRoutePath(tokens, segments[i + 1] !== void 0 && segments[i + 1] !== "index");
339
- const path = withLeadingSlash(joinURL(route.path, routePath.replace(INDEX_PAGE_RE, "/")));
340
- const child = parent.find((parentRoute) => parentRoute.name === route.name && parentRoute.path === path.replace("([^/]*)*", "(.*)*"));
341
- if (child && child.children) {
342
- parent = child.children;
343
- route.path = "";
344
- } else if (segmentName === "index" && !route.path) {
345
- route.path += "/";
346
- } else if (segmentName !== "index") {
347
- route.path += routePath;
348
- }
349
- }
350
- if (routeGroups.length > 0) {
351
- route.meta ||= {};
352
- route.meta.groups = routeGroups;
353
- }
354
- parent.push(route);
355
- }
356
- return prepareRoutes(routes);
357
- }
358
365
  async function augmentPages(routes, vfs, ctx = {}) {
359
366
  ctx.augmentedPages ??= /* @__PURE__ */ new Set();
360
367
  for (const route of routes) {
361
368
  if (route.file && !ctx.pagesToSkip?.has(route.file)) {
362
- const fileContent = route.file in vfs ? vfs[route.file] : fs.readFileSync(ctx.fullyResolvedPaths?.has(route.file) ? route.file : await resolvePath(route.file), "utf-8");
369
+ const fileContent = vfs[route.file] ?? fs.readFileSync(ctx.fullyResolvedPaths?.has(route.file) ? route.file : await resolvePath(route.file), "utf-8");
363
370
  const routeMeta = getRouteMeta(fileContent, route.file, ctx.extraExtractionKeys);
364
371
  if (route.meta) {
365
372
  routeMeta.meta = defu({}, routeMeta.meta, route.meta);
@@ -376,7 +383,7 @@ async function augmentPages(routes, vfs, ctx = {}) {
376
383
  }
377
384
  return ctx.augmentedPages;
378
385
  }
379
- const SFC_SCRIPT_RE = /<script(?<attrs>[^>]*)>(?<content>[\s\S]*?)<\/script[^>]*>/gi;
386
+ const SFC_SCRIPT_RE = /<script(?=\s|>)(?<attrs>[^>]*)>(?<content>[\s\S]*?)<\/script\s*>/gi;
380
387
  function extractScriptContent(sfc) {
381
388
  const contents = [];
382
389
  for (const match of sfc.matchAll(SFC_SCRIPT_RE)) {
@@ -497,153 +504,35 @@ function getRouteMeta(contents, absolutePath, extraExtractionKeys = /* @__PURE__
497
504
  extractCache[absolutePath] = extractedData;
498
505
  return klona(extractedData);
499
506
  }
500
- const ESCAPE_CHARS_RE = /[\\:]/g;
501
- function getRoutePath(tokens, hasSucceedingSegment = false) {
502
- return tokens.reduce((path, token) => {
503
- switch (token.type) {
504
- case SegmentTokenType.optional:
505
- return path + `:${token.value}?`;
506
- case SegmentTokenType.dynamic:
507
- return path + `:${token.value}()`;
508
- case SegmentTokenType.catchall:
509
- return path + (hasSucceedingSegment ? `:${token.value}([^/]*)*` : `:${token.value}(.*)*`);
510
- case SegmentTokenType.group:
511
- return path;
512
- case SegmentTokenType.static:
513
- default:
514
- return path + token.value.replace(ESCAPE_CHARS_RE, "\\$&");
515
- }
516
- }, "/");
517
- }
518
- const PARAM_CHAR_RE = /[\w.]/;
519
- function parseSegment(segment, absolutePath) {
520
- let state = SegmentParserState.initial;
521
- let i = 0;
522
- let buffer = "";
523
- const tokens = [];
524
- function consumeBuffer() {
525
- if (!buffer) {
526
- return;
527
- }
528
- if (state === SegmentParserState.initial) {
529
- throw new Error("wrong state");
530
- }
531
- tokens.push({ type: state, value: buffer });
532
- buffer = "";
533
- }
534
- while (i < segment.length) {
535
- const c = segment[i];
536
- switch (state) {
537
- case SegmentParserState.initial:
538
- buffer = "";
539
- if (c === "[") {
540
- state = SegmentParserState.dynamic;
541
- } else if (c === "(") {
542
- state = SegmentParserState.group;
543
- } else {
544
- i--;
545
- state = SegmentParserState.static;
546
- }
547
- break;
548
- case SegmentParserState.static:
549
- if (c === "[") {
550
- consumeBuffer();
551
- state = SegmentParserState.dynamic;
552
- } else if (c === "(") {
553
- consumeBuffer();
554
- state = SegmentParserState.group;
555
- } else {
556
- buffer += c;
557
- }
558
- break;
559
- case SegmentParserState.catchall:
560
- case SegmentParserState.dynamic:
561
- case SegmentParserState.optional:
562
- case SegmentParserState.group:
563
- if (buffer === "...") {
564
- buffer = "";
565
- state = SegmentParserState.catchall;
566
- }
567
- if (c === "[" && state === SegmentParserState.dynamic) {
568
- state = SegmentParserState.optional;
569
- }
570
- if (c === "]" && (state !== SegmentParserState.optional || segment[i - 1] === "]")) {
571
- if (!buffer) {
572
- throw new Error("Empty param");
573
- } else {
574
- consumeBuffer();
575
- }
576
- state = SegmentParserState.initial;
577
- } else if (c === ")" && state === SegmentParserState.group) {
578
- if (!buffer) {
579
- throw new Error("Empty group");
580
- } else {
581
- consumeBuffer();
582
- }
583
- state = SegmentParserState.initial;
584
- } else if (c && PARAM_CHAR_RE.test(c)) {
585
- buffer += c;
586
- } else if (state === SegmentParserState.dynamic || state === SegmentParserState.optional) {
587
- if (c !== "[" && c !== "]") {
588
- logger.warn(`'\`${c}\`' is not allowed in a dynamic route parameter and has been ignored. Consider renaming \`${absolutePath}\`.`);
589
- }
590
- }
591
- break;
592
- }
593
- i++;
594
- }
595
- if (state === SegmentParserState.dynamic) {
596
- throw new Error(`Unfinished param "${buffer}"`);
507
+ function serializeRouteValue(value, skipSerialisation = false) {
508
+ if (skipSerialisation || value === void 0) {
509
+ return void 0;
597
510
  }
598
- consumeBuffer();
599
- return tokens;
511
+ return JSON.stringify(value);
600
512
  }
601
- function findRouteByName(name, routes) {
602
- for (const route of routes) {
603
- if (route.name === name) {
604
- return route;
605
- }
606
- if (route.children && route.children.length > 0) {
607
- const child = findRouteByName(name, route.children);
608
- if (child) {
609
- return child;
610
- }
611
- }
513
+ function normalizeComponent(page, pageImport, routeName) {
514
+ if (page.mode === "server") {
515
+ return `() => createIslandPage(${routeName})`;
612
516
  }
613
- }
614
- const NESTED_PAGE_RE = /\//g;
615
- function prepareRoutes(routes, parent, names = /* @__PURE__ */ new Set()) {
616
- for (const route of routes) {
617
- if (route.name) {
618
- route.name = route.name.replace(INDEX_PAGE_RE, "").replace(NESTED_PAGE_RE, "-");
619
- if (names.has(route.name)) {
620
- const existingRoute = findRouteByName(route.name, routes);
621
- const extra = existingRoute?.name ? `is the same as \`${existingRoute.file}\`` : "is a duplicate";
622
- logger.warn(`Route name generated for \`${route.file}\` ${extra}. You may wish to set a custom name using \`definePageMeta\` within the page file.`);
623
- }
624
- }
625
- if (parent && route.path[0] === "/") {
626
- route.path = route.path.slice(1);
627
- }
628
- if (route.children?.length) {
629
- route.children = prepareRoutes(route.children, route, names);
630
- }
631
- if (route.children?.find((childRoute) => childRoute.path === "")) {
632
- delete route.name;
633
- }
634
- if (route.name) {
635
- names.add(route.name);
636
- }
517
+ if (page.mode === "client") {
518
+ return `() => createClientPage(${pageImport})`;
637
519
  }
638
- return routes;
520
+ return pageImport;
639
521
  }
640
- function serializeRouteValue(value, skipSerialisation = false) {
641
- if (skipSerialisation || value === void 0) {
642
- return void 0;
522
+ function normalizeComponentWithName(page, isSyncImport, pageImportName, pageImport, routeName, metaRouteName) {
523
+ if (isSyncImport) {
524
+ return `Object.assign(${pageImportName}, { __name: ${metaRouteName} })`;
643
525
  }
644
- return JSON.stringify(value);
526
+ if (page.mode === "server") {
527
+ return `() => createIslandPage(${routeName})`;
528
+ }
529
+ if (page.mode === "client") {
530
+ return `() => createClientPage(${pageImport}).then((c) => Object.assign(c, { __name: ${metaRouteName} }))`;
531
+ }
532
+ return `${pageImport}.then((m) => Object.assign(m.default, { __name: ${metaRouteName} }))`;
645
533
  }
646
534
  function normalizeRoutes(routes, metaImports = /* @__PURE__ */ new Set(), options) {
535
+ const nuxt = useNuxt();
647
536
  return {
648
537
  imports: metaImports,
649
538
  routes: genArrayFromRaw(routes.map((page) => {
@@ -683,15 +572,18 @@ function normalizeRoutes(routes, metaImports = /* @__PURE__ */ new Set(), option
683
572
  if (page._sync) {
684
573
  metaImports.add(genImport(file, [{ name: "default", as: pageImportName }]));
685
574
  }
686
- const pageImport = page._sync && page.mode !== "client" ? pageImportName : genDynamicImport(file);
575
+ const isSyncImport = page._sync && page.mode !== "client";
576
+ const pageImport = isSyncImport ? pageImportName : genDynamicImport(file);
577
+ const metaRouteName = `${metaImportName}?.name ?? ${route.name}`;
578
+ const component = nuxt.options.experimental.normalizePageNames ? normalizeComponentWithName(page, isSyncImport, pageImportName, pageImport, route.name, metaRouteName) : normalizeComponent(page, pageImport, route.name);
687
579
  const metaRoute = {
688
- name: `${metaImportName}?.name ?? ${route.name}`,
580
+ name: metaRouteName,
689
581
  path: `${metaImportName}?.path ?? ${route.path}`,
690
582
  props: `${metaImportName}?.props ?? ${route.props ?? false}`,
691
583
  meta: `${metaImportName} || {}`,
692
584
  alias: `${metaImportName}?.alias || []`,
693
585
  redirect: `${metaImportName}?.redirect`,
694
- component: page.mode === "server" ? `() => createIslandPage(${route.name})` : page.mode === "client" ? `() => createClientPage(${pageImport})` : pageImport
586
+ component
695
587
  };
696
588
  if (page.mode === "server") {
697
589
  metaImports.add(`
@@ -866,12 +758,10 @@ const PageMetaPlugin = (options = {}) => createUnplugin(() => {
866
758
  return {
867
759
  name: "nuxt:pages-macros-transform",
868
760
  enforce: "post",
869
- transformInclude(id) {
870
- return !!parseMacroQuery(id).macro;
871
- },
872
761
  transform: {
873
762
  filter: {
874
763
  id: {
764
+ include: /[?&]macro=true\b/,
875
765
  exclude: [/(?:\?|%3F).*type=(?:style|template)/]
876
766
  },
877
767
  code: {
@@ -919,7 +809,7 @@ const PageMetaPlugin = (options = {}) => createUnplugin(() => {
919
809
  if (!hasMacro && !code.includes("export { default }") && !code.includes("__nuxt_page_meta")) {
920
810
  if (!code) {
921
811
  s.append(options.dev ? CODE_DEV_EMPTY + CODE_HMR : CODE_EMPTY);
922
- const { pathname } = parseURL(decodeURIComponent(pathToFileURL(id).href));
812
+ const { pathname } = parseModuleId(id);
923
813
  logger.error(`The file \`${pathname}\` is not a valid page as it has no content.`);
924
814
  } else {
925
815
  s.overwrite(0, code.length, options.dev ? CODE_DEV_EMPTY + CODE_HMR : CODE_EMPTY);
@@ -1033,21 +923,46 @@ const PageMetaPlugin = (options = {}) => createUnplugin(() => {
1033
923
  const metaCode = code.slice(meta.start, meta.end);
1034
924
  const m = new MagicString(metaCode);
1035
925
  if (meta.type === "ObjectExpression") {
926
+ const omitProp = (prop, i) => {
927
+ const nextProperty = meta.properties[i + 1];
928
+ if (nextProperty) {
929
+ m.overwrite(prop.start - meta.start, nextProperty.start - meta.start, "");
930
+ } else if (code[prop.end] === ",") {
931
+ m.overwrite(prop.start - meta.start, prop.end - meta.start + 1, "");
932
+ } else {
933
+ m.overwrite(prop.start - meta.start, prop.end - meta.start, "");
934
+ }
935
+ };
1036
936
  for (let i = 0; i < meta.properties.length; i++) {
1037
937
  const prop = meta.properties[i];
1038
- if (prop.type === "Property" && prop.key.type === "Identifier" && options.extractedKeys?.includes(prop.key.name)) {
938
+ if (prop.type !== "Property" || prop.key.type !== "Identifier") {
939
+ continue;
940
+ }
941
+ if (options.extractedKeys?.includes(prop.key.name)) {
1039
942
  const { serializable } = isSerializable(metaCode, prop.value);
1040
- if (!serializable) {
1041
- continue;
943
+ if (serializable) {
944
+ omitProp(prop, i);
1042
945
  }
1043
- const nextProperty = meta.properties[i + 1];
1044
- if (nextProperty) {
1045
- m.overwrite(prop.start - meta.start, nextProperty.start - meta.start, "");
1046
- } else if (code[prop.end] === ",") {
1047
- m.overwrite(prop.start - meta.start, prop.end - meta.start + 1, "");
1048
- } else {
1049
- m.overwrite(prop.start - meta.start, prop.end - meta.start, "");
946
+ } else if (prop.key.name === "layout" && prop.value.type === "ObjectExpression") {
947
+ for (const layoutProp of prop.value.properties) {
948
+ if (layoutProp.type !== "Property" || layoutProp.key.type !== "Identifier") {
949
+ continue;
950
+ }
951
+ if (layoutProp.key.name === "name") {
952
+ m.appendLeft(
953
+ prop.start - meta.start,
954
+ `layout: ${code.slice(layoutProp.value.start, layoutProp.value.end)},
955
+ `
956
+ );
957
+ } else if (layoutProp.key.name === "props") {
958
+ m.appendLeft(
959
+ prop.start - meta.start,
960
+ `layoutProps: ${code.slice(layoutProp.value.start, layoutProp.value.end)},
961
+ `
962
+ );
963
+ }
1050
964
  }
965
+ omitProp(prop, i);
1051
966
  }
1052
967
  }
1053
968
  }
@@ -1114,11 +1029,17 @@ const MACRO_RE = /&macro=true/;
1114
1029
  function rewriteQuery(id) {
1115
1030
  return id.replace(/\?.+$/, (r) => "?macro=true&" + r.replace(QUERY_START_RE, "").replace(MACRO_RE, ""));
1116
1031
  }
1032
+ const MACRO_QUERY_RE$1 = /[?&]macro=true(?:&|$)/;
1033
+ const TYPE_PARAM_RE = /[?&]type=([^?&]+)/;
1034
+ const LANG_PARAM_RE = /[?&]lang=([^?&]+)/;
1117
1035
  function parseMacroQuery(id) {
1118
- const { search } = parseURL(decodeURIComponent(isAbsolute(id) ? pathToFileURL(id).href : id).replace(/\?macro=true$/, ""));
1119
- const query = parseQuery(search);
1120
- if (id.includes("?macro=true")) {
1121
- return { macro: "true", ...query };
1036
+ const { search } = parseModuleId(id);
1037
+ const query = {
1038
+ type: TYPE_PARAM_RE.exec(search)?.[1],
1039
+ lang: LANG_PARAM_RE.exec(search)?.[1] ?? void 0
1040
+ };
1041
+ if (MACRO_QUERY_RE$1.test(search)) {
1042
+ query.macro = "true";
1122
1043
  }
1123
1044
  return query;
1124
1045
  }
@@ -1178,13 +1099,13 @@ const RouteInjectionPlugin = (nuxt) => createUnplugin(() => {
1178
1099
  });
1179
1100
 
1180
1101
  const OPTIONAL_PARAM_RE = /^\/?:.*(?:\?|\(\.\*\)\*)$/;
1181
- const runtimeDir = resolve(distDir, "pages/runtime");
1182
1102
  const pagesImportPresets = [
1183
- { imports: ["definePageMeta"], from: resolve(runtimeDir, "composables") },
1103
+ { imports: ["definePageMeta"], from: "#app/composables/pages" },
1104
+ { imports: ["PageMeta"], from: "#app/composables/pages", type: true },
1184
1105
  { imports: ["useLink"], from: "vue-router" }
1185
1106
  ];
1186
1107
  const routeRulesPresets = [
1187
- { imports: ["defineRouteRules"], from: resolve(runtimeDir, "composables") }
1108
+ { imports: ["defineRouteRules"], from: "#app/composables/pages" }
1188
1109
  ];
1189
1110
  async function resolveRouterOptions(nuxt, builtInRouterOptions) {
1190
1111
  const context = {
@@ -1210,6 +1131,7 @@ const pagesModule = defineNuxtModule({
1210
1131
  pattern: `**/*{${nuxt.options.extensions.join(",")}}`
1211
1132
  }),
1212
1133
  async setup(_options, nuxt) {
1134
+ const runtimeDir = resolve(distDir, "pages/runtime");
1213
1135
  const options = typeof _options === "boolean" ? { enabled: _options ?? nuxt.options.pages, pattern: `**/*{${nuxt.options.extensions.join(",")}}` } : { ..._options };
1214
1136
  options.pattern = Array.isArray(options.pattern) ? [...new Set(options.pattern)] : options.pattern;
1215
1137
  let inlineRulesCache = {};
@@ -1224,19 +1146,34 @@ const pagesModule = defineNuxtModule({
1224
1146
  };
1225
1147
  });
1226
1148
  }
1227
- const resolvePagesRoutes$1 = async (pattern, nuxt2) => {
1228
- const pages = await resolvePagesRoutes(pattern, nuxt2);
1229
- if (nuxt2.options.experimental.inlineRouteRules) {
1149
+ const useExperimentalTypedPages = nuxt.options.experimental.typedPages;
1150
+ const builtInRouterOptions = await findPath(resolve(runtimeDir, "router.options")) || resolve(runtimeDir, "router.options");
1151
+ const pagesDirs = getLayerDirectories(nuxt).map((dirs) => dirs.appPages);
1152
+ const pagesCtx = nuxt.options.dev ? createPagesContext({
1153
+ roots: pagesDirs,
1154
+ shouldUseServerComponents: !!nuxt.options.experimental.componentIslands
1155
+ }) : void 0;
1156
+ const isPagePattern = picomatch(
1157
+ Array.isArray(options.pattern) ? options.pattern : [options.pattern]
1158
+ );
1159
+ const handleRouteRules = async (pages) => {
1160
+ if (nuxt.options.experimental.inlineRouteRules) {
1230
1161
  const routeRules = globRouteRulesFromPages(pages);
1231
1162
  await updateRouteConfig?.(routeRules);
1232
1163
  } else {
1233
1164
  removePagesRules(pages);
1234
1165
  }
1166
+ };
1167
+ const resolvePagesRoutes$1 = async (pattern, nuxt2) => {
1168
+ const pages = await resolvePagesRoutes(pattern, nuxt2, pagesCtx);
1169
+ await handleRouteRules(pages);
1235
1170
  return pages;
1236
1171
  };
1237
- const useExperimentalTypedPages = nuxt.options.experimental.typedPages;
1238
- const builtInRouterOptions = await findPath(resolve(runtimeDir, "router.options")) || resolve(runtimeDir, "router.options");
1239
- const pagesDirs = getLayerDirectories(nuxt).map((dirs) => dirs.appPages);
1172
+ const augmentAndResolvePages = async (pages, trackedFiles, nuxt2) => {
1173
+ const resolved = await augmentAndResolve(pages, trackedFiles, nuxt2);
1174
+ await handleRouteRules(resolved);
1175
+ return resolved;
1176
+ };
1240
1177
  nuxt.options.alias["#vue-router"] = "vue-router";
1241
1178
  const routerPath = await resolveTypePath("vue-router", "", nuxt.options.modulesDir) || "vue-router";
1242
1179
  nuxt.hook("prepare:types", ({ tsConfig }) => {
@@ -1369,7 +1306,7 @@ const pagesModule = defineNuxtModule({
1369
1306
  route.addToMeta(page.meta);
1370
1307
  }
1371
1308
  if (page.alias) {
1372
- route.addAlias(page.alias);
1309
+ route.addAlias(Array.isArray(page.alias) ? page.alias : [page.alias]);
1373
1310
  }
1374
1311
  if (page.name) {
1375
1312
  route.name = page.name;
@@ -1387,7 +1324,6 @@ const pagesModule = defineNuxtModule({
1387
1324
  };
1388
1325
  nuxt.hook("prepare:types", ({ references }) => {
1389
1326
  references.push({ path: declarationFile });
1390
- references.push({ types: "unplugin-vue-router/client" });
1391
1327
  });
1392
1328
  const context = createRoutesContext(resolveOptions(typedRouterOptions));
1393
1329
  await mkdir(dirname(declarationFile), { recursive: true });
@@ -1405,8 +1341,17 @@ const pagesModule = defineNuxtModule({
1405
1341
  }
1406
1342
  });
1407
1343
  }
1408
- nuxt.hook("prepare:types", ({ references }) => {
1344
+ nuxt.hook("prepare:types", ({ references, tsConfig }) => {
1409
1345
  references.push({ types: useExperimentalTypedPages ? "vue-router/auto-routes" : "vue-router" });
1346
+ tsConfig.vueCompilerOptions ||= {};
1347
+ tsConfig.vueCompilerOptions.plugins ||= [];
1348
+ tsConfig.vueCompilerOptions.plugins.push("vue-router/volar/sfc-route-blocks");
1349
+ if (useExperimentalTypedPages) {
1350
+ tsConfig.vueCompilerOptions.plugins.push({
1351
+ name: "vue-router/volar/sfc-typed-router",
1352
+ options: { rootDir: nuxt.options.rootDir }
1353
+ });
1354
+ }
1410
1355
  });
1411
1356
  nuxt.hook("imports:sources", (sources) => {
1412
1357
  const routerImports = sources.find((s) => s.from === "#app/composables/router" && s.imports.includes("onBeforeRouteLeave"));
@@ -1434,7 +1379,28 @@ const pagesModule = defineNuxtModule({
1434
1379
  if (event === "change" && !shouldAlwaysRegenerate) {
1435
1380
  return;
1436
1381
  }
1437
- if (shouldAlwaysRegenerate || updateTemplatePaths.some((dir) => path.startsWith(dir))) {
1382
+ const layerIndex = pagesDirs.findIndex((dir) => path.startsWith(dir));
1383
+ const isInPagesDir = layerIndex !== -1;
1384
+ if (pagesCtx && isInPagesDir && !shouldAlwaysRegenerate) {
1385
+ try {
1386
+ if (event === "add") {
1387
+ const relativeToDir = relative(pagesDirs[layerIndex], path);
1388
+ if (!isPagePattern(relativeToDir) || isIgnored(path)) {
1389
+ return;
1390
+ }
1391
+ pagesCtx.addFile(path, layerIndex);
1392
+ } else if (event === "unlink") {
1393
+ if (!pagesCtx.removeFile(path)) {
1394
+ return;
1395
+ }
1396
+ }
1397
+ const pages = pagesCtx.emit();
1398
+ nuxt.apps.default.pages = await augmentAndResolvePages(pages, pagesCtx.trackedFiles, nuxt);
1399
+ } catch (err) {
1400
+ logger.warn("Incremental route update failed, performing full rebuild", err);
1401
+ nuxt.apps.default.pages = await resolvePagesRoutes$1(options.pattern, nuxt);
1402
+ }
1403
+ } else if (shouldAlwaysRegenerate || updateTemplatePaths.some((dir) => path.startsWith(dir))) {
1438
1404
  nuxt.apps.default.pages = await resolvePagesRoutes$1(options.pattern, nuxt);
1439
1405
  }
1440
1406
  });
@@ -1491,7 +1457,7 @@ const pagesModule = defineNuxtModule({
1491
1457
  ...nitro.options.ssrRoutes || [],
1492
1458
  ...toRou3Patterns(nuxt.apps.default?.pages || [])
1493
1459
  ];
1494
- if (!nitro.options.static && !nitro.options.prerender.crawlLinks) {
1460
+ if (!nitro.options.static) {
1495
1461
  const routeRulesRouter = createRouter();
1496
1462
  for (const [route, rules] of Object.entries(nitro.options.routeRules)) {
1497
1463
  addRoute(routeRulesRouter, void 0, route, rules);
@@ -1673,23 +1639,25 @@ const pagesModule = defineNuxtModule({
1673
1639
  addTypeTemplate({
1674
1640
  filename: "types/layouts.d.ts",
1675
1641
  getContents: ({ app }) => {
1676
- const imports = /* @__PURE__ */ new Set();
1677
- const interfaceKeyValues = /* @__PURE__ */ new Map();
1678
- for (const layout of Object.values(app.layouts)) {
1679
- const varName = genSafeVariableName(layout.name);
1680
- imports.add(genImport(layout.file, varName));
1681
- interfaceKeyValues.set(layout.name, varName);
1682
- }
1683
1642
  return [
1684
- ...Array.from(imports),
1685
1643
  "import type { ComputedRef, MaybeRef } from 'vue'",
1644
+ "",
1645
+ "type ComponentProps<T> = T extends new(...args: any) => { $props: infer P } ? NonNullable<P>",
1646
+ " : T extends (props: infer P, ...args: any) => any ? P",
1647
+ " : {}",
1648
+ "",
1686
1649
  "declare module 'nuxt/app' {",
1687
1650
  " interface NuxtLayouts {",
1688
- ...Array.from(interfaceKeyValues.entries()).map(([key, value]) => ` '${key}': InstanceType<typeof ${value}>['$props'],`),
1689
- "}",
1651
+ ...Object.values(app.layouts).map((layout) => ` ${genObjectKey(layout.name)}: ComponentProps<${genInlineTypeImport(layout.file)}>`),
1652
+ " }",
1690
1653
  " export type LayoutKey = keyof NuxtLayouts extends never ? string : keyof NuxtLayouts",
1691
1654
  " interface PageMeta {",
1692
- " layout?: MaybeRef<LayoutKey | false> | ComputedRef<LayoutKey | false>",
1655
+ " layout?: MaybeRef<LayoutKey | false> | ComputedRef<LayoutKey | false> | {",
1656
+ " [K in LayoutKey]: {",
1657
+ " name?: MaybeRef<K | false> | ComputedRef<K | false>",
1658
+ " props?: NuxtLayouts[K]",
1659
+ " }",
1660
+ " }[LayoutKey]",
1693
1661
  " }",
1694
1662
  "}"
1695
1663
  ].join("\n");
@@ -1700,9 +1668,10 @@ const pagesModule = defineNuxtModule({
1700
1668
  filename: "types/view-transitions.d.ts",
1701
1669
  getContents: () => {
1702
1670
  return [
1671
+ "import type { ViewTransitionPageOptions } from '../types/config'",
1703
1672
  "declare module 'nuxt/app' {",
1704
1673
  " interface PageMeta {",
1705
- " viewTransition?: boolean | 'always'",
1674
+ " viewTransition?: ViewTransitionPageOptions['enabled'] | ViewTransitionPageOptions",
1706
1675
  " }",
1707
1676
  "}",
1708
1677
  "export {}"
@@ -1729,12 +1698,19 @@ if (import.meta.hot) {
1729
1698
  import.meta.hot.invalidate('[nuxt] Cannot replace routes because there is no active router. Reloading.')
1730
1699
  return
1731
1700
  }
1701
+ const addedRoutes = router.getRoutes().filter(r => !r._initial)
1732
1702
  router.clearRoutes()
1733
1703
  const routes = generateRoutes(mod.default || mod)
1734
1704
  function addRoutes (routes) {
1735
1705
  for (const route of routes) {
1736
1706
  router.addRoute(route)
1737
1707
  }
1708
+ for (const route of router.getRoutes()) {
1709
+ route._initial = true
1710
+ }
1711
+ for (const route of addedRoutes) {
1712
+ router.addRoute(route)
1713
+ }
1738
1714
  router.isReady().then(() => {
1739
1715
  // Resolve the current path against the new routes to get updated meta
1740
1716
  const newRoute = router.resolve(router.currentRoute.value.fullPath)
@@ -1758,12 +1734,16 @@ export function handleHotUpdate(_router, _generateRoutes) {
1758
1734
  import.meta.hot.data ||= {}
1759
1735
  import.meta.hot.data.router = _router
1760
1736
  import.meta.hot.data.generateRoutes = _generateRoutes
1737
+ for (const route of _router.getRoutes()) {
1738
+ route._initial = true
1739
+ }
1761
1740
  }
1762
1741
  }
1763
1742
  `
1764
1743
  );
1765
1744
 
1766
1745
  const UNHEAD_LIB_RE = /node_modules[/\\](?:@unhead[/\\][^/\\]+|unhead)[/\\]/;
1746
+ const NUXT_HEAD_RE = /node_modules[/\\]nuxt[/\\]dist[/\\]head[/\\]runtime[/\\]/;
1767
1747
  function toImports(specifiers) {
1768
1748
  return specifiers.map((specifier) => {
1769
1749
  const imported = specifier.imported;
@@ -1779,7 +1759,7 @@ const UnheadImportsPlugin = (options) => createUnplugin(() => {
1779
1759
  enforce: "post",
1780
1760
  transformInclude(id) {
1781
1761
  id = normalize(id);
1782
- return (isJS(id) || isVue(id, { type: ["script"] })) && !id.startsWith("virtual:") && !id.startsWith(normalize(distDir)) && !UNHEAD_LIB_RE.test(id);
1762
+ return (isJS(id) || isVue(id, { type: ["script"] })) && !id.startsWith("virtual:") && !id.startsWith(normalize(distDir)) && !UNHEAD_LIB_RE.test(id) && !NUXT_HEAD_RE.test(id);
1783
1763
  },
1784
1764
  transform: {
1785
1765
  filter: {
@@ -1916,7 +1896,7 @@ const granularAppPresets = [
1916
1896
  from: "#app/composables/component"
1917
1897
  },
1918
1898
  {
1919
- imports: ["useAsyncData", "useLazyAsyncData", "useNuxtData", "refreshNuxtData", "clearNuxtData"],
1899
+ imports: ["useAsyncData", "useLazyAsyncData", "useNuxtData", "refreshNuxtData", "clearNuxtData", "createUseAsyncData"],
1920
1900
  from: "#app/composables/asyncData"
1921
1901
  },
1922
1902
  {
@@ -1936,7 +1916,7 @@ const granularAppPresets = [
1936
1916
  from: "#app/composables/error"
1937
1917
  },
1938
1918
  {
1939
- imports: ["useFetch", "useLazyFetch"],
1919
+ imports: ["useFetch", "useLazyFetch", "createUseFetch"],
1940
1920
  from: "#app/composables/fetch"
1941
1921
  },
1942
1922
  {
@@ -1987,6 +1967,10 @@ const granularAppPresets = [
1987
1967
  imports: ["useRouteAnnouncer"],
1988
1968
  from: "#app/composables/route-announcer"
1989
1969
  },
1970
+ {
1971
+ imports: ["useAnnouncer"],
1972
+ from: "#app/composables/announcer"
1973
+ },
1990
1974
  {
1991
1975
  imports: ["useRuntimeHook"],
1992
1976
  from: "#app/composables/runtime-hook"
@@ -2249,7 +2233,7 @@ const componentsIslandsTemplate = {
2249
2233
  }
2250
2234
  };
2251
2235
  const NON_VUE_RE = /\b\.(?!vue)\w+$/g;
2252
- function resolveComponentTypes(app, baseDir) {
2236
+ function resolveComponentTypes(app, baseDir, dynamic) {
2253
2237
  const serverPlaceholderPath = resolve(distDir, "app/components/server-placeholder");
2254
2238
  const componentTypes = [];
2255
2239
  for (const c of app.components) {
@@ -2257,7 +2241,7 @@ function resolveComponentTypes(app, baseDir) {
2257
2241
  continue;
2258
2242
  }
2259
2243
  const filePath = c.declarationPath || c.filePath;
2260
- let type = genDynamicTypeImport(isAbsolute(filePath) ? relative(baseDir, filePath).replace(NON_VUE_RE, "") : filePath.replace(NON_VUE_RE, ""), c.export);
2244
+ let type = dynamic ? renderDynamicTypeImport(baseDir, filePath, c.export) : renderLegacyTypeImport(baseDir, filePath, c.export);
2261
2245
  if (c.mode === "server") {
2262
2246
  if (app.components.some((other) => other.pascalName === c.pascalName && other.mode === "client")) {
2263
2247
  if (c.filePath.startsWith(serverPlaceholderPath)) {
@@ -2271,6 +2255,12 @@ function resolveComponentTypes(app, baseDir) {
2271
2255
  }
2272
2256
  return componentTypes;
2273
2257
  }
2258
+ function renderDynamicTypeImport(baseDir, filePath, exportName) {
2259
+ return genDynamicTypeImport(isAbsolute(filePath) ? relative(baseDir, filePath).replace(NON_VUE_RE, "") : filePath.replace(NON_VUE_RE, ""), exportName);
2260
+ }
2261
+ function renderLegacyTypeImport(baseDir, filePath, exportName) {
2262
+ return `typeof ${genDynamicImport(isAbsolute(filePath) ? relative(baseDir, filePath).replace(NON_VUE_RE, "") : filePath.replace(NON_VUE_RE, ""), { wrapper: false })}['${exportName}']`;
2263
+ }
2274
2264
  const islandType = "type IslandComponent<T> = DefineComponent<{}, {refresh: () => Promise<void>}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, SlotsType<{ fallback: { error: unknown } }>> & T";
2275
2265
  const hydrationTypes = `
2276
2266
  type HydrationStrategies = {
@@ -2288,7 +2278,7 @@ const componentsDeclarationTemplate = {
2288
2278
  filename: "components.d.ts",
2289
2279
  write: true,
2290
2280
  getContents: ({ app, nuxt }) => {
2291
- const componentTypes = resolveComponentTypes(app, nuxt.options.buildDir);
2281
+ const componentTypes = resolveComponentTypes(app, nuxt.options.buildDir, nuxt.options.experimental.typescriptPlugin);
2292
2282
  return `
2293
2283
  import type { DefineComponent, SlotsType } from 'vue'
2294
2284
  ${nuxt.options.experimental.componentIslands ? islandType : ""}
@@ -2304,14 +2294,14 @@ export const componentNames: string[]
2304
2294
  const componentsTypeTemplate = {
2305
2295
  filename: "types/components.d.ts",
2306
2296
  getContents: ({ app, nuxt }) => {
2307
- const componentTypes = resolveComponentTypes(app, join(nuxt.options.buildDir, "types"));
2297
+ const componentTypes = resolveComponentTypes(app, join(nuxt.options.buildDir, "types"), nuxt.options.experimental.typescriptPlugin);
2308
2298
  return `
2309
2299
  import type { DefineComponent, SlotsType } from 'vue'
2310
2300
  ${nuxt.options.experimental.componentIslands ? islandType : ""}
2311
2301
  ${hydrationTypes}
2312
2302
  interface _GlobalComponents {
2313
- ${componentTypes.map(([pascalName, type]) => ` '${pascalName}': ${type}`).join("\n")}
2314
- ${componentTypes.map(([pascalName, type]) => ` 'Lazy${pascalName}': LazyComponent<${type}>`).join("\n")}
2303
+ ${componentTypes.map(([pascalName, type]) => ` ${genObjectKey(pascalName)}: ${type}`).join("\n")}
2304
+ ${componentTypes.map(([pascalName, type]) => ` ${genObjectKey(`Lazy${pascalName}`)}: LazyComponent<${type}>`).join("\n")}
2315
2305
  }
2316
2306
 
2317
2307
  declare module 'vue' {
@@ -2618,7 +2608,7 @@ const IslandsTransformPlugin = (options) => createUnplugin((_options, meta) => {
2618
2608
  const islands = components.filter(
2619
2609
  (component) => component.island || component.mode === "server" && !components.some((c) => c.pascalName === component.pascalName && c.mode === "client")
2620
2610
  );
2621
- const { pathname } = parseURL(decodeURIComponent(pathToFileURL(id).href));
2611
+ const { pathname } = parseModuleId(normalize(id));
2622
2612
  return islands.some((c) => c.filePath === pathname);
2623
2613
  },
2624
2614
  transform: {
@@ -2819,6 +2809,7 @@ function TransformPlugin$1(nuxt, options) {
2819
2809
  virtualImports: ["#components"],
2820
2810
  injectAtEnd: true
2821
2811
  });
2812
+ const rootDirWithSlash = nuxt.options.rootDir.replace(/\/?$/, "/");
2822
2813
  function getComponentsImports() {
2823
2814
  const components = options.getComponents(options.mode);
2824
2815
  const clientOrServerModes = /* @__PURE__ */ new Set(["client", "server"]);
@@ -2852,11 +2843,11 @@ function TransformPlugin$1(nuxt, options) {
2852
2843
  id: COMPONENT_QUERY_RE
2853
2844
  },
2854
2845
  handler(_code, id) {
2855
- const { search } = parseURL(id);
2856
- const query = parseQuery$1(search);
2857
- const mode = query.nuxt_component;
2846
+ const { search } = parseModuleId(id);
2847
+ const params = new URLSearchParams(search);
2848
+ const mode = params.get("nuxt_component");
2858
2849
  const bare = id.replace(/\?.*/, "");
2859
- const componentExport = query.nuxt_component_export || "default";
2850
+ const componentExport = params.get("nuxt_component_export") || "default";
2860
2851
  const exportWording = componentExport === "default" ? "export default" : `export const ${componentExport} =`;
2861
2852
  if (mode === "async") {
2862
2853
  return {
@@ -2885,7 +2876,7 @@ function TransformPlugin$1(nuxt, options) {
2885
2876
  map: null
2886
2877
  };
2887
2878
  } else if (mode === "server" || mode === "server,async") {
2888
- const name = query.nuxt_component_name;
2879
+ const name = params.get("nuxt_component_name");
2889
2880
  return {
2890
2881
  code: [
2891
2882
  `import { createServerComponent } from ${JSON.stringify(options.serverComponentRuntime)}`,
@@ -2911,9 +2902,15 @@ function TransformPlugin$1(nuxt, options) {
2911
2902
  code: /#components/
2912
2903
  },
2913
2904
  async handler(code, id) {
2914
- const pkg = isAbsolute(id) && /node_modules[\\/](?!\.virtual)/.test(id) ? await readPackage(id, { try: true }) : void 0;
2915
- if (isObject(pkg) && isObject(pkg.imports) && Object.hasOwn(pkg.imports, "#components")) {
2916
- return;
2905
+ if (isAbsolute(id) && (/node_modules[\\/](?!\.virtual)/.test(id) || !id.includes(rootDirWithSlash))) {
2906
+ let pkg;
2907
+ try {
2908
+ pkg = await readPackage(id);
2909
+ } catch {
2910
+ }
2911
+ if (isObject(pkg) && isObject(pkg.imports) && Object.keys(pkg.imports).some((k) => k.includes("#components"))) {
2912
+ return;
2913
+ }
2917
2914
  }
2918
2915
  componentUnimport.modifyDynamicImports((imports) => {
2919
2916
  imports.length = 0;
@@ -2942,70 +2939,71 @@ const TreeShakeTemplatePlugin = (options) => createUnplugin(() => {
2942
2939
  return {
2943
2940
  name: "nuxt:tree-shake-template",
2944
2941
  enforce: "post",
2945
- transformInclude(id) {
2946
- const { pathname } = parseURL(decodeURIComponent(pathToFileURL(id).href));
2947
- return pathname.endsWith(".vue");
2948
- },
2949
- transform(code, id) {
2950
- const components = options.getComponents();
2951
- if (!regexpMap.has(components)) {
2952
- const serverPlaceholderPath = resolve(distDir, "app/components/server-placeholder");
2953
- const clientOnlyComponents = components.filter((c) => c.mode === "client" && !components.some((other) => other.mode !== "client" && other.pascalName === c.pascalName && !other.filePath.startsWith(serverPlaceholderPath))).flatMap((c) => [c.pascalName, c.kebabName.replaceAll("-", "_")]).concat(["ClientOnly", "client_only"]);
2954
- regexpMap.set(components, [new RegExp(`(${clientOnlyComponents.join("|")})`), new RegExp(`^(${clientOnlyComponents.map((c) => `(?:(?:_unref\\()?(?:_component_)?(?:Lazy|lazy_)?${c}\\)?)`).join("|")})$`), clientOnlyComponents]);
2955
- }
2956
- const s = new MagicString(code);
2957
- const [COMPONENTS_RE, COMPONENTS_IDENTIFIERS_RE] = regexpMap.get(components);
2958
- if (!COMPONENTS_RE.test(code)) {
2959
- return;
2960
- }
2961
- const componentsToRemoveSet = /* @__PURE__ */ new Set();
2962
- const { program: ast } = parseAndWalk(code, id, (node) => {
2963
- if (!isSsrRender(node)) {
2964
- return;
2942
+ transform: {
2943
+ filter: {
2944
+ id: { include: VUE_ID_RE }
2945
+ },
2946
+ handler(code, id) {
2947
+ const components = options.getComponents();
2948
+ if (!regexpMap.has(components)) {
2949
+ const serverPlaceholderPath = resolve(distDir, "app/components/server-placeholder");
2950
+ const clientOnlyComponents = components.filter((c) => c.mode === "client" && !components.some((other) => other.mode !== "client" && other.pascalName === c.pascalName && !other.filePath.startsWith(serverPlaceholderPath))).flatMap((c) => [c.pascalName, c.kebabName.replaceAll("-", "_")]).concat(["ClientOnly", "client_only"]);
2951
+ regexpMap.set(components, [new RegExp(`(${clientOnlyComponents.join("|")})`), new RegExp(`^(${clientOnlyComponents.map((c) => `(?:(?:_unref\\()?(?:_component_)?(?:Lazy|lazy_)?${c}\\)?)`).join("|")})$`), clientOnlyComponents]);
2965
2952
  }
2966
- const [componentCall, _, children] = node.arguments;
2967
- if (!componentCall) {
2953
+ const s = new MagicString(code);
2954
+ const [COMPONENTS_RE, COMPONENTS_IDENTIFIERS_RE] = regexpMap.get(components);
2955
+ if (!COMPONENTS_RE.test(code)) {
2968
2956
  return;
2969
2957
  }
2970
- if (componentCall.type === "Identifier" || componentCall.type === "MemberExpression" || componentCall.type === "CallExpression") {
2971
- const componentName = getComponentName(node);
2972
- if (!componentName || !COMPONENTS_IDENTIFIERS_RE.test(componentName) || children?.type !== "ObjectExpression") {
2958
+ const componentsToRemoveSet = /* @__PURE__ */ new Set();
2959
+ const { program: ast } = parseAndWalk(code, id, (node) => {
2960
+ if (!isSsrRender(node)) {
2973
2961
  return;
2974
2962
  }
2975
- const isClientOnlyComponent = CLIENT_ONLY_NAME_RE.test(componentName);
2976
- const slotsToRemove = isClientOnlyComponent ? children.properties.filter((prop) => prop.type === "Property" && prop.key.type === "Identifier" && !PLACEHOLDER_EXACT_RE.test(prop.key.name)) : children.properties;
2977
- for (const slot of slotsToRemove) {
2978
- s.remove(slot.start, slot.end + 1);
2979
- const removedCode = `({${code.slice(slot.start, slot.end + 1)}})`;
2980
- const currentState = s.toString();
2981
- parseAndWalk(removedCode, id, (node2) => {
2982
- if (!isSsrRender(node2)) {
2983
- return;
2984
- }
2985
- const name = getComponentName(node2);
2986
- if (!name) {
2987
- return;
2988
- }
2989
- const nameToRemove = isComponentNotCalledInSetup(currentState, id, name);
2990
- if (nameToRemove) {
2991
- componentsToRemoveSet.add(nameToRemove);
2992
- }
2993
- });
2963
+ const [componentCall, _, children] = node.arguments;
2964
+ if (!componentCall) {
2965
+ return;
2966
+ }
2967
+ if (componentCall.type === "Identifier" || componentCall.type === "MemberExpression" || componentCall.type === "CallExpression") {
2968
+ const componentName = getComponentName(node);
2969
+ if (!componentName || !COMPONENTS_IDENTIFIERS_RE.test(componentName) || children?.type !== "ObjectExpression") {
2970
+ return;
2971
+ }
2972
+ const isClientOnlyComponent = CLIENT_ONLY_NAME_RE.test(componentName);
2973
+ const slotsToRemove = isClientOnlyComponent ? children.properties.filter((prop) => prop.type === "Property" && prop.key.type === "Identifier" && !PLACEHOLDER_EXACT_RE.test(prop.key.name)) : children.properties;
2974
+ for (const slot of slotsToRemove) {
2975
+ s.remove(slot.start, slot.end + 1);
2976
+ const removedCode = `({${code.slice(slot.start, slot.end + 1)}})`;
2977
+ const currentState = s.toString();
2978
+ parseAndWalk(removedCode, id, (node2) => {
2979
+ if (!isSsrRender(node2)) {
2980
+ return;
2981
+ }
2982
+ const name = getComponentName(node2);
2983
+ if (!name) {
2984
+ return;
2985
+ }
2986
+ const nameToRemove = isComponentNotCalledInSetup(currentState, id, name);
2987
+ if (nameToRemove) {
2988
+ componentsToRemoveSet.add(nameToRemove);
2989
+ }
2990
+ });
2991
+ }
2994
2992
  }
2993
+ });
2994
+ const componentsToRemove = [...componentsToRemoveSet];
2995
+ const removedNodes = /* @__PURE__ */ new WeakSet();
2996
+ for (const componentName of componentsToRemove) {
2997
+ removeImportDeclaration(ast, componentName, s);
2998
+ removeVariableDeclarator(ast, componentName, s, removedNodes);
2999
+ removeFromSetupReturn(ast, componentName, s);
3000
+ }
3001
+ if (s.hasChanged()) {
3002
+ return {
3003
+ code: s.toString(),
3004
+ map: options.sourcemap ? s.generateMap({ hires: true }) : void 0
3005
+ };
2995
3006
  }
2996
- });
2997
- const componentsToRemove = [...componentsToRemoveSet];
2998
- const removedNodes = /* @__PURE__ */ new WeakSet();
2999
- for (const componentName of componentsToRemove) {
3000
- removeImportDeclaration(ast, componentName, s);
3001
- removeVariableDeclarator(ast, componentName, s, removedNodes);
3002
- removeFromSetupReturn(ast, componentName, s);
3003
- }
3004
- if (s.hasChanged()) {
3005
- return {
3006
- code: s.toString(),
3007
- map: options.sourcemap ? s.generateMap({ hires: true }) : void 0
3008
- };
3009
3007
  }
3010
3008
  }
3011
3009
  };
@@ -3916,102 +3914,1392 @@ function addDeclarationTemplates(ctx, options) {
3916
3914
  if (!nitroImport || i.dtsDisabled || nitroImport.dtsDisabled) {
3917
3915
  continue;
3918
3916
  }
3917
+ if (i.from !== nitroImport.from) {
3918
+ continue;
3919
+ }
3919
3920
  sharedImports.push(i);
3920
3921
  }
3921
3922
  await cacheImportPaths(sharedImports);
3922
- return GENERATED_BY_COMMENT + toTypeDeclarationFile(sharedImports, { resolvePath: r });
3923
+ const handCraftedDeclarations = `
3924
+ const useRuntimeConfig: (event?: import('h3').H3Event) => import('nuxt/schema').RuntimeConfig
3925
+ const useAppConfig: () => import('nuxt/schema').AppConfig
3926
+ const defineAppConfig: <C extends import('nuxt/schema').AppConfigInput>(config: C) => C
3927
+ const createError: typeof import('h3')['createError']
3928
+ const setResponseStatus: typeof import('h3')['setResponseStatus']`;
3929
+ return GENERATED_BY_COMMENT + toTypeDeclarationFile(sharedImports, { resolvePath: r }).replace(
3930
+ /^declare global \{$/m,
3931
+ `declare global {${handCraftedDeclarations}`
3932
+ );
3923
3933
  }
3924
3934
  });
3925
3935
  }
3926
3936
 
3927
- const runtimeDependencies = [
3928
- // other deps
3929
- "devalue",
3930
- "klona",
3931
- // unjs ecosystem
3932
- "defu",
3933
- "ufo",
3934
- "h3",
3935
- "destr",
3936
- "consola",
3937
- "hookable",
3938
- "unctx",
3939
- "cookie-es",
3940
- "perfect-debounce",
3941
- "ohash",
3942
- "pathe",
3943
- "uncrypto"
3944
- ];
3945
-
3946
- const version = "4.3.0";
3947
- const pkg = {
3948
- version: version};
3949
-
3950
- function createImportProtectionPatterns(nuxt, options) {
3951
- const patterns = [];
3952
- const context = contextFlags[options.context];
3953
- patterns.push([
3954
- /^(nuxt|nuxt3|nuxt-nightly)$/,
3955
- `\`nuxt\`, or \`nuxt-nightly\` cannot be imported directly in ${context}.` + (options.context === "nuxt-app" ? " Instead, import runtime Nuxt composables from `#app` or `#imports`." : "")
3956
- ]);
3957
- patterns.push([
3958
- /^((~|~~|@|@@)?\/)?nuxt\.config(\.|$)/,
3959
- "Importing directly from a `nuxt.config` file is not allowed. Instead, use runtime config or a module."
3960
- ]);
3961
- patterns.push([/(^|node_modules\/)@vue\/composition-api/]);
3962
- for (const mod of nuxt.options._installedModules) {
3963
- if (mod.entryPath) {
3964
- patterns.push([
3965
- new RegExp(`^${escapeRE(mod.entryPath)}$`),
3966
- "Importing directly from module entry-points is not allowed."
3967
- ]);
3968
- }
3969
- }
3970
- for (const i of [/(^|node_modules\/)@nuxt\/(cli|kit|test-utils)/, /(^|node_modules\/)nuxi/, /(^|node_modules\/)nitro(?:pack)?(?:-nightly)?(?:$|\/)(?!(?:dist\/)?(?:node_modules|presets|runtime|types))/, /(^|node_modules\/)nuxt\/(config|kit|schema)/]) {
3971
- patterns.push([i, `This module cannot be imported in ${context}.`]);
3972
- }
3973
- if (options.context === "nitro-app" || options.context === "shared") {
3974
- for (const i of ["#app", /^#build(\/|$)/]) {
3975
- patterns.push([i, `Vue app aliases are not allowed in ${context}.`]);
3937
+ function createScanPluginContext(code, filePath) {
3938
+ let parseResult = null;
3939
+ let parsedStaticImports = null;
3940
+ const pluginScanThisContext = {
3941
+ walkParsed: (...args) => {
3942
+ if (parseResult) {
3943
+ const options = typeof args[0] === "function" ? { enter: args[0] } : args[0];
3944
+ walk(parseResult.program, options);
3945
+ return parseResult;
3946
+ }
3947
+ parseResult = parseAndWalk.call(null, code, filePath, args[0]);
3948
+ return parseResult;
3949
+ },
3950
+ getParsedStaticImports: () => {
3951
+ if (parsedStaticImports) {
3952
+ return parsedStaticImports;
3953
+ }
3954
+ const imports = findStaticImports(code);
3955
+ parsedStaticImports = imports.map((i) => parseStaticImport(i));
3956
+ return parsedStaticImports;
3976
3957
  }
3958
+ };
3959
+ return pluginScanThisContext;
3960
+ }
3961
+ function matchWithStringOrRegex(value, matcher) {
3962
+ if (typeof matcher === "string") {
3963
+ return value === matcher;
3964
+ } else if (matcher instanceof RegExp) {
3965
+ return matcher.test(value);
3977
3966
  }
3978
- if (options.context === "nuxt-app" || options.context === "shared") {
3979
- patterns.push([
3980
- new RegExp(escapeRE(relative(nuxt.options.srcDir, resolve(nuxt.options.srcDir, nuxt.options.serverDir || "server"))) + "\\/(api|routes|middleware|plugins)\\/"),
3981
- `Importing from server is not allowed in ${context}.`
3982
- ]);
3983
- patterns.push([
3984
- /^#server(\/|$)/,
3985
- `Server aliases are not allowed in ${context}.`
3986
- ]);
3987
- }
3988
- return patterns;
3967
+ return false;
3989
3968
  }
3990
- const contextFlags = {
3991
- "nitro-app": "server runtime",
3992
- "nuxt-app": "the Vue part of your app",
3993
- "shared": "the #shared directory"
3994
- };
3995
3969
 
3996
- const TRANSFORM_MARKER = "/* _processed_nuxt_unctx_transform */\n";
3997
- const TRANSFORM_MARKER_RE = /^\/\* _processed_nuxt_unctx_transform \*\/\n/;
3998
- const UnctxTransformPlugin = (options) => createUnplugin(() => {
3999
- const transformer = createTransformer(options.transformerOptions);
4000
- return {
4001
- name: "unctx:transform",
4002
- enforce: "post",
4003
- transformInclude(id) {
4004
- return isVue(id, { type: ["template", "script"] }) || isJS(id);
4005
- },
4006
- transform: {
4007
- filter: {
4008
- ...transformer.filter,
4009
- code: {
4010
- ...transformer.filter.code,
4011
- exclude: TRANSFORM_MARKER_RE
4012
- }
4013
- },
4014
- handler(code) {
3970
+ function processImports(imports, alias) {
3971
+ const directImports = /* @__PURE__ */ new Map();
3972
+ const namespaces = /* @__PURE__ */ new Map();
3973
+ for (const i of imports) {
3974
+ const resolvedSpecifier = stripExtension(resolveAlias$1(i.specifier, alias));
3975
+ const namedImports = i.namedImports ?? {};
3976
+ for (const originalIdentifier in namedImports) {
3977
+ const localIdentifier = namedImports[originalIdentifier] || originalIdentifier;
3978
+ directImports.set(localIdentifier, {
3979
+ originalName: originalIdentifier,
3980
+ source: resolvedSpecifier
3981
+ });
3982
+ }
3983
+ if (i.namespacedImport || i.defaultImport) {
3984
+ if (!namespaces.has(resolvedSpecifier)) {
3985
+ namespaces.set(resolvedSpecifier, {
3986
+ namespaces: /* @__PURE__ */ new Set()
3987
+ });
3988
+ }
3989
+ }
3990
+ if (i.defaultImport) {
3991
+ const namespace = i.defaultImport;
3992
+ const entry = namespaces.get(resolvedSpecifier);
3993
+ entry.namespaces.add(namespace);
3994
+ directImports.set(i.defaultImport, {
3995
+ originalName: "default",
3996
+ source: resolvedSpecifier
3997
+ });
3998
+ } else if (i.namespacedImport) {
3999
+ const namespace = i.namespacedImport;
4000
+ const entry = namespaces.get(resolvedSpecifier);
4001
+ entry.namespaces.add(namespace);
4002
+ }
4003
+ }
4004
+ return {
4005
+ directImports,
4006
+ namespaces
4007
+ };
4008
+ }
4009
+ function parseStaticFunctionCall(node, filter) {
4010
+ const callExpression = node.type === "CallExpression" ? node : node.type === "ChainExpression" && node.expression.type === "CallExpression" ? node.expression : null;
4011
+ if (!callExpression) {
4012
+ return null;
4013
+ }
4014
+ let functionName;
4015
+ let identifierNode;
4016
+ if (callExpression.callee.type === "Identifier") {
4017
+ functionName = callExpression.callee.name;
4018
+ identifierNode = callExpression.callee;
4019
+ } else if (callExpression.callee.type === "ParenthesizedExpression") {
4020
+ if (callExpression.callee.expression.type === "Identifier") {
4021
+ functionName = callExpression.callee.expression.name;
4022
+ identifierNode = callExpression.callee;
4023
+ } else if ((callExpression.callee.expression.type === "TSAsExpression" || callExpression.callee.expression.type === "TSTypeAssertion" || callExpression.callee.expression.type === "TSNonNullExpression") && callExpression.callee.expression.expression.type === "Identifier") {
4024
+ functionName = callExpression.callee.expression.expression.name;
4025
+ identifierNode = callExpression.callee;
4026
+ }
4027
+ }
4028
+ if (functionName && identifierNode && filter.test(functionName)) {
4029
+ return {
4030
+ name: functionName,
4031
+ namespace: null,
4032
+ node: identifierNode,
4033
+ callExpression
4034
+ };
4035
+ }
4036
+ function getParsedMemberExpression(memberExpression) {
4037
+ let memberObjectName;
4038
+ if (memberExpression.object.type === "Identifier") {
4039
+ memberObjectName = memberExpression.object.name;
4040
+ } else if (memberExpression.object.type === "ParenthesizedExpression") {
4041
+ if (memberExpression.object.expression.type === "Identifier") {
4042
+ memberObjectName = memberExpression.object.expression.name;
4043
+ } else if ((memberExpression.object.expression.type === "TSAsExpression" || memberExpression.object.expression.type === "TSTypeAssertion" || memberExpression.object.expression.type === "TSNonNullExpression") && memberExpression.object.expression.expression.type === "Identifier") {
4044
+ memberObjectName = memberExpression.object.expression.expression.name;
4045
+ }
4046
+ }
4047
+ if (memberObjectName) {
4048
+ if (memberExpression.property.type === "Identifier" && filter.test(memberExpression.property.name)) {
4049
+ return {
4050
+ name: memberExpression.property.name,
4051
+ namespace: memberObjectName,
4052
+ node: memberExpression.property
4053
+ };
4054
+ }
4055
+ if (memberExpression.property.type === "Literal" && typeof memberExpression.property.value === "string" && filter.test(memberExpression.property.value)) {
4056
+ return {
4057
+ name: memberExpression.property.value,
4058
+ namespace: memberObjectName,
4059
+ node: memberExpression
4060
+ };
4061
+ }
4062
+ }
4063
+ return null;
4064
+ }
4065
+ if (callExpression.callee.type === "MemberExpression") {
4066
+ const val = getParsedMemberExpression(callExpression.callee);
4067
+ if (val) {
4068
+ return {
4069
+ ...val,
4070
+ callExpression
4071
+ };
4072
+ }
4073
+ } else if (callExpression.callee.type === "ParenthesizedExpression" && callExpression.callee.expression.type === "MemberExpression") {
4074
+ const val = getParsedMemberExpression(callExpression.callee.expression);
4075
+ if (val) {
4076
+ return {
4077
+ ...val,
4078
+ node: callExpression.callee,
4079
+ callExpression
4080
+ };
4081
+ }
4082
+ }
4083
+ return null;
4084
+ }
4085
+ function parseStaticExportIdentifiers(node, filter) {
4086
+ if (node.type === "ExportNamedDeclaration" && node.exportKind !== "type") {
4087
+ if (node.declaration?.type === "VariableDeclaration") {
4088
+ return node.declaration.declarations.map((d) => {
4089
+ if (d.id.type === "Identifier" && (true)) {
4090
+ return {
4091
+ localName: d.id.name,
4092
+ exportedName: d.id.name
4093
+ };
4094
+ }
4095
+ return null;
4096
+ }).filter((v) => !!v);
4097
+ }
4098
+ if (node.declaration?.type === "FunctionDeclaration") {
4099
+ if (node.declaration.id?.type === "Identifier" && (true)) {
4100
+ return [{
4101
+ localName: node.declaration.id.name,
4102
+ exportedName: node.declaration.id.name
4103
+ }];
4104
+ }
4105
+ return [];
4106
+ }
4107
+ if (node.declaration?.type === "ClassDeclaration") {
4108
+ if (node.declaration.id?.type === "Identifier" && (true)) {
4109
+ return [{
4110
+ localName: node.declaration.id.name,
4111
+ exportedName: node.declaration.id.name
4112
+ }];
4113
+ }
4114
+ return [];
4115
+ }
4116
+ if (node.specifiers && node.specifiers.length) {
4117
+ return node.specifiers.map((s) => {
4118
+ if (s.exported.type === "Identifier" && s.exportKind !== "type" && s.local.type === "Identifier" && (true)) {
4119
+ return {
4120
+ localName: s.local.name,
4121
+ exportedName: s.exported.name
4122
+ };
4123
+ }
4124
+ return null;
4125
+ }).filter((v) => !!v);
4126
+ }
4127
+ return [];
4128
+ }
4129
+ if (node.type === "ExportDefaultDeclaration" && (!node.exportKind || node.exportKind === "value")) {
4130
+ if (node.declaration.type === "Identifier") {
4131
+ return [{
4132
+ localName: node.declaration.name,
4133
+ exportedName: "default"
4134
+ }];
4135
+ }
4136
+ if (node.declaration.type === "FunctionDeclaration") {
4137
+ if (node.declaration.id?.type === "Identifier") {
4138
+ return [{
4139
+ localName: node.declaration.id.name,
4140
+ exportedName: "default"
4141
+ }];
4142
+ }
4143
+ return [];
4144
+ }
4145
+ if (node.declaration.type === "ClassDeclaration") {
4146
+ if (node.declaration.id?.type === "Identifier") {
4147
+ return [{
4148
+ localName: node.declaration.id.name,
4149
+ exportedName: "default"
4150
+ }];
4151
+ }
4152
+ return [];
4153
+ }
4154
+ }
4155
+ if (node.type === "TSExportAssignment") {
4156
+ if (node.expression.type === "Identifier") {
4157
+ {
4158
+ return [{
4159
+ localName: node.expression.name,
4160
+ exportedName: "default"
4161
+ }];
4162
+ }
4163
+ }
4164
+ }
4165
+ return [];
4166
+ }
4167
+
4168
+ function parseKeyedFunctionFactory(node, filter, scopeTracker) {
4169
+ if (node.type === "ExportNamedDeclaration") {
4170
+ let processVariableDeclarator = function(node2) {
4171
+ if (node2.init?.type !== "CallExpression" && node2.init?.type !== "ChainExpression") {
4172
+ return;
4173
+ }
4174
+ const functionCallMeta = parseStaticFunctionCall(node2.init, filter);
4175
+ if (functionCallMeta && node2?.id.type === "Identifier") {
4176
+ parsed.push({
4177
+ factoryName: functionCallMeta.name,
4178
+ factoryNode: functionCallMeta.node,
4179
+ functionName: node2.id.name,
4180
+ namespace: functionCallMeta.namespace
4181
+ });
4182
+ }
4183
+ };
4184
+ const parsed = [];
4185
+ if (node.declaration?.type === "VariableDeclaration") {
4186
+ for (const d of node.declaration.declarations) {
4187
+ processVariableDeclarator(d);
4188
+ }
4189
+ } else if (node.specifiers.length) {
4190
+ for (const specifier of node.specifiers) {
4191
+ if (specifier.type !== "ExportSpecifier" || specifier.exported.type !== "Identifier" || specifier.local.type !== "Identifier") {
4192
+ continue;
4193
+ }
4194
+ const declaration = scopeTracker.getDeclaration(specifier.local.name);
4195
+ if (declaration?.type !== "Variable") {
4196
+ continue;
4197
+ }
4198
+ for (const d of declaration.variableNode.declarations) {
4199
+ processVariableDeclarator(d);
4200
+ }
4201
+ }
4202
+ }
4203
+ return parsed;
4204
+ }
4205
+ if (node.type === "ExportDefaultDeclaration" && node.declaration.type === "CallExpression") {
4206
+ const functionCallMeta = parseStaticFunctionCall(node.declaration, filter);
4207
+ if (functionCallMeta) {
4208
+ return [{
4209
+ factoryName: functionCallMeta.name,
4210
+ factoryNode: functionCallMeta.node,
4211
+ functionName: "default",
4212
+ namespace: functionCallMeta.namespace
4213
+ }];
4214
+ }
4215
+ return [];
4216
+ }
4217
+ return [];
4218
+ }
4219
+ function createFactoryProcessor(filePath, scopeTracker, namesToFactoryMeta, imports, autoImportsToSources, alias) {
4220
+ const { directImports, namespaces } = processImports(imports, alias);
4221
+ const localFactoryNames = new Set(namesToFactoryMeta.keys());
4222
+ for (const [localName, directImport] of directImports) {
4223
+ if (namesToFactoryMeta.has(directImport.originalName)) {
4224
+ localFactoryNames.add(localName);
4225
+ }
4226
+ }
4227
+ const LOCAL_FACTORY_NAMES_RE = new RegExp(`\\b(${[...localFactoryNames].map((f) => escapeRE(f)).join("|")})\\b`);
4228
+ function _resolvePath(path) {
4229
+ let p = path;
4230
+ if (isAbsolute(p)) {
4231
+ return p;
4232
+ }
4233
+ p = resolveAlias$1(p, alias);
4234
+ if (isAbsolute(p)) {
4235
+ return p;
4236
+ }
4237
+ return join(parse$1(filePath).dir, p);
4238
+ }
4239
+ function getFactoryByLocalName(localName) {
4240
+ if (!localName) {
4241
+ return void 0;
4242
+ }
4243
+ const directImport = directImports.get(localName);
4244
+ if (directImport) {
4245
+ return namesToFactoryMeta.get(directImport.originalName);
4246
+ }
4247
+ return namesToFactoryMeta.get(localName);
4248
+ }
4249
+ function processFactory(walkContext, node, handler) {
4250
+ const parsedFactoryCalls = parseKeyedFunctionFactory(node, LOCAL_FACTORY_NAMES_RE, scopeTracker);
4251
+ if (!parsedFactoryCalls.length) {
4252
+ return;
4253
+ }
4254
+ for (const parsedFactoryCall of parsedFactoryCalls) {
4255
+ let isFactoryImport = function(node2) {
4256
+ return node2?.type === "Import" && node2.importNode.importKind !== "type";
4257
+ };
4258
+ const factoryMeta = getFactoryByLocalName(parsedFactoryCall.factoryName);
4259
+ if (!factoryMeta) {
4260
+ logger.error(`[nuxt:compiler] No factory function found for \`${parsedFactoryCall.functionName}\` in file \`${filePath}\`. This is a Nuxt bug.`);
4261
+ continue;
4262
+ }
4263
+ const scopeTrackerNode = scopeTracker.getDeclaration(!parsedFactoryCall.namespace ? parsedFactoryCall.factoryName : parsedFactoryCall.namespace);
4264
+ let importSourceResolved;
4265
+ if (isFactoryImport(scopeTrackerNode)) {
4266
+ importSourceResolved = stripExtension(_resolvePath(scopeTrackerNode.importNode.source.value));
4267
+ } else if (!scopeTrackerNode) {
4268
+ const autoImportedSource = autoImportsToSources?.get(factoryMeta.name);
4269
+ if (autoImportedSource) {
4270
+ importSourceResolved = stripExtension(_resolvePath(autoImportedSource));
4271
+ }
4272
+ }
4273
+ if (!importSourceResolved) {
4274
+ continue;
4275
+ }
4276
+ const resolvedFactorySource = factoryMeta.source;
4277
+ if (!parsedFactoryCall.namespace && // and the factory is imported directly
4278
+ (isFactoryImport(scopeTrackerNode) && // import { createUseFetch } from '...'
4279
+ (scopeTrackerNode.node.type === "ImportSpecifier" && scopeTrackerNode.node.importKind !== "type" || scopeTrackerNode.node.type === "ImportDefaultSpecifier" && factoryMeta.name === "default") && importSourceResolved === resolvedFactorySource || !scopeTrackerNode && importSourceResolved === resolvedFactorySource)) {
4280
+ handler({ parseFactoryResult: parsedFactoryCall, factory: factoryMeta });
4281
+ walkContext.skip();
4282
+ continue;
4283
+ }
4284
+ if (parsedFactoryCall.namespace) {
4285
+ const namespacedImportMeta = namespaces.get(resolvedFactorySource);
4286
+ const namespaceScopeTrackerNode = scopeTracker.getDeclaration(parsedFactoryCall.namespace);
4287
+ if (namespacedImportMeta && namespacedImportMeta.namespaces.has(parsedFactoryCall.namespace) && namespaceScopeTrackerNode?.type === "Import" && namespaceScopeTrackerNode.node.type === "ImportNamespaceSpecifier") {
4288
+ handler({ parseFactoryResult: parsedFactoryCall, factory: factoryMeta });
4289
+ }
4290
+ walkContext.skip();
4291
+ continue;
4292
+ }
4293
+ logger.debug(`[nuxt:compiler] The factory function \`${factoryMeta.name}\` used to create \`${parsedFactoryCall.functionName}\` in file \`${filePath}\` is not imported and is not in auto-imports. Skipping processing.`);
4294
+ }
4295
+ }
4296
+ return {
4297
+ processFactory
4298
+ };
4299
+ }
4300
+ function scanFileForFactories(id, code, namesToFactoryMeta, autoImportsToSources, alias) {
4301
+ const results = [];
4302
+ const context = createScanPluginContext(code, id);
4303
+ const scopeTracker = new ScopeTracker({
4304
+ preserveExitedScopes: true
4305
+ });
4306
+ const { processFactory } = createFactoryProcessor(id, scopeTracker, namesToFactoryMeta, context.getParsedStaticImports(), autoImportsToSources, alias);
4307
+ context.walkParsed({
4308
+ scopeTracker
4309
+ });
4310
+ scopeTracker.freeze();
4311
+ let isWalkingSupportedSubtree = false;
4312
+ context.walkParsed({
4313
+ scopeTracker,
4314
+ enter(node) {
4315
+ if (node.type !== "Program" && !isWalkingSupportedSubtree && node.type !== "ExportNamedDeclaration" && node.type !== "ExportDefaultDeclaration" && node.type !== "ImportDeclaration") {
4316
+ this.skip();
4317
+ }
4318
+ if (node.type !== "ExportNamedDeclaration" && node.type !== "ExportDefaultDeclaration") {
4319
+ return;
4320
+ }
4321
+ isWalkingSupportedSubtree = true;
4322
+ processFactory(this, node, ({ parseFactoryResult, factory }) => {
4323
+ results.push({
4324
+ name: parseFactoryResult.functionName,
4325
+ source: id,
4326
+ argumentLength: factory.argumentLength
4327
+ });
4328
+ });
4329
+ },
4330
+ leave(node) {
4331
+ if (node.type === "ExportNamedDeclaration" || node.type === "ExportDefaultDeclaration" || node.type === "ImportDeclaration") {
4332
+ isWalkingSupportedSubtree = false;
4333
+ }
4334
+ }
4335
+ });
4336
+ return results;
4337
+ }
4338
+ const KeyedFunctionFactoriesScanPlugin = (options) => {
4339
+ const fileResults = /* @__PURE__ */ new Map();
4340
+ const namesToFactoryMeta = new Map(options.factories.map((f) => [f.name, {
4341
+ ...f,
4342
+ source: stripExtension(resolveAlias$1(f.source, options.alias))
4343
+ }]));
4344
+ const KEYED_FUNCTION_FACTORY_NAMES_RE = new RegExp(`\\b(${options.factories.map((f) => escapeRE(f.name)).join("|")})\\b`);
4345
+ const result = {
4346
+ fileResults,
4347
+ factoryNamesRegex: KEYED_FUNCTION_FACTORY_NAMES_RE,
4348
+ namesToFactoryMeta
4349
+ };
4350
+ return {
4351
+ result,
4352
+ name: "nuxt:keyed-function-factories",
4353
+ filter: {
4354
+ id: { include: JS_EXT_RE },
4355
+ code: { include: KEYED_FUNCTION_FACTORY_NAMES_RE }
4356
+ },
4357
+ scan({ id, autoImportsToSources }) {
4358
+ const results = [];
4359
+ const scopeTracker = new ScopeTracker({
4360
+ preserveExitedScopes: true
4361
+ });
4362
+ const { processFactory } = createFactoryProcessor(id, scopeTracker, namesToFactoryMeta, this.getParsedStaticImports(), autoImportsToSources, options.alias);
4363
+ this.walkParsed({
4364
+ scopeTracker
4365
+ });
4366
+ scopeTracker.freeze();
4367
+ let isWalkingSupportedSubtree = false;
4368
+ this.walkParsed({
4369
+ scopeTracker,
4370
+ enter(node) {
4371
+ if (node.type !== "Program" && !isWalkingSupportedSubtree && node.type !== "ExportNamedDeclaration" && node.type !== "ExportDefaultDeclaration" && node.type !== "ImportDeclaration") {
4372
+ this.skip();
4373
+ }
4374
+ if (node.type !== "ExportNamedDeclaration" && node.type !== "ExportDefaultDeclaration") {
4375
+ return;
4376
+ }
4377
+ isWalkingSupportedSubtree = true;
4378
+ processFactory(this, node, ({ parseFactoryResult, factory }) => {
4379
+ results.push({
4380
+ name: parseFactoryResult.functionName,
4381
+ source: id,
4382
+ argumentLength: factory.argumentLength
4383
+ });
4384
+ });
4385
+ },
4386
+ leave(node) {
4387
+ if (node.type === "ExportNamedDeclaration" || node.type === "ExportDefaultDeclaration" || node.type === "ImportDeclaration") {
4388
+ isWalkingSupportedSubtree = false;
4389
+ }
4390
+ }
4391
+ });
4392
+ fileResults.set(id, results);
4393
+ },
4394
+ afterScan: (nuxt) => {
4395
+ for (const functions of fileResults.values()) {
4396
+ nuxt.options.optimization.keyedComposables.push(...functions);
4397
+ }
4398
+ }
4399
+ };
4400
+ };
4401
+ const KeyedFunctionFactoriesPlugin = (options) => createUnplugin(() => {
4402
+ const namesToFactoryMeta = new Map(options.factories.map((f) => [f.name, {
4403
+ ...f,
4404
+ source: stripExtension(resolveAlias$1(f.source, options.alias))
4405
+ }]));
4406
+ const KEYED_FUNCTION_FACTORY_NAMES_RE = new RegExp(`\\b(${options.factories.map((f) => escapeRE(f.name)).join("|")})\\b`);
4407
+ return {
4408
+ name: "nuxt:compiler:keyed-function-factories",
4409
+ enforce: "post",
4410
+ transform: {
4411
+ filter: {
4412
+ id: {
4413
+ include: JS_EXT_RE,
4414
+ exclude: [NUXT_LIB_RE, STYLE_QUERY_RE$1, MACRO_QUERY_RE$2]
4415
+ },
4416
+ code: { include: KEYED_FUNCTION_FACTORY_NAMES_RE }
4417
+ },
4418
+ async handler(code, id) {
4419
+ const s = new MagicString(code);
4420
+ const scopeTracker = new ScopeTracker({
4421
+ preserveExitedScopes: true
4422
+ });
4423
+ const autoImports = await options.getAutoImports();
4424
+ const autoImportsToSources = new Map(autoImports.map((i) => [i.as || i.name, i.from]));
4425
+ const { processFactory } = createFactoryProcessor(
4426
+ id,
4427
+ scopeTracker,
4428
+ namesToFactoryMeta,
4429
+ findStaticImports(code).map((i) => parseStaticImport(i)),
4430
+ autoImportsToSources,
4431
+ options.alias
4432
+ );
4433
+ function rewriteFactoryMacro(node) {
4434
+ if (node.type === "Identifier") {
4435
+ if (code[node.end] === "?" && code[node.end + 1] === ".") {
4436
+ s.overwrite(
4437
+ node.start,
4438
+ node.end + 2,
4439
+ `${node.name}?.__nuxt_factory`
4440
+ );
4441
+ } else {
4442
+ s.overwrite(
4443
+ node.start,
4444
+ node.end,
4445
+ `${node.name}.__nuxt_factory`
4446
+ );
4447
+ }
4448
+ } else if (code[node.end] === "?" && code[node.end + 1] === ".") {
4449
+ s.appendLeft(node.end + 2, "__nuxt_factory");
4450
+ } else {
4451
+ s.appendLeft(node.end, ".__nuxt_factory");
4452
+ }
4453
+ }
4454
+ const { program } = parseAndWalk(code, id, {
4455
+ scopeTracker
4456
+ });
4457
+ scopeTracker.freeze();
4458
+ walk(program, {
4459
+ // no need for a scope tracker pre-pass, since we only care about imports
4460
+ // and we only consider the root scope (because that's where an export would be - so no shadowing)
4461
+ scopeTracker,
4462
+ enter(node) {
4463
+ if (node.type !== "ExportNamedDeclaration" && node.type !== "ExportDefaultDeclaration") {
4464
+ return;
4465
+ }
4466
+ processFactory(this, node, ({ parseFactoryResult }) => {
4467
+ rewriteFactoryMacro(parseFactoryResult.factoryNode);
4468
+ });
4469
+ }
4470
+ });
4471
+ if (s.hasChanged()) {
4472
+ return {
4473
+ code: s.toString(),
4474
+ map: options.sourcemap ? s.generateMap({ hires: true }) : void 0
4475
+ };
4476
+ }
4477
+ }
4478
+ }
4479
+ };
4480
+ });
4481
+
4482
+ const stringTypes = ["Literal", "TemplateLiteral"];
4483
+ const SUPPORTED_EXT_RE$1 = /^[^?]*\.(?:m?[jt]sx?|vue)(?:$|\?)/;
4484
+ const SCRIPT_RE$1 = /(?<=<script[^>]*>)[\s\S]*?(?=<\/script>)/i;
4485
+ const NUXT_INJECTED_MARKER = "/* nuxt-injected */";
4486
+ function buildKeyedFunctionsState(keyedFunctions) {
4487
+ const namesToSourcesToFunctionMeta = /* @__PURE__ */ new Map();
4488
+ const defaultExportSources = /* @__PURE__ */ new Set();
4489
+ for (const f of keyedFunctions) {
4490
+ let functionName = f.name;
4491
+ const fnSource = typeof f.source === "string" ? stripExtension(f.source) : "";
4492
+ if (f.name === "default") {
4493
+ const parsedSource = parse$1(f.source);
4494
+ defaultExportSources.add(parsedSource.name);
4495
+ functionName = camelCase(parsedSource.name);
4496
+ }
4497
+ if (import.meta.dev) {
4498
+ const sourcesToFunctionMeta2 = namesToSourcesToFunctionMeta.get(functionName);
4499
+ const existingEntry = sourcesToFunctionMeta2?.get(fnSource);
4500
+ if (existingEntry?.source && existingEntry.source === fnSource) {
4501
+ logger.warn(`[nuxt:compiler] [keyed-functions] Duplicate function name \`${functionName}\`${functionName !== f.name ? ` defined as \`${f.name}\`` : ""} with ${f.source ? `the same source \`${f.source}\`` : "no source"} found. Overwriting the existing entry.`);
4502
+ }
4503
+ }
4504
+ let sourcesToFunctionMeta = namesToSourcesToFunctionMeta.get(functionName);
4505
+ if (!sourcesToFunctionMeta) {
4506
+ sourcesToFunctionMeta = /* @__PURE__ */ new Map();
4507
+ namesToSourcesToFunctionMeta.set(functionName, sourcesToFunctionMeta);
4508
+ }
4509
+ sourcesToFunctionMeta.set(fnSource, {
4510
+ ...f,
4511
+ // TODO: use only `fnSource` in Nuxt 5
4512
+ source: typeof f.source === "string" ? fnSource : f.source
4513
+ });
4514
+ }
4515
+ const sources = /* @__PURE__ */ new Set();
4516
+ for (const sourcesToFunctionMeta of namesToSourcesToFunctionMeta.values()) {
4517
+ for (const f of sourcesToFunctionMeta.values()) {
4518
+ if (f.source && typeof f.source === "string") {
4519
+ sources.add(f.source);
4520
+ }
4521
+ }
4522
+ }
4523
+ const codeIncludeRE = new RegExp(`\\b(${[...namesToSourcesToFunctionMeta.keys(), ...defaultExportSources].map((f) => escapeRE(f)).join("|")})\\b`);
4524
+ return { namesToSourcesToFunctionMeta, defaultExportSources, sources, codeIncludeRE };
4525
+ }
4526
+ const KeyedFunctionsPlugin = (options) => createUnplugin(() => {
4527
+ let state = buildKeyedFunctionsState(options.keyedFunctions);
4528
+ let lastKeyedFunctionsLength = options.keyedFunctions.length;
4529
+ function getState() {
4530
+ if (options.dev && options.getKeyedFunctions) {
4531
+ const current = options.getKeyedFunctions();
4532
+ if (current.length !== lastKeyedFunctionsLength) {
4533
+ state = buildKeyedFunctionsState(current);
4534
+ lastKeyedFunctionsLength = current.length;
4535
+ }
4536
+ }
4537
+ return state;
4538
+ }
4539
+ return {
4540
+ name: "nuxt:compiler:keyed-functions",
4541
+ enforce: "post",
4542
+ transform: {
4543
+ filter: {
4544
+ id: {
4545
+ include: SUPPORTED_EXT_RE$1,
4546
+ exclude: [NUXT_LIB_RE, STYLE_QUERY_RE$1, MACRO_QUERY_RE$2]
4547
+ },
4548
+ // In dev mode, skip the static code filter to allow HMR-added composables to be processed.
4549
+ // In production, use the static regex for performance.
4550
+ ...!options.dev && { code: { include: state.codeIncludeRE } }
4551
+ },
4552
+ async handler(code, _id) {
4553
+ const { namesToSourcesToFunctionMeta, sources } = getState();
4554
+ if (options.dev) {
4555
+ const { codeIncludeRE } = getState();
4556
+ if (!codeIncludeRE.test(code)) {
4557
+ return;
4558
+ }
4559
+ }
4560
+ const { 0: script = code, index: codeIndex = 0 } = code.match(SCRIPT_RE$1) || { 0: code, index: 0 };
4561
+ const id = stripExtension(_id);
4562
+ const { directImports, namespaces } = processImports(findStaticImports(script).map((i) => parseStaticImport(i)), options.alias);
4563
+ const shouldConsiderExports = sources.has(id);
4564
+ const localNamesToExportedName = /* @__PURE__ */ new Map();
4565
+ const possibleLocalFunctionNames = new Set(namesToSourcesToFunctionMeta.keys());
4566
+ for (const [localName, directImport] of directImports) {
4567
+ const functionName = directImport.originalName === "default" ? camelCase(parse$1(directImport.source).name) : directImport.originalName;
4568
+ if (namesToSourcesToFunctionMeta.has(functionName)) {
4569
+ possibleLocalFunctionNames.add(localName);
4570
+ }
4571
+ }
4572
+ const autoImports = await options.getAutoImports();
4573
+ const autoImportsToSources = new Map(autoImports.map((i) => [i.as || i.name, i.from]));
4574
+ function getFunctionMetaByLocalName(localName, source) {
4575
+ if (!localName) {
4576
+ return;
4577
+ }
4578
+ const exportedName = localNamesToExportedName.get(localName);
4579
+ if (exportedName) {
4580
+ return namesToSourcesToFunctionMeta.get(exportedName)?.get(source);
4581
+ }
4582
+ const directImport = directImports.get(localName);
4583
+ if (directImport) {
4584
+ const functionName = directImport.originalName === "default" ? camelCase(parse$1(directImport.source).name) : directImport.originalName;
4585
+ const sourcesToMetas = namesToSourcesToFunctionMeta.get(functionName);
4586
+ if (!sourcesToMetas) {
4587
+ return;
4588
+ }
4589
+ const fnMeta = sourcesToMetas.get(source);
4590
+ if (fnMeta) {
4591
+ return fnMeta;
4592
+ }
4593
+ if (source.startsWith(options.appDir)) {
4594
+ for (const [fnSource, meta] of sourcesToMetas) {
4595
+ if (meta.name !== functionName || !fnSource.startsWith(options.appDir)) {
4596
+ continue;
4597
+ }
4598
+ return meta;
4599
+ }
4600
+ }
4601
+ const backwardsCompatibleFnMeta = sourcesToMetas.get("");
4602
+ if (backwardsCompatibleFnMeta?.source === void 0) {
4603
+ const autoImportResolvedSource = stripExtension(resolveAlias$1(autoImportsToSources.get(localName) ?? ""));
4604
+ if (autoImportResolvedSource === source) {
4605
+ return backwardsCompatibleFnMeta;
4606
+ }
4607
+ } else if (backwardsCompatibleFnMeta.source instanceof RegExp && backwardsCompatibleFnMeta.source.test(source)) {
4608
+ return backwardsCompatibleFnMeta;
4609
+ }
4610
+ return;
4611
+ }
4612
+ return namesToSourcesToFunctionMeta.get(localName)?.get(source);
4613
+ }
4614
+ function _resolvePath(path) {
4615
+ let p = path;
4616
+ if (isAbsolute(p)) {
4617
+ return p;
4618
+ }
4619
+ p = resolveAlias$1(p, options.alias);
4620
+ if (isAbsolute(p)) {
4621
+ return p;
4622
+ }
4623
+ return join(parse$1(id).dir, p);
4624
+ }
4625
+ const s = new MagicString(code);
4626
+ let count = 0;
4627
+ const scopeTracker = new ScopeTracker({
4628
+ preserveExitedScopes: true
4629
+ });
4630
+ const { program } = parseAndWalk(code, _id, {
4631
+ scopeTracker,
4632
+ enter(node) {
4633
+ if (!shouldConsiderExports) {
4634
+ return;
4635
+ }
4636
+ if (node.type !== "ExportNamedDeclaration" && node.type !== "ExportDefaultDeclaration") {
4637
+ return;
4638
+ }
4639
+ const result = parseStaticExportIdentifiers(node);
4640
+ for (const exportMeta of result) {
4641
+ const { localName, exportedName } = exportMeta;
4642
+ const functionName = exportedName === "default" ? camelCase(parse$1(id).name) : getFunctionMetaByLocalName(exportedName, id)?.name;
4643
+ if (!functionName) {
4644
+ continue;
4645
+ }
4646
+ localNamesToExportedName.set(localName, functionName);
4647
+ }
4648
+ }
4649
+ });
4650
+ scopeTracker.freeze();
4651
+ for (const localName of localNamesToExportedName.keys()) {
4652
+ possibleLocalFunctionNames.add(localName);
4653
+ }
4654
+ const LOCAL_FUNCTION_NAMES_RE = new RegExp(`\\b(${[...possibleLocalFunctionNames].map((f) => escapeRE(f)).join("|")})\\b`);
4655
+ function processKeyedFunction(walkContext, node, handler) {
4656
+ if (node.type !== "CallExpression" && node.type !== "ChainExpression") {
4657
+ return;
4658
+ }
4659
+ const parsedCall = parseStaticFunctionCall(node, LOCAL_FUNCTION_NAMES_RE);
4660
+ if (!parsedCall) {
4661
+ return;
4662
+ }
4663
+ const functionScopeTrackerNode = scopeTracker.getDeclaration(!parsedCall.namespace ? parsedCall.name : parsedCall.namespace);
4664
+ function isKeyedFunctionImport(node2) {
4665
+ return node2?.type === "Import" && node2.importNode.importKind !== "type";
4666
+ }
4667
+ let importSourceResolved;
4668
+ if (localNamesToExportedName.has(parsedCall.name) && functionScopeTrackerNode?.scope === "") {
4669
+ importSourceResolved = id;
4670
+ } else if (isKeyedFunctionImport(functionScopeTrackerNode)) {
4671
+ importSourceResolved = stripExtension(_resolvePath(functionScopeTrackerNode.importNode.source.value));
4672
+ }
4673
+ if (!importSourceResolved) {
4674
+ walkContext.skip();
4675
+ return;
4676
+ }
4677
+ const fnMeta = getFunctionMetaByLocalName(parsedCall.name, importSourceResolved);
4678
+ if (!fnMeta) {
4679
+ walkContext.skip();
4680
+ return;
4681
+ }
4682
+ if (!parsedCall.namespace) {
4683
+ if (parsedCall.callExpression.arguments.length >= fnMeta.argumentLength && !parsedCall.callExpression.arguments.some((a) => a.type === "SpreadElement")) {
4684
+ walkContext.skip();
4685
+ return;
4686
+ }
4687
+ if (
4688
+ // the function is imported
4689
+ isKeyedFunctionImport(functionScopeTrackerNode) && // import { useKeyed } from '...'
4690
+ (functionScopeTrackerNode.node.type === "ImportSpecifier" && functionScopeTrackerNode.node.importKind !== "type" || functionScopeTrackerNode.node.type === "ImportDefaultSpecifier" && fnMeta.name === "default") && // the function is imported from the correct source when `source` is specified
4691
+ (typeof fnMeta.source === "string" && stripExtension(fnMeta.source) === importSourceResolved || !fnMeta.source && stripExtension(_resolvePath(autoImportsToSources.get(parsedCall.name) ?? "")) === importSourceResolved || fnMeta.source instanceof RegExp && fnMeta.source.test(importSourceResolved) || typeof fnMeta.source === "string" && fnMeta.source.startsWith(options.appDir)) || localNamesToExportedName.has(parsedCall.name) && functionScopeTrackerNode?.scope === ""
4692
+ ) {
4693
+ handler({ parsedCall, fnMeta });
4694
+ }
4695
+ walkContext.skip();
4696
+ return;
4697
+ }
4698
+ if (parsedCall.namespace) {
4699
+ const namespacedImportMeta = namespaces.get(importSourceResolved);
4700
+ const namespaceScopeTrackerNode = scopeTracker.getDeclaration(parsedCall.namespace);
4701
+ if (namespacedImportMeta && namespacedImportMeta.namespaces.has(parsedCall.namespace) && namespaceScopeTrackerNode?.type === "Import" && namespaceScopeTrackerNode.node.type === "ImportNamespaceSpecifier") {
4702
+ handler({ parsedCall, fnMeta });
4703
+ }
4704
+ walkContext.skip();
4705
+ return;
4706
+ }
4707
+ }
4708
+ walk(program, {
4709
+ scopeTracker,
4710
+ enter(node) {
4711
+ processKeyedFunction(this, node, ({ parsedCall, fnMeta }) => {
4712
+ const lastArgument = parsedCall.callExpression.arguments[parsedCall.callExpression.arguments.length - 1];
4713
+ if (lastArgument?.type === "Literal" && typeof lastArgument.value === "string" && lastArgument.end + NUXT_INJECTED_MARKER.length + 1 < parsedCall.callExpression.end) {
4714
+ let wasKeyInjected = true;
4715
+ for (let i2 = 0; i2 < NUXT_INJECTED_MARKER.length; i2++) {
4716
+ if (code[codeIndex + lastArgument.end + 1 + i2] !== NUXT_INJECTED_MARKER[i2]) {
4717
+ wasKeyInjected = false;
4718
+ break;
4719
+ }
4720
+ }
4721
+ if (wasKeyInjected) {
4722
+ return;
4723
+ }
4724
+ }
4725
+ switch (parsedCall.name) {
4726
+ case "useState":
4727
+ if (stringTypes.includes(parsedCall.callExpression.arguments[0]?.type) && typeof fnMeta.source === "string" && stripExtension(fnMeta.source) === stripExtension(resolveAlias$1("#app/composables/state", options.alias))) {
4728
+ return;
4729
+ }
4730
+ break;
4731
+ case "useFetch":
4732
+ case "useLazyFetch":
4733
+ if (stringTypes.includes(parsedCall.callExpression.arguments[1]?.type) && typeof fnMeta.source === "string" && stripExtension(fnMeta.source) === stripExtension(resolveAlias$1("#app/composables/fetch", options.alias))) {
4734
+ return;
4735
+ }
4736
+ break;
4737
+ case "useAsyncData":
4738
+ case "useLazyAsyncData":
4739
+ if (stringTypes.includes(parsedCall.callExpression.arguments[0]?.type) && typeof fnMeta.source === "string" && stripExtension(fnMeta.source) === stripExtension(resolveAlias$1("#app/composables/asyncData", options.alias))) {
4740
+ return;
4741
+ }
4742
+ break;
4743
+ }
4744
+ let i = codeIndex + parsedCall.callExpression.end - 2;
4745
+ while (i >= codeIndex + parsedCall.callExpression.start && isWhitespace(code[i])) {
4746
+ i--;
4747
+ }
4748
+ const endsWithComma = code[i] === ",";
4749
+ s.appendLeft(
4750
+ codeIndex + parsedCall.callExpression.end - 1,
4751
+ (parsedCall.callExpression.arguments.length && !endsWithComma ? ", " : "") + "'$" + hash(`${_id}-${++count}`).slice(0, 10) + `' ${NUXT_INJECTED_MARKER}`
4752
+ );
4753
+ });
4754
+ }
4755
+ });
4756
+ if (s.hasChanged()) {
4757
+ return {
4758
+ code: s.toString(),
4759
+ map: options.sourcemap ? s.generateMap({ hires: true }) : void 0
4760
+ };
4761
+ }
4762
+ }
4763
+ }
4764
+ };
4765
+ });
4766
+
4767
+ const compilerModule = defineNuxtModule({
4768
+ meta: {
4769
+ name: "nuxt:compiler",
4770
+ configKey: "compiler"
4771
+ },
4772
+ defaults: {
4773
+ scan: true
4774
+ },
4775
+ setup(_options, nuxt) {
4776
+ let unimport;
4777
+ nuxt.hook("imports:context", (ctx) => {
4778
+ unimport = ctx;
4779
+ });
4780
+ let scanResult;
4781
+ let scanDirPaths = [];
4782
+ let normalizedKeyedFunctions = [];
4783
+ nuxt.hook("build:before", async () => {
4784
+ addBuildPlugin(KeyedFunctionFactoriesPlugin({
4785
+ sourcemap: !!nuxt.options.sourcemap.server || !!nuxt.options.sourcemap.client,
4786
+ factories: nuxt.options.optimization.keyedComposableFactories,
4787
+ alias: nuxt.options.alias,
4788
+ getAutoImports: () => unimport?.getImports() || Promise.resolve([])
4789
+ }));
4790
+ if (_options.scan) {
4791
+ const scanPlugin = KeyedFunctionFactoriesScanPlugin({
4792
+ factories: nuxt.options.optimization.keyedComposableFactories,
4793
+ alias: nuxt.options.alias
4794
+ });
4795
+ scanResult = scanPlugin.result;
4796
+ await runScanPlugins([scanPlugin]);
4797
+ }
4798
+ normalizedKeyedFunctions = await Promise.all(nuxt.options.optimization.keyedComposables.map(async ({ source, ...rest }) => ({
4799
+ ...rest,
4800
+ source: typeof source === "string" ? await resolvePath(source, { fallbackToOriginal: true }) : source
4801
+ })));
4802
+ addBuildPlugin(KeyedFunctionsPlugin({
4803
+ sourcemap: !!nuxt.options.sourcemap.server || !!nuxt.options.sourcemap.client,
4804
+ keyedFunctions: normalizedKeyedFunctions,
4805
+ getKeyedFunctions: () => normalizedKeyedFunctions,
4806
+ alias: nuxt.options.alias,
4807
+ getAutoImports: () => unimport?.getImports() || Promise.resolve([]),
4808
+ appDir: nuxt.options.appDir,
4809
+ dev: nuxt.options.dev
4810
+ }));
4811
+ });
4812
+ async function runScanPlugins(plugins) {
4813
+ const autoImports = await unimport?.getImports() || [];
4814
+ const autoImportsToSources = new Map(autoImports.map((i) => [i.as || i.name, i.from]));
4815
+ const dirPaths = /* @__PURE__ */ new Set();
4816
+ const scanDirs = [];
4817
+ for (const layer of nuxt.options._layers) {
4818
+ if (layer.config?.compiler?.scan === false) {
4819
+ continue;
4820
+ }
4821
+ const composablesDir = resolve(layer.config.srcDir, "composables");
4822
+ if (!isDirectorySync(composablesDir) || dirPaths.has(composablesDir)) {
4823
+ continue;
4824
+ }
4825
+ dirPaths.add(composablesDir);
4826
+ const extensions = nuxt.options.extensions.map((e) => normalizeExtension(e));
4827
+ scanDirs.push({
4828
+ path: composablesDir,
4829
+ extensions,
4830
+ pattern: `**/*.{${extensions.join(",")}}`,
4831
+ ignore: [`**/*.{${DECLARATION_EXTENSIONS.join(",")}}`]
4832
+ });
4833
+ }
4834
+ scanDirPaths = scanDirs.map((d) => d.path);
4835
+ const _filePaths = [];
4836
+ await Promise.all(scanDirs.map(async (dir) => {
4837
+ const files = await resolveFiles(dir.path, dir.pattern, { ignore: dir.ignore });
4838
+ _filePaths.push(...files);
4839
+ }));
4840
+ const filePaths = await Promise.all(_filePaths.map((filePath) => resolvePath(filePath)));
4841
+ for (const filePath of filePaths) {
4842
+ const isFileWantedByPlugin = plugins.some((p) => {
4843
+ if (!p.filter?.id) {
4844
+ return true;
4845
+ }
4846
+ return matchFilter(filePath, p.filter.id);
4847
+ });
4848
+ if (!isFileWantedByPlugin) {
4849
+ continue;
4850
+ }
4851
+ try {
4852
+ const contents = await readFile(filePath, "utf-8");
4853
+ const pluginScanThisContext = createScanPluginContext(contents, filePath);
4854
+ await Promise.all(plugins.map(async (plugin) => {
4855
+ if (plugin.filter?.id && !matchFilter(filePath, plugin.filter.id)) {
4856
+ return;
4857
+ }
4858
+ if (plugin.filter?.code && !matchFilter(contents, plugin.filter.code)) {
4859
+ return;
4860
+ }
4861
+ try {
4862
+ await plugin.scan.call(pluginScanThisContext, { id: filePath, code: contents, nuxt, autoImportsToSources });
4863
+ } catch (e) {
4864
+ logger.error(`[nuxt:compiler] Plugin \`${plugin.name}\` failed to scan file \`${filePath}\``, e);
4865
+ }
4866
+ }));
4867
+ } catch (e) {
4868
+ logger.error(`[nuxt:compiler] Cannot read file \`${filePath}\``, e);
4869
+ }
4870
+ }
4871
+ await Promise.all(plugins.map(async (plugin) => {
4872
+ if (!plugin.afterScan) {
4873
+ return;
4874
+ }
4875
+ try {
4876
+ await plugin.afterScan(nuxt);
4877
+ } catch (e) {
4878
+ logger.error(`[nuxt:compiler] Error in \`afterScan\` hook of plugin \`${plugin.name}\``, e);
4879
+ }
4880
+ }));
4881
+ }
4882
+ if (nuxt.options.dev && _options.scan) {
4883
+ nuxt.hook("builder:watch", async (event, relativePath) => {
4884
+ if (!scanResult || !["add", "change", "unlink"].includes(event)) {
4885
+ return;
4886
+ }
4887
+ const absolutePath = resolve(nuxt.options.srcDir, relativePath);
4888
+ const isInScanDir = scanDirPaths.some((dir) => absolutePath === dir || absolutePath.startsWith(dir + "/"));
4889
+ if (!isInScanDir) {
4890
+ return;
4891
+ }
4892
+ const { fileResults, factoryNamesRegex, namesToFactoryMeta } = scanResult;
4893
+ const oldEntries = fileResults.get(absolutePath);
4894
+ if (oldEntries?.length) {
4895
+ const resolvedSource = await resolvePath(absolutePath, { fallbackToOriginal: true });
4896
+ for (let i = normalizedKeyedFunctions.length - 1; i >= 0; i--) {
4897
+ if (normalizedKeyedFunctions[i].source === resolvedSource) {
4898
+ normalizedKeyedFunctions.splice(i, 1);
4899
+ }
4900
+ }
4901
+ fileResults.delete(absolutePath);
4902
+ }
4903
+ if (event === "unlink") {
4904
+ return;
4905
+ }
4906
+ let contents;
4907
+ try {
4908
+ contents = await readFile(absolutePath, "utf-8");
4909
+ } catch {
4910
+ return;
4911
+ }
4912
+ if (!factoryNamesRegex.test(contents)) {
4913
+ return;
4914
+ }
4915
+ const autoImports = await unimport?.getImports() || [];
4916
+ const autoImportsToSources = new Map(autoImports.map((i) => [i.as || i.name, i.from]));
4917
+ const newEntries = scanFileForFactories(
4918
+ absolutePath,
4919
+ contents,
4920
+ namesToFactoryMeta,
4921
+ autoImportsToSources,
4922
+ nuxt.options.alias
4923
+ );
4924
+ if (newEntries.length) {
4925
+ fileResults.set(absolutePath, newEntries);
4926
+ const normalized = await Promise.all(newEntries.map(async ({ source, ...rest }) => ({
4927
+ ...rest,
4928
+ source: typeof source === "string" ? await resolvePath(source, { fallbackToOriginal: true }) : source
4929
+ })));
4930
+ normalizedKeyedFunctions.push(...normalized);
4931
+ }
4932
+ });
4933
+ }
4934
+ }
4935
+ });
4936
+ function matchFilter(input, filter) {
4937
+ if (typeof filter === "function") {
4938
+ return filter(input);
4939
+ }
4940
+ const include = filter.include ? toArray(filter.include).some((v) => matchWithStringOrRegex(input, v)) : true;
4941
+ const exclude = filter.exclude ? toArray(filter.exclude).some((v) => matchWithStringOrRegex(input, v)) : false;
4942
+ return include && !exclude;
4943
+ }
4944
+
4945
+ async function getVueHash(nuxt) {
4946
+ const id = "vue";
4947
+ const { hash: hash2 } = await getHashes(nuxt, {
4948
+ id,
4949
+ cwd: (layer) => layer.config.srcDir || layer.cwd,
4950
+ patterns: (layer) => {
4951
+ const srcDir = layer.config.srcDir || layer.cwd;
4952
+ return [
4953
+ "**",
4954
+ `!${relative(srcDir, layer.config.serverDir || join(layer.cwd, "server"))}/**`,
4955
+ `!${relative(srcDir, resolve$1(layer.cwd, layer.config.dir?.public || "public"))}/**`,
4956
+ "!node_modules/**",
4957
+ "!nuxt.config.*"
4958
+ ];
4959
+ },
4960
+ configOverrides: {
4961
+ buildId: void 0,
4962
+ serverDir: void 0,
4963
+ nitro: void 0,
4964
+ devServer: void 0,
4965
+ runtimeConfig: void 0,
4966
+ logLevel: void 0,
4967
+ devServerHandlers: void 0,
4968
+ devtools: void 0
4969
+ }
4970
+ });
4971
+ const cacheFile = join(getCacheDir(nuxt), id, hash2 + ".tar");
4972
+ const buildIdCacheFile = cacheFile.replace(".tar", ".buildid");
4973
+ return {
4974
+ hash: hash2,
4975
+ async collectCache() {
4976
+ const start = Date.now();
4977
+ await writeCache(nuxt.options.buildDir, nuxt.options.buildDir, cacheFile);
4978
+ await mkdir(dirname(buildIdCacheFile), { recursive: true });
4979
+ await writeFile(buildIdCacheFile, nuxt.options.buildId);
4980
+ const elapsed = Date.now() - start;
4981
+ consola.success(`Cached Vue client and server builds in \`${elapsed}ms\`.`);
4982
+ },
4983
+ async restoreCache() {
4984
+ const start = Date.now();
4985
+ const res = await restoreCacheFromFile(nuxt.options.buildDir, cacheFile);
4986
+ const elapsed = Date.now() - start;
4987
+ if (res) {
4988
+ consola.success(`Restored Vue client and server builds from cache in \`${elapsed}ms\`.`);
4989
+ }
4990
+ return res;
4991
+ }
4992
+ };
4993
+ }
4994
+ async function restoreCachedBuildId(nuxt) {
4995
+ const { hash: hash2 } = await getVueHash(nuxt);
4996
+ const cacheDir = getCacheDir(nuxt);
4997
+ const buildIdCacheFile = join(cacheDir, "vue", hash2 + ".buildid");
4998
+ if (!existsSync(buildIdCacheFile)) {
4999
+ return;
5000
+ }
5001
+ const cachedBuildId = (await readFile(buildIdCacheFile, "utf-8")).trim();
5002
+ if (!cachedBuildId || !/^[\w-]+$/.test(cachedBuildId)) {
5003
+ return;
5004
+ }
5005
+ nuxt.options.buildId = cachedBuildId;
5006
+ nuxt.options.runtimeConfig.app.buildId = cachedBuildId;
5007
+ consola.debug(`Restored cached buildId: ${cachedBuildId}`);
5008
+ }
5009
+ async function cleanupCaches(nuxt) {
5010
+ const start = Date.now();
5011
+ const caches = await glob(["*/*.tar", "*/*.buildid"], {
5012
+ cwd: getCacheDir(nuxt),
5013
+ absolute: true
5014
+ });
5015
+ if (caches.length >= 10) {
5016
+ const cachesWithMeta = await Promise.all(caches.map(async (cache) => {
5017
+ return [cache, await stat(cache).then((r) => r.mtime.getTime()).catch(() => 0)];
5018
+ }));
5019
+ cachesWithMeta.sort((a, b) => a[1] - b[1]);
5020
+ for (const [cache] of cachesWithMeta.slice(0, cachesWithMeta.length - 10)) {
5021
+ await unlink(cache);
5022
+ }
5023
+ const elapsed = Date.now() - start;
5024
+ consola.success(`Cleaned up old build caches in \`${elapsed}ms\`.`);
5025
+ }
5026
+ }
5027
+ async function getHashes(nuxt, options) {
5028
+ if (nuxt[`_${options.id}BuildHash`]) {
5029
+ return nuxt[`_${options.id}BuildHash`];
5030
+ }
5031
+ const start = Date.now();
5032
+ const hashSources = [];
5033
+ let layerCtr = 0;
5034
+ for (const layer of nuxt.options._layers) {
5035
+ if (layer.cwd.includes("node_modules")) {
5036
+ continue;
5037
+ }
5038
+ const layerName = `layer#${layerCtr++}`;
5039
+ hashSources.push({
5040
+ name: `${layerName}:config`,
5041
+ data: serialize({
5042
+ ...layer.config,
5043
+ ...options.configOverrides || {}
5044
+ })
5045
+ });
5046
+ const normalizeFiles = (files) => files.map((f) => ({
5047
+ name: f.name,
5048
+ size: f.attrs?.size,
5049
+ data: hash(f.data)
5050
+ })).sort((a, b) => a.name.localeCompare(b.name));
5051
+ const isIgnored = createIsIgnored(nuxt);
5052
+ const sourceFiles = await readFilesRecursive(options.cwd(layer), {
5053
+ shouldIgnore: isIgnored,
5054
+ // TODO: Validate if works with absolute paths
5055
+ cwd: nuxt.options.rootDir,
5056
+ patterns: options.patterns(layer)
5057
+ });
5058
+ hashSources.push({
5059
+ name: `${layerName}:src`,
5060
+ data: normalizeFiles(sourceFiles)
5061
+ });
5062
+ const rootFiles = await readFilesRecursive(layer.config?.rootDir || layer.cwd, {
5063
+ shouldIgnore: isIgnored,
5064
+ // TODO: Validate if works with absolute paths
5065
+ cwd: nuxt.options.rootDir,
5066
+ patterns: [
5067
+ ".nuxtrc",
5068
+ ".npmrc",
5069
+ "package.json",
5070
+ "package-lock.json",
5071
+ "yarn.lock",
5072
+ "pnpm-lock.yaml",
5073
+ "tsconfig.json",
5074
+ "bun.lock",
5075
+ "bun.lockb"
5076
+ ]
5077
+ });
5078
+ hashSources.push({
5079
+ name: `${layerName}:root`,
5080
+ data: normalizeFiles(rootFiles)
5081
+ });
5082
+ }
5083
+ hashSources.sort((a, b) => a.name.localeCompare(b.name));
5084
+ const res = nuxt[`_${options.id}BuildHash`] = {
5085
+ hash: hash(hashSources),
5086
+ sources: hashSources
5087
+ };
5088
+ const elapsed = Date.now() - start;
5089
+ consola.debug(`Computed \`${options.id}\` build hash in \`${elapsed}ms\`.`);
5090
+ return res;
5091
+ }
5092
+ async function readFilesRecursive(dir, opts) {
5093
+ if (Array.isArray(dir)) {
5094
+ return (await Promise.all(dir.map((d) => readFilesRecursive(d, opts)))).flat();
5095
+ }
5096
+ const files = await glob(opts.patterns, { cwd: dir });
5097
+ const fileEntries = await Promise.all(files.map(async (fileName) => {
5098
+ if (!opts.shouldIgnore?.(fileName)) {
5099
+ const file = await readFileWithMeta(dir, fileName);
5100
+ if (!file) {
5101
+ return;
5102
+ }
5103
+ return {
5104
+ ...file,
5105
+ name: relative(opts.cwd, join(dir, file.name))
5106
+ };
5107
+ }
5108
+ }));
5109
+ return fileEntries.filter(Boolean);
5110
+ }
5111
+ async function readFileWithMeta(dir, fileName, count = 0) {
5112
+ let fd = void 0;
5113
+ try {
5114
+ fd = await open(resolve$1(dir, fileName));
5115
+ const stats = await fd.stat();
5116
+ if (!stats?.isFile()) {
5117
+ return;
5118
+ }
5119
+ const mtime = stats.mtime.getTime();
5120
+ const data = await fd.readFile();
5121
+ if ((await fd.stat()).mtime.getTime() !== mtime) {
5122
+ await fd.close();
5123
+ fd = void 0;
5124
+ if (count < 5) {
5125
+ return await readFileWithMeta(dir, fileName, count + 1);
5126
+ }
5127
+ console.warn(`Failed to read file \`${fileName}\` as it changed during read.`);
5128
+ return;
5129
+ }
5130
+ return {
5131
+ name: fileName,
5132
+ data,
5133
+ attrs: {
5134
+ mtime,
5135
+ size: stats.size
5136
+ }
5137
+ };
5138
+ } catch (err) {
5139
+ console.warn(`Failed to read file \`${fileName}\`:`, err);
5140
+ } finally {
5141
+ await fd?.close();
5142
+ }
5143
+ }
5144
+ async function restoreCacheFromFile(cwd, cacheFile) {
5145
+ if (!existsSync(cacheFile)) {
5146
+ return false;
5147
+ }
5148
+ const resolvedCwd = resolve$1(cwd) + "/";
5149
+ const files = parseTar(await readFile(cacheFile));
5150
+ for (const file of files) {
5151
+ let fd = void 0;
5152
+ try {
5153
+ const filePath = resolve$1(cwd, file.name);
5154
+ if (!filePath.startsWith(resolvedCwd)) {
5155
+ consola.warn(`Skipping unsafe cache path: ${file.name}`);
5156
+ continue;
5157
+ }
5158
+ await mkdir(dirname(filePath), { recursive: true });
5159
+ const existingStats = await stat(filePath).catch(() => null);
5160
+ const cachedSize = file.data?.byteLength ?? 0;
5161
+ if (existingStats?.isFile() && existingStats.size === cachedSize) {
5162
+ const lastModified = Number.parseInt(file.attrs?.mtime?.toString().padEnd(13, "0") || "0");
5163
+ if (existingStats.mtime.getTime() >= lastModified) {
5164
+ consola.debug(`Skipping \`${file.name}\` (up to date or newer than cache)`);
5165
+ continue;
5166
+ }
5167
+ }
5168
+ fd = await open(filePath, "w");
5169
+ await fd.writeFile(file.data);
5170
+ } catch (err) {
5171
+ console.error(err);
5172
+ } finally {
5173
+ await fd?.close();
5174
+ }
5175
+ }
5176
+ return true;
5177
+ }
5178
+ async function writeCache(cwd, sources, cacheFile) {
5179
+ const fileEntries = await readFilesRecursive(sources, {
5180
+ patterns: ["**/*", "!analyze/**"],
5181
+ cwd
5182
+ });
5183
+ const tarData = createTar(fileEntries);
5184
+ await mkdir(dirname(cacheFile), { recursive: true });
5185
+ await writeFile(cacheFile, tarData);
5186
+ }
5187
+ function getCacheDir(nuxt) {
5188
+ let cacheDir = join(nuxt.options.workspaceDir, "node_modules");
5189
+ if (!existsSync(cacheDir)) {
5190
+ for (const dir of [...nuxt.options.modulesDir].sort((a, b) => a.length - b.length)) {
5191
+ if (existsSync(dir)) {
5192
+ cacheDir = dir;
5193
+ break;
5194
+ }
5195
+ }
5196
+ }
5197
+ return join(cacheDir, ".cache/nuxt/builds");
5198
+ }
5199
+
5200
+ const runtimeDependencies = [
5201
+ // other deps
5202
+ "devalue",
5203
+ "klona",
5204
+ // deliberate exports from nitro builder
5205
+ "@nuxt/nitro-server/h3",
5206
+ // unjs ecosystem
5207
+ "defu",
5208
+ "ufo",
5209
+ "destr",
5210
+ "consola",
5211
+ "hookable",
5212
+ "unctx",
5213
+ "cookie-es",
5214
+ "perfect-debounce",
5215
+ "ohash",
5216
+ "pathe",
5217
+ "uncrypto"
5218
+ ];
5219
+
5220
+ const version = "4.4.2";
5221
+ const pkg = {
5222
+ version: version};
5223
+
5224
+ function createImportProtectionPatterns(nuxt, options) {
5225
+ const patterns = [];
5226
+ const context = contextFlags[options.context];
5227
+ patterns.push([
5228
+ /^(nuxt|nuxt3|nuxt-nightly)$/,
5229
+ `\`nuxt\` or \`nuxt-nightly\` cannot be imported directly in ${context}.`,
5230
+ options.context === "nuxt-app" ? ["Import runtime Nuxt composables from `#app` or `#imports` instead."] : ["Use `#app` or `#imports` for runtime composables in your Vue app code."]
5231
+ ]);
5232
+ patterns.push([
5233
+ /^((~|~~|@|@@)?\/)?nuxt\.config(\.|$)/,
5234
+ "Importing directly from a `nuxt.config` file is not allowed.",
5235
+ ["Use `useRuntimeConfig()` to access runtime config in your app.", "Use `useAppConfig()` to access config that doesn't need to be changed at runtime.", "Use a Nuxt module to access build-time configuration."]
5236
+ ]);
5237
+ patterns.push([/(^|node_modules\/)@vue\/composition-api/]);
5238
+ for (const mod of nuxt.options._installedModules) {
5239
+ if (mod.entryPath) {
5240
+ patterns.push([
5241
+ new RegExp(`^${escapeRE(mod.entryPath)}$`),
5242
+ "Importing directly from module entry-points is not allowed.",
5243
+ ["Import from the module's runtime directory instead (e.g. `my-module/runtime/...`)."]
5244
+ ]);
5245
+ }
5246
+ }
5247
+ for (const i of [/(^|node_modules\/)@nuxt\/(cli|kit|test-utils)/, /(^|node_modules\/)nuxi/, /(^|node_modules\/)nitro(?:pack)?(?:-nightly)?(?:$|\/)(?!(?:dist\/)?(?:node_modules|presets|runtime|types))/, /(^|node_modules\/)nuxt\/(config|kit|schema)/]) {
5248
+ patterns.push([
5249
+ i,
5250
+ `This module cannot be imported in ${context}.`,
5251
+ ["These are build-time only packages and cannot be used at runtime."]
5252
+ ]);
5253
+ }
5254
+ if (options.context === "nitro-app" || options.context === "shared") {
5255
+ for (const i of ["#app", /^#build(\/|$)/]) {
5256
+ patterns.push([
5257
+ i,
5258
+ `Vue app aliases are not allowed in ${context}.`,
5259
+ ["Move this code to your Vue app directory or use a shared utility."]
5260
+ ]);
5261
+ }
5262
+ }
5263
+ if (options.context === "nuxt-app" || options.context === "shared") {
5264
+ const serverRelative = escapeRE(relative(nuxt.options.rootDir, resolve(nuxt.options.srcDir, nuxt.options.serverDir || "server")));
5265
+ patterns.push([
5266
+ new RegExp("^" + serverRelative + "\\/(api|routes|middleware|plugins)\\/"),
5267
+ `Importing from server is not allowed in ${context}.`,
5268
+ ["Use `$fetch()` or `useFetch()` to fetch data from server routes.", "Move shared logic to the `shared/` directory."]
5269
+ ]);
5270
+ patterns.push([
5271
+ /^#server(\/|$)/,
5272
+ `Server aliases are not allowed in ${context}.`,
5273
+ ["Use `$fetch()` or `useFetch()` to call server endpoints.", "Move shared logic to the `shared/` directory."]
5274
+ ]);
5275
+ }
5276
+ return patterns;
5277
+ }
5278
+ const contextFlags = {
5279
+ "nitro-app": "server runtime",
5280
+ "nuxt-app": "the Vue part of your app",
5281
+ "shared": "the #shared directory"
5282
+ };
5283
+
5284
+ const TRANSFORM_MARKER = "/* _processed_nuxt_unctx_transform */\n";
5285
+ const TRANSFORM_MARKER_RE = /^\/\* _processed_nuxt_unctx_transform \*\/\n/;
5286
+ const UnctxTransformPlugin = (options) => createUnplugin(() => {
5287
+ const transformer = createTransformer(options.transformerOptions);
5288
+ return {
5289
+ name: "unctx:transform",
5290
+ enforce: "post",
5291
+ transformInclude(id) {
5292
+ return isVue(id, { type: ["template", "script"] }) || isJS(id);
5293
+ },
5294
+ transform: {
5295
+ filter: {
5296
+ ...transformer.filter,
5297
+ code: {
5298
+ ...transformer.filter.code,
5299
+ exclude: TRANSFORM_MARKER_RE
5300
+ }
5301
+ },
5302
+ handler(code) {
4015
5303
  if (!transformer.shouldTransform(code)) {
4016
5304
  return;
4017
5305
  }
@@ -4176,7 +5464,7 @@ const LayerAliasingPlugin = (options) => createUnplugin((_options, meta) => {
4176
5464
  filter: {
4177
5465
  id: ALIAS_ID_RE
4178
5466
  },
4179
- async handler(id, importer) {
5467
+ handler(id, importer) {
4180
5468
  if (!importer) {
4181
5469
  return;
4182
5470
  }
@@ -4186,7 +5474,7 @@ const LayerAliasingPlugin = (options) => createUnplugin((_options, meta) => {
4186
5474
  }
4187
5475
  const resolvedId = resolveAlias$1(id, aliases[layer]);
4188
5476
  if (resolvedId !== id) {
4189
- return await this.resolve(resolvedId, importer, { skipSelf: true });
5477
+ return this.resolve(resolvedId, importer, { skipSelf: true });
4190
5478
  }
4191
5479
  }
4192
5480
  }
@@ -4238,6 +5526,549 @@ async function loadServerBuilder(nuxt, builder = "@nuxt/nitro-server") {
4238
5526
  }
4239
5527
  }
4240
5528
 
5529
+ function getMemorySnapshot() {
5530
+ const mem = process.memoryUsage();
5531
+ return { rss: mem.rss, heapUsed: mem.heapUsed, heapTotal: mem.heapTotal };
5532
+ }
5533
+ function formatBytes(bytes) {
5534
+ const abs = Math.abs(bytes);
5535
+ if (abs < 1024) {
5536
+ return `${bytes} B`;
5537
+ }
5538
+ if (abs < 1024 * 1024) {
5539
+ return `${(bytes / 1024).toFixed(1)} KB`;
5540
+ }
5541
+ return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;
5542
+ }
5543
+ function formatDuration(ms) {
5544
+ if (ms < 1) {
5545
+ return `${Math.round(ms * 1e3)}\xB5s`;
5546
+ }
5547
+ if (ms < 1e3) {
5548
+ return `${Math.round(ms)}ms`;
5549
+ }
5550
+ if (ms < 6e4) {
5551
+ return `${(ms / 1e3).toFixed(1)}s`;
5552
+ }
5553
+ return `${(ms / 6e4).toFixed(1)}min`;
5554
+ }
5555
+ const ANSI_RE = /\x1B\[[0-9;]*m/g;
5556
+ function pad(str, width, align = "left") {
5557
+ const stripped = str.replace(ANSI_RE, "");
5558
+ const diff = width - stripped.length;
5559
+ if (diff <= 0) {
5560
+ return str;
5561
+ }
5562
+ const padding = " ".repeat(diff);
5563
+ return align === "right" ? padding + str : str + padding;
5564
+ }
5565
+ function round(n) {
5566
+ return Math.round(n * 100) / 100;
5567
+ }
5568
+ const SLOW_HOOK_THRESHOLD_MS = Number(process.env.NUXT_PERF_SLOW_HOOK_MS) || 50;
5569
+ const HOOK_PHASE_THRESHOLD_MS = 5;
5570
+ class NuxtPerfProfiler {
5571
+ #phases = [];
5572
+ #phaseStack = [];
5573
+ #hookPhases = [];
5574
+ #hookStartStack = [];
5575
+ #modules = [];
5576
+ #bundlerPluginTimings = /* @__PURE__ */ new Map();
5577
+ #bundlerPluginSpans = [];
5578
+ #globalStart;
5579
+ #globalMemoryBefore;
5580
+ #unsubscribe;
5581
+ /** Offset to convert performance.now() (ms) to epoch microseconds for trace events */
5582
+ #baseTs;
5583
+ constructor(options) {
5584
+ if (options?.startTime) {
5585
+ this.#globalStart = performance.now() - (Date.now() - options.startTime);
5586
+ } else {
5587
+ this.#globalStart = performance.now();
5588
+ }
5589
+ this.#globalMemoryBefore = getMemorySnapshot();
5590
+ this.#baseTs = Date.now() * 1e3 - this.#globalStart * 1e3;
5591
+ }
5592
+ installHookInterceptors(hooks) {
5593
+ const unsubBefore = hooks.beforeEach((event) => {
5594
+ this.#hookStartStack.push({
5595
+ name: event.name,
5596
+ time: performance.now(),
5597
+ memory: getMemorySnapshot()
5598
+ });
5599
+ });
5600
+ const unsubAfter = hooks.afterEach((event) => {
5601
+ let startIdx = -1;
5602
+ for (let i = this.#hookStartStack.length - 1; i >= 0; i--) {
5603
+ if (this.#hookStartStack[i].name === event.name) {
5604
+ startIdx = i;
5605
+ break;
5606
+ }
5607
+ }
5608
+ if (startIdx === -1) {
5609
+ return;
5610
+ }
5611
+ const start = this.#hookStartStack.splice(startIdx, 1)[0];
5612
+ const endTime = performance.now();
5613
+ const memoryAfter = getMemorySnapshot();
5614
+ const duration = round(endTime - start.time);
5615
+ this.#hookPhases.push({
5616
+ name: `hook:${event.name}`,
5617
+ startTime: start.time,
5618
+ endTime,
5619
+ duration,
5620
+ memoryBefore: start.memory,
5621
+ memoryAfter,
5622
+ memoryDelta: {
5623
+ rss: memoryAfter.rss - start.memory.rss,
5624
+ heapUsed: memoryAfter.heapUsed - start.memory.heapUsed
5625
+ }
5626
+ });
5627
+ });
5628
+ this.#unsubscribe = () => {
5629
+ unsubBefore();
5630
+ unsubAfter();
5631
+ };
5632
+ }
5633
+ startPhase(name) {
5634
+ this.#phaseStack.push({
5635
+ name,
5636
+ startTime: performance.now(),
5637
+ memoryBefore: getMemorySnapshot()
5638
+ });
5639
+ }
5640
+ endPhase(name) {
5641
+ let phaseIdx = this.#phaseStack.length - 1;
5642
+ if (name) {
5643
+ phaseIdx = -1;
5644
+ for (let i = this.#phaseStack.length - 1; i >= 0; i--) {
5645
+ if (this.#phaseStack[i].name === name) {
5646
+ phaseIdx = i;
5647
+ break;
5648
+ }
5649
+ }
5650
+ if (phaseIdx === -1) {
5651
+ return;
5652
+ }
5653
+ }
5654
+ if (phaseIdx < 0) {
5655
+ return;
5656
+ }
5657
+ const open = this.#phaseStack.splice(phaseIdx, 1)[0];
5658
+ const endTime = performance.now();
5659
+ const memoryAfter = getMemorySnapshot();
5660
+ this.#phases.push({
5661
+ name: open.name,
5662
+ startTime: open.startTime,
5663
+ endTime,
5664
+ duration: round(endTime - open.startTime),
5665
+ memoryBefore: open.memoryBefore,
5666
+ memoryAfter,
5667
+ memoryDelta: {
5668
+ rss: memoryAfter.rss - open.memoryBefore.rss,
5669
+ heapUsed: memoryAfter.heapUsed - open.memoryBefore.heapUsed
5670
+ }
5671
+ });
5672
+ }
5673
+ recordBundlerPluginHook(pluginName, hookName, durationMs, startTime) {
5674
+ let plugin = this.#bundlerPluginTimings.get(pluginName);
5675
+ if (!plugin) {
5676
+ plugin = /* @__PURE__ */ new Map();
5677
+ this.#bundlerPluginTimings.set(pluginName, plugin);
5678
+ }
5679
+ const entry = plugin.get(hookName);
5680
+ if (entry) {
5681
+ entry.totalTime += durationMs;
5682
+ entry.count++;
5683
+ if (durationMs > entry.maxTime) {
5684
+ entry.maxTime = durationMs;
5685
+ }
5686
+ } else {
5687
+ plugin.set(hookName, { totalTime: durationMs, count: 1, maxTime: durationMs });
5688
+ }
5689
+ if (startTime != null && durationMs > 0.1) {
5690
+ this.#bundlerPluginSpans.push({ pluginName, hookName, startTime, durationMs });
5691
+ }
5692
+ }
5693
+ collectModuleTimings(installedModules) {
5694
+ for (const mod of installedModules) {
5695
+ const name = mod.meta?.name || "(anonymous)";
5696
+ const setupTime = mod.timings?.setup;
5697
+ if (setupTime != null) {
5698
+ this.#modules.push({ name, setupTime });
5699
+ }
5700
+ }
5701
+ }
5702
+ getReport() {
5703
+ const totalDuration = round(performance.now() - this.#globalStart);
5704
+ const globalMemoryAfter = getMemorySnapshot();
5705
+ const allPhases = [...this.#phases];
5706
+ for (const hp of this.#hookPhases) {
5707
+ if (hp.duration < HOOK_PHASE_THRESHOLD_MS) {
5708
+ continue;
5709
+ }
5710
+ const hookName = hp.name.slice(5);
5711
+ const alreadyCovered = this.#phases.some(
5712
+ (p) => p.name === hookName && p.startTime <= hp.startTime && p.endTime >= hp.endTime
5713
+ );
5714
+ if (!alreadyCovered) {
5715
+ allPhases.push({ ...hp, name: hookName });
5716
+ }
5717
+ }
5718
+ allPhases.sort((a, b) => a.startTime - b.startTime);
5719
+ function computeOwn(phase) {
5720
+ const children = allPhases.filter(
5721
+ (other) => other !== phase && other.startTime >= phase.startTime && other.endTime <= phase.endTime
5722
+ );
5723
+ const directChildren = children.filter(
5724
+ (child) => !children.some(
5725
+ (other) => other !== child && child.startTime >= other.startTime && child.endTime <= other.endTime
5726
+ )
5727
+ );
5728
+ let childTime = 0;
5729
+ let childRss = 0;
5730
+ let childHeap = 0;
5731
+ for (const child of directChildren) {
5732
+ childTime += child.duration;
5733
+ childRss += child.memoryDelta.rss;
5734
+ childHeap += child.memoryDelta.heapUsed;
5735
+ }
5736
+ return {
5737
+ ownDuration: round(Math.max(0, phase.duration - childTime)),
5738
+ ownMemoryDelta: {
5739
+ rss: phase.memoryDelta.rss - childRss,
5740
+ heapUsed: phase.memoryDelta.heapUsed - childHeap
5741
+ }
5742
+ };
5743
+ }
5744
+ return {
5745
+ totalDuration,
5746
+ totalMemoryDelta: {
5747
+ rss: globalMemoryAfter.rss - this.#globalMemoryBefore.rss,
5748
+ heapUsed: globalMemoryAfter.heapUsed - this.#globalMemoryBefore.heapUsed
5749
+ },
5750
+ phases: allPhases.map((p) => {
5751
+ const own = computeOwn(p);
5752
+ return {
5753
+ name: p.name,
5754
+ duration: p.duration,
5755
+ ownDuration: own.ownDuration,
5756
+ memoryBefore: p.memoryBefore,
5757
+ memoryAfter: p.memoryAfter,
5758
+ memoryDelta: p.memoryDelta,
5759
+ ownMemoryDelta: own.ownMemoryDelta
5760
+ };
5761
+ }),
5762
+ slowHooks: this.#computeSlowHooks(new Set(allPhases.map((p) => p.name))),
5763
+ modules: [...this.#modules].sort((a, b) => b.setupTime - a.setupTime),
5764
+ bundlerPlugins: [...this.#bundlerPluginTimings.entries()].map(([name, hookMap]) => {
5765
+ const hooks = {};
5766
+ for (const [hookName, t] of hookMap) {
5767
+ hooks[hookName] = {
5768
+ totalTime: round(t.totalTime),
5769
+ count: t.count,
5770
+ maxTime: round(t.maxTime),
5771
+ avgTime: round(t.totalTime / t.count)
5772
+ };
5773
+ }
5774
+ return { name, hooks };
5775
+ }).sort((a, b) => {
5776
+ const aTotal = Object.values(a.hooks).reduce((s, h) => s + h.totalTime, 0);
5777
+ const bTotal = Object.values(b.hooks).reduce((s, h) => s + h.totalTime, 0);
5778
+ return bTotal - aTotal;
5779
+ }),
5780
+ timestamp: (/* @__PURE__ */ new Date()).toISOString()
5781
+ };
5782
+ }
5783
+ printReport(options) {
5784
+ const report = this.getReport();
5785
+ const isSubPhase = (name) => name.startsWith("module:") || name.startsWith("vite:");
5786
+ const topPhases = report.phases.filter((p) => !isSubPhase(p.name));
5787
+ const subPhases = report.phases.filter((p) => isSubPhase(p.name));
5788
+ consola.log("");
5789
+ consola.log(colors.bold(colors.cyan(` ${options?.title || "Nuxt Performance Report"}`)));
5790
+ consola.log("");
5791
+ const colPhase = 24;
5792
+ const colDuration = 10;
5793
+ const colRss = 14;
5794
+ const colHeap = 14;
5795
+ const maxDuration = Math.max(...topPhases.map((p) => p.ownDuration), 1);
5796
+ const header = [
5797
+ pad(colors.bold("Phase"), colPhase),
5798
+ pad(colors.bold("Duration"), colDuration, "right"),
5799
+ pad(colors.bold("RSS Delta"), colRss, "right"),
5800
+ pad(colors.bold("Heap Delta"), colHeap, "right")
5801
+ ].join(" ");
5802
+ consola.log(` ${header}`);
5803
+ const separator = " " + colors.dim("\u2500".repeat(colPhase + colDuration + colRss + colHeap + 6));
5804
+ consola.log(separator);
5805
+ const maxRss = Math.max(...topPhases.map((p) => Math.abs(p.ownMemoryDelta.rss)), 1);
5806
+ const barMax = 20;
5807
+ for (const phase of topPhases) {
5808
+ const dur = phase.ownDuration;
5809
+ const rss = phase.ownMemoryDelta.rss;
5810
+ const heap = phase.ownMemoryDelta.heapUsed;
5811
+ const durBar = dur > 0 ? Math.max(1, Math.round(Math.log(dur + 1) / Math.log(maxDuration + 1) * (barMax / 2))) : 0;
5812
+ const memBar = Math.abs(rss) > 0 ? Math.max(0, Math.round(Math.abs(rss) / maxRss * (barMax / 2))) : 0;
5813
+ const durColor = dur > 1e3 ? colors.red : dur > 200 ? colors.yellow : colors.green;
5814
+ const memColor = Math.abs(rss) > 50 * 1024 * 1024 ? colors.red : Math.abs(rss) > 10 * 1024 * 1024 ? colors.yellow : colors.green;
5815
+ const bar = durColor("\u2588".repeat(durBar)) + memColor("\u2591".repeat(memBar));
5816
+ const row = [
5817
+ pad(phase.name, colPhase),
5818
+ pad(durColor(formatDuration(dur)), colDuration, "right"),
5819
+ pad(memColor((rss >= 0 ? "+" : "") + formatBytes(rss)), colRss, "right"),
5820
+ pad((heap >= 0 ? "+" : "") + formatBytes(heap), colHeap, "right")
5821
+ ].join(" ");
5822
+ consola.log(` ${row} ${bar}`);
5823
+ }
5824
+ consola.log(separator);
5825
+ const totalRow = [
5826
+ pad(colors.bold("Total"), colPhase),
5827
+ pad(colors.bold(formatDuration(report.totalDuration)), colDuration, "right"),
5828
+ pad(colors.bold((report.totalMemoryDelta.rss >= 0 ? "+" : "") + formatBytes(report.totalMemoryDelta.rss)), colRss, "right"),
5829
+ pad(colors.bold((report.totalMemoryDelta.heapUsed >= 0 ? "+" : "") + formatBytes(report.totalMemoryDelta.heapUsed)), colHeap, "right")
5830
+ ].join(" ");
5831
+ consola.log(` ${totalRow}`);
5832
+ consola.log("");
5833
+ const significantModules = report.modules.filter((m) => m.setupTime > 5);
5834
+ if (significantModules.length > 0) {
5835
+ consola.log(colors.bold(` Modules`));
5836
+ consola.log("");
5837
+ for (const mod of significantModules) {
5838
+ const durColor = mod.setupTime > 500 ? colors.red : mod.setupTime > 100 ? colors.yellow : colors.dim;
5839
+ consola.log(` ${colors.dim("\u2022")} ${mod.name} ${colors.dim("\u2014")} ${durColor(formatDuration(mod.setupTime))}`);
5840
+ }
5841
+ consola.log("");
5842
+ }
5843
+ if (report.bundlerPlugins.length > 0) {
5844
+ const rows = [];
5845
+ for (const plugin of report.bundlerPlugins) {
5846
+ for (const [hook, timing] of Object.entries(plugin.hooks)) {
5847
+ if (timing.avgTime > 0.5) {
5848
+ rows.push({ plugin: plugin.name, hook, timing });
5849
+ }
5850
+ }
5851
+ }
5852
+ rows.sort((a, b) => b.timing.avgTime - a.timing.avgTime);
5853
+ if (rows.length > 0) {
5854
+ consola.log(colors.bold(` Bundler Plugins`));
5855
+ consola.log("");
5856
+ for (const { plugin, hook, timing } of rows.slice(0, 15)) {
5857
+ const durColor = timing.avgTime > 10 ? colors.red : timing.avgTime > 2 ? colors.yellow : colors.dim;
5858
+ const avgLabel = formatDuration(timing.avgTime) + "/file";
5859
+ const maxLabel = timing.maxTime > timing.avgTime * 2 ? `, max ${formatDuration(timing.maxTime)}` : "";
5860
+ consola.log(` ${colors.dim("\u2022")} ${plugin} ${colors.dim(hook)} ${colors.dim("\u2014")} ${durColor(avgLabel)}${colors.dim(maxLabel)} ${colors.dim(`(${timing.count} calls)`)}`);
5861
+ }
5862
+ consola.log("");
5863
+ }
5864
+ }
5865
+ if (report.slowHooks.length > 0) {
5866
+ consola.log(colors.bold(colors.yellow(` Slow Hooks (>${SLOW_HOOK_THRESHOLD_MS}ms own time)`)));
5867
+ consola.log("");
5868
+ for (const hook of report.slowHooks) {
5869
+ const durColor = hook.ownDuration > 500 ? colors.red : hook.ownDuration > 200 ? colors.yellow : colors.dim;
5870
+ const ownLabel = hook.ownDuration !== hook.duration ? `${formatDuration(hook.ownDuration)} own / ${formatDuration(hook.duration)} total` : formatDuration(hook.ownDuration);
5871
+ consola.log(` ${colors.dim("\u2022")} ${hook.name} ${colors.dim("\u2014")} ${durColor(ownLabel)} ${colors.dim(`(${hook.phase})`)}`);
5872
+ }
5873
+ consola.log("");
5874
+ }
5875
+ const significantSubs = subPhases.filter((p) => p.duration > 5);
5876
+ if (significantSubs.length > 0) {
5877
+ consola.log(colors.bold(` Details`));
5878
+ consola.log("");
5879
+ for (const phase of significantSubs) {
5880
+ const durColor = phase.duration > 500 ? colors.red : phase.duration > 100 ? colors.yellow : colors.dim;
5881
+ consola.log(` ${colors.dim("\u2022")} ${phase.name} ${colors.dim("\u2014")} ${durColor(formatDuration(phase.duration))}`);
5882
+ }
5883
+ consola.log("");
5884
+ }
5885
+ }
5886
+ /**
5887
+ * Generate trace events in Chrome Trace Event format.
5888
+ * The output can be loaded in chrome://tracing or https://ui.perfetto.dev
5889
+ */
5890
+ getTraceEvents() {
5891
+ const events = [];
5892
+ const pid = 1;
5893
+ const tidPhases = 1;
5894
+ const tidHooks = 2;
5895
+ const tidPluginBase = 10;
5896
+ const toUs = (ms) => Math.round(ms * 1e3 + this.#baseTs);
5897
+ events.push(
5898
+ { name: "process_name", ph: "M", ts: 0, pid, tid: 0, cat: "", args: { name: "Nuxt Build" } },
5899
+ { name: "thread_name", ph: "M", ts: 0, pid, tid: tidPhases, cat: "", args: { name: "Build" } },
5900
+ { name: "thread_name", ph: "M", ts: 0, pid, tid: tidHooks, cat: "", args: { name: "Hooks" } }
5901
+ );
5902
+ const durUs = (ms) => Math.round(ms * 1e3);
5903
+ const globalEnd = performance.now();
5904
+ events.push(
5905
+ { name: "nuxt build", cat: "build", ph: "B", ts: toUs(this.#globalStart), pid, tid: tidPhases },
5906
+ { name: "nuxt build", cat: "build", ph: "E", ts: toUs(globalEnd), pid, tid: tidPhases }
5907
+ );
5908
+ for (const phase of this.#phases) {
5909
+ events.push(
5910
+ {
5911
+ name: phase.name,
5912
+ cat: "phase",
5913
+ ph: "B",
5914
+ ts: toUs(phase.startTime),
5915
+ pid,
5916
+ tid: tidPhases,
5917
+ args: {
5918
+ memoryBefore: { rss: phase.memoryBefore.rss, heapUsed: phase.memoryBefore.heapUsed }
5919
+ }
5920
+ },
5921
+ {
5922
+ name: phase.name,
5923
+ cat: "phase",
5924
+ ph: "E",
5925
+ ts: toUs(phase.endTime),
5926
+ pid,
5927
+ tid: tidPhases,
5928
+ args: {
5929
+ memoryDelta: { rss: phase.memoryDelta.rss, heapUsed: phase.memoryDelta.heapUsed }
5930
+ }
5931
+ }
5932
+ );
5933
+ }
5934
+ for (const hp of this.#hookPhases) {
5935
+ const name = hp.name.startsWith("hook:") ? hp.name.slice(5) : hp.name;
5936
+ events.push(
5937
+ { name, cat: "hook", ph: "B", ts: toUs(hp.startTime), pid, tid: tidHooks },
5938
+ { name, cat: "hook", ph: "E", ts: toUs(hp.endTime), pid, tid: tidHooks }
5939
+ );
5940
+ }
5941
+ const viteSpans = this.#bundlerPluginSpans.filter((s) => !s.pluginName.startsWith("nitro:"));
5942
+ const nitroSpans = this.#bundlerPluginSpans.filter((s) => s.pluginName.startsWith("nitro:"));
5943
+ const emitPluginSpans = (spans, label, baseTid) => {
5944
+ if (spans.length === 0) {
5945
+ return;
5946
+ }
5947
+ const sorted = [...spans].sort((a, b) => a.startTime - b.startTime);
5948
+ const laneEnds = [];
5949
+ for (const span of sorted) {
5950
+ const endTime = span.startTime + span.durationMs;
5951
+ let lane = laneEnds.findIndex((end) => end < span.startTime);
5952
+ if (lane === -1) {
5953
+ lane = laneEnds.length;
5954
+ laneEnds.push(0);
5955
+ }
5956
+ laneEnds[lane] = endTime;
5957
+ const tid = baseTid + lane;
5958
+ events.push({
5959
+ name: `${span.pluginName}:${span.hookName}`,
5960
+ cat: label,
5961
+ ph: "X",
5962
+ ts: toUs(span.startTime),
5963
+ dur: durUs(span.durationMs),
5964
+ pid,
5965
+ tid
5966
+ });
5967
+ }
5968
+ for (let i = 0; i < laneEnds.length; i++) {
5969
+ const suffix = laneEnds.length > 1 ? ` ${i + 1}` : "";
5970
+ events.push({
5971
+ name: "thread_name",
5972
+ ph: "M",
5973
+ ts: 0,
5974
+ pid,
5975
+ tid: baseTid + i,
5976
+ cat: "",
5977
+ args: { name: `${label}${suffix}` }
5978
+ });
5979
+ }
5980
+ };
5981
+ emitPluginSpans(viteSpans, "Vite Plugins", tidPluginBase);
5982
+ emitPluginSpans(nitroSpans, "Nitro Plugins", tidPluginBase + 100);
5983
+ for (const phase of this.#phases) {
5984
+ events.push(
5985
+ {
5986
+ name: "Memory",
5987
+ cat: "memory",
5988
+ ph: "C",
5989
+ ts: toUs(phase.startTime),
5990
+ pid,
5991
+ tid: 0,
5992
+ args: { rss: phase.memoryBefore.rss, heapUsed: phase.memoryBefore.heapUsed }
5993
+ },
5994
+ {
5995
+ name: "Memory",
5996
+ cat: "memory",
5997
+ ph: "C",
5998
+ ts: toUs(phase.endTime),
5999
+ pid,
6000
+ tid: 0,
6001
+ args: { rss: phase.memoryAfter.rss, heapUsed: phase.memoryAfter.heapUsed }
6002
+ }
6003
+ );
6004
+ }
6005
+ return events;
6006
+ }
6007
+ writeReport(buildDir, options) {
6008
+ const report = this.getReport();
6009
+ const reportPath = join(buildDir, "perf-report.json");
6010
+ const relativeReportPath = relative(process.cwd(), reportPath).replace(/^(?![^.]{1,2}\/)/, "./");
6011
+ const tracePath = join(buildDir, "perf-trace.json");
6012
+ const relativeTracePath = relative(process.cwd(), tracePath).replace(/^(?![^.]{1,2}\/)/, "./");
6013
+ try {
6014
+ mkdirSync(buildDir, { recursive: true });
6015
+ writeFileSync(reportPath, JSON.stringify(report, null, 2), "utf-8");
6016
+ writeFileSync(tracePath, JSON.stringify({ traceEvents: this.getTraceEvents() }), "utf-8");
6017
+ if (!options?.quiet) {
6018
+ consola.log(colors.dim(` Data dump written to ${relativeReportPath}`));
6019
+ consola.log(colors.dim(` Trace written to ${relativeTracePath} (load in \`https://ui.perfetto.dev\`)`));
6020
+ consola.log("");
6021
+ }
6022
+ } catch {
6023
+ }
6024
+ return reportPath;
6025
+ }
6026
+ dispose() {
6027
+ this.#unsubscribe?.();
6028
+ }
6029
+ #computeSlowHooks(reportedPhaseNames) {
6030
+ const hooks = [];
6031
+ for (const hp of this.#hookPhases) {
6032
+ if (hp.duration < SLOW_HOOK_THRESHOLD_MS) {
6033
+ continue;
6034
+ }
6035
+ const hookName = hp.name.slice(5);
6036
+ if (reportedPhaseNames.has(hookName)) {
6037
+ continue;
6038
+ }
6039
+ let attributedTime = 0;
6040
+ for (const phase of this.#phases) {
6041
+ const overlapStart = Math.max(phase.startTime, hp.startTime);
6042
+ const overlapEnd = Math.min(phase.endTime, hp.endTime);
6043
+ if (overlapEnd > overlapStart) {
6044
+ attributedTime += overlapEnd - overlapStart;
6045
+ }
6046
+ }
6047
+ for (const other of this.#hookPhases) {
6048
+ if (other === hp) {
6049
+ continue;
6050
+ }
6051
+ if (other.startTime >= hp.startTime && other.endTime <= hp.endTime) {
6052
+ attributedTime += other.duration;
6053
+ }
6054
+ }
6055
+ const ownDuration = round(hp.duration - round(attributedTime));
6056
+ if (ownDuration >= SLOW_HOOK_THRESHOLD_MS) {
6057
+ const parentPhase = this.#phases.find(
6058
+ (p) => p.startTime <= hp.startTime && p.endTime >= hp.endTime
6059
+ );
6060
+ hooks.push({
6061
+ name: hookName,
6062
+ duration: hp.duration,
6063
+ ownDuration,
6064
+ phase: parentPhase?.name || "(between phases)"
6065
+ });
6066
+ }
6067
+ }
6068
+ return hooks.sort((a, b) => b.ownDuration - a.ownDuration);
6069
+ }
6070
+ }
6071
+
4241
6072
  const schemaModule = defineNuxtModule({
4242
6073
  meta: {
4243
6074
  name: "nuxt:nuxt-config-schema"
@@ -4359,654 +6190,201 @@ declare module '@nuxt/schema' {
4359
6190
  interface NuxtOptions extends Omit<NuxtCustomSchema, 'appConfig'> {}
4360
6191
  interface CustomAppConfig extends _CustomAppConfig {}
4361
6192
  }
4362
-
4363
- declare module 'nuxt/schema' {
4364
- interface NuxtConfig extends Omit<NuxtCustomSchema, 'appConfig'> {}
4365
- interface NuxtOptions extends Omit<NuxtCustomSchema, 'appConfig'> {}
4366
- interface CustomAppConfig extends _CustomAppConfig {}
4367
- }
4368
- `;
4369
- const typesPath = resolve(nuxt.options.buildDir, "schema/nuxt.schema.d.ts");
4370
- await writeFile(typesPath, types, "utf8");
4371
- await nuxt.hooks.callHook("schema:written");
4372
- }
4373
- }
4374
- });
4375
-
4376
- const internalOrderMap = {
4377
- // -40: custom payload revivers (user)
4378
- "user-revivers": -40,
4379
- // -20: pre (user) <-- pre mapped to this
4380
- "user-pre": -20,
4381
- // 0: default (user) <-- default behavior
4382
- "user-default": 0,
4383
- // +20: post (user) <-- post mapped to this
4384
- "user-post": 20};
4385
- const orderMap = {
4386
- pre: internalOrderMap["user-pre"],
4387
- default: internalOrderMap["user-default"],
4388
- post: internalOrderMap["user-post"]
4389
- };
4390
- const metaCache = {};
4391
- function extractMetadata(code, loader = "ts") {
4392
- let meta = {};
4393
- if (metaCache[code]) {
4394
- return metaCache[code];
4395
- }
4396
- if (/defineNuxtPlugin\s*\([\w(]/.test(code)) {
4397
- return {};
4398
- }
4399
- parseAndWalk(code, `file.${loader}`, (node) => {
4400
- if (node.type !== "CallExpression" || node.callee.type !== "Identifier") {
4401
- return;
4402
- }
4403
- const name = "name" in node.callee && node.callee.name;
4404
- if (name !== "defineNuxtPlugin" && name !== "definePayloadPlugin") {
4405
- return;
4406
- }
4407
- if (name === "definePayloadPlugin") {
4408
- meta.order = internalOrderMap["user-revivers"];
4409
- }
4410
- const metaArg = node.arguments[1];
4411
- if (metaArg) {
4412
- if (metaArg.type !== "ObjectExpression") {
4413
- throw new Error("Invalid plugin metadata");
4414
- }
4415
- meta = extractMetaFromObject(metaArg.properties);
4416
- }
4417
- const plugin = node.arguments[0];
4418
- if (plugin?.type === "ObjectExpression") {
4419
- meta = defu(extractMetaFromObject(plugin.properties), meta);
4420
- }
4421
- meta.order ||= orderMap[meta.enforce || "default"] || orderMap.default;
4422
- delete meta.enforce;
4423
- });
4424
- metaCache[code] = meta;
4425
- return meta;
4426
- }
4427
- const keys = {
4428
- name: "name",
4429
- order: "order",
4430
- enforce: "enforce",
4431
- dependsOn: "dependsOn"
4432
- };
4433
- function isMetadataKey(key) {
4434
- return typeof key !== "string" ? key.name in keys : key in keys;
4435
- }
4436
- function extractMetaFromObject(properties) {
4437
- const meta = {};
4438
- for (const property of properties) {
4439
- if (property.type === "SpreadElement" || !("name" in property.key)) {
4440
- throw new Error("Invalid plugin metadata");
4441
- }
4442
- const propertyKey = property.key.name;
4443
- if (!isMetadataKey(propertyKey)) {
4444
- continue;
4445
- }
4446
- if (property.value.type === "Literal") {
4447
- meta[propertyKey] = property.value.value;
4448
- }
4449
- if (property.value.type === "UnaryExpression" && property.value.argument.type === "Literal") {
4450
- meta[propertyKey] = JSON.parse(property.value.operator + property.value.argument.raw);
4451
- }
4452
- if (propertyKey === "dependsOn" && property.value.type === "ArrayExpression") {
4453
- if (property.value.elements.some((e) => !e || e.type !== "Literal" || typeof e.value !== "string")) {
4454
- throw new Error("dependsOn must take an array of string literals");
4455
- }
4456
- meta[propertyKey] = property.value.elements.map((e) => e.value);
4457
- }
4458
- }
4459
- return meta;
4460
- }
4461
- const RemovePluginMetadataPlugin = (nuxt) => createUnplugin(() => {
4462
- return {
4463
- name: "nuxt:remove-plugin-metadata",
4464
- transform(code, id) {
4465
- id = normalize(id);
4466
- const plugin = nuxt.apps.default?.plugins.find((p) => p.src === id);
4467
- if (!plugin) {
4468
- return;
4469
- }
4470
- if (!code.trim()) {
4471
- logger.warn(`Plugin \`${plugin.src}\` has no content.`);
4472
- return {
4473
- code: "export default () => {}",
4474
- map: null
4475
- };
4476
- }
4477
- const exports$1 = findExports(code);
4478
- const defaultExport = exports$1.find((e) => e.type === "default" || e.name === "default");
4479
- if (!defaultExport) {
4480
- logger.warn(`Plugin \`${plugin.src}\` has no default export and will be ignored at build time. Add \`export default defineNuxtPlugin(() => {})\` to your plugin.`);
4481
- return {
4482
- code: "export default () => {}",
4483
- map: null
4484
- };
4485
- }
4486
- const s = new MagicString(code);
4487
- let wrapped = false;
4488
- const wrapperNames = /* @__PURE__ */ new Set(["defineNuxtPlugin", "definePayloadPlugin"]);
4489
- try {
4490
- parseAndWalk(code, id, (node) => {
4491
- if (node.type === "ImportSpecifier" && node.imported.type === "Identifier" && (node.imported.name === "defineNuxtPlugin" || node.imported.name === "definePayloadPlugin")) {
4492
- wrapperNames.add(node.local.name);
4493
- }
4494
- if (node.type !== "CallExpression" || node.callee.type !== "Identifier") {
4495
- return;
4496
- }
4497
- const name = "name" in node.callee && node.callee.name;
4498
- if (!name || !wrapperNames.has(name)) {
4499
- return;
4500
- }
4501
- wrapped = true;
4502
- if (!("order" in plugin) && !("name" in plugin)) {
4503
- return;
4504
- }
4505
- for (const [argIndex, arg] of node.arguments.entries()) {
4506
- if (arg.type !== "ObjectExpression") {
4507
- continue;
4508
- }
4509
- for (const [propertyIndex, property] of arg.properties.entries()) {
4510
- if (property.type === "SpreadElement" || !("name" in property.key)) {
4511
- continue;
4512
- }
4513
- const propertyKey = property.key.name;
4514
- if (propertyKey === "order" || propertyKey === "enforce" || propertyKey === "name") {
4515
- const nextNode = arg.properties[propertyIndex + 1] || node.arguments[argIndex + 1];
4516
- const nextIndex = nextNode?.start || arg.end - 1;
4517
- s.remove(property.start, nextIndex);
4518
- }
4519
- }
4520
- }
4521
- });
4522
- } catch (e) {
4523
- logger.error(e);
4524
- return;
4525
- }
4526
- if (!wrapped) {
4527
- logger.warn(`Plugin \`${plugin.src}\` is not wrapped in \`defineNuxtPlugin\`. It is advised to wrap your plugins as in the future this may enable enhancements.`);
4528
- }
4529
- if (s.hasChanged()) {
4530
- return {
4531
- code: s.toString(),
4532
- map: nuxt.options.sourcemap.client || nuxt.options.sourcemap.server ? s.generateMap({ hires: true }) : null
4533
- };
4534
- }
4535
- }
4536
- };
4537
- });
4538
-
4539
- const AsyncContextInjectionPlugin = (nuxt) => createUnplugin(() => {
4540
- return {
4541
- name: "nuxt:vue-async-context",
4542
- transformInclude(id) {
4543
- return isVue(id, { type: ["template", "script"] });
4544
- },
4545
- transform: {
4546
- filter: {
4547
- code: { include: /_withAsyncContext/ }
4548
- },
4549
- handler(code) {
4550
- const s = new MagicString(code);
4551
- s.prepend('import { withAsyncContext as _withAsyncContext } from "#app/composables/asyncContext";\n');
4552
- s.replace(/withAsyncContext as _withAsyncContext,?/, "");
4553
- if (s.hasChanged()) {
4554
- return {
4555
- code: s.toString(),
4556
- map: nuxt.options.sourcemap.client || nuxt.options.sourcemap.server ? s.generateMap({ hires: true }) : void 0
4557
- };
4558
- }
4559
- }
4560
- }
4561
- };
4562
- });
4563
-
4564
- function processImports(imports) {
4565
- const directImports = /* @__PURE__ */ new Map();
4566
- const namespaces = /* @__PURE__ */ new Map();
4567
- for (const i of imports) {
4568
- const resolvedSpecifier = stripExtension(resolveAlias$1(i.specifier));
4569
- const namedImports = i.namedImports ?? {};
4570
- for (const originalIdentifier in namedImports) {
4571
- const localIdentifier = namedImports[originalIdentifier] || originalIdentifier;
4572
- directImports.set(localIdentifier, {
4573
- originalName: originalIdentifier,
4574
- source: resolvedSpecifier
4575
- });
4576
- }
4577
- if (i.namespacedImport || i.defaultImport) {
4578
- if (!namespaces.has(resolvedSpecifier)) {
4579
- namespaces.set(resolvedSpecifier, {
4580
- namespaces: /* @__PURE__ */ new Set()
4581
- });
4582
- }
4583
- }
4584
- if (i.defaultImport) {
4585
- const namespace = i.defaultImport;
4586
- const entry = namespaces.get(resolvedSpecifier);
4587
- entry.namespaces.add(namespace);
4588
- directImports.set(i.defaultImport, {
4589
- originalName: "default",
4590
- source: resolvedSpecifier
4591
- });
4592
- } else if (i.namespacedImport) {
4593
- const namespace = i.namespacedImport;
4594
- const entry = namespaces.get(resolvedSpecifier);
4595
- entry.namespaces.add(namespace);
4596
- }
4597
- }
4598
- return {
4599
- directImports,
4600
- namespaces
4601
- };
4602
- }
4603
- function parseStaticFunctionCall(node, filter) {
4604
- const callExpression = node.type === "CallExpression" ? node : node.type === "ChainExpression" && node.expression.type === "CallExpression" ? node.expression : null;
4605
- if (!callExpression) {
4606
- return null;
4607
- }
4608
- let functionName;
4609
- let identifierNode;
4610
- if (callExpression.callee.type === "Identifier") {
4611
- functionName = callExpression.callee.name;
4612
- identifierNode = callExpression.callee;
4613
- } else if (callExpression.callee.type === "ParenthesizedExpression") {
4614
- if (callExpression.callee.expression.type === "Identifier") {
4615
- functionName = callExpression.callee.expression.name;
4616
- identifierNode = callExpression.callee;
4617
- } else if ((callExpression.callee.expression.type === "TSAsExpression" || callExpression.callee.expression.type === "TSTypeAssertion" || callExpression.callee.expression.type === "TSNonNullExpression") && callExpression.callee.expression.expression.type === "Identifier") {
4618
- functionName = callExpression.callee.expression.expression.name;
4619
- identifierNode = callExpression.callee;
6193
+
6194
+ declare module 'nuxt/schema' {
6195
+ interface NuxtConfig extends Omit<NuxtCustomSchema, 'appConfig'> {}
6196
+ interface NuxtOptions extends Omit<NuxtCustomSchema, 'appConfig'> {}
6197
+ interface CustomAppConfig extends _CustomAppConfig {}
6198
+ }
6199
+ `;
6200
+ const typesPath = resolve(nuxt.options.buildDir, "schema/nuxt.schema.d.ts");
6201
+ await writeFile(typesPath, types, "utf8");
6202
+ await nuxt.hooks.callHook("schema:written");
4620
6203
  }
4621
6204
  }
4622
- if (functionName && identifierNode && filter.test(functionName)) {
4623
- return {
4624
- name: functionName,
4625
- namespace: null,
4626
- node: identifierNode,
4627
- callExpression
4628
- };
6205
+ });
6206
+
6207
+ const internalOrderMap = {
6208
+ // -40: custom payload revivers (user)
6209
+ "user-revivers": -40,
6210
+ // -20: pre (user) <-- pre mapped to this
6211
+ "user-pre": -20,
6212
+ // 0: default (user) <-- default behavior
6213
+ "user-default": 0,
6214
+ // +20: post (user) <-- post mapped to this
6215
+ "user-post": 20};
6216
+ const orderMap = {
6217
+ pre: internalOrderMap["user-pre"],
6218
+ default: internalOrderMap["user-default"],
6219
+ post: internalOrderMap["user-post"]
6220
+ };
6221
+ const metaCache = {};
6222
+ function extractMetadata(code, loader = "ts") {
6223
+ let meta = {};
6224
+ if (metaCache[code]) {
6225
+ return metaCache[code];
4629
6226
  }
4630
- function getParsedMemberExpression(memberExpression) {
4631
- let memberObjectName;
4632
- if (memberExpression.object.type === "Identifier") {
4633
- memberObjectName = memberExpression.object.name;
4634
- } else if (memberExpression.object.type === "ParenthesizedExpression") {
4635
- if (memberExpression.object.expression.type === "Identifier") {
4636
- memberObjectName = memberExpression.object.expression.name;
4637
- } else if ((memberExpression.object.expression.type === "TSAsExpression" || memberExpression.object.expression.type === "TSTypeAssertion" || memberExpression.object.expression.type === "TSNonNullExpression") && memberExpression.object.expression.expression.type === "Identifier") {
4638
- memberObjectName = memberExpression.object.expression.expression.name;
4639
- }
4640
- }
4641
- if (memberObjectName) {
4642
- if (memberExpression.property.type === "Identifier" && filter.test(memberExpression.property.name)) {
4643
- return {
4644
- name: memberExpression.property.name,
4645
- namespace: memberObjectName,
4646
- node: memberExpression.property
4647
- };
4648
- }
4649
- if (memberExpression.property.type === "Literal" && typeof memberExpression.property.value === "string" && filter.test(memberExpression.property.value)) {
4650
- return {
4651
- name: memberExpression.property.value,
4652
- namespace: memberObjectName,
4653
- node: memberExpression
4654
- };
4655
- }
4656
- }
4657
- return null;
6227
+ if (/defineNuxtPlugin\s*\([\w(]/.test(code)) {
6228
+ return {};
4658
6229
  }
4659
- if (callExpression.callee.type === "MemberExpression") {
4660
- const val = getParsedMemberExpression(callExpression.callee);
4661
- if (val) {
4662
- return {
4663
- ...val,
4664
- callExpression
4665
- };
6230
+ parseAndWalk(code, `file.${loader}`, (node) => {
6231
+ if (node.type !== "CallExpression" || node.callee.type !== "Identifier") {
6232
+ return;
4666
6233
  }
4667
- } else if (callExpression.callee.type === "ParenthesizedExpression" && callExpression.callee.expression.type === "MemberExpression") {
4668
- const val = getParsedMemberExpression(callExpression.callee.expression);
4669
- if (val) {
4670
- return {
4671
- ...val,
4672
- node: callExpression.callee,
4673
- callExpression
4674
- };
6234
+ const name = "name" in node.callee && node.callee.name;
6235
+ if (name !== "defineNuxtPlugin" && name !== "definePayloadPlugin") {
6236
+ return;
4675
6237
  }
4676
- }
4677
- return null;
4678
- }
4679
- function parseStaticExportIdentifiers(node, filter) {
4680
- if (node.type === "ExportNamedDeclaration" && node.exportKind !== "type") {
4681
- if (node.declaration?.type === "VariableDeclaration") {
4682
- return node.declaration.declarations.map((d) => {
4683
- if (d.id.type === "Identifier" && (true)) {
4684
- return {
4685
- localName: d.id.name,
4686
- exportedName: d.id.name
4687
- };
4688
- }
4689
- return null;
4690
- }).filter((v) => !!v);
6238
+ if (name === "definePayloadPlugin") {
6239
+ meta.order = internalOrderMap["user-revivers"];
4691
6240
  }
4692
- if (node.declaration?.type === "FunctionDeclaration") {
4693
- if (node.declaration.id?.type === "Identifier" && (true)) {
4694
- return [{
4695
- localName: node.declaration.id.name,
4696
- exportedName: node.declaration.id.name
4697
- }];
6241
+ const metaArg = node.arguments[1];
6242
+ if (metaArg) {
6243
+ if (metaArg.type !== "ObjectExpression") {
6244
+ throw new Error("Invalid plugin metadata");
4698
6245
  }
4699
- return [];
6246
+ meta = extractMetaFromObject(metaArg.properties);
4700
6247
  }
4701
- if (node.declaration?.type === "ClassDeclaration") {
4702
- if (node.declaration.id?.type === "Identifier" && (true)) {
4703
- return [{
4704
- localName: node.declaration.id.name,
4705
- exportedName: node.declaration.id.name
4706
- }];
4707
- }
4708
- return [];
6248
+ const plugin = node.arguments[0];
6249
+ if (plugin?.type === "ObjectExpression") {
6250
+ meta = defu(extractMetaFromObject(plugin.properties), meta);
4709
6251
  }
4710
- if (node.specifiers && node.specifiers.length) {
4711
- return node.specifiers.map((s) => {
4712
- if (s.exported.type === "Identifier" && s.exportKind !== "type" && s.local.type === "Identifier" && (true)) {
4713
- return {
4714
- localName: s.local.name,
4715
- exportedName: s.exported.name
4716
- };
4717
- }
4718
- return null;
4719
- }).filter((v) => !!v);
6252
+ meta.order ||= orderMap[meta.enforce || "default"] || orderMap.default;
6253
+ delete meta.enforce;
6254
+ });
6255
+ metaCache[code] = meta;
6256
+ return meta;
6257
+ }
6258
+ const keys = {
6259
+ name: "name",
6260
+ order: "order",
6261
+ enforce: "enforce",
6262
+ dependsOn: "dependsOn"
6263
+ };
6264
+ function isMetadataKey(key) {
6265
+ return typeof key !== "string" ? key.name in keys : key in keys;
6266
+ }
6267
+ function extractMetaFromObject(properties) {
6268
+ const meta = {};
6269
+ for (const property of properties) {
6270
+ if (property.type === "SpreadElement" || !("name" in property.key)) {
6271
+ throw new Error("Invalid plugin metadata");
4720
6272
  }
4721
- return [];
4722
- }
4723
- if (node.type === "ExportDefaultDeclaration" && (!node.exportKind || node.exportKind === "value")) {
4724
- if (node.declaration.type === "Identifier") {
4725
- return [{
4726
- localName: node.declaration.name,
4727
- exportedName: "default"
4728
- }];
6273
+ const propertyKey = property.key.name;
6274
+ if (!isMetadataKey(propertyKey)) {
6275
+ continue;
4729
6276
  }
4730
- if (node.declaration.type === "FunctionDeclaration") {
4731
- if (node.declaration.id?.type === "Identifier") {
4732
- return [{
4733
- localName: node.declaration.id.name,
4734
- exportedName: "default"
4735
- }];
4736
- }
4737
- return [];
6277
+ if (property.value.type === "Literal") {
6278
+ meta[propertyKey] = property.value.value;
4738
6279
  }
4739
- if (node.declaration.type === "ClassDeclaration") {
4740
- if (node.declaration.id?.type === "Identifier") {
4741
- return [{
4742
- localName: node.declaration.id.name,
4743
- exportedName: "default"
4744
- }];
4745
- }
4746
- return [];
6280
+ if (property.value.type === "UnaryExpression" && property.value.argument.type === "Literal") {
6281
+ meta[propertyKey] = JSON.parse(property.value.operator + property.value.argument.raw);
4747
6282
  }
4748
- }
4749
- if (node.type === "TSExportAssignment") {
4750
- if (node.expression.type === "Identifier") {
4751
- {
4752
- return [{
4753
- localName: node.expression.name,
4754
- exportedName: "default"
4755
- }];
6283
+ if (propertyKey === "dependsOn" && property.value.type === "ArrayExpression") {
6284
+ if (property.value.elements.some((e) => !e || e.type !== "Literal" || typeof e.value !== "string")) {
6285
+ throw new Error("dependsOn must take an array of string literals");
4756
6286
  }
6287
+ meta[propertyKey] = property.value.elements.map((e) => e.value);
4757
6288
  }
4758
6289
  }
4759
- return [];
4760
- }
4761
-
4762
- const stringTypes = ["Literal", "TemplateLiteral"];
4763
- const NUXT_LIB_RE = /node_modules\/(?:nuxt|nuxt3|nuxt-nightly|@nuxt)\//;
4764
- const SUPPORTED_EXT_RE$1 = /\.(?:m?[jt]sx?|vue)/;
4765
- const SCRIPT_RE$1 = /(?<=<script[^>]*>)[\s\S]*?(?=<\/script>)/i;
4766
- const NUXT_INJECTED_MARKER = "/* nuxt-injected */";
4767
- function shouldTransformFile(id, extensions) {
4768
- const { pathname, search } = parseURL(decodeURIComponent(pathToFileURL(id).href));
4769
- return !NUXT_LIB_RE.test(pathname) && (extensions instanceof RegExp ? extensions.test(pathname) : new RegExp(`\\.(${extensions.map((e) => escapeRE(e)).join("|")})$`).test(pathname)) && parseQuery(search).type !== "style" && !parseQuery(search).macro;
6290
+ return meta;
4770
6291
  }
4771
- const KeyedFunctionsPlugin = (options) => createUnplugin(() => {
4772
- const namesToSourcesToFunctionMeta = /* @__PURE__ */ new Map();
4773
- const defaultExportSources = /* @__PURE__ */ new Set();
4774
- for (const f of options.keyedFunctions) {
4775
- let functionName = f.name;
4776
- const fnSource = typeof f.source === "string" ? stripExtension(f.source) : "";
4777
- if (f.name === "default") {
4778
- const parsedSource = parse$1(f.source);
4779
- defaultExportSources.add(parsedSource.name);
4780
- functionName = camelCase(parsedSource.name);
4781
- }
4782
- if (import.meta.dev) {
4783
- const sourcesToFunctionMeta2 = namesToSourcesToFunctionMeta.get(functionName);
4784
- const existingEntry = sourcesToFunctionMeta2?.get(fnSource);
4785
- if (existingEntry?.source && existingEntry.source === fnSource) {
4786
- logger.warn(`[nuxt:compiler] [keyed-functions] Duplicate function name \`${functionName}\`${functionName !== f.name ? ` defined as \`${f.name}\`` : ""} with ${f.source ? `the same source \`${f.source}\`` : "no source"} found. Overwriting the existing entry.`);
6292
+ const RemovePluginMetadataPlugin = (nuxt) => createUnplugin(() => {
6293
+ return {
6294
+ name: "nuxt:remove-plugin-metadata",
6295
+ transform(code, id) {
6296
+ id = normalize(id);
6297
+ const plugin = nuxt.apps.default?.plugins.find((p) => p.src === id);
6298
+ if (!plugin) {
6299
+ return;
4787
6300
  }
4788
- }
4789
- let sourcesToFunctionMeta = namesToSourcesToFunctionMeta.get(functionName);
4790
- if (!sourcesToFunctionMeta) {
4791
- sourcesToFunctionMeta = /* @__PURE__ */ new Map();
4792
- namesToSourcesToFunctionMeta.set(functionName, sourcesToFunctionMeta);
4793
- }
4794
- sourcesToFunctionMeta.set(fnSource, {
4795
- ...f,
4796
- // TODO: use only `fnSource` in Nuxt 5
4797
- source: typeof f.source === "string" ? fnSource : f.source
4798
- });
4799
- }
4800
- const sources = /* @__PURE__ */ new Set();
4801
- for (const sourcesToFunctionMeta of namesToSourcesToFunctionMeta.values()) {
4802
- for (const f of sourcesToFunctionMeta.values()) {
4803
- if (f.source && typeof f.source === "string") {
4804
- sources.add(f.source);
6301
+ if (!code.trim()) {
6302
+ logger.warn(`Plugin \`${plugin.src}\` has no content.`);
6303
+ return {
6304
+ code: "export default () => {}",
6305
+ map: null
6306
+ };
6307
+ }
6308
+ const exports$1 = findExports(code);
6309
+ const defaultExport = exports$1.find((e) => e.type === "default" || e.name === "default");
6310
+ if (!defaultExport) {
6311
+ logger.warn(`Plugin \`${plugin.src}\` has no default export and will be ignored at build time. Add \`export default defineNuxtPlugin(() => {})\` to your plugin.`);
6312
+ return {
6313
+ code: "export default () => {}",
6314
+ map: null
6315
+ };
4805
6316
  }
4806
- }
4807
- }
4808
- const CODE_INCLUDE_RE = new RegExp(`\\b(${[...namesToSourcesToFunctionMeta.keys(), ...defaultExportSources].map((f) => escapeRE(f)).join("|")})\\b`);
4809
- return {
4810
- name: "nuxt:compiler:keyed-functions",
4811
- enforce: "post",
4812
- transformInclude: (id) => shouldTransformFile(id, SUPPORTED_EXT_RE$1),
4813
- transform: {
4814
- filter: {
4815
- code: { include: CODE_INCLUDE_RE }
4816
- },
4817
- async handler(code, _id) {
4818
- const { 0: script = code, index: codeIndex = 0 } = code.match(SCRIPT_RE$1) || { 0: code, index: 0 };
4819
- const id = stripExtension(_id);
4820
- const { directImports, namespaces } = processImports(findStaticImports(script).map((i) => parseStaticImport(i)));
4821
- const shouldConsiderExports = sources.has(id);
4822
- const localNamesToExportedName = /* @__PURE__ */ new Map();
4823
- const possibleLocalFunctionNames = new Set(namesToSourcesToFunctionMeta.keys());
4824
- for (const [localName, directImport] of directImports) {
4825
- const functionName = directImport.originalName === "default" ? camelCase(parse$1(directImport.source).name) : directImport.originalName;
4826
- if (namesToSourcesToFunctionMeta.has(functionName)) {
4827
- possibleLocalFunctionNames.add(localName);
4828
- }
4829
- }
4830
- const autoImports = await options.getAutoImports();
4831
- const autoImportsToSources = new Map(autoImports.map((i) => [i.as || i.name, i.from]));
4832
- function getFunctionMetaByLocalName(localName, source) {
4833
- if (!localName) {
4834
- return;
4835
- }
4836
- const exportedName = localNamesToExportedName.get(localName);
4837
- if (exportedName) {
4838
- return namesToSourcesToFunctionMeta.get(exportedName)?.get(source);
4839
- }
4840
- const directImport = directImports.get(localName);
4841
- if (directImport) {
4842
- const functionName = directImport.originalName === "default" ? camelCase(parse$1(directImport.source).name) : directImport.originalName;
4843
- const sourcesToMetas = namesToSourcesToFunctionMeta.get(functionName);
4844
- if (!sourcesToMetas) {
4845
- return;
4846
- }
4847
- const fnMeta = sourcesToMetas.get(source);
4848
- if (fnMeta) {
4849
- return fnMeta;
4850
- }
4851
- const backwardsCompatibleFnMeta = sourcesToMetas.get("");
4852
- if (backwardsCompatibleFnMeta?.source === void 0) {
4853
- const autoImportResolvedSource = stripExtension(resolveAlias$1(autoImportsToSources.get(localName) ?? ""));
4854
- if (autoImportResolvedSource === source) {
4855
- return backwardsCompatibleFnMeta;
4856
- }
4857
- } else if (backwardsCompatibleFnMeta.source instanceof RegExp && backwardsCompatibleFnMeta.source.test(source)) {
4858
- return backwardsCompatibleFnMeta;
4859
- }
4860
- return;
4861
- }
4862
- return namesToSourcesToFunctionMeta.get(localName)?.get(source);
4863
- }
4864
- function _resolvePath(path) {
4865
- let p = path;
4866
- if (isAbsolute(p)) {
4867
- return p;
4868
- }
4869
- p = resolveAlias$1(p, options.alias);
4870
- if (isAbsolute(p)) {
4871
- return p;
4872
- }
4873
- return join(parse$1(id).dir, p);
4874
- }
4875
- const s = new MagicString(code);
4876
- let count = 0;
4877
- const scopeTracker = new ScopeTracker({
4878
- preserveExitedScopes: true
4879
- });
4880
- const { program } = parseAndWalk(code, _id, {
4881
- scopeTracker,
4882
- enter(node) {
4883
- if (!shouldConsiderExports) {
4884
- return;
4885
- }
4886
- if (node.type !== "ExportNamedDeclaration" && node.type !== "ExportDefaultDeclaration") {
4887
- return;
4888
- }
4889
- const result = parseStaticExportIdentifiers(node);
4890
- for (const exportMeta of result) {
4891
- const { localName, exportedName } = exportMeta;
4892
- const functionName = exportedName === "default" ? camelCase(parse$1(id).name) : getFunctionMetaByLocalName(exportedName, id)?.name;
4893
- if (!functionName) {
4894
- continue;
4895
- }
4896
- localNamesToExportedName.set(localName, functionName);
4897
- }
4898
- }
4899
- });
4900
- scopeTracker.freeze();
4901
- for (const localName of localNamesToExportedName.keys()) {
4902
- possibleLocalFunctionNames.add(localName);
4903
- }
4904
- const LOCAL_FUNCTION_NAMES_RE = new RegExp(`\\b(${[...possibleLocalFunctionNames].map((f) => escapeRE(f)).join("|")})\\b`);
4905
- function processKeyedFunction(walkContext, node, handler) {
4906
- if (node.type !== "CallExpression" && node.type !== "ChainExpression") {
4907
- return;
4908
- }
4909
- const parsedCall = parseStaticFunctionCall(node, LOCAL_FUNCTION_NAMES_RE);
4910
- if (!parsedCall) {
4911
- return;
4912
- }
4913
- const functionScopeTrackerNode = scopeTracker.getDeclaration(!parsedCall.namespace ? parsedCall.name : parsedCall.namespace);
4914
- function isKeyedFunctionImport(node2) {
4915
- return node2?.type === "Import" && node2.importNode.importKind !== "type";
4916
- }
4917
- let importSourceResolved;
4918
- if (localNamesToExportedName.has(parsedCall.name) && functionScopeTrackerNode?.scope === "") {
4919
- importSourceResolved = id;
4920
- } else if (isKeyedFunctionImport(functionScopeTrackerNode)) {
4921
- importSourceResolved = stripExtension(_resolvePath(functionScopeTrackerNode.importNode.source.value));
6317
+ const s = new MagicString(code);
6318
+ let wrapped = false;
6319
+ const wrapperNames = /* @__PURE__ */ new Set(["defineNuxtPlugin", "definePayloadPlugin"]);
6320
+ try {
6321
+ parseAndWalk(code, id, (node) => {
6322
+ if (node.type === "ImportSpecifier" && node.imported.type === "Identifier" && (node.imported.name === "defineNuxtPlugin" || node.imported.name === "definePayloadPlugin")) {
6323
+ wrapperNames.add(node.local.name);
4922
6324
  }
4923
- if (!importSourceResolved) {
4924
- walkContext.skip();
6325
+ if (node.type !== "CallExpression" || node.callee.type !== "Identifier") {
4925
6326
  return;
4926
6327
  }
4927
- const fnMeta = getFunctionMetaByLocalName(parsedCall.name, importSourceResolved);
4928
- if (!fnMeta) {
4929
- walkContext.skip();
6328
+ const name = "name" in node.callee && node.callee.name;
6329
+ if (!name || !wrapperNames.has(name)) {
4930
6330
  return;
4931
6331
  }
4932
- if (!parsedCall.namespace) {
4933
- if (parsedCall.callExpression.arguments.length >= fnMeta.argumentLength && !parsedCall.callExpression.arguments.some((a) => a.type === "SpreadElement")) {
4934
- walkContext.skip();
4935
- return;
4936
- }
4937
- if (
4938
- // the function is imported
4939
- isKeyedFunctionImport(functionScopeTrackerNode) && // import { useKeyed } from '...'
4940
- (functionScopeTrackerNode.node.type === "ImportSpecifier" && functionScopeTrackerNode.node.importKind !== "type" || functionScopeTrackerNode.node.type === "ImportDefaultSpecifier" && fnMeta.name === "default") && // the function is imported from the correct source when `source` is specified
4941
- (typeof fnMeta.source === "string" && stripExtension(fnMeta.source) === importSourceResolved || !fnMeta.source && stripExtension(_resolvePath(autoImportsToSources.get(parsedCall.name) ?? "")) === importSourceResolved || fnMeta.source instanceof RegExp && fnMeta.source.test(importSourceResolved)) || localNamesToExportedName.has(parsedCall.name) && functionScopeTrackerNode?.scope === ""
4942
- ) {
4943
- handler({ parsedCall, fnMeta });
4944
- }
4945
- walkContext.skip();
6332
+ wrapped = true;
6333
+ if (!("order" in plugin) && !("name" in plugin)) {
4946
6334
  return;
4947
6335
  }
4948
- if (parsedCall.namespace) {
4949
- const namespacedImportMeta = namespaces.get(importSourceResolved);
4950
- const namespaceScopeTrackerNode = scopeTracker.getDeclaration(parsedCall.namespace);
4951
- if (namespacedImportMeta && namespacedImportMeta.namespaces.has(parsedCall.namespace) && namespaceScopeTrackerNode?.type === "Import" && namespaceScopeTrackerNode.node.type === "ImportNamespaceSpecifier") {
4952
- handler({ parsedCall, fnMeta });
6336
+ for (const [argIndex, arg] of node.arguments.entries()) {
6337
+ if (arg.type !== "ObjectExpression") {
6338
+ continue;
4953
6339
  }
4954
- walkContext.skip();
4955
- return;
4956
- }
4957
- }
4958
- walk(program, {
4959
- scopeTracker,
4960
- enter(node) {
4961
- processKeyedFunction(this, node, ({ parsedCall, fnMeta }) => {
4962
- const lastArgument = parsedCall.callExpression.arguments[parsedCall.callExpression.arguments.length - 1];
4963
- if (lastArgument?.type === "Literal" && typeof lastArgument.value === "string" && lastArgument.end + NUXT_INJECTED_MARKER.length + 1 < parsedCall.callExpression.end) {
4964
- let wasKeyInjected = true;
4965
- for (let i2 = 0; i2 < NUXT_INJECTED_MARKER.length; i2++) {
4966
- if (code[codeIndex + lastArgument.end + 1 + i2] !== NUXT_INJECTED_MARKER[i2]) {
4967
- wasKeyInjected = false;
4968
- break;
4969
- }
4970
- }
4971
- if (wasKeyInjected) {
4972
- return;
4973
- }
4974
- }
4975
- switch (parsedCall.name) {
4976
- case "useState":
4977
- if (stringTypes.includes(parsedCall.callExpression.arguments[0]?.type) && typeof fnMeta.source === "string" && stripExtension(fnMeta.source) === stripExtension(resolveAlias$1("#app/composables/state", options.alias))) {
4978
- return;
4979
- }
4980
- break;
4981
- case "useFetch":
4982
- case "useLazyFetch":
4983
- if (stringTypes.includes(parsedCall.callExpression.arguments[1]?.type) && typeof fnMeta.source === "string" && stripExtension(fnMeta.source) === stripExtension(resolveAlias$1("#app/composables/fetch", options.alias))) {
4984
- return;
4985
- }
4986
- break;
4987
- case "useAsyncData":
4988
- case "useLazyAsyncData":
4989
- if (stringTypes.includes(parsedCall.callExpression.arguments[0]?.type) && typeof fnMeta.source === "string" && stripExtension(fnMeta.source) === stripExtension(resolveAlias$1("#app/composables/asyncData", options.alias))) {
4990
- return;
4991
- }
4992
- break;
6340
+ for (const [propertyIndex, property] of arg.properties.entries()) {
6341
+ if (property.type === "SpreadElement" || !("name" in property.key)) {
6342
+ continue;
4993
6343
  }
4994
- let i = codeIndex + parsedCall.callExpression.end - 2;
4995
- while (i >= codeIndex + parsedCall.callExpression.start && isWhitespace(code[i])) {
4996
- i--;
6344
+ const propertyKey = property.key.name;
6345
+ if (propertyKey === "order" || propertyKey === "enforce" || propertyKey === "name") {
6346
+ const nextNode = arg.properties[propertyIndex + 1] || node.arguments[argIndex + 1];
6347
+ const nextIndex = nextNode?.start || arg.end - 1;
6348
+ s.remove(property.start, nextIndex);
4997
6349
  }
4998
- const endsWithComma = code[i] === ",";
4999
- s.appendLeft(
5000
- codeIndex + parsedCall.callExpression.end - 1,
5001
- (parsedCall.callExpression.arguments.length && !endsWithComma ? ", " : "") + "'$" + hash(`${_id}-${++count}`).slice(0, 10) + `' ${NUXT_INJECTED_MARKER}`
5002
- );
5003
- });
6350
+ }
5004
6351
  }
5005
6352
  });
6353
+ } catch (e) {
6354
+ logger.error(e);
6355
+ return;
6356
+ }
6357
+ if (!wrapped) {
6358
+ logger.warn(`Plugin \`${plugin.src}\` is not wrapped in \`defineNuxtPlugin\`. It is advised to wrap your plugins as in the future this may enable enhancements.`);
6359
+ }
6360
+ if (s.hasChanged()) {
6361
+ return {
6362
+ code: s.toString(),
6363
+ map: nuxt.options.sourcemap.client || nuxt.options.sourcemap.server ? s.generateMap({ hires: true }) : null
6364
+ };
6365
+ }
6366
+ }
6367
+ };
6368
+ });
6369
+
6370
+ const AsyncContextInjectionPlugin = (nuxt) => createUnplugin(() => {
6371
+ return {
6372
+ name: "nuxt:vue-async-context",
6373
+ transformInclude(id) {
6374
+ return isVue(id, { type: ["template", "script"] });
6375
+ },
6376
+ transform: {
6377
+ filter: {
6378
+ code: { include: /_withAsyncContext/ }
6379
+ },
6380
+ handler(code) {
6381
+ const s = new MagicString(code);
6382
+ s.prepend('import { withAsyncContext as _withAsyncContext } from "#app/composables/asyncContext";\n');
6383
+ s.replace(/withAsyncContext as _withAsyncContext,?/, "");
5006
6384
  if (s.hasChanged()) {
5007
6385
  return {
5008
6386
  code: s.toString(),
5009
- map: options.sourcemap ? s.generateMap({ hires: true }) : void 0
6387
+ map: nuxt.options.sourcemap.client || nuxt.options.sourcemap.server ? s.generateMap({ hires: true }) : void 0
5010
6388
  };
5011
6389
  }
5012
6390
  }
@@ -5071,8 +6449,10 @@ function PrehydrateTransformPlugin(options = {}) {
5071
6449
 
5072
6450
  const functionsToExtract = /* @__PURE__ */ new Set(["useAsyncData", "useLazyAsyncData"]);
5073
6451
  const FUNCTIONS_RE = /\buse(?:Lazy)?AsyncData\b/;
5074
- const SUPPORTED_EXT_RE = /\.(?:m?[jt]sx?|vue)$/;
6452
+ const SUPPORTED_EXT_RE = /^[^?]*\.(?:m?[jt]sx?|vue)(?:$|\?)/;
5075
6453
  const SCRIPT_RE = /(?<=<script[^>]*>)[\s\S]*?(?=<\/script>)/i;
6454
+ const STYLE_QUERY_RE = /[?&]type=style/;
6455
+ const MACRO_QUERY_RE = /[?&]macro(?:=|&|$)/;
5076
6456
  const ExtractAsyncDataHandlersPlugin = (options) => createUnplugin(() => {
5077
6457
  const asyncDatas = {};
5078
6458
  let count = 0;
@@ -5089,14 +6469,11 @@ const ExtractAsyncDataHandlersPlugin = (options) => createUnplugin(() => {
5089
6469
  return asyncDatas[id];
5090
6470
  }
5091
6471
  },
5092
- transformInclude(id) {
5093
- const { pathname, search } = parseURL(decodeURIComponent(pathToFileURL(id).href));
5094
- return SUPPORTED_EXT_RE.test(pathname) && parseQuery(search).type !== "style" && !parseQuery(search).macro;
5095
- },
5096
6472
  transform: {
5097
6473
  filter: {
5098
6474
  id: {
5099
- exclude: [/nuxt\/(src|dist)\/app/]
6475
+ include: SUPPORTED_EXT_RE,
6476
+ exclude: [/nuxt\/(src|dist)\/app/, STYLE_QUERY_RE, MACRO_QUERY_RE]
5100
6477
  },
5101
6478
  code: { include: FUNCTIONS_RE }
5102
6479
  },
@@ -5310,8 +6687,13 @@ function escapeDirectory(path) {
5310
6687
  function createNuxt(options) {
5311
6688
  const hooks = createHooks();
5312
6689
  const { callHook, callHookParallel, callHookWith } = hooks;
5313
- hooks.callHook = (...args) => runWithNuxtContext(nuxt, () => callHook(...args));
5314
- hooks.callHookParallel = (...args) => runWithNuxtContext(nuxt, () => callHookParallel(...args));
6690
+ if (options.experimental.asyncCallHook) {
6691
+ hooks.callHook = (...args) => Promise.resolve().then(() => runWithNuxtContext(nuxt, () => callHook(...args)));
6692
+ hooks.callHookParallel = (...args) => Promise.resolve().then(() => runWithNuxtContext(nuxt, () => callHookParallel(...args)) ?? []);
6693
+ } else {
6694
+ hooks.callHook = (...args) => runWithNuxtContext(nuxt, () => callHook(...args));
6695
+ hooks.callHookParallel = (...args) => runWithNuxtContext(nuxt, () => callHookParallel(...args));
6696
+ }
5315
6697
  hooks.callHookWith = (...args) => runWithNuxtContext(nuxt, () => callHookWith(...args));
5316
6698
  const nuxt = {
5317
6699
  __name: randomUUID(),
@@ -5322,7 +6704,9 @@ function createNuxt(options) {
5322
6704
  addHooks: hooks.addHooks,
5323
6705
  hook: hooks.hook,
5324
6706
  ready: () => runWithNuxtContext(nuxt, () => initNuxt(nuxt)),
5325
- close: () => hooks.callHook("close", nuxt),
6707
+ close: async () => {
6708
+ await hooks.callHook("close", nuxt);
6709
+ },
5326
6710
  vfs: {},
5327
6711
  apps: {},
5328
6712
  runWithContext: (fn) => runWithNuxtContext(nuxt, fn),
@@ -5391,6 +6775,7 @@ const nightlies = {
5391
6775
  };
5392
6776
  let warnedAboutCompatDate = false;
5393
6777
  async function initNuxt(nuxt) {
6778
+ nuxt._perf?.startPhase("init");
5394
6779
  const layerDirs = getLayerDirectories(nuxt);
5395
6780
  for (const config of nuxt.options._layers.map((layer) => layer.config).reverse()) {
5396
6781
  if (config.hooks) {
@@ -5473,7 +6858,7 @@ Using \`${fallbackCompatibilityDate}\` as fallback. More info at: ${colors.under
5473
6858
  tsConfig: { compilerOptions: { paths: { ...paths } } }
5474
6859
  });
5475
6860
  });
5476
- const serverBuilderTypePath = typeof nuxt.options.server.builder === "string" ? nuxt.options.server.builder === "@nuxt/nitro-server" ? resolveModulePath(nuxt.options.server.builder, { from: import.meta.url }) : nuxt.options.server.builder : void 0;
6861
+ const serverBuilderReference = typeof nuxt.options.server.builder === "string" ? nuxt.options.server.builder === "@nuxt/nitro-server" ? { path: resolveModulePath(nuxt.options.server.builder, { from: import.meta.url }).replace(".mjs", ".d.mts") } : { types: nuxt.options.server.builder } : void 0;
5477
6862
  nuxt.hook("prepare:types", async (opts) => {
5478
6863
  opts.references.push({ path: resolve(nuxt.options.buildDir, "types/plugins.d.ts") });
5479
6864
  if (nuxt.options.typescript.shim) {
@@ -5487,13 +6872,13 @@ Using \`${fallbackCompatibilityDate}\` as fallback. More info at: ${colors.under
5487
6872
  opts.nodeReferences.push({ path: resolve(nuxt.options.buildDir, "types/runtime-config.d.ts") });
5488
6873
  opts.nodeReferences.push({ path: resolve(nuxt.options.buildDir, "types/app.config.d.ts") });
5489
6874
  opts.nodeReferences.push({ types: "nuxt" });
5490
- opts.nodeReferences.push({ types: relative(nuxt.options.buildDir, resolveModulePath("@nuxt/vite-builder", { from: import.meta.url })) });
6875
+ opts.nodeReferences.push({ path: resolveModulePath("@nuxt/vite-builder", { from: import.meta.url }).replace(".mjs", ".d.mts") });
5491
6876
  if (typeof nuxt.options.builder === "string" && nuxt.options.builder !== "@nuxt/vite-builder") {
5492
6877
  opts.nodeReferences.push({ types: nuxt.options.builder });
5493
6878
  }
5494
- if (serverBuilderTypePath) {
5495
- opts.references.push({ types: serverBuilderTypePath });
5496
- opts.nodeReferences.push({ types: serverBuilderTypePath });
6879
+ if (serverBuilderReference) {
6880
+ opts.references.push(serverBuilderReference);
6881
+ opts.nodeReferences.push(serverBuilderReference);
5497
6882
  }
5498
6883
  opts.sharedReferences.push({ path: resolve(nuxt.options.buildDir, "types/runtime-config.d.ts") });
5499
6884
  opts.sharedReferences.push({ path: resolve(nuxt.options.buildDir, "types/app.config.d.ts") });
@@ -5514,8 +6899,8 @@ Using \`${fallbackCompatibilityDate}\` as fallback. More info at: ${colors.under
5514
6899
  nuxt.hook("nitro:prepare:types", (opts) => {
5515
6900
  opts.references.push({ path: resolve(nuxt.options.buildDir, "types/app.config.d.ts") });
5516
6901
  opts.references.push({ path: resolve(nuxt.options.buildDir, "types/runtime-config.d.ts") });
5517
- if (serverBuilderTypePath) {
5518
- opts.references.push({ types: serverBuilderTypePath });
6902
+ if (serverBuilderReference) {
6903
+ opts.references.push(serverBuilderReference);
5519
6904
  }
5520
6905
  });
5521
6906
  if (nuxt.options.scripts) {
@@ -5563,31 +6948,48 @@ Using \`${fallbackCompatibilityDate}\` as fallback. More info at: ${colors.under
5563
6948
  composables: nuxt.options.optimization.treeShake.composables.client
5564
6949
  }), { server: false });
5565
6950
  }
5566
- const sharedDir = withTrailingSlash(resolve(nuxt.options.rootDir, nuxt.options.dir.shared));
5567
- const relativeSharedDir = withTrailingSlash(relative(nuxt.options.rootDir, resolve(nuxt.options.rootDir, nuxt.options.dir.shared)));
5568
- const sharedPatterns = [/^#shared\//, new RegExp("^" + escapeRE(sharedDir)), new RegExp("^" + escapeRE(relativeSharedDir))];
5569
- const sharedProtectionConfig = {
5570
- cwd: nuxt.options.rootDir,
5571
- include: sharedPatterns,
5572
- patterns: createImportProtectionPatterns(nuxt, { context: "shared" })
5573
- };
5574
- addBuildPlugin({
5575
- vite: () => ImpoundPlugin.vite(sharedProtectionConfig),
5576
- webpack: () => ImpoundPlugin.webpack(sharedProtectionConfig),
5577
- rspack: () => ImpoundPlugin.rspack(sharedProtectionConfig)
5578
- }, { server: false });
5579
- const nuxtProtectionConfig = {
5580
- cwd: nuxt.options.rootDir,
5581
- // Exclude top-level resolutions by plugins
5582
- exclude: [relative(nuxt.options.rootDir, join(nuxt.options.srcDir, "index.html")), ...sharedPatterns],
5583
- patterns: createImportProtectionPatterns(nuxt, { context: "nuxt-app" })
5584
- };
5585
- addBuildPlugin({
5586
- webpack: () => ImpoundPlugin.webpack(nuxtProtectionConfig),
5587
- rspack: () => ImpoundPlugin.rspack(nuxtProtectionConfig)
5588
- });
5589
- addVitePlugin(() => Object.assign(ImpoundPlugin.vite({ ...nuxtProtectionConfig, error: false }), { name: "nuxt:import-protection" }), { client: false });
5590
- addVitePlugin(() => Object.assign(ImpoundPlugin.vite({ ...nuxtProtectionConfig, error: true }), { name: "nuxt:import-protection" }), { server: false });
6951
+ if (!nuxt.options.test) {
6952
+ const sharedDir = withTrailingSlash(resolve(nuxt.options.rootDir, nuxt.options.dir.shared));
6953
+ const relativeSharedDir = withTrailingSlash(relative(nuxt.options.rootDir, resolve(nuxt.options.rootDir, nuxt.options.dir.shared)));
6954
+ const sharedPatterns = [/^#shared\//, new RegExp("^" + escapeRE(sharedDir)), new RegExp("^" + escapeRE(relativeSharedDir))];
6955
+ const sharedProtectionConfig = {
6956
+ cwd: nuxt.options.rootDir,
6957
+ trace: true,
6958
+ include: sharedPatterns,
6959
+ patterns: createImportProtectionPatterns(nuxt, { context: "shared" })
6960
+ };
6961
+ addBuildPlugin({
6962
+ vite: () => ImpoundPlugin.vite(sharedProtectionConfig),
6963
+ webpack: () => ImpoundPlugin.webpack(sharedProtectionConfig),
6964
+ rspack: () => ImpoundPlugin.rspack(sharedProtectionConfig)
6965
+ }, { server: false, prepend: true });
6966
+ const nuxtProtectionConfig = {
6967
+ cwd: nuxt.options.rootDir,
6968
+ trace: true,
6969
+ // Exclude top-level resolutions by plugins
6970
+ exclude: [relative(nuxt.options.rootDir, join(nuxt.options.srcDir, "index.html")), ...sharedPatterns],
6971
+ patterns: createImportProtectionPatterns(nuxt, { context: "nuxt-app" })
6972
+ };
6973
+ addBuildPlugin({
6974
+ webpack: () => ImpoundPlugin.webpack(nuxtProtectionConfig),
6975
+ rspack: () => ImpoundPlugin.rspack(nuxtProtectionConfig)
6976
+ });
6977
+ for (const envOptions of [
6978
+ { client: false },
6979
+ { server: false }
6980
+ ]) {
6981
+ const error = envOptions.client === false ? false : true;
6982
+ const vitePlugins = [ImpoundPlugin.vite({ ...nuxtProtectionConfig, error, ...error === false && { warn: "once" } })].flat().map((p) => Object.assign(p, { name: `nuxt:import-protection:${p.name}` }));
6983
+ const mainPlugins = vitePlugins.filter((p) => !p.name.includes("trace"));
6984
+ const tracePlugins = vitePlugins.filter((p) => p.name.includes("trace"));
6985
+ if (mainPlugins.length) {
6986
+ addVitePlugin(() => mainPlugins, { ...envOptions, prepend: true });
6987
+ }
6988
+ if (tracePlugins.length) {
6989
+ addVitePlugin(() => tracePlugins, envOptions);
6990
+ }
6991
+ }
6992
+ }
5591
6993
  });
5592
6994
  if (!nuxt.options.dev) {
5593
6995
  addBuildPlugin(DevOnlyPlugin({
@@ -5633,6 +7035,8 @@ Using \`${fallbackCompatibilityDate}\` as fallback. More info at: ${colors.under
5633
7035
  nuxt.options.modulesDir.push(join(dirs.root, "node_modules"));
5634
7036
  }
5635
7037
  }
7038
+ nuxt._perf?.endPhase("init");
7039
+ nuxt._perf?.startPhase("modules");
5636
7040
  await nuxt.callHook("modules:before");
5637
7041
  const { paths: watchedModulePaths, resolvedModulePaths, modules } = await resolveModules(nuxt);
5638
7042
  nuxt.options.watch.push(...watchedModulePaths);
@@ -5700,6 +7104,13 @@ Using \`${fallbackCompatibilityDate}\` as fallback. More info at: ${colors.under
5700
7104
  filePath: resolve(nuxt.options.appDir, "components/nuxt-route-announcer"),
5701
7105
  mode: "client"
5702
7106
  });
7107
+ addComponent({
7108
+ name: "NuxtAnnouncer",
7109
+ priority: 10,
7110
+ // built-in that we do not expect the user to override
7111
+ filePath: resolve(nuxt.options.appDir, "components/nuxt-announcer"),
7112
+ mode: "client"
7113
+ });
5703
7114
  if (nuxt.options.experimental.clientFallback) {
5704
7115
  addComponent({
5705
7116
  name: "NuxtClientFallback",
@@ -5740,21 +7151,9 @@ Using \`${fallbackCompatibilityDate}\` as fallback. More info at: ${colors.under
5740
7151
  await installModules(modules, resolvedModulePaths, nuxt);
5741
7152
  nuxt._ignore = ignore(nuxt.options.ignoreOptions);
5742
7153
  nuxt._ignore.add(resolveIgnorePatterns());
5743
- let unimport;
5744
- nuxt.hook("imports:context", (ctx) => {
5745
- unimport = ctx;
5746
- });
5747
7154
  await nuxt.callHook("modules:done");
5748
- const normalizedKeyedFunctions = await Promise.all(nuxt.options.optimization.keyedComposables.map(async ({ source, ...rest }) => ({
5749
- ...rest,
5750
- source: typeof source === "string" ? await resolvePath(source, { fallbackToOriginal: true }) ?? source : source
5751
- })));
5752
- addBuildPlugin(KeyedFunctionsPlugin({
5753
- sourcemap: !!nuxt.options.sourcemap.server || !!nuxt.options.sourcemap.client,
5754
- keyedFunctions: normalizedKeyedFunctions,
5755
- alias: nuxt.options.alias,
5756
- getAutoImports: unimport.getImports
5757
- }));
7155
+ nuxt._perf?.endPhase("modules");
7156
+ nuxt._perf?.collectModuleTimings(nuxt.options._installedModules);
5758
7157
  nuxt.options.css = nuxt.options.css.filter((value, index, array) => !array.includes(value, index + 1));
5759
7158
  if (nuxt.options.experimental.componentIslands) {
5760
7159
  addComponent({
@@ -5849,6 +7248,7 @@ export default defineNuxtPlugin({
5849
7248
  });
5850
7249
  addModuleTranspiles(nuxt);
5851
7250
  await bundleServer(nuxt);
7251
+ nuxt._perf?.startPhase("ready");
5852
7252
  if (nuxt.options.experimental.payloadExtraction) {
5853
7253
  addPlugin(resolve(nuxt.options.appDir, "plugins/payload.client"));
5854
7254
  }
@@ -5856,9 +7256,21 @@ export default defineNuxtPlugin({
5856
7256
  logger.info(`Running with compatibility version \`${nuxt.options.future.compatibilityVersion}\``);
5857
7257
  }
5858
7258
  await nuxt.callHook("ready", nuxt);
7259
+ nuxt._perf?.endPhase("ready");
5859
7260
  }
5860
7261
  async function loadNuxt(opts) {
7262
+ let perf;
7263
+ const cliStartTime = globalThis.__nuxt_cli__?.startTime;
7264
+ const perfOverride = typeof opts.overrides?.debug === "object" && opts.overrides.debug.perf;
7265
+ if (perfOverride) {
7266
+ perf = new NuxtPerfProfiler({ startTime: cliStartTime });
7267
+ perf.startPhase("config");
7268
+ }
5861
7269
  const options = await loadNuxtConfig(opts);
7270
+ if (!perf && typeof options.debug === "object" && options.debug.perf) {
7271
+ perf = new NuxtPerfProfiler({ startTime: cliStartTime });
7272
+ }
7273
+ perf?.endPhase("config");
5862
7274
  options.appDir = options.alias["#app"] = withTrailingSlash(resolve(distDir, "app"));
5863
7275
  options._majorVersion = 4;
5864
7276
  for (const key in options.app.head || {}) {
@@ -5886,6 +7298,7 @@ async function loadNuxt(opts) {
5886
7298
  }
5887
7299
  }
5888
7300
  options._modules.push(pagesModule, metaModule, componentsModule);
7301
+ options._modules.push(compilerModule);
5889
7302
  const importIncludes = [];
5890
7303
  for (const layer of options._layers) {
5891
7304
  if (layer.cwd && layer.cwd.includes("node_modules")) {
@@ -5934,6 +7347,29 @@ async function loadNuxt(opts) {
5934
7347
  }
5935
7348
  });
5936
7349
  const nuxt = createNuxt(options);
7350
+ if (perf) {
7351
+ nuxt._perf = perf;
7352
+ perf.installHookInterceptors(nuxt.hooks);
7353
+ const quiet = typeof options.debug === "object" && options.debug.perf === "quiet";
7354
+ const title = nuxt.options.dev ? "Nuxt Performance Report" : "Nuxt Build Performance";
7355
+ let flushed = false;
7356
+ const flush = () => {
7357
+ if (flushed || !perf) {
7358
+ return;
7359
+ }
7360
+ flushed = true;
7361
+ if (!quiet) {
7362
+ perf.printReport({ title });
7363
+ }
7364
+ perf.writeReport(nuxt.options.buildDir, { quiet });
7365
+ perf.dispose();
7366
+ };
7367
+ nuxt.hook("close", flush);
7368
+ process.once("exit", flush);
7369
+ nuxt.hook("close", () => {
7370
+ process.off("exit", flush);
7371
+ });
7372
+ }
5937
7373
  nuxt.runWithContext(() => {
5938
7374
  if (opts.overrides?.hooks) {
5939
7375
  nuxt.hooks.addHooks(opts.overrides.hooks);
@@ -5942,6 +7378,9 @@ async function loadNuxt(opts) {
5942
7378
  createDebugger(nuxt.hooks, { tag: "nuxt" });
5943
7379
  }
5944
7380
  });
7381
+ if (!nuxt.options._prepare && !nuxt.options.dev && nuxt.options.experimental.buildCache) {
7382
+ nuxt.hooks.hookOnce("modules:before", () => restoreCachedBuildId(nuxt));
7383
+ }
5945
7384
  if (opts.ready !== false) {
5946
7385
  await nuxt.ready();
5947
7386
  }
@@ -6510,7 +7949,7 @@ const nuxtConfigTemplate = {
6510
7949
  const shouldEnableComponentIslands = ctx.nuxt.options.experimental.componentIslands && (ctx.nuxt.options.dev || ctx.nuxt.options.experimental.componentIslands !== "auto" || ctx.app.pages?.some((p) => p.mode === "server") || ctx.app.components?.some((c) => c.mode === "server" && !ctx.app.components.some((other) => other.pascalName === c.pascalName && other.mode === "client")));
6511
7950
  const nitro = useNitro();
6512
7951
  const hasCachedRoutes = Object.values(nitro.options.routeRules).some((r) => r.isr || r.cache);
6513
- const payloadExtraction = !!ctx.nuxt.options.experimental.payloadExtraction && (nitro.options.static || hasCachedRoutes || nitro.options.prerender.routes.length > 0 || Object.values(nitro.options.routeRules).some((r) => r.prerender));
7952
+ const payloadExtraction = !!ctx.nuxt.options.experimental.payloadExtraction && (nitro.options.static || hasCachedRoutes || nitro.options.prerender.routes && nitro.options.prerender.routes.length > 0 || Object.values(nitro.options.routeRules).some((r) => r.prerender));
6514
7953
  return [
6515
7954
  ...Object.entries(ctx.nuxt.options.app).map(([k, v]) => `export const ${camelCase("app-" + k)} = ${JSON.stringify(v)}`),
6516
7955
  `export const renderJsonPayloads = ${!!ctx.nuxt.options.experimental.renderJsonPayloads}`,
@@ -6525,6 +7964,7 @@ const nuxtConfigTemplate = {
6525
7964
  `export const devLogs = ${JSON.stringify(ctx.nuxt.options.features.devLogs)}`,
6526
7965
  `export const nuxtLinkDefaults = ${JSON.stringify(ctx.nuxt.options.experimental.defaults.nuxtLink)}`,
6527
7966
  `export const asyncDataDefaults = ${JSON.stringify(ctx.nuxt.options.experimental.defaults.useAsyncData)}`,
7967
+ `export const useStateDefaults = ${JSON.stringify(ctx.nuxt.options.experimental.defaults.useState)}`,
6528
7968
  `export const fetchDefaults = ${JSON.stringify(fetchDefaults)}`,
6529
7969
  `export const vueAppRootContainer = ${ctx.nuxt.options.app.rootAttrs.id ? `'#${ctx.nuxt.options.app.rootAttrs.id}'` : `'body > ${ctx.nuxt.options.app.rootTag}'`}`,
6530
7970
  `export const viewTransition = ${ctx.nuxt.options.experimental.viewTransition}`,
@@ -6537,7 +7977,8 @@ const nuxtConfigTemplate = {
6537
7977
  `export const purgeCachedData = ${!!ctx.nuxt.options.experimental.purgeCachedData}`,
6538
7978
  `export const granularCachedData = ${!!ctx.nuxt.options.experimental.granularCachedData}`,
6539
7979
  `export const pendingWhenIdle = ${!!ctx.nuxt.options.experimental.pendingWhenIdle}`,
6540
- `export const alwaysRunFetchOnKeyChange = ${!!ctx.nuxt.options.experimental.alwaysRunFetchOnKeyChange}`
7980
+ `export const alwaysRunFetchOnKeyChange = ${!!ctx.nuxt.options.experimental.alwaysRunFetchOnKeyChange}`,
7981
+ `export const asyncCallHook = ${!!ctx.nuxt.options.experimental.asyncCallHook}`
6541
7982
  ].join("\n\n");
6542
7983
  }
6543
7984
  };
@@ -6647,9 +8088,9 @@ async function generateApp(nuxt, app, options = {}) {
6647
8088
  changedTemplates.push(template);
6648
8089
  }
6649
8090
  const perf = performance.now() - start;
6650
- const setupTime = Math.round(perf * 100) / 100;
6651
- if (nuxt.options.debug && nuxt.options.debug.templates || setupTime > 500) {
6652
- logger.info(`Compiled \`${template.filename}\` in ${setupTime}ms`);
8091
+ const compileTime = Math.round(perf * 100) / 100;
8092
+ if (nuxt.options.debug && nuxt.options.debug.templates || compileTime > 500) {
8093
+ logger.info(`Compiled \`${template.filename}\` in ${compileTime}ms`);
6653
8094
  }
6654
8095
  if (template.modified && template.write) {
6655
8096
  dirs.add(dirname(fullPath));
@@ -6773,7 +8214,7 @@ async function annotatePlugins(nuxt, plugins) {
6773
8214
  const _plugins = [];
6774
8215
  for (const plugin of plugins) {
6775
8216
  try {
6776
- const code = plugin.src in nuxt.vfs ? nuxt.vfs[plugin.src] : await promises.readFile(plugin.src, "utf-8");
8217
+ const code = nuxt.vfs[plugin.src] ?? await promises.readFile(plugin.src, "utf-8");
6777
8218
  _plugins.push({
6778
8219
  ...await extractMetadata(code, IS_TSX.test(plugin.src) ? "tsx" : "ts"),
6779
8220
  ...plugin
@@ -6831,29 +8272,29 @@ ${warningsAsList}`;
6831
8272
  logger.warn(warning);
6832
8273
  }
6833
8274
  }
6834
- async function checkViteConfig() {
6835
- return await checkAndWarnAboutConfigFileExistence({
8275
+ function checkViteConfig() {
8276
+ return checkAndWarnAboutConfigFileExistence({
6836
8277
  fileName: "vite.config",
6837
8278
  extensions: [".js", ".mjs", ".ts", ".cjs", ".mts", ".cts"],
6838
8279
  createWarningMessage: (foundFile) => `Using \`${foundFile}\` is not supported together with Nuxt. Use \`options.vite\` instead. You can read more in \`https://nuxt.com/docs/4.x/api/nuxt-config#vite\`.`
6839
8280
  });
6840
8281
  }
6841
- async function checkWebpackConfig() {
6842
- return await checkAndWarnAboutConfigFileExistence({
8282
+ function checkWebpackConfig() {
8283
+ return checkAndWarnAboutConfigFileExistence({
6843
8284
  fileName: "webpack.config",
6844
8285
  extensions: [".js", ".mjs", ".ts", ".cjs", ".mts", ".cts", "coffee"],
6845
8286
  createWarningMessage: (foundFile) => `Using \`${foundFile}\` is not supported together with Nuxt. Use \`options.webpack\` instead. You can read more in \`https://nuxt.com/docs/4.x/api/nuxt-config#webpack-1\`.`
6846
8287
  });
6847
8288
  }
6848
- async function checkNitroConfig() {
6849
- return await checkAndWarnAboutConfigFileExistence({
8289
+ function checkNitroConfig() {
8290
+ return checkAndWarnAboutConfigFileExistence({
6850
8291
  fileName: "nitro.config",
6851
8292
  extensions: [".ts", ".mts"],
6852
8293
  createWarningMessage: (foundFile) => `Using \`${foundFile}\` is not supported together with Nuxt. Use \`options.nitro\` instead. You can read more in \`https://nuxt.com/docs/4.x/api/nuxt-config#nitro\`.`
6853
8294
  });
6854
8295
  }
6855
- async function checkPostCSSConfig() {
6856
- return await checkAndWarnAboutConfigFileExistence({
8296
+ function checkPostCSSConfig() {
8297
+ return checkAndWarnAboutConfigFileExistence({
6857
8298
  fileName: "postcss.config",
6858
8299
  extensions: [".js", ".cjs"],
6859
8300
  createWarningMessage: (foundFile) => `Using \`${foundFile}\` is not supported together with Nuxt. Use \`options.postcss\` instead. You can read more in \`https://nuxt.com/docs/4.x/api/nuxt-config#postcss\`.`
@@ -6867,240 +8308,13 @@ async function checkAndWarnAboutConfigFileExistence(options) {
6867
8308
  }
6868
8309
  }
6869
8310
 
6870
- async function getVueHash(nuxt) {
6871
- const id = "vue";
6872
- const { hash: hash2 } = await getHashes(nuxt, {
6873
- id,
6874
- cwd: (layer) => layer.config.srcDir || layer.cwd,
6875
- patterns: (layer) => {
6876
- const srcDir = layer.config.srcDir || layer.cwd;
6877
- return [
6878
- "**",
6879
- `!${relative(srcDir, layer.config.serverDir || join(layer.cwd, "server"))}/**`,
6880
- `!${relative(srcDir, resolve$1(layer.cwd, layer.config.dir?.public || "public"))}/**`,
6881
- "!node_modules/**",
6882
- "!nuxt.config.*"
6883
- ];
6884
- },
6885
- configOverrides: {
6886
- buildId: void 0,
6887
- serverDir: void 0,
6888
- nitro: void 0,
6889
- devServer: void 0,
6890
- runtimeConfig: void 0,
6891
- logLevel: void 0,
6892
- devServerHandlers: void 0,
6893
- devtools: void 0
6894
- }
6895
- });
6896
- const cacheFile = join(getCacheDir(nuxt), id, hash2 + ".tar");
6897
- return {
6898
- hash: hash2,
6899
- async collectCache() {
6900
- const start = Date.now();
6901
- await writeCache(nuxt.options.buildDir, nuxt.options.buildDir, cacheFile);
6902
- const elapsed = Date.now() - start;
6903
- consola.success(`Cached Vue client and server builds in \`${elapsed}ms\`.`);
6904
- },
6905
- async restoreCache() {
6906
- const start = Date.now();
6907
- const res = await restoreCache(nuxt.options.buildDir, cacheFile);
6908
- const elapsed = Date.now() - start;
6909
- if (res) {
6910
- consola.success(`Restored Vue client and server builds from cache in \`${elapsed}ms\`.`);
6911
- }
6912
- return res;
6913
- }
6914
- };
6915
- }
6916
- async function cleanupCaches(nuxt) {
6917
- const start = Date.now();
6918
- const caches = await glob(["*/*.tar"], {
6919
- cwd: getCacheDir(nuxt),
6920
- absolute: true
6921
- });
6922
- if (caches.length >= 10) {
6923
- const cachesWithMeta = await Promise.all(caches.map(async (cache) => {
6924
- return [cache, await stat(cache).then((r) => r.mtime.getTime()).catch(() => 0)];
6925
- }));
6926
- cachesWithMeta.sort((a, b) => a[1] - b[1]);
6927
- for (const [cache] of cachesWithMeta.slice(0, cachesWithMeta.length - 10)) {
6928
- await unlink(cache);
6929
- }
6930
- const elapsed = Date.now() - start;
6931
- consola.success(`Cleaned up old build caches in \`${elapsed}ms\`.`);
6932
- }
6933
- }
6934
- async function getHashes(nuxt, options) {
6935
- if (nuxt[`_${options.id}BuildHash`]) {
6936
- return nuxt[`_${options.id}BuildHash`];
6937
- }
6938
- const start = Date.now();
6939
- const hashSources = [];
6940
- let layerCtr = 0;
6941
- for (const layer of nuxt.options._layers) {
6942
- if (layer.cwd.includes("node_modules")) {
6943
- continue;
6944
- }
6945
- const layerName = `layer#${layerCtr++}`;
6946
- hashSources.push({
6947
- name: `${layerName}:config`,
6948
- data: serialize({
6949
- ...layer.config,
6950
- ...options.configOverrides || {}
6951
- })
6952
- });
6953
- const normalizeFiles = (files) => files.map((f) => ({
6954
- name: f.name,
6955
- size: f.attrs?.size,
6956
- data: hash(f.data)
6957
- })).sort((a, b) => a.name.localeCompare(b.name));
6958
- const isIgnored = createIsIgnored(nuxt);
6959
- const sourceFiles = await readFilesRecursive(options.cwd(layer), {
6960
- shouldIgnore: isIgnored,
6961
- // TODO: Validate if works with absolute paths
6962
- cwd: nuxt.options.rootDir,
6963
- patterns: options.patterns(layer)
6964
- });
6965
- hashSources.push({
6966
- name: `${layerName}:src`,
6967
- data: normalizeFiles(sourceFiles)
6968
- });
6969
- const rootFiles = await readFilesRecursive(layer.config?.rootDir || layer.cwd, {
6970
- shouldIgnore: isIgnored,
6971
- // TODO: Validate if works with absolute paths
6972
- cwd: nuxt.options.rootDir,
6973
- patterns: [
6974
- ".nuxtrc",
6975
- ".npmrc",
6976
- "package.json",
6977
- "package-lock.json",
6978
- "yarn.lock",
6979
- "pnpm-lock.yaml",
6980
- "tsconfig.json",
6981
- "bun.lock",
6982
- "bun.lockb"
6983
- ]
6984
- });
6985
- hashSources.push({
6986
- name: `${layerName}:root`,
6987
- data: normalizeFiles(rootFiles)
6988
- });
6989
- }
6990
- hashSources.sort((a, b) => a.name.localeCompare(b.name));
6991
- const res = nuxt[`_${options.id}BuildHash`] = {
6992
- hash: hash(hashSources),
6993
- sources: hashSources
6994
- };
6995
- const elapsed = Date.now() - start;
6996
- consola.debug(`Computed \`${options.id}\` build hash in \`${elapsed}ms\`.`);
6997
- return res;
6998
- }
6999
- async function readFilesRecursive(dir, opts) {
7000
- if (Array.isArray(dir)) {
7001
- return (await Promise.all(dir.map((d) => readFilesRecursive(d, opts)))).flat();
7002
- }
7003
- const files = await glob(opts.patterns, { cwd: dir });
7004
- const fileEntries = await Promise.all(files.map(async (fileName) => {
7005
- if (!opts.shouldIgnore?.(fileName)) {
7006
- const file = await readFileWithMeta(dir, fileName);
7007
- if (!file) {
7008
- return;
7009
- }
7010
- return {
7011
- ...file,
7012
- name: relative(opts.cwd, join(dir, file.name))
7013
- };
7014
- }
7015
- }));
7016
- return fileEntries.filter(Boolean);
7017
- }
7018
- async function readFileWithMeta(dir, fileName, count = 0) {
7019
- let fd = void 0;
7020
- try {
7021
- fd = await open(resolve$1(dir, fileName));
7022
- const stats = await fd.stat();
7023
- if (!stats?.isFile()) {
7024
- return;
7025
- }
7026
- const mtime = stats.mtime.getTime();
7027
- const data = await fd.readFile();
7028
- if ((await fd.stat()).mtime.getTime() !== mtime) {
7029
- if (count < 5) {
7030
- return readFileWithMeta(dir, fileName, count + 1);
7031
- }
7032
- console.warn(`Failed to read file \`${fileName}\` as it changed during read.`);
7033
- return;
7034
- }
7035
- return {
7036
- name: fileName,
7037
- data,
7038
- attrs: {
7039
- mtime,
7040
- size: stats.size
7041
- }
7042
- };
7043
- } catch (err) {
7044
- console.warn(`Failed to read file \`${fileName}\`:`, err);
7045
- } finally {
7046
- await fd?.close();
7047
- }
7048
- }
7049
- async function restoreCache(cwd, cacheFile) {
7050
- if (!existsSync(cacheFile)) {
7051
- return false;
7052
- }
7053
- const files = parseTar(await readFile(cacheFile));
7054
- for (const file of files) {
7055
- let fd = void 0;
7056
- try {
7057
- const filePath = resolve$1(cwd, file.name);
7058
- await mkdir(dirname(filePath), { recursive: true });
7059
- fd = await open(filePath, "w");
7060
- const stats = await fd.stat().catch(() => null);
7061
- if (stats?.isFile() && stats.size) {
7062
- const lastModified = Number.parseInt(file.attrs?.mtime?.toString().padEnd(13, "0") || "0");
7063
- if (stats.mtime.getTime() >= lastModified) {
7064
- consola.debug(`Skipping \`${file.name}\` (up to date or newer than cache)`);
7065
- continue;
7066
- }
7067
- }
7068
- await fd.writeFile(file.data);
7069
- } catch (err) {
7070
- console.error(err);
7071
- } finally {
7072
- await fd?.close();
7073
- }
7074
- }
7075
- return true;
7076
- }
7077
- async function writeCache(cwd, sources, cacheFile) {
7078
- const fileEntries = await readFilesRecursive(sources, {
7079
- patterns: ["**/*", "!analyze/**"],
7080
- cwd
7081
- });
7082
- const tarData = createTar(fileEntries);
7083
- await mkdir(dirname(cacheFile), { recursive: true });
7084
- await writeFile(cacheFile, tarData);
7085
- }
7086
- function getCacheDir(nuxt) {
7087
- let cacheDir = join(nuxt.options.workspaceDir, "node_modules");
7088
- if (!existsSync(cacheDir)) {
7089
- for (const dir of [...nuxt.options.modulesDir].sort((a, b) => a.length - b.length)) {
7090
- if (existsSync(dir)) {
7091
- cacheDir = dir;
7092
- break;
7093
- }
7094
- }
7095
- }
7096
- return join(cacheDir, ".cache/nuxt/builds");
7097
- }
7098
-
7099
8311
  async function build(nuxt) {
8312
+ nuxt._perf?.startPhase("app:generate");
7100
8313
  const app = createApp(nuxt);
7101
8314
  nuxt.apps.default = app;
7102
8315
  const generateApp$1 = debounce(() => generateApp(nuxt, app), void 0, { leading: true });
7103
8316
  await generateApp$1();
8317
+ nuxt._perf?.endPhase("app:generate");
7104
8318
  if (nuxt.options.dev) {
7105
8319
  watch(nuxt);
7106
8320
  nuxt.hook("builder:watch", async (event, relativePath) => {
@@ -7131,7 +8345,8 @@ async function build(nuxt) {
7131
8345
  const { restoreCache, collectCache } = await getVueHash(nuxt);
7132
8346
  if (await restoreCache()) {
7133
8347
  await nuxt.callHook("build:done");
7134
- return await nuxt.callHook("close", nuxt);
8348
+ await nuxt.callHook("close", nuxt);
8349
+ return;
7135
8350
  }
7136
8351
  nuxt.hooks.hookOnce("nitro:build:before", () => collectCache());
7137
8352
  nuxt.hooks.hookOnce("close", () => cleanupCaches(nuxt));
@@ -7146,7 +8361,9 @@ async function build(nuxt) {
7146
8361
  checkForExternalConfigurationFiles().catch((e) => logger.warn("Problem checking for external configuration files.", e));
7147
8362
  });
7148
8363
  }
8364
+ nuxt._perf?.startPhase("build:bundle");
7149
8365
  await bundle(nuxt);
8366
+ nuxt._perf?.endPhase("build:bundle");
7150
8367
  await nuxt.callHook("build:done");
7151
8368
  if (!nuxt.options.dev) {
7152
8369
  await nuxt.callHook("close", nuxt);
@@ -7172,7 +8389,15 @@ async function watch(nuxt) {
7172
8389
  function createWatcher() {
7173
8390
  const nuxt = useNuxt();
7174
8391
  const isIgnored2 = createIsIgnored(nuxt);
7175
- const watcher = watch$1(getLayerDirectories(nuxt).map((dirs) => dirs.app), {
8392
+ const layerDirs = getLayerDirectories(nuxt);
8393
+ const paths = [];
8394
+ for (const layer of layerDirs) {
8395
+ paths.push(layer.app);
8396
+ if (!layer.server.startsWith(layer.app.replace(/\/?$/, "/"))) {
8397
+ paths.push(layer.server);
8398
+ }
8399
+ }
8400
+ const watcher = watch$1(paths, {
7176
8401
  ...nuxt.options.watchers.chokidar,
7177
8402
  ignoreInitial: true,
7178
8403
  ignored: [isIgnored2, /[\\/]node_modules[\\/]/]
@@ -7305,10 +8530,12 @@ async function loadBuilder(nuxt, builder) {
7305
8530
  function resolvePathsToWatch(nuxt, opts = {}) {
7306
8531
  const pathsToWatch = /* @__PURE__ */ new Set();
7307
8532
  for (const dirs of getLayerDirectories(nuxt)) {
7308
- if (!dirs.app || isIgnored(dirs.app)) {
7309
- continue;
8533
+ if (!isIgnored(dirs.app)) {
8534
+ pathsToWatch.add(dirs.app);
8535
+ }
8536
+ if (!isIgnored(dirs.server) && !dirs.server.startsWith(dirs.app.replace(/\/?$/, "/"))) {
8537
+ pathsToWatch.add(dirs.server);
7310
8538
  }
7311
- pathsToWatch.add(dirs.app);
7312
8539
  }
7313
8540
  for (const pattern of nuxt.options.watch) {
7314
8541
  if (typeof pattern !== "string") {