diff --git a/docs/2.guide/3.going-further/1.experimental-features.md b/docs/2.guide/3.going-further/1.experimental-features.md index 0bfebe9903..c244dab8ba 100644 --- a/docs/2.guide/3.going-further/1.experimental-features.md +++ b/docs/2.guide/3.going-further/1.experimental-features.md @@ -124,31 +124,6 @@ Read more in `defineRouteRules` utility. :read-more{to="/docs/guide/concepts/rendering#hybrid-rendering" icon="i-ph-medal-duotone"} -## inlineSSRStyles - -Inlines styles when rendering HTML. This is currently available only when using Vite. -You can also pass a function that receives the path of a Vue component and returns a boolean indicating whether to inline the styles for that component. - -```ts [nuxt.config.ts] -export defineNuxtConfig({ - experimental: { - inlineSSRStyles: true // or a function to determine inlining - } -}) -``` - -## noScripts - -Disables rendering of Nuxt scripts and JS resource hints. Can also be configured granularly within `routeRules`. - -```ts [nuxt.config.ts] -export defineNuxtConfig({ - experimental: { - noScripts: true - } -}) -``` - ## renderJsonPayloads Allows rendering of JSON payloads with support for revivifying complex types. diff --git a/docs/2.guide/3.going-further/1.features.md b/docs/2.guide/3.going-further/1.features.md new file mode 100644 index 0000000000..8c86256ec0 --- /dev/null +++ b/docs/2.guide/3.going-further/1.features.md @@ -0,0 +1,55 @@ +--- +title: "Features" +description: "Enable or disable optional Nuxt features to unlock new possibilities." +--- + +Some features of Nuxt are available on an opt-in basis, or can be disabled based on your needs. + +## `features` + +### inlineStyles + +Inlines styles when rendering HTML. This is currently available only when using Vite. + +You can also pass a function that receives the path of a Vue component and returns a boolean indicating whether to inline the styles for that component. + +```ts [nuxt.config.ts] +export defineNuxtConfig({ + features: { + inlineStyles: true // or a function to determine inlining + } +}) +``` + +### noScripts + +Disables rendering of Nuxt scripts and JS resource hints. Can also be configured granularly within `routeRules`. + +```ts [nuxt.config.ts] +export defineNuxtConfig({ + features: { + noScripts: true + } +}) +``` + +## `future` + +There is also a `future` namespace for behavior that will likely become default in a early opting-in to new features that will become default in a future (possibly major) version of the framework. + +### typescriptBundlerResolution + +This enables 'Bundler' module resolution mode for TypeScript, which is the recommended setting +for frameworks like Nuxt and [Vite](https://vitejs.dev/guide/performance.html#reduce-resolve-operations). + +It improves type support when using modern libraries with `exports`. + +See [the original TypeScript pull request](https://github.com/microsoft/TypeScript/pull/51669). + +```ts [nuxt.config.ts] +export defineNuxtConfig({ + future: { + typescriptBundlerResolution: true + } +}) +``` diff --git a/packages/kit/src/template.ts b/packages/kit/src/template.ts index 8ce2e0081e..0f3e78e490 100644 --- a/packages/kit/src/template.ts +++ b/packages/kit/src/template.ts @@ -128,7 +128,7 @@ export async function writeTypes (nuxt: Nuxt) { jsxImportSource: 'vue', target: 'ESNext', module: 'ESNext', - moduleResolution: nuxt.options.experimental?.typescriptBundlerResolution ? 'Bundler' : 'Node', + moduleResolution: nuxt.options.future?.typescriptBundlerResolution || (nuxt.options.experimental as any)?.typescriptBundlerResolution ? 'Bundler' : 'Node', skipLibCheck: true, isolatedModules: true, useDefineForClassFields: true, diff --git a/packages/nuxt/src/core/nitro.ts b/packages/nuxt/src/core/nitro.ts index 756da9e99f..8303c4fee7 100644 --- a/packages/nuxt/src/core/nitro.ts +++ b/packages/nuxt/src/core/nitro.ts @@ -51,7 +51,7 @@ export async function initNitro (nuxt: Nuxt & { _nitro?: Nitro }) { buildDir: nuxt.options.buildDir, experimental: { asyncContext: nuxt.options.experimental.asyncContext, - typescriptBundlerResolution: nuxt.options.experimental.typescriptBundlerResolution || nuxt.options.typescript?.tsConfig?.compilerOptions?.moduleResolution?.toLowerCase() === 'bundler' || _nitroConfig.typescript?.tsConfig?.compilerOptions?.moduleResolution?.toLowerCase() === 'bundler' + typescriptBundlerResolution: nuxt.options.future.typescriptBundlerResolution || nuxt.options.typescript?.tsConfig?.compilerOptions?.moduleResolution?.toLowerCase() === 'bundler' || _nitroConfig.typescript?.tsConfig?.compilerOptions?.moduleResolution?.toLowerCase() === 'bundler' }, framework: { name: 'nuxt', @@ -117,7 +117,7 @@ export async function initNitro (nuxt: Nuxt & { _nitro?: Nitro }) { // TODO: address upstream issue with defu types...? ...nuxt.options.runtimeConfig.nitro satisfies RuntimeConfig['nitro'] as any } - } , + }, appConfig: nuxt.options.appConfig, appConfigFiles: nuxt.options._layers.map( layer => resolve(layer.config.srcDir, 'app.config') @@ -207,8 +207,8 @@ export async function initNitro (nuxt: Nuxt & { _nitro?: Nitro }) { replace: { 'process.env.NUXT_NO_SSR': nuxt.options.ssr === false, 'process.env.NUXT_EARLY_HINTS': nuxt.options.experimental.writeEarlyHints !== false, - 'process.env.NUXT_NO_SCRIPTS': !!nuxt.options.experimental.noScripts && !nuxt.options.dev, - 'process.env.NUXT_INLINE_STYLES': !!nuxt.options.experimental.inlineSSRStyles, + 'process.env.NUXT_NO_SCRIPTS': !!nuxt.options.features.noScripts && !nuxt.options.dev, + 'process.env.NUXT_INLINE_STYLES': !!nuxt.options.features.inlineStyles, 'process.env.NUXT_JSON_PAYLOADS': !!nuxt.options.experimental.renderJsonPayloads, 'process.env.NUXT_COMPONENT_ISLANDS': !!nuxt.options.experimental.componentIslands, 'process.env.NUXT_ASYNC_CONTEXT': !!nuxt.options.experimental.asyncContext, diff --git a/packages/nuxt/src/core/nuxt.ts b/packages/nuxt/src/core/nuxt.ts index da541b99d8..2ce4f00db3 100644 --- a/packages/nuxt/src/core/nuxt.ts +++ b/packages/nuxt/src/core/nuxt.ts @@ -164,7 +164,7 @@ async function initNuxt (nuxt: Nuxt) { } // TODO: [Experimental] Avoid emitting assets when flag is enabled - if (nuxt.options.experimental.noScripts && !nuxt.options.dev) { + if (nuxt.options.features.noScripts && !nuxt.options.dev) { nuxt.hook('build:manifest', async (manifest) => { for (const file in manifest) { if (manifest[file].resourceType === 'script') { diff --git a/packages/schema/src/config/experimental.ts b/packages/schema/src/config/experimental.ts index d2dc8a15ae..c20e709e68 100644 --- a/packages/schema/src/config/experimental.ts +++ b/packages/schema/src/config/experimental.ts @@ -1,6 +1,63 @@ import { defineUntypedSchema } from 'untyped' export default defineUntypedSchema({ + future: { + /** + * This enables 'Bundler' module resolution mode for TypeScript, which is the recommended setting + * for frameworks like Nuxt and Vite. + * + * It improves type support when using modern libraries with `exports`. + * + * See https://github.com/microsoft/TypeScript/pull/51669 + */ + typescriptBundlerResolution: { + async $resolve (val, get) { + // TODO: remove in v3.10 + val = val ?? await get('experimental').then((e: Record) => e?.typescriptBundlerResolution) + if (typeof val === 'boolean') { return val } + const setting = await get('typescript.tsConfig.compilerOptions.moduleResolution') + if (setting) { + return setting.toLowerCase() === 'bundler' + } + return false + } + }, + }, + /** + * `future` is for early opting-in to new features that will become default in a future + * (possibly major) version of the framework. + */ + features: { + /** + * Inline styles when rendering HTML (currently vite only). + * + * You can also pass a function that receives the path of a Vue component + * and returns a boolean indicating whether to inline the styles for that component. + * @type {boolean | ((id?: string) => boolean)} + */ + inlineStyles: { + async $resolve (val, get) { + // TODO: remove in v3.10 + val = val ?? await get('experimental').then((e: Record) => e?.inlineSSRStyles) + if (val === false || (await get('dev')) || (await get('ssr')) === false || (await get('builder')) === '@nuxt/webpack-builder') { + return false + } + // Enabled by default for vite prod with ssr + return val ?? true + } + }, + + /** + * Turn off rendering of Nuxt scripts and JS resource hints. + * You can also disable scripts more granularly within `routeRules`. + */ + noScripts: { + async $resolve (val, get) { + // TODO: remove in v3.10 + return val ?? await get('experimental').then((e: Record) => e?.noScripts) ?? false + } + }, + }, experimental: { /** * Set to true to generate an async entry point for the Vue bundle (for module federation support). @@ -72,29 +129,6 @@ export default defineUntypedSchema({ */ restoreState: false, - /** - * Inline styles when rendering HTML (currently vite only). - * - * You can also pass a function that receives the path of a Vue component - * and returns a boolean indicating whether to inline the styles for that component. - * @type {boolean | ((id?: string) => boolean)} - */ - inlineSSRStyles: { - async $resolve (val, get) { - if (val === false || (await get('dev')) || (await get('ssr')) === false || (await get('builder')) === '@nuxt/webpack-builder') { - return false - } - // Enabled by default for vite prod with ssr - return val ?? true - } - }, - - /** - * Turn off rendering of Nuxt scripts and JS resource hints. - * You can also disable scripts more granularly within `routeRules`. - */ - noScripts: false, - /** Render JSON payloads with support for revivifying complex types. */ renderJsonPayloads: true, @@ -152,27 +186,6 @@ export default defineUntypedSchema({ */ configSchema: true, - /** - * This enables 'Bundler' module resolution mode for TypeScript, which is the recommended setting - * for frameworks like Nuxt and Vite. - * - * It improves type support when using modern libraries with `exports`. - * - * This is only not enabled by default because it could be a breaking change for some projects. - * - * See https://github.com/microsoft/TypeScript/pull/51669 - */ - typescriptBundlerResolution: { - async $resolve (val, get) { - if (typeof val === 'boolean') { return val } - const setting = await get('typescript.tsConfig.compilerOptions.moduleResolution') - if (setting) { - return setting.toLowerCase() === 'bundler' - } - return false - } - }, - /** * Whether or not to add a compatibility layer for modules, plugins or user code relying on the old * `@vueuse/head` API. diff --git a/packages/vite/src/vite.ts b/packages/vite/src/vite.ts index ce5e044fa1..a4ce67b190 100644 --- a/packages/vite/src/vite.ts +++ b/packages/vite/src/vite.ts @@ -166,7 +166,7 @@ export const bundle: NuxtBuilder['bundle'] = async (nuxt) => { srcDir: ctx.nuxt.options.srcDir, clientCSSMap, chunksWithInlinedCSS, - shouldInline: ctx.nuxt.options.experimental.inlineSSRStyles, + shouldInline: ctx.nuxt.options.features.inlineStyles, components: ctx.nuxt.apps.default.components, globalCSS: ctx.nuxt.options.css, mode: isServer ? 'server' : 'client', diff --git a/test/fixtures/basic-types/nuxt.config.ts b/test/fixtures/basic-types/nuxt.config.ts index ceaef7be62..a22ee55a97 100644 --- a/test/fixtures/basic-types/nuxt.config.ts +++ b/test/fixtures/basic-types/nuxt.config.ts @@ -4,6 +4,8 @@ export default defineNuxtConfig({ experimental: { typedPages: true, appManifest: true, + }, + future: { typescriptBundlerResolution: process.env.MODULE_RESOLUTION === 'bundler' }, buildDir: process.env.NITRO_BUILD_DIR, diff --git a/test/fixtures/basic/nuxt.config.ts b/test/fixtures/basic/nuxt.config.ts index 5d3a861ead..b9d986aa94 100644 --- a/test/fixtures/basic/nuxt.config.ts +++ b/test/fixtures/basic/nuxt.config.ts @@ -191,13 +191,15 @@ export default defineNuxtConfig({ } } }, + features: { + inlineStyles: id => !!id && !id.includes('assets.vue'), + }, experimental: { typedPages: true, polyfillVueUseHead: true, respectNoSSRHeader: true, clientFallback: true, restoreState: true, - inlineSSRStyles: id => !!id && !id.includes('assets.vue'), componentIslands: { selectiveClient: true },