mirror of
https://github.com/nuxt/nuxt.git
synced 2024-11-25 15:15:19 +00:00
feat(schema): v4 opt-in with future.compatibilityVersion
(#26925)
This commit is contained in:
parent
ed79a1d4bf
commit
505e706dcb
2
.github/workflows/ci.yml
vendored
2
.github/workflows/ci.yml
vendored
@ -198,6 +198,7 @@ jobs:
|
|||||||
builder: ["vite", "webpack"]
|
builder: ["vite", "webpack"]
|
||||||
context: ["async", "default"]
|
context: ["async", "default"]
|
||||||
manifest: ["manifest-on", "manifest-off"]
|
manifest: ["manifest-on", "manifest-off"]
|
||||||
|
version: ["v4", "v3"]
|
||||||
node: [18]
|
node: [18]
|
||||||
exclude:
|
exclude:
|
||||||
- env: "dev"
|
- env: "dev"
|
||||||
@ -234,6 +235,7 @@ jobs:
|
|||||||
TEST_BUILDER: ${{ matrix.builder }}
|
TEST_BUILDER: ${{ matrix.builder }}
|
||||||
TEST_MANIFEST: ${{ matrix.manifest }}
|
TEST_MANIFEST: ${{ matrix.manifest }}
|
||||||
TEST_CONTEXT: ${{ matrix.context }}
|
TEST_CONTEXT: ${{ matrix.context }}
|
||||||
|
TEST_V4: ${{ matrix.version == 'v4' }}
|
||||||
SKIP_BUNDLE_SIZE: ${{ github.event_name != 'push' || matrix.env == 'dev' || matrix.builder == 'webpack' || matrix.context == 'default' || runner.os == 'Windows' }}
|
SKIP_BUNDLE_SIZE: ${{ github.event_name != 'push' || matrix.env == 'dev' || matrix.builder == 'webpack' || matrix.context == 'default' || runner.os == 'Windows' }}
|
||||||
|
|
||||||
- uses: codecov/codecov-action@84508663e988701840491b86de86b666e8a86bed # v4.3.0
|
- uses: codecov/codecov-action@84508663e988701840491b86de86b666e8a86bed # v4.3.0
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import { promises as fsp, mkdirSync, writeFileSync } from 'node:fs'
|
import { promises as fsp, mkdirSync, writeFileSync } from 'node:fs'
|
||||||
import { dirname, join, relative, resolve } from 'pathe'
|
import { dirname, join, relative, resolve } from 'pathe'
|
||||||
import { defu } from 'defu'
|
import { defu } from 'defu'
|
||||||
import { compileTemplate, findPath, logger, normalizePlugin, normalizeTemplate, resolveAlias, resolveFiles, resolvePath, templateUtils, tryResolveModule } from '@nuxt/kit'
|
import { compileTemplate as _compileTemplate, findPath, logger, normalizePlugin, normalizeTemplate, resolveAlias, resolveFiles, resolvePath, templateUtils, tryResolveModule } from '@nuxt/kit'
|
||||||
import type { Nuxt, NuxtApp, NuxtPlugin, NuxtTemplate, ResolvedNuxtTemplate } from 'nuxt/schema'
|
import type { Nuxt, NuxtApp, NuxtPlugin, NuxtTemplate, ResolvedNuxtTemplate } from 'nuxt/schema'
|
||||||
|
|
||||||
import * as defaultTemplates from './templates'
|
import * as defaultTemplates from './templates'
|
||||||
@ -39,6 +39,8 @@ export async function generateApp (nuxt: Nuxt, app: NuxtApp, options: { filter?:
|
|||||||
const filteredTemplates = (app.templates as Array<ResolvedNuxtTemplate<any>>)
|
const filteredTemplates = (app.templates as Array<ResolvedNuxtTemplate<any>>)
|
||||||
.filter(template => !options.filter || options.filter(template))
|
.filter(template => !options.filter || options.filter(template))
|
||||||
|
|
||||||
|
const compileTemplate = nuxt.options.experimental.compileTemplate ? _compileTemplate : futureCompileTemplate
|
||||||
|
|
||||||
const writes: Array<() => void> = []
|
const writes: Array<() => void> = []
|
||||||
await Promise.allSettled(filteredTemplates
|
await Promise.allSettled(filteredTemplates
|
||||||
.map(async (template) => {
|
.map(async (template) => {
|
||||||
@ -91,6 +93,24 @@ export async function generateApp (nuxt: Nuxt, app: NuxtApp, options: { filter?:
|
|||||||
}
|
}
|
||||||
|
|
||||||
/** @internal */
|
/** @internal */
|
||||||
|
async function futureCompileTemplate<T> (template: NuxtTemplate<T>, ctx: { nuxt: Nuxt, app: NuxtApp, utils?: unknown }) {
|
||||||
|
delete ctx.utils
|
||||||
|
|
||||||
|
if (template.src) {
|
||||||
|
try {
|
||||||
|
return await fsp.readFile(template.src, 'utf-8')
|
||||||
|
} catch (err) {
|
||||||
|
logger.error(`[nuxt] Error reading template from \`${template.src}\``)
|
||||||
|
throw err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (template.getContents) {
|
||||||
|
return template.getContents({ ...ctx, options: template.options! })
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new Error('[nuxt] Invalid template. Templates must have either `src` or `getContents`: ' + JSON.stringify(template))
|
||||||
|
}
|
||||||
|
|
||||||
export async function resolveApp (nuxt: Nuxt, app: NuxtApp) {
|
export async function resolveApp (nuxt: Nuxt, app: NuxtApp) {
|
||||||
// Resolve main (app.vue)
|
// Resolve main (app.vue)
|
||||||
if (!app.mainComponent) {
|
if (!app.mainComponent) {
|
||||||
|
@ -85,7 +85,7 @@ function createWatcher () {
|
|||||||
})
|
})
|
||||||
|
|
||||||
// TODO: consider moving to emit absolute path in 3.8 or 4.0
|
// TODO: consider moving to emit absolute path in 3.8 or 4.0
|
||||||
watcher.on('all', (event, path) => nuxt.callHook('builder:watch', event, normalize(relative(nuxt.options.srcDir, path))))
|
watcher.on('all', (event, path) => nuxt.callHook('builder:watch', event, nuxt.options.experimental.relativeWatchPaths ? normalize(relative(nuxt.options.srcDir, path)) : normalize(path)))
|
||||||
nuxt.hook('close', () => watcher?.close())
|
nuxt.hook('close', () => watcher?.close())
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -116,7 +116,7 @@ function createGranularWatcher () {
|
|||||||
path = normalize(path)
|
path = normalize(path)
|
||||||
if (!pending) {
|
if (!pending) {
|
||||||
// TODO: consider moving to emit absolute path in 3.8 or 4.0
|
// TODO: consider moving to emit absolute path in 3.8 or 4.0
|
||||||
nuxt.callHook('builder:watch', event, relative(nuxt.options.srcDir, path))
|
nuxt.callHook('builder:watch', event, nuxt.options.experimental.relativeWatchPaths ? relative(nuxt.options.srcDir, path) : path)
|
||||||
}
|
}
|
||||||
if (event === 'unlinkDir' && path in watchers) {
|
if (event === 'unlinkDir' && path in watchers) {
|
||||||
watchers[path]?.close()
|
watchers[path]?.close()
|
||||||
@ -125,7 +125,7 @@ function createGranularWatcher () {
|
|||||||
if (event === 'addDir' && path !== dir && !ignoredDirs.has(path) && !pathsToWatch.includes(path) && !(path in watchers) && !isIgnored(path)) {
|
if (event === 'addDir' && path !== dir && !ignoredDirs.has(path) && !pathsToWatch.includes(path) && !(path in watchers) && !isIgnored(path)) {
|
||||||
watchers[path] = chokidar.watch(path, { ...nuxt.options.watchers.chokidar, ignored: [isIgnored] })
|
watchers[path] = chokidar.watch(path, { ...nuxt.options.watchers.chokidar, ignored: [isIgnored] })
|
||||||
// TODO: consider moving to emit absolute path in 3.8 or 4.0
|
// TODO: consider moving to emit absolute path in 3.8 or 4.0
|
||||||
watchers[path].on('all', (event, p) => nuxt.callHook('builder:watch', event, normalize(relative(nuxt.options.srcDir, p))))
|
watchers[path].on('all', (event, p) => nuxt.callHook('builder:watch', event, nuxt.options.experimental.relativeWatchPaths ? normalize(relative(nuxt.options.srcDir, p)) : normalize(p)))
|
||||||
nuxt.hook('close', () => watchers[path]?.close())
|
nuxt.hook('close', () => watchers[path]?.close())
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
@ -159,7 +159,7 @@ async function createParcelWatcher () {
|
|||||||
for (const event of events) {
|
for (const event of events) {
|
||||||
if (isIgnored(event.path)) { continue }
|
if (isIgnored(event.path)) { continue }
|
||||||
// TODO: consider moving to emit absolute path in 3.8 or 4.0
|
// TODO: consider moving to emit absolute path in 3.8 or 4.0
|
||||||
nuxt.callHook('builder:watch', watchEvents[event.type], normalize(relative(nuxt.options.srcDir, event.path)))
|
nuxt.callHook('builder:watch', watchEvents[event.type], nuxt.options.experimental.relativeWatchPaths ? normalize(relative(nuxt.options.srcDir, event.path)) : normalize(event.path))
|
||||||
}
|
}
|
||||||
}, {
|
}, {
|
||||||
ignore: [
|
ignore: [
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import { pathToFileURL } from 'node:url'
|
import { pathToFileURL } from 'node:url'
|
||||||
import { existsSync, promises as fsp, readFileSync } from 'node:fs'
|
import { existsSync, promises as fsp, readFileSync } from 'node:fs'
|
||||||
import { cpus } from 'node:os'
|
import { cpus } from 'node:os'
|
||||||
import { join, normalize, relative, resolve } from 'pathe'
|
import { join, relative, resolve } from 'pathe'
|
||||||
import { createRouter as createRadixRouter, exportMatcher, toRouteMatcher } from 'radix3'
|
import { createRouter as createRadixRouter, exportMatcher, toRouteMatcher } from 'radix3'
|
||||||
import { randomUUID } from 'uncrypto'
|
import { randomUUID } from 'uncrypto'
|
||||||
import { joinURL, withTrailingSlash } from 'ufo'
|
import { joinURL, withTrailingSlash } from 'ufo'
|
||||||
@ -409,8 +409,9 @@ export async function initNitro (nuxt: Nuxt & { _nitro?: Nitro }) {
|
|||||||
|
|
||||||
// Trigger Nitro reload when SPA loading template changes
|
// Trigger Nitro reload when SPA loading template changes
|
||||||
const spaLoadingTemplateFilePath = await spaLoadingTemplatePath(nuxt)
|
const spaLoadingTemplateFilePath = await spaLoadingTemplatePath(nuxt)
|
||||||
nuxt.hook('builder:watch', async (_event, path) => {
|
nuxt.hook('builder:watch', async (_event, relativePath) => {
|
||||||
if (normalize(path) === spaLoadingTemplateFilePath) {
|
const path = resolve(nuxt.options.srcDir, relativePath)
|
||||||
|
if (path === spaLoadingTemplateFilePath) {
|
||||||
await nitro.hooks.callHook('rollup:reload')
|
await nitro.hooks.callHook('rollup:reload')
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
@ -324,7 +324,7 @@ export default defineNuxtModule({
|
|||||||
}
|
}
|
||||||
|
|
||||||
nuxt.hook('builder:watch', async (event, relativePath) => {
|
nuxt.hook('builder:watch', async (event, relativePath) => {
|
||||||
const path = join(nuxt.options.srcDir, relativePath)
|
const path = resolve(nuxt.options.srcDir, relativePath)
|
||||||
if (!(path in pageToGlobMap)) { return }
|
if (!(path in pageToGlobMap)) { return }
|
||||||
if (event === 'unlink') {
|
if (event === 'unlink') {
|
||||||
delete inlineRules[path]
|
delete inlineRules[path]
|
||||||
|
@ -6,6 +6,11 @@ export default defineUntypedSchema({
|
|||||||
* (possibly major) version of the framework.
|
* (possibly major) version of the framework.
|
||||||
*/
|
*/
|
||||||
future: {
|
future: {
|
||||||
|
/**
|
||||||
|
* Enable early access to Nuxt v4 features or flags.
|
||||||
|
* @type {3 | 4}
|
||||||
|
*/
|
||||||
|
compatibilityVersion: 3,
|
||||||
/**
|
/**
|
||||||
* This enables 'Bundler' module resolution mode for TypeScript, which is the recommended setting
|
* This enables 'Bundler' module resolution mode for TypeScript, which is the recommended setting
|
||||||
* for frameworks like Nuxt and Vite.
|
* for frameworks like Nuxt and Vite.
|
||||||
@ -329,7 +334,11 @@ export default defineUntypedSchema({
|
|||||||
* Options that apply to `useAsyncData` (and also therefore `useFetch`)
|
* Options that apply to `useAsyncData` (and also therefore `useFetch`)
|
||||||
*/
|
*/
|
||||||
useAsyncData: {
|
useAsyncData: {
|
||||||
deep: true,
|
deep: {
|
||||||
|
async $resolve (val, get) {
|
||||||
|
return val ?? !((await get('future') as Record<string, unknown>).compatibilityVersion === 4)
|
||||||
|
},
|
||||||
|
},
|
||||||
},
|
},
|
||||||
/** @type {Pick<typeof import('ofetch')['FetchOptions'], 'timeout' | 'retry' | 'retryDelay' | 'retryStatusCodes'>} */
|
/** @type {Pick<typeof import('ofetch')['FetchOptions'], 'timeout' | 'retry' | 'retryDelay' | 'retryStatusCodes'>} */
|
||||||
useFetch: {},
|
useFetch: {},
|
||||||
@ -349,5 +358,42 @@ export default defineUntypedSchema({
|
|||||||
* @type {boolean}
|
* @type {boolean}
|
||||||
*/
|
*/
|
||||||
clientNodeCompat: false,
|
clientNodeCompat: false,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Whether to use `lodash.template` to compile Nuxt templates.
|
||||||
|
*
|
||||||
|
* This flag will be removed with the release of v4 and exists only for
|
||||||
|
* advance testing within Nuxt v3.12+.
|
||||||
|
*/
|
||||||
|
compileTemplate: {
|
||||||
|
async $resolve (val, get) {
|
||||||
|
return val ?? ((await get('future') as Record<string, unknown>).compatibilityVersion !== 4)
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Whether to provide a legacy `templateUtils` object (with `serialize`,
|
||||||
|
* `importName` and `importSources`) when compiling Nuxt templates.
|
||||||
|
*
|
||||||
|
* This flag will be removed with the release of v4 and exists only for
|
||||||
|
* advance testing within Nuxt v3.12+.
|
||||||
|
*/
|
||||||
|
templateUtils: {
|
||||||
|
async $resolve (val, get) {
|
||||||
|
return val ?? ((await get('future') as Record<string, unknown>).compatibilityVersion !== 4)
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Whether to provide relative paths in the `builder:watch` hook.
|
||||||
|
*
|
||||||
|
* This flag will be removed with the release of v4 and exists only for
|
||||||
|
* advance testing within Nuxt v3.12+.
|
||||||
|
*/
|
||||||
|
relativeWatchPaths: {
|
||||||
|
async $resolve (val, get) {
|
||||||
|
return val ?? ((await get('future') as Record<string, unknown>).compatibilityVersion !== 4)
|
||||||
|
},
|
||||||
|
},
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
1
test/fixtures/basic-types/nuxt.config.ts
vendored
1
test/fixtures/basic-types/nuxt.config.ts
vendored
@ -7,6 +7,7 @@ export default defineNuxtConfig({
|
|||||||
},
|
},
|
||||||
future: {
|
future: {
|
||||||
typescriptBundlerResolution: process.env.MODULE_RESOLUTION === 'bundler',
|
typescriptBundlerResolution: process.env.MODULE_RESOLUTION === 'bundler',
|
||||||
|
compatibilityVersion: process.env.TEST_V4 === 'true' ? 4 : 3,
|
||||||
},
|
},
|
||||||
buildDir: process.env.NITRO_BUILD_DIR,
|
buildDir: process.env.NITRO_BUILD_DIR,
|
||||||
builder: process.env.TEST_BUILDER as 'webpack' | 'vite' ?? 'vite',
|
builder: process.env.TEST_BUILDER as 'webpack' | 'vite' ?? 'vite',
|
||||||
|
1
test/fixtures/basic/nuxt.config.ts
vendored
1
test/fixtures/basic/nuxt.config.ts
vendored
@ -11,6 +11,7 @@ declare module 'nitropack' {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export default defineNuxtConfig({
|
export default defineNuxtConfig({
|
||||||
|
future: { compatibilityVersion: process.env.TEST_V4 === 'true' ? 4 : 3 },
|
||||||
app: {
|
app: {
|
||||||
pageTransition: true,
|
pageTransition: true,
|
||||||
layoutTransition: true,
|
layoutTransition: true,
|
||||||
|
1
test/fixtures/minimal-types/nuxt.config.ts
vendored
1
test/fixtures/minimal-types/nuxt.config.ts
vendored
@ -1,3 +1,4 @@
|
|||||||
export default defineNuxtConfig({
|
export default defineNuxtConfig({
|
||||||
|
future: { compatibilityVersion: process.env.TEST_V4 === 'true' ? 4 : 3 },
|
||||||
experimental: { appManifest: true },
|
experimental: { appManifest: true },
|
||||||
})
|
})
|
||||||
|
1
test/fixtures/minimal/nuxt.config.ts
vendored
1
test/fixtures/minimal/nuxt.config.ts
vendored
@ -3,6 +3,7 @@ import { fileURLToPath } from 'node:url'
|
|||||||
const testWithInlineVue = process.env.EXTERNAL_VUE === 'false'
|
const testWithInlineVue = process.env.EXTERNAL_VUE === 'false'
|
||||||
|
|
||||||
export default defineNuxtConfig({
|
export default defineNuxtConfig({
|
||||||
|
future: { compatibilityVersion: process.env.TEST_V4 === 'true' ? 4 : 3 },
|
||||||
pages: false,
|
pages: false,
|
||||||
experimental: {
|
experimental: {
|
||||||
externalVue: !testWithInlineVue,
|
externalVue: !testWithInlineVue,
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
// https://nuxt.com/docs/api/nuxt-config
|
// https://nuxt.com/docs/api/nuxt-config
|
||||||
export default defineNuxtConfig({
|
export default defineNuxtConfig({
|
||||||
|
future: { compatibilityVersion: process.env.TEST_V4 === 'true' ? 4 : 3 },
|
||||||
experimental: {
|
experimental: {
|
||||||
externalVue: false,
|
externalVue: false,
|
||||||
},
|
},
|
||||||
|
1
test/fixtures/suspense/nuxt.config.ts
vendored
1
test/fixtures/suspense/nuxt.config.ts
vendored
@ -3,6 +3,7 @@ import { fileURLToPath } from 'node:url'
|
|||||||
const testWithInlineVue = process.env.EXTERNAL_VUE === 'false'
|
const testWithInlineVue = process.env.EXTERNAL_VUE === 'false'
|
||||||
|
|
||||||
export default defineNuxtConfig({
|
export default defineNuxtConfig({
|
||||||
|
future: { compatibilityVersion: process.env.TEST_V4 === 'true' ? 4 : 3 },
|
||||||
experimental: {
|
experimental: {
|
||||||
externalVue: !testWithInlineVue,
|
externalVue: !testWithInlineVue,
|
||||||
},
|
},
|
||||||
|
Loading…
Reference in New Issue
Block a user