Merge branch 'main' into main

This commit is contained in:
Peter Buglavecz 2024-11-20 21:45:38 +01:00 committed by GitHub
commit 8612666471
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
26 changed files with 1200 additions and 1194 deletions

View File

@ -248,7 +248,7 @@ jobs:
TEST_PAYLOAD: ${{ matrix.payload }} TEST_PAYLOAD: ${{ matrix.payload }}
SKIP_BUNDLE_SIZE: ${{ github.event_name != 'push' || matrix.env == 'dev' || matrix.builder == 'webpack' || matrix.context == 'default' || matrix.payload == 'js' || runner.os == 'Windows' }} SKIP_BUNDLE_SIZE: ${{ github.event_name != 'push' || matrix.env == 'dev' || matrix.builder == 'webpack' || matrix.context == 'default' || matrix.payload == 'js' || runner.os == 'Windows' }}
- uses: codecov/codecov-action@5c47607acb93fed5485fdbf7232e8a31425f672a # v5.0.2 - uses: codecov/codecov-action@985343d70564a82044c1b7fcb84c2fa05405c1a2 # v5.0.4
if: github.event_name != 'push' && matrix.env == 'built' && matrix.builder == 'vite' && matrix.context == 'default' && matrix.os == 'ubuntu-latest' && matrix.manifest == 'manifest-on' if: github.event_name != 'push' && matrix.env == 'built' && matrix.builder == 'vite' && matrix.context == 'default' && matrix.os == 'ubuntu-latest' && matrix.manifest == 'manifest-on'
with: with:
token: ${{ secrets.CODECOV_TOKEN }} token: ${{ secrets.CODECOV_TOKEN }}

View File

@ -29,7 +29,7 @@ jobs:
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
- name: Lychee link checker - name: Lychee link checker
uses: lycheeverse/lychee-action@5cd5ba7877bce8b3973756ae3c9474ce1e50be2f # for v1.8.0 uses: lycheeverse/lychee-action@4aa18b6ccdac05029fab067313a6a04f941e6494 # for v1.8.0
with: with:
# arguments with file types to check # arguments with file types to check
args: >- args: >-

View File

@ -1,84 +0,0 @@
name: release-pr
on:
issue_comment:
types: [created]
env:
# 7 GiB by default on GitHub, setting to 6 GiB
NODE_OPTIONS: --max-old-space-size=6144
permissions:
contents: read
jobs:
release-pr:
if: github.repository == 'nuxt/nuxt' && github.event.issue.pull_request && github.event.comment.body == '/trigger release'
concurrency:
group: release
permissions:
id-token: write
pull-requests: write
runs-on: ubuntu-latest
timeout-minutes: 20
steps:
- name: Ensure action is by maintainer
uses: octokit/request-action@dad4362715b7fb2ddedf9772c8670824af564f0d # v2.4.0
id: check_role
with:
route: GET /repos/nuxt/nuxt/collaborators/${{ github.event.comment.user.login }}
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Get PR Info
id: pr
env:
PR_NUMBER: ${{ github.event.issue.number }}
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
GH_REPO: ${{ github.repository }}
COMMENT_AT: ${{ github.event.comment.created_at }}
run: |
pr="$(gh api -H "Accept: application/vnd.github+json" -H "X-GitHub-Api-Version: 2022-11-28" /repos/"${GH_REPO}"/pulls/"${PR_NUMBER}")"
head_sha="$(echo "$pr" | jq -r .head.sha)"
updated_at="$(echo "$pr" | jq -r .updated_at)"
if [[ $(date -d "$updated_at" +%s) -gt $(date -d "$COMMENT_AT" +%s) ]]; then
exit 1
fi
echo "head_sha=$head_sha" >> "$GITHUB_OUTPUT"
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
with:
ref: ${{ steps.pr.outputs.head_sha }}
fetch-depth: 1
- run: corepack enable
- uses: actions/setup-node@39370e3970a6d050c480ffad4ff0ed4d3fdee5af # v4.1.0
with:
node-version: 20
cache: "pnpm"
- name: Install dependencies
run: pnpm install
- name: Build
run: pnpm build
- name: Release Edge
run: ./scripts/release-edge.sh pr-${{ github.event.issue.number }}
env:
NODE_AUTH_TOKEN: ${{ secrets.NODE_AUTH_TOKEN }}
NPM_CONFIG_PROVENANCE: true
- name: Post comment
uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea # v7.0.1
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
script: |
github.rest.issues.createComment({
issue_number: context.issue.number,
owner: context.repo.owner,
repo: context.repo.repo,
body: `:rocket: Release triggered! You can now install [nuxt@npm:nuxt-nightly@pr-${{ github.event.issue.number }}](https://www.npmjs.com/package/nuxt-nightly/v/pr-${{ github.event.issue.number }})`
})

View File

@ -21,8 +21,8 @@ Or follow the steps below to set up a new Nuxt project on your computer.
<!-- markdownlint-disable-next-line MD001 --> <!-- markdownlint-disable-next-line MD001 -->
#### Prerequisites #### Prerequisites
- **Node.js** - [`v18.0.0`](https://nodejs.org/en) or newer - **Node.js** - [`18.x`](https://nodejs.org/en) or newer (but we recommend the [active LTS release](https://github.com/nodejs/release#release-schedule))
- **Text editor** - We recommend [Visual Studio Code](https://code.visualstudio.com/) with the [official Vue extension](https://marketplace.visualstudio.com/items?itemName=Vue.volar) (previously known as Volar) - **Text editor** - There is no IDE requirement, but we recommend [Visual Studio Code](https://code.visualstudio.com/) with the [official Vue extension](https://marketplace.visualstudio.com/items?itemName=Vue.volar) (previously known as Volar) or [WebStorm](https://www.jetbrains.com/webstorm/), which, along with [other JetBrains IDEs](https://www.jetbrains.com/ides/), offers great Nuxt support right out-of-the-box.
- **Terminal** - In order to run Nuxt commands - **Terminal** - In order to run Nuxt commands
::note ::note

View File

@ -189,7 +189,6 @@ export default createConfigForNuxt({
}, },
}, },
// Sort rule keys in eslint config // Sort rule keys in eslint config
// @ts-expect-error incorrect types 🤔
{ {
files: ['**/eslint.config.mjs'], files: ['**/eslint.config.mjs'],
name: 'local/sort-eslint-config', name: 'local/sort-eslint-config',

View File

@ -40,42 +40,42 @@
"@nuxt/ui-templates": "workspace:*", "@nuxt/ui-templates": "workspace:*",
"@nuxt/vite-builder": "workspace:*", "@nuxt/vite-builder": "workspace:*",
"@nuxt/webpack-builder": "workspace:*", "@nuxt/webpack-builder": "workspace:*",
"@types/node": "22.9.0", "@types/node": "22.9.1",
"@unhead/dom": "1.11.11", "@unhead/dom": "1.11.11",
"@unhead/shared": "1.11.11", "@unhead/shared": "1.11.11",
"@unhead/vue": "1.11.11", "@unhead/vue": "1.11.11",
"@unhead/schema": "1.11.11", "@unhead/schema": "1.11.11",
"@unhead/ssr": "1.11.11", "@unhead/ssr": "1.11.11",
"@vue/compiler-core": "3.5.12", "@vue/compiler-core": "3.5.13",
"@vue/compiler-dom": "3.5.12", "@vue/compiler-dom": "3.5.13",
"@vue/shared": "3.5.12", "@vue/shared": "3.5.13",
"c12": "2.0.1", "c12": "2.0.1",
"h3": "npm:h3-nightly@2.0.0-1718872656.6765a6e", "h3": "npm:h3-nightly@2.0.0-1718872656.6765a6e",
"jiti": "2.4.0", "jiti": "2.4.0",
"magic-string": "^0.30.12", "magic-string": "^0.30.13",
"nitro": "npm:nitro-nightly@3.0.0-beta-28796231.359af68d", "nitro": "npm:nitro-nightly@3.0.0-beta-28796231.359af68d",
"nuxt": "workspace:*", "nuxt": "workspace:*",
"ohash": "1.1.4", "ohash": "1.1.4",
"postcss": "8.4.49", "postcss": "8.4.49",
"rollup": "4.26.0", "rollup": "4.27.3",
"send": ">=1.1.0", "send": ">=1.1.0",
"typescript": "5.6.3", "typescript": "5.6.3",
"ufo": "1.5.4", "ufo": "1.5.4",
"unbuild": "3.0.0-rc.11", "unbuild": "3.0.0-rc.11",
"unhead": "1.11.11", "unhead": "1.11.11",
"vite": "5.4.11", "vite": "5.4.11",
"vue": "3.5.12" "vue": "3.5.13"
}, },
"devDependencies": { "devDependencies": {
"@eslint/js": "9.14.0", "@eslint/js": "9.15.0",
"@nuxt/eslint-config": "0.6.1", "@nuxt/eslint-config": "0.7.1",
"@nuxt/kit": "workspace:*", "@nuxt/kit": "workspace:*",
"@nuxt/rspack-builder": "workspace:*", "@nuxt/rspack-builder": "workspace:*",
"@nuxt/test-utils": "3.14.4", "@nuxt/test-utils": "3.14.4",
"@nuxt/webpack-builder": "workspace:*", "@nuxt/webpack-builder": "workspace:*",
"@testing-library/vue": "8.1.0", "@testing-library/vue": "8.1.0",
"@types/eslint__js": "8.42.3", "@types/eslint__js": "8.42.3",
"@types/node": "22.9.0", "@types/node": "22.9.1",
"@types/semver": "7.5.8", "@types/semver": "7.5.8",
"@unhead/schema": "1.11.11", "@unhead/schema": "1.11.11",
"@unhead/vue": "1.11.11", "@unhead/vue": "1.11.11",
@ -89,9 +89,9 @@
"cssnano": "7.0.6", "cssnano": "7.0.6",
"destr": "2.0.3", "destr": "2.0.3",
"devalue": "5.1.1", "devalue": "5.1.1",
"eslint": "9.14.0", "eslint": "9.15.0",
"eslint-plugin-no-only-tests": "3.3.0", "eslint-plugin-no-only-tests": "3.3.0",
"eslint-plugin-perfectionist": "3.9.1", "eslint-plugin-perfectionist": "4.0.3",
"eslint-typegen": "0.3.2", "eslint-typegen": "0.3.2",
"h3": "npm:h3-nightly@2.0.0-1718872656.6765a6e", "h3": "npm:h3-nightly@2.0.0-1718872656.6765a6e",
"happy-dom": "15.11.6", "happy-dom": "15.11.6",
@ -103,10 +103,10 @@
"nuxt-content-twoslash": "0.1.1", "nuxt-content-twoslash": "0.1.1",
"ofetch": "1.4.1", "ofetch": "1.4.1",
"pathe": "1.1.2", "pathe": "1.1.2",
"playwright-core": "1.48.2", "playwright-core": "1.49.0",
"rimraf": "6.0.1", "rimraf": "6.0.1",
"semver": "7.6.3", "semver": "7.6.3",
"sherif": "1.0.1", "sherif": "1.0.2",
"std-env": "3.8.0", "std-env": "3.8.0",
"tinyexec": "0.3.1", "tinyexec": "0.3.1",
"tinyglobby": "0.2.10", "tinyglobby": "0.2.10",
@ -114,11 +114,11 @@
"ufo": "1.5.4", "ufo": "1.5.4",
"vitest": "2.1.5", "vitest": "2.1.5",
"vitest-environment-nuxt": "1.0.1", "vitest-environment-nuxt": "1.0.1",
"vue": "3.5.12", "vue": "3.5.13",
"vue-router": "4.4.5", "vue-router": "4.4.5",
"vue-tsc": "2.1.10" "vue-tsc": "2.1.10"
}, },
"packageManager": "pnpm@9.13.2", "packageManager": "pnpm@9.14.1",
"engines": { "engines": {
"node": "^16.10.0 || >=18.0.0" "node": "^16.10.0 || >=18.0.0"
}, },

View File

@ -48,7 +48,7 @@
"untyped": "^1.5.1" "untyped": "^1.5.1"
}, },
"devDependencies": { "devDependencies": {
"@rspack/core": "1.1.1", "@rspack/core": "1.1.2",
"@types/hash-sum": "1.0.2", "@types/hash-sum": "1.0.2",
"@types/semver": "7.5.8", "@types/semver": "7.5.8",
"nitro": "npm:nitro-nightly@3.0.0-beta-28796231.359af68d", "nitro": "npm:nitro-nightly@3.0.0-beta-28796231.359af68d",

View File

@ -5,6 +5,7 @@ import { dirname, isAbsolute, join, resolve } from 'pathe'
import { defu } from 'defu' import { defu } from 'defu'
import { createJiti } from 'jiti' import { createJiti } from 'jiti'
import { resolve as resolveModule } from 'mlly' import { resolve as resolveModule } from 'mlly'
import { isRelative } from 'ufo'
import { useNuxt } from '../context' import { useNuxt } from '../context'
import { resolveAlias, resolvePath } from '../resolve' import { resolveAlias, resolvePath } from '../resolve'
import { logger } from '../logger' import { logger } from '../logger'
@ -78,14 +79,23 @@ export async function loadNuxtModuleInstance (nuxtModule: string | NuxtModule, n
// Import if input is string // Import if input is string
if (typeof nuxtModule === 'string') { if (typeof nuxtModule === 'string') {
const paths = [join(nuxtModule, 'nuxt'), join(nuxtModule, 'module'), nuxtModule, join(nuxt.options.rootDir, nuxtModule)] const paths = new Set<string>()
nuxtModule = resolveAlias(nuxtModule, nuxt.options.alias)
if (isRelative(nuxtModule)) {
nuxtModule = resolve(nuxt.options.rootDir, nuxtModule)
}
paths.add(join(nuxtModule, 'nuxt'))
paths.add(join(nuxtModule, 'module'))
paths.add(nuxtModule)
for (const path of paths) { for (const path of paths) {
for (const parentURL of nuxt.options.modulesDir) { for (const parentURL of nuxt.options.modulesDir) {
try { try {
const resolved = resolveAlias(path, nuxt.options.alias) const src = isAbsolute(path)
const src = isAbsolute(resolved) ? pathToFileURL(await resolvePath(path, { cwd: parentURL, fallbackToOriginal: false, extensions: nuxt.options.extensions })).href
? pathToFileURL(await resolvePath(resolved, { cwd: parentURL, fallbackToOriginal: false, extensions: nuxt.options.extensions })).href : await resolveModule(path, { url: pathToFileURL(parentURL.replace(/\/node_modules\/?$/, '')), extensions: nuxt.options.extensions })
: await resolveModule(resolved, { url: pathToFileURL(parentURL.replace(/\/node_modules\/?$/, '')), extensions: nuxt.options.extensions })
nuxtModule = await jiti.import(src, { default: true }) as NuxtModule nuxtModule = await jiti.import(src, { default: true }) as NuxtModule

View File

@ -69,7 +69,7 @@
"@unhead/shared": "^1.11.11", "@unhead/shared": "^1.11.11",
"@unhead/ssr": "^1.11.11", "@unhead/ssr": "^1.11.11",
"@unhead/vue": "^1.11.11", "@unhead/vue": "^1.11.11",
"@vue/shared": "^3.5.12", "@vue/shared": "^3.5.13",
"acorn": "8.14.0", "acorn": "8.14.0",
"c12": "^2.0.1", "c12": "^2.0.1",
"chokidar": "^4.0.1", "chokidar": "^4.0.1",
@ -91,7 +91,7 @@
"jiti": "^2.4.0", "jiti": "^2.4.0",
"klona": "^2.0.6", "klona": "^2.0.6",
"knitwork": "^1.1.0", "knitwork": "^1.1.0",
"magic-string": "^0.30.12", "magic-string": "^0.30.13",
"mlly": "^1.7.3", "mlly": "^1.7.3",
"nanotar": "^0.1.1", "nanotar": "^0.1.1",
"nitro": "npm:nitro-nightly@3.0.0-beta-28796231.359af68d", "nitro": "npm:nitro-nightly@3.0.0-beta-28796231.359af68d",
@ -119,7 +119,7 @@
"unplugin-vue-router": "^0.10.8", "unplugin-vue-router": "^0.10.8",
"unstorage": "^1.13.1", "unstorage": "^1.13.1",
"untyped": "^1.5.1", "untyped": "^1.5.1",
"vue": "^3.5.12", "vue": "^3.5.13",
"vue-bundle-renderer": "^2.1.1", "vue-bundle-renderer": "^2.1.1",
"vue-devtools-stub": "^0.1.0", "vue-devtools-stub": "^0.1.0",
"vue-router": "^4.4.5" "vue-router": "^4.4.5"
@ -130,7 +130,7 @@
"@parcel/watcher": "2.5.0", "@parcel/watcher": "2.5.0",
"@types/estree": "1.0.6", "@types/estree": "1.0.6",
"@vitejs/plugin-vue": "5.2.0", "@vitejs/plugin-vue": "5.2.0",
"@vue/compiler-sfc": "3.5.12", "@vue/compiler-sfc": "3.5.13",
"unbuild": "3.0.0-rc.11", "unbuild": "3.0.0-rc.11",
"vite": "5.4.11", "vite": "5.4.11",
"vitest": "2.1.5" "vitest": "2.1.5"

View File

@ -16,7 +16,6 @@ import layouts from '#build/layouts'
// @ts-expect-error virtual file // @ts-expect-error virtual file
import { appLayoutTransition as defaultLayoutTransition } from '#build/nuxt.config.mjs' import { appLayoutTransition as defaultLayoutTransition } from '#build/nuxt.config.mjs'
// TODO: revert back to defineAsyncComponent when https://github.com/vuejs/core/issues/6638 is resolved
const LayoutLoader = defineComponent({ const LayoutLoader = defineComponent({
name: 'LayoutLoader', name: 'LayoutLoader',
inheritAttrs: false, inheritAttrs: false,
@ -24,13 +23,10 @@ const LayoutLoader = defineComponent({
name: String, name: String,
layoutProps: Object, layoutProps: Object,
}, },
async setup (props, context) { setup (props, context) {
// This is a deliberate hack - this component must always be called with an explicit key to ensure // This is a deliberate hack - this component must always be called with an explicit key to ensure
// that setup reruns when the name changes. // that setup reruns when the name changes.
return () => h(layouts[props.name], props.layoutProps, context.slots)
const LayoutComponent = await layouts[props.name]().then((r: any) => r.default || r)
return () => h(LayoutComponent, props.layoutProps, context.slots)
}, },
}) })

View File

@ -10,7 +10,6 @@ import { generateApp as _generateApp, createApp } from './app'
import { checkForExternalConfigurationFiles } from './external-config-files' import { checkForExternalConfigurationFiles } from './external-config-files'
import { cleanupCaches, getVueHash } from './cache' import { cleanupCaches, getVueHash } from './cache'
const IS_RESTART_PATH_RE = /^(?:app\.|error\.|plugins\/|middleware\/|layouts\/)/i
export async function build (nuxt: Nuxt) { export async function build (nuxt: Nuxt) {
const app = createApp(nuxt) const app = createApp(nuxt)
nuxt.apps.default = app nuxt.apps.default = app
@ -21,19 +20,24 @@ export async function build (nuxt: Nuxt) {
if (nuxt.options.dev) { if (nuxt.options.dev) {
watch(nuxt) watch(nuxt)
nuxt.hook('builder:watch', async (event, relativePath) => { nuxt.hook('builder:watch', async (event, relativePath) => {
if (event === 'change') { return } // Unset mainComponent and errorComponent if app or error component is changed
if (event === 'add' || event === 'unlink') {
const path = resolve(nuxt.options.srcDir, relativePath) const path = resolve(nuxt.options.srcDir, relativePath)
const relativePaths = nuxt.options._layers.map(l => relative(l.config.srcDir || l.cwd, path)) for (const layer of nuxt.options._layers) {
const restartPath = relativePaths.find(relativePath => IS_RESTART_PATH_RE.test(relativePath)) const relativePath = relative(layer.config.srcDir || layer.cwd, path)
if (restartPath) { if (relativePath.match(/^app\./i)) {
if (restartPath.startsWith('app')) {
app.mainComponent = undefined app.mainComponent = undefined
break
} }
if (restartPath.startsWith('error')) { if (relativePath.match(/^error\./i)) {
app.errorComponent = undefined app.errorComponent = undefined
break
} }
}
}
// Recompile app templates
await generateApp() await generateApp()
}
}) })
nuxt.hook('builder:generateApp', (options) => { nuxt.hook('builder:generateApp', (options) => {
// Bypass debounce if we are selectively invalidating templates // Bypass debounce if we are selectively invalidating templates

View File

@ -408,11 +408,15 @@ async function initNuxt (nuxt: Nuxt) {
} }
// Add <NuxtWelcome> // Add <NuxtWelcome>
// TODO: revert when deep server component config is properly bundle-split: https://github.com/nuxt/nuxt/pull/29956
const islandsConfig = nuxt.options.experimental.componentIslands
if (nuxt.options.dev || !(typeof islandsConfig === 'object' && islandsConfig.selectiveClient === 'deep')) {
addComponent({ addComponent({
name: 'NuxtWelcome', name: 'NuxtWelcome',
priority: 10, // built-in that we do not expect the user to override priority: 10, // built-in that we do not expect the user to override
filePath: resolve(nuxt.options.appDir, 'components/welcome'), filePath: resolve(nuxt.options.appDir, 'components/welcome'),
}) })
}
addComponent({ addComponent({
name: 'NuxtLayout', name: 'NuxtLayout',

View File

@ -36,7 +36,7 @@ export const createImportProtectionPatterns = (nuxt: { options: NuxtOptions }, o
]) ])
} }
for (const i of [/(^|node_modules\/)@nuxt\/(kit|test-utils)/, /(^|node_modules\/)nuxi/, /(^|node_modules\/)nitro(?:pack)?(?:-nightly)?(?:$|\/)(?!(?:dist\/)?(?:presets|runtime|types))/, /(^|node_modules\/)nuxt\/(config|kit|schema)/]) { for (const i of [/(^|node_modules\/)@nuxt\/(kit|test-utils)/, /(^|node_modules\/)nuxi/, /(^|node_modules\/)nitro(?:pack)?(?:-nightly)?(?:$|\/)(?!(?:dist\/)?(?:node_modules|presets|runtime|types))/, /(^|node_modules\/)nuxt\/(config|kit|schema)/]) {
patterns.push([i, `This module cannot be imported in ${context}.`]) patterns.push([i, `This module cannot be imported in ${context}.`])
} }

View File

@ -125,18 +125,26 @@ export const RemovePluginMetadataPlugin = (nuxt: Nuxt) => createUnplugin(() => {
const plugin = nuxt.apps.default?.plugins.find(p => p.src === id) const plugin = nuxt.apps.default?.plugins.find(p => p.src === id)
if (!plugin) { return } if (!plugin) { return }
const s = new MagicString(code) if (!code.trim()) {
logger.warn(`Plugin \`${plugin.src}\` has no content.`)
return {
code: 'export default () => {}',
map: null,
}
}
const exports = findExports(code) const exports = findExports(code)
const defaultExport = exports.find(e => e.type === 'default' || e.name === 'default') const defaultExport = exports.find(e => e.type === 'default' || e.name === 'default')
if (!defaultExport) { if (!defaultExport) {
logger.warn(`Plugin \`${plugin.src}\` has no default export and will be ignored at build time. Add \`export default defineNuxtPlugin(() => {})\` to your plugin.`) logger.warn(`Plugin \`${plugin.src}\` has no default export and will be ignored at build time. Add \`export default defineNuxtPlugin(() => {})\` to your plugin.`)
s.overwrite(0, code.length, 'export default () => {}')
return { return {
code: s.toString(), code: 'export default () => {}',
map: nuxt.options.sourcemap.client || nuxt.options.sourcemap.server ? s.generateMap({ hires: true }) : null, map: null,
} }
} }
const s = new MagicString(code)
let wrapped = false let wrapped = false
const wrapperNames = new Set(['defineNuxtPlugin', 'definePayloadPlugin']) const wrapperNames = new Set(['defineNuxtPlugin', 'definePayloadPlugin'])

View File

@ -285,9 +285,10 @@ export const layoutTemplate: NuxtTemplate = {
filename: 'layouts.mjs', filename: 'layouts.mjs',
getContents ({ app }) { getContents ({ app }) {
const layoutsObject = genObjectFromRawEntries(Object.values(app.layouts).map(({ name, file }) => { const layoutsObject = genObjectFromRawEntries(Object.values(app.layouts).map(({ name, file }) => {
return [name, genDynamicImport(file)] return [name, `defineAsyncComponent(${genDynamicImport(file)})`]
})) }))
return [ return [
`import { defineAsyncComponent } from 'vue'`,
`export default ${layoutsObject}`, `export default ${layoutsObject}`,
].join('\n') ].join('\n')
}, },

View File

@ -107,12 +107,9 @@ export default defineNuxtModule<Partial<ImportsOptions>>({
const priorities = nuxt.options._layers.map((layer, i) => [layer.config.srcDir, -i] as const).sort(([a], [b]) => b.length - a.length) const priorities = nuxt.options._layers.map((layer, i) => [layer.config.srcDir, -i] as const).sort(([a], [b]) => b.length - a.length)
const IMPORTS_TEMPLATE_RE = /\/imports\.(?:d\.ts|mjs)$/
function isImportsTemplate (template: ResolvedNuxtTemplate) { function isImportsTemplate (template: ResolvedNuxtTemplate) {
return [ return IMPORTS_TEMPLATE_RE.test(template.filename)
'/types/imports.d.ts',
'/imports.d.ts',
'/imports.mjs',
].some(i => template.filename.endsWith(i))
} }
const regenerateImports = async () => { const regenerateImports = async () => {

View File

@ -77,8 +77,8 @@ export async function resolvePagesRoutes (): Promise<NuxtPage[]> {
} else { } else {
const augmentedPages = await augmentPages(pages, nuxt.vfs) const augmentedPages = await augmentPages(pages, nuxt.vfs)
await nuxt.callHook('pages:extend', pages) await nuxt.callHook('pages:extend', pages)
await augmentPages(pages, nuxt.vfs, augmentedPages) await augmentPages(pages, nuxt.vfs, { pagesToSkip: augmentedPages })
augmentedPages.clear() augmentedPages?.clear()
} }
await nuxt.callHook('pages:resolved', pages) await nuxt.callHook('pages:resolved', pages)
@ -155,9 +155,14 @@ export function generateRoutesFromFiles (files: ScannedFile[], options: Generate
return prepareRoutes(routes) return prepareRoutes(routes)
} }
export async function augmentPages (routes: NuxtPage[], vfs: Record<string, string>, augmentedPages = new Set<string>()) { interface AugmentPagesContext {
pagesToSkip?: Set<string>
augmentedPages?: Set<string>
}
export async function augmentPages (routes: NuxtPage[], vfs: Record<string, string>, ctx: AugmentPagesContext = {}) {
ctx.augmentedPages ??= new Set()
for (const route of routes) { for (const route of routes) {
if (route.file && !augmentedPages.has(route.file)) { if (route.file && !ctx.pagesToSkip?.has(route.file)) {
const fileContent = route.file in vfs ? vfs[route.file]! : fs.readFileSync(await resolvePath(route.file), 'utf-8') const fileContent = route.file in vfs ? vfs[route.file]! : fs.readFileSync(await resolvePath(route.file), 'utf-8')
const routeMeta = await getRouteMeta(fileContent, route.file) const routeMeta = await getRouteMeta(fileContent, route.file)
if (route.meta) { if (route.meta) {
@ -165,14 +170,14 @@ export async function augmentPages (routes: NuxtPage[], vfs: Record<string, stri
} }
Object.assign(route, routeMeta) Object.assign(route, routeMeta)
augmentedPages.add(route.file) ctx.augmentedPages.add(route.file)
} }
if (route.children && route.children.length > 0) { if (route.children && route.children.length > 0) {
await augmentPages(route.children, vfs, augmentedPages) await augmentPages(route.children, vfs, ctx)
} }
} }
return augmentedPages return ctx.augmentedPages
} }
const SFC_SCRIPT_RE = /<script(?<attrs>[^>]*)>(?<content>[\s\S]*?)<\/script[^>]*>/gi const SFC_SCRIPT_RE = /<script(?<attrs>[^>]*)>(?<content>[\s\S]*?)<\/script[^>]*>/gi

View File

@ -24,6 +24,7 @@ const testsToTriggerOn = [
['some-nuxt-module', 'components/Component.vue', true], ['some-nuxt-module', 'components/Component.vue', true],
['/root/src/server/api/test.ts', 'components/Component.vue', true], ['/root/src/server/api/test.ts', 'components/Component.vue', true],
['src/server/api/test.ts', 'components/Component.vue', true], ['src/server/api/test.ts', 'components/Component.vue', true],
['node_modules/nitropack/node_modules/crossws/dist/adapters/bun.mjs', 'node_modules/nitropack/dist/presets/bun/runtime/bun.mjs', false],
] as const ] as const
describe('import protection', () => { describe('import protection', () => {

View File

@ -31,7 +31,7 @@
"dependencies": { "dependencies": {
"@nuxt/friendly-errors-webpack-plugin": "^2.6.0", "@nuxt/friendly-errors-webpack-plugin": "^2.6.0",
"@nuxt/kit": "workspace:*", "@nuxt/kit": "workspace:*",
"@rspack/core": "^1.1.1", "@rspack/core": "^1.1.2",
"autoprefixer": "^10.4.20", "autoprefixer": "^10.4.20",
"css-loader": "^7.1.2", "css-loader": "^7.1.2",
"css-minimizer-webpack-plugin": "^7.0.0", "css-minimizer-webpack-plugin": "^7.0.0",
@ -48,7 +48,7 @@
"jiti": "^2.4.0", "jiti": "^2.4.0",
"knitwork": "^1.1.0", "knitwork": "^1.1.0",
"lodash-es": "4.17.21", "lodash-es": "4.17.21",
"magic-string": "^0.30.12", "magic-string": "^0.30.13",
"memfs": "^4.14.0", "memfs": "^4.14.0",
"mlly": "^1.7.3", "mlly": "^1.7.3",
"ohash": "^1.1.4", "ohash": "^1.1.4",
@ -81,9 +81,9 @@
"@types/pify": "5.0.4", "@types/pify": "5.0.4",
"@types/webpack-bundle-analyzer": "4.7.0", "@types/webpack-bundle-analyzer": "4.7.0",
"@types/webpack-hot-middleware": "2.25.9", "@types/webpack-hot-middleware": "2.25.9",
"rollup": "4.26.0", "rollup": "4.27.3",
"unbuild": "3.0.0-rc.11", "unbuild": "3.0.0-rc.11",
"vue": "3.5.12" "vue": "3.5.13"
}, },
"peerDependencies": { "peerDependencies": {
"vue": "^3.3.4" "vue": "^3.3.4"

View File

@ -42,8 +42,8 @@
"@unhead/schema": "1.11.11", "@unhead/schema": "1.11.11",
"@vitejs/plugin-vue": "5.2.0", "@vitejs/plugin-vue": "5.2.0",
"@vitejs/plugin-vue-jsx": "4.1.0", "@vitejs/plugin-vue-jsx": "4.1.0",
"@vue/compiler-core": "3.5.12", "@vue/compiler-core": "3.5.13",
"@vue/compiler-sfc": "3.5.12", "@vue/compiler-sfc": "3.5.13",
"@vue/language-core": "2.1.10", "@vue/language-core": "2.1.10",
"esbuild-loader": "4.2.2", "esbuild-loader": "4.2.2",
"h3": "npm:h3-nightly@2.0.0-1718872656.6765a6e", "h3": "npm:h3-nightly@2.0.0-1718872656.6765a6e",
@ -54,7 +54,7 @@
"unctx": "2.3.1", "unctx": "2.3.1",
"unenv": "1.10.0", "unenv": "1.10.0",
"vite": "5.4.11", "vite": "5.4.11",
"vue": "3.5.12", "vue": "3.5.13",
"vue-bundle-renderer": "2.1.1", "vue-bundle-renderer": "2.1.1",
"vue-loader": "17.4.2", "vue-loader": "17.4.2",
"vue-router": "4.4.5", "vue-router": "4.4.5",

View File

@ -18,7 +18,7 @@
"test": "pnpm lint && pnpm build" "test": "pnpm lint && pnpm build"
}, },
"devDependencies": { "devDependencies": {
"@unocss/reset": "0.64.0", "@unocss/reset": "0.64.1",
"beasties": "0.1.0", "beasties": "0.1.0",
"html-validate": "8.25.0", "html-validate": "8.25.0",
"htmlnano": "2.1.1", "htmlnano": "2.1.1",
@ -29,7 +29,7 @@
"scule": "1.3.0", "scule": "1.3.0",
"tinyexec": "0.3.1", "tinyexec": "0.3.1",
"tinyglobby": "0.2.10", "tinyglobby": "0.2.10",
"unocss": "0.64.0", "unocss": "0.64.1",
"vite": "5.4.11" "vite": "5.4.11"
} }
} }

View File

@ -27,9 +27,9 @@
"@nuxt/schema": "workspace:*", "@nuxt/schema": "workspace:*",
"@types/clear": "0.1.4", "@types/clear": "0.1.4",
"@types/estree": "1.0.6", "@types/estree": "1.0.6",
"rollup": "4.26.0", "rollup": "4.27.3",
"unbuild": "3.0.0-rc.11", "unbuild": "3.0.0-rc.11",
"vue": "3.5.12" "vue": "3.5.13"
}, },
"dependencies": { "dependencies": {
"@nuxt/kit": "workspace:*", "@nuxt/kit": "workspace:*",
@ -49,7 +49,7 @@
"h3": "npm:h3-nightly@2.0.0-1718872656.6765a6e", "h3": "npm:h3-nightly@2.0.0-1718872656.6765a6e",
"jiti": "^2.4.0", "jiti": "^2.4.0",
"knitwork": "^1.1.0", "knitwork": "^1.1.0",
"magic-string": "^0.30.12", "magic-string": "^0.30.13",
"mlly": "^1.7.3", "mlly": "^1.7.3",
"ohash": "^1.1.4", "ohash": "^1.1.4",
"pathe": "^1.1.2", "pathe": "^1.1.2",

View File

@ -45,13 +45,13 @@ export function viteNodePlugin (ctx: ViteBuildContext): VitePlugin {
markInvalidate(mod) markInvalidate(mod)
} }
} }
for (const plugin of ctx.nuxt.options.plugins) {
markInvalidates(server.moduleGraph.getModulesByFile(typeof plugin === 'string' ? plugin : plugin.src)) if (ctx.nuxt.apps.default) {
} for (const template of ctx.nuxt.apps.default.templates) {
for (const template of ctx.nuxt.options.build.templates) {
markInvalidates(server.moduleGraph.getModulesByFile(template.dst!)) markInvalidates(server.moduleGraph.getModulesByFile(template.dst!))
} }
} }
}
server.middlewares.use('/__nuxt_vite_node__', toNodeListener(createViteNodeApp(ctx, invalidates))) server.middlewares.use('/__nuxt_vite_node__', toNodeListener(createViteNodeApp(ctx, invalidates)))

View File

@ -47,7 +47,7 @@
"jiti": "^2.4.0", "jiti": "^2.4.0",
"knitwork": "^1.1.0", "knitwork": "^1.1.0",
"lodash-es": "4.17.21", "lodash-es": "4.17.21",
"magic-string": "^0.30.12", "magic-string": "^0.30.13",
"memfs": "^4.14.0", "memfs": "^4.14.0",
"mini-css-extract-plugin": "^2.9.2", "mini-css-extract-plugin": "^2.9.2",
"mlly": "^1.7.3", "mlly": "^1.7.3",
@ -76,15 +76,15 @@
}, },
"devDependencies": { "devDependencies": {
"@nuxt/schema": "workspace:*", "@nuxt/schema": "workspace:*",
"@rspack/core": "1.1.1", "@rspack/core": "1.1.2",
"@types/hash-sum": "1.0.2", "@types/hash-sum": "1.0.2",
"@types/lodash-es": "4.17.12", "@types/lodash-es": "4.17.12",
"@types/pify": "5.0.4", "@types/pify": "5.0.4",
"@types/webpack-bundle-analyzer": "4.7.0", "@types/webpack-bundle-analyzer": "4.7.0",
"@types/webpack-hot-middleware": "2.25.9", "@types/webpack-hot-middleware": "2.25.9",
"rollup": "4.26.0", "rollup": "4.27.3",
"unbuild": "3.0.0-rc.11", "unbuild": "3.0.0-rc.11",
"vue": "3.5.12" "vue": "3.5.13"
}, },
"peerDependencies": { "peerDependencies": {
"vue": "^3.3.4" "vue": "^3.3.4"

File diff suppressed because it is too large Load Diff

View File

@ -40,7 +40,7 @@ describe.skipIf(process.env.SKIP_BUNDLE_SIZE === 'true' || process.env.ECOSYSTEM
expect.soft(roundToKilobytes(serverStats.totalBytes)).toMatchInlineSnapshot(`"208k"`) expect.soft(roundToKilobytes(serverStats.totalBytes)).toMatchInlineSnapshot(`"208k"`)
const modules = await analyzeSizes(['node_modules/**/*'], serverDir) const modules = await analyzeSizes(['node_modules/**/*'], serverDir)
expect.soft(roundToKilobytes(modules.totalBytes)).toMatchInlineSnapshot(`"1394k"`) expect.soft(roundToKilobytes(modules.totalBytes)).toMatchInlineSnapshot(`"1396k"`)
const packages = modules.files const packages = modules.files
.filter(m => m.endsWith('package.json')) .filter(m => m.endsWith('package.json'))
@ -78,7 +78,7 @@ describe.skipIf(process.env.SKIP_BUNDLE_SIZE === 'true' || process.env.ECOSYSTEM
const serverDir = join(rootDir, '.output-inline/server') const serverDir = join(rootDir, '.output-inline/server')
const serverStats = await analyzeSizes(['**/*.mjs', '!node_modules'], serverDir) const serverStats = await analyzeSizes(['**/*.mjs', '!node_modules'], serverDir)
expect.soft(roundToKilobytes(serverStats.totalBytes)).toMatchInlineSnapshot(`"557k"`) expect.soft(roundToKilobytes(serverStats.totalBytes)).toMatchInlineSnapshot(`"559k"`)
const modules = await analyzeSizes(['node_modules/**/*'], serverDir) const modules = await analyzeSizes(['node_modules/**/*'], serverDir)
expect.soft(roundToKilobytes(modules.totalBytes)).toMatchInlineSnapshot(`"94.4k"`) expect.soft(roundToKilobytes(modules.totalBytes)).toMatchInlineSnapshot(`"94.4k"`)