mirror of
https://github.com/nuxt/nuxt.git
synced 2025-02-13 12:18:15 +00:00
Merge branch 'main' of github.com:nuxt/nuxt into feat/unhead-v2
This commit is contained in:
commit
ec6dfd2c49
14
.github/copilot-instructions.md
vendored
Normal file
14
.github/copilot-instructions.md
vendored
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
# Core Requirements
|
||||||
|
|
||||||
|
- The end goal is stability, speed and great user experience.
|
||||||
|
|
||||||
|
## Code Quality Requirements
|
||||||
|
|
||||||
|
- Follow standard TypeScript conventions and best practices
|
||||||
|
- Use the Composition API when creating Vue components
|
||||||
|
- Use clear, descriptive variable and function names
|
||||||
|
- Add comments to explain complex logic or non-obvious implementations
|
||||||
|
- Write unit tests for core functionality using `vitest`
|
||||||
|
- Write end-to-end tests using Playwright and `@nuxt/test-utils`
|
||||||
|
- Keep functions focused and manageable (generally under 50 lines)
|
||||||
|
- Use error handling patterns consistently
|
@ -90,22 +90,35 @@ Read more about `$fetch`.
|
|||||||
|
|
||||||
### Pass Client Headers to the API
|
### Pass Client Headers to the API
|
||||||
|
|
||||||
During server-side-rendering, since the `$fetch` request takes place 'internally' within the server, it won't include the user's browser cookies.
|
When calling `useFetch` on the server, Nuxt will use [`useRequestFetch`](/docs/api/composables/use-request-fetch) to proxy client headers and cookies (with the exception of headers not meant to be forwarded, like `host`).
|
||||||
|
|
||||||
We can use [`useRequestHeaders`](/docs/api/composables/use-request-headers) to access and proxy cookies to the API from server-side.
|
```vue
|
||||||
|
<script setup lang="ts">
|
||||||
|
const { data } = await useFetch('/api/echo');
|
||||||
|
</script>
|
||||||
|
```
|
||||||
|
|
||||||
The example below adds the request headers to an isomorphic `$fetch` call to ensure that the API endpoint has access to the same `cookie` header originally sent by the user.
|
```ts
|
||||||
|
// /api/echo.ts
|
||||||
|
export default defineEventHandler(event => parseCookies(event))
|
||||||
|
```
|
||||||
|
|
||||||
|
Alternatively, the example below shows how to use [`useRequestHeaders`](/docs/api/composables/use-request-headers) to access and send cookies to the API from a server-side request (originating on the client). Using an isomorphic `$fetch` call, we ensure that the API endpoint has access to the same `cookie` header originally sent by the user's browser. This is only necessary if you aren't using `useFetch`.
|
||||||
|
|
||||||
```vue
|
```vue
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
const headers = useRequestHeaders(['cookie'])
|
const headers = useRequestHeaders(['cookie'])
|
||||||
|
|
||||||
async function getCurrentUser() {
|
async function getCurrentUser() {
|
||||||
return await $fetch('/api/me', { headers: headers.value })
|
return await $fetch('/api/me', { headers })
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
```
|
```
|
||||||
|
|
||||||
|
::tip
|
||||||
|
You can also use [`useRequestFetch`](/docs/api/composables/use-request-fetch) to proxy headers to the call automatically.
|
||||||
|
::
|
||||||
|
|
||||||
::caution
|
::caution
|
||||||
Be very careful before proxying headers to an external API and just include headers that you need. Not all headers are safe to be bypassed and might introduce unwanted behavior. Here is a list of common headers that are NOT to be proxied:
|
Be very careful before proxying headers to an external API and just include headers that you need. Not all headers are safe to be bypassed and might introduce unwanted behavior. Here is a list of common headers that are NOT to be proxied:
|
||||||
|
|
||||||
@ -115,10 +128,6 @@ Be very careful before proxying headers to an external API and just include head
|
|||||||
- `cf-connecting-ip`, `cf-ray`
|
- `cf-connecting-ip`, `cf-ray`
|
||||||
::
|
::
|
||||||
|
|
||||||
::tip
|
|
||||||
You can also use [`useRequestFetch`](/docs/api/composables/use-request-fetch) to proxy headers to the call automatically.
|
|
||||||
::
|
|
||||||
|
|
||||||
## `useFetch`
|
## `useFetch`
|
||||||
|
|
||||||
The [`useFetch`](/docs/api/composables/use-fetch) composable uses `$fetch` under-the-hood to make SSR-safe network calls in the setup function.
|
The [`useFetch`](/docs/api/composables/use-fetch) composable uses `$fetch` under-the-hood to make SSR-safe network calls in the setup function.
|
||||||
|
@ -18,10 +18,16 @@ One of the core features of Nuxt is the layers and extending support. You can ex
|
|||||||
|
|
||||||
## Usage
|
## Usage
|
||||||
|
|
||||||
By default, any layers within your project in the `~/layers` directory will be automatically registered as layers in your project
|
By default, any layers within your project in the `~~/layers` directory will be automatically registered as layers in your project.
|
||||||
|
|
||||||
::note
|
::note
|
||||||
Layer auto-registration was introduced in Nuxt v3.12.0
|
Layer auto-registration was introduced in Nuxt v3.12.0.
|
||||||
|
::
|
||||||
|
|
||||||
|
In addition, named layer aliases to the `srcDir` of each of these layers will automatically be created. For example, you will be able to access the `~~/layers/test` layer via `#layers/test`.
|
||||||
|
|
||||||
|
::note
|
||||||
|
Named layer aliases were introduced in Nuxt v3.16.0.
|
||||||
::
|
::
|
||||||
|
|
||||||
In addition, you can extend from a layer by adding the [extends](/docs/api/nuxt-config#extends) property to your [`nuxt.config`](/docs/guide/directory-structure/nuxt-config) file.
|
In addition, you can extend from a layer by adding the [extends](/docs/api/nuxt-config#extends) property to your [`nuxt.config`](/docs/guide/directory-structure/nuxt-config) file.
|
||||||
|
@ -472,3 +472,41 @@ Alternatively, you can render the template alongside the Nuxt app root by settin
|
|||||||
```
|
```
|
||||||
|
|
||||||
This avoids a white flash when hydrating a client-only page.
|
This avoids a white flash when hydrating a client-only page.
|
||||||
|
|
||||||
|
## decorators
|
||||||
|
|
||||||
|
This option enables enabling decorator syntax across your entire Nuxt/Nitro app, powered by [esbuild](https://github.com/evanw/esbuild/releases/tag/v0.21.3).
|
||||||
|
|
||||||
|
For a long time, TypeScript has had support for decorators via `compilerOptions.experimentalDecorators`. This implementation predated the TC39 standardization process. Now, decorators are a [Stage 3 Proposal](https://github.com/tc39/proposal-decorators), and supported without special configuration in TS 5.0+ (see https://github.com/microsoft/TypeScript/pull/52582 and https://devblogs.microsoft.com/typescript/announcing-typescript-5-0-beta/#decorators).
|
||||||
|
|
||||||
|
Enabling `experimental.decorators` enables support for the TC39 proposal, **NOT** for TypeScript's previous `compilerOptions.experimentalDecorators` implementation.
|
||||||
|
|
||||||
|
::warning
|
||||||
|
Note that there may be changes before this finally lands in the JS standard.
|
||||||
|
::
|
||||||
|
|
||||||
|
### Usage
|
||||||
|
|
||||||
|
```ts twoslash [nuxt.config.ts]
|
||||||
|
export default defineNuxtConfig({
|
||||||
|
experimental: {
|
||||||
|
decorators: true,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
```
|
||||||
|
|
||||||
|
```ts [app.vue]
|
||||||
|
function something (_method: () => unknown) {
|
||||||
|
return () => 'decorated'
|
||||||
|
}
|
||||||
|
|
||||||
|
class SomeClass {
|
||||||
|
@something
|
||||||
|
public someMethod () {
|
||||||
|
return 'initial'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const value = new SomeClass().someMethod()
|
||||||
|
// this will return 'decorated'
|
||||||
|
```
|
||||||
|
@ -164,9 +164,25 @@ When publishing the layer as a private npm package, you need to make sure you lo
|
|||||||
|
|
||||||
## Tips
|
## Tips
|
||||||
|
|
||||||
|
### Named Layer Aliases
|
||||||
|
|
||||||
|
Auto-scanned layers (from your `~~/layers` directory) automatically create aliases. For example, you can access your `~~/layers/test` layer via `#layers/test`.
|
||||||
|
|
||||||
|
If you want to create named layer aliases for other layers, you can specify a name in the configuration of the layer.
|
||||||
|
|
||||||
|
```ts [nuxt.config.ts]
|
||||||
|
export default defineNuxtConfig({
|
||||||
|
$meta: {
|
||||||
|
name: 'example',
|
||||||
|
},
|
||||||
|
})
|
||||||
|
```
|
||||||
|
|
||||||
|
This will produce an alias of `#layers/example` which points to your layer.
|
||||||
|
|
||||||
### Relative Paths and Aliases
|
### Relative Paths and Aliases
|
||||||
|
|
||||||
When importing using aliases (such as `~/` and `@/`) in a layer components and composables, note that aliases are resolved relative to the user's project paths. As a workaround, you can **use relative paths** to import them. We are working on a better solution for named layer aliases.
|
When importing using global aliases (such as `~/` and `@/`) in a layer components and composables, note that these aliases are resolved relative to the user's project paths. As a workaround, you can **use relative paths** to import them, or use named layer aliases.
|
||||||
|
|
||||||
Also when using relative paths in `nuxt.config` file of a layer, (with exception of nested `extends`) they are resolved relative to user's project instead of the layer. As a workaround, use full resolved paths in `nuxt.config`:
|
Also when using relative paths in `nuxt.config` file of a layer, (with exception of nested `extends`) they are resolved relative to user's project instead of the layer. As a workaround, use full resolved paths in `nuxt.config`:
|
||||||
|
|
||||||
|
16
package.json
16
package.json
@ -11,7 +11,7 @@
|
|||||||
"build": "pnpm --filter './packages/**' prepack",
|
"build": "pnpm --filter './packages/**' prepack",
|
||||||
"build:stub": "pnpm dev:prepare",
|
"build:stub": "pnpm dev:prepare",
|
||||||
"dev": "pnpm play",
|
"dev": "pnpm play",
|
||||||
"dev:prepare": "pnpm --filter './packages/**' prepack --stub && pnpm --filter './packages/ui-templates' build",
|
"dev:prepare": "pnpm --filter './packages/**' prepack --stub && pnpm --filter './packages/ui-templates' build && nuxi prepare",
|
||||||
"debug:prepare": "TIMINGS_DEBUG=true pnpm dev:prepare",
|
"debug:prepare": "TIMINGS_DEBUG=true pnpm dev:prepare",
|
||||||
"debug:build": "TIMINGS_DEBUG=true pnpm build",
|
"debug:build": "TIMINGS_DEBUG=true pnpm build",
|
||||||
"debug:dev": "rm -rf **/node_modules/.cache/jiti && pnpm nuxi dev",
|
"debug:dev": "rm -rf **/node_modules/.cache/jiti && pnpm nuxi dev",
|
||||||
@ -52,21 +52,21 @@
|
|||||||
"@vue/compiler-core": "3.5.13",
|
"@vue/compiler-core": "3.5.13",
|
||||||
"@vue/compiler-dom": "3.5.13",
|
"@vue/compiler-dom": "3.5.13",
|
||||||
"@vue/shared": "3.5.13",
|
"@vue/shared": "3.5.13",
|
||||||
"c12": "2.0.1",
|
"c12": "2.0.2",
|
||||||
"h3": "npm:h3-nightly@1.14.0-20250122-114730-3f9e703",
|
"h3": "npm:h3-nightly@1.14.0-20250122-114730-3f9e703",
|
||||||
"jiti": "2.4.2",
|
"jiti": "2.4.2",
|
||||||
"magic-string": "^0.30.17",
|
"magic-string": "^0.30.17",
|
||||||
"nitro": "npm:nitro-nightly@3.0.0-beta-28969273.f7aa9de6",
|
"nitro": "npm:nitro-nightly@3.0.0-beta-28969273.f7aa9de6",
|
||||||
"nuxt": "workspace:*",
|
"nuxt": "workspace:*",
|
||||||
"ohash": "1.1.4",
|
"ohash": "1.1.4",
|
||||||
"postcss": "8.5.1",
|
"postcss": "8.5.2",
|
||||||
"rollup": "4.34.6",
|
"rollup": "4.34.6",
|
||||||
"send": ">=1.1.0",
|
"send": ">=1.1.0",
|
||||||
"typescript": "5.7.3",
|
"typescript": "5.7.3",
|
||||||
"ufo": "1.5.4",
|
"ufo": "1.5.4",
|
||||||
"unbuild": "3.3.1",
|
"unbuild": "3.3.1",
|
||||||
"unhead": "2.0.0-alpha.9",
|
"unhead": "2.0.0-alpha.9",
|
||||||
"unimport": "4.1.0",
|
"unimport": "4.1.1",
|
||||||
"vite": "6.1.0",
|
"vite": "6.1.0",
|
||||||
"vue": "3.5.13"
|
"vue": "3.5.13"
|
||||||
},
|
},
|
||||||
@ -98,16 +98,16 @@
|
|||||||
"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.20.0",
|
"eslint": "9.20.1",
|
||||||
"eslint-plugin-no-only-tests": "3.3.0",
|
"eslint-plugin-no-only-tests": "3.3.0",
|
||||||
"eslint-plugin-perfectionist": "4.8.0",
|
"eslint-plugin-perfectionist": "4.8.0",
|
||||||
"eslint-typegen": "1.0.0",
|
"eslint-typegen": "1.0.0",
|
||||||
"estree-walker": "3.0.3",
|
"estree-walker": "3.0.3",
|
||||||
"h3": "npm:h3-nightly@1.14.0-20250122-114730-3f9e703",
|
"h3": "npm:h3-nightly@1.14.0-20250122-114730-3f9e703",
|
||||||
"happy-dom": "17.0.2",
|
"happy-dom": "17.0.4",
|
||||||
"installed-check": "9.3.0",
|
"installed-check": "9.3.0",
|
||||||
"jiti": "2.4.2",
|
"jiti": "2.4.2",
|
||||||
"knip": "5.43.6",
|
"knip": "5.44.0",
|
||||||
"magic-string": "0.30.17",
|
"magic-string": "0.30.17",
|
||||||
"markdownlint-cli": "0.44.0",
|
"markdownlint-cli": "0.44.0",
|
||||||
"memfs": "4.17.0",
|
"memfs": "4.17.0",
|
||||||
@ -134,6 +134,6 @@
|
|||||||
"vue-tsc": "2.2.0",
|
"vue-tsc": "2.2.0",
|
||||||
"webpack": "5.97.1"
|
"webpack": "5.97.1"
|
||||||
},
|
},
|
||||||
"packageManager": "pnpm@10.2.1",
|
"packageManager": "pnpm@10.3.0",
|
||||||
"version": ""
|
"version": ""
|
||||||
}
|
}
|
||||||
|
@ -27,7 +27,7 @@
|
|||||||
"test:attw": "attw --pack"
|
"test:attw": "attw --pack"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"c12": "^2.0.1",
|
"c12": "^2.0.2",
|
||||||
"consola": "^3.4.0",
|
"consola": "^3.4.0",
|
||||||
"defu": "^6.1.4",
|
"defu": "^6.1.4",
|
||||||
"destr": "^2.0.3",
|
"destr": "^2.0.3",
|
||||||
@ -45,12 +45,12 @@
|
|||||||
"std-env": "^3.8.0",
|
"std-env": "^3.8.0",
|
||||||
"ufo": "^1.5.4",
|
"ufo": "^1.5.4",
|
||||||
"unctx": "^2.4.1",
|
"unctx": "^2.4.1",
|
||||||
"unimport": "^4.1.0",
|
"unimport": "^4.1.1",
|
||||||
"untyped": "^1.5.2"
|
"untyped": "^1.5.2"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@nuxt/schema": "workspace:*",
|
"@nuxt/schema": "workspace:*",
|
||||||
"@rspack/core": "1.2.2",
|
"@rspack/core": "1.2.3",
|
||||||
"@types/semver": "7.5.8",
|
"@types/semver": "7.5.8",
|
||||||
"nitro": "npm:nitro-nightly@3.0.0-beta-28969273.f7aa9de6",
|
"nitro": "npm:nitro-nightly@3.0.0-beta-28969273.f7aa9de6",
|
||||||
"unbuild": "3.3.1",
|
"unbuild": "3.3.1",
|
||||||
@ -59,6 +59,6 @@
|
|||||||
"webpack": "5.97.1"
|
"webpack": "5.97.1"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=18.20.6"
|
"node": ">=18.12.0"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,9 +1,22 @@
|
|||||||
import { getContext } from 'unctx'
|
import { AsyncLocalStorage } from 'node:async_hooks'
|
||||||
|
import { createContext, getContext } from 'unctx'
|
||||||
import type { Nuxt } from '@nuxt/schema'
|
import type { Nuxt } from '@nuxt/schema'
|
||||||
|
|
||||||
/** Direct access to the Nuxt context - see https://github.com/unjs/unctx. */
|
/**
|
||||||
|
* Direct access to the Nuxt global context - see https://github.com/unjs/unctx.
|
||||||
|
* @deprecated Use `getNuxtCtx` instead
|
||||||
|
*/
|
||||||
export const nuxtCtx = getContext<Nuxt>('nuxt')
|
export const nuxtCtx = getContext<Nuxt>('nuxt')
|
||||||
|
|
||||||
|
/** async local storage for the name of the current nuxt instance */
|
||||||
|
const asyncNuxtStorage = createContext<Nuxt>({
|
||||||
|
asyncContext: true,
|
||||||
|
AsyncLocalStorage,
|
||||||
|
})
|
||||||
|
|
||||||
|
/** Direct access to the Nuxt context with asyncLocalStorage - see https://github.com/unjs/unctx. */
|
||||||
|
export const getNuxtCtx = () => asyncNuxtStorage.tryUse()
|
||||||
|
|
||||||
// TODO: Use use/tryUse from unctx. https://github.com/unjs/unctx/issues/6
|
// TODO: Use use/tryUse from unctx. https://github.com/unjs/unctx/issues/6
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -16,7 +29,7 @@ export const nuxtCtx = getContext<Nuxt>('nuxt')
|
|||||||
* ```
|
* ```
|
||||||
*/
|
*/
|
||||||
export function useNuxt (): Nuxt {
|
export function useNuxt (): Nuxt {
|
||||||
const instance = nuxtCtx.tryUse()
|
const instance = asyncNuxtStorage.tryUse() || nuxtCtx.tryUse()
|
||||||
if (!instance) {
|
if (!instance) {
|
||||||
throw new Error('Nuxt instance is unavailable!')
|
throw new Error('Nuxt instance is unavailable!')
|
||||||
}
|
}
|
||||||
@ -36,5 +49,9 @@ export function useNuxt (): Nuxt {
|
|||||||
* ```
|
* ```
|
||||||
*/
|
*/
|
||||||
export function tryUseNuxt (): Nuxt | null {
|
export function tryUseNuxt (): Nuxt | null {
|
||||||
return nuxtCtx.tryUse()
|
return asyncNuxtStorage.tryUse() || nuxtCtx.tryUse()
|
||||||
|
}
|
||||||
|
|
||||||
|
export function runWithNuxtContext<T extends (...args: any[]) => any> (nuxt: Nuxt, fn: T) {
|
||||||
|
return asyncNuxtStorage.call(nuxt, fn) as ReturnType<T>
|
||||||
}
|
}
|
||||||
|
@ -18,7 +18,7 @@ export type { ExtendConfigOptions, ExtendViteConfigOptions, ExtendWebpackConfigO
|
|||||||
export { assertNuxtCompatibility, checkNuxtCompatibility, getNuxtVersion, hasNuxtCompatibility, isNuxtMajorVersion, normalizeSemanticVersion, isNuxt2, isNuxt3 } from './compatibility'
|
export { assertNuxtCompatibility, checkNuxtCompatibility, getNuxtVersion, hasNuxtCompatibility, isNuxtMajorVersion, normalizeSemanticVersion, isNuxt2, isNuxt3 } from './compatibility'
|
||||||
export { addComponent, addComponentsDir } from './components'
|
export { addComponent, addComponentsDir } from './components'
|
||||||
export type { AddComponentOptions } from './components'
|
export type { AddComponentOptions } from './components'
|
||||||
export { nuxtCtx, tryUseNuxt, useNuxt } from './context'
|
export { getNuxtCtx, runWithNuxtContext, tryUseNuxt, useNuxt, nuxtCtx } from './context'
|
||||||
export { createIsIgnored, isIgnored, resolveIgnorePatterns } from './ignore'
|
export { createIsIgnored, isIgnored, resolveIgnorePatterns } from './ignore'
|
||||||
export { addLayout } from './layout'
|
export { addLayout } from './layout'
|
||||||
export { addRouteMiddleware, extendPages, extendRouteRules } from './pages'
|
export { addRouteMiddleware, extendPages, extendRouteRules } from './pages'
|
||||||
|
@ -7,7 +7,7 @@ import { loadConfig } from 'c12'
|
|||||||
import type { NuxtConfig, NuxtOptions } from '@nuxt/schema'
|
import type { NuxtConfig, NuxtOptions } from '@nuxt/schema'
|
||||||
import { globby } from 'globby'
|
import { globby } from 'globby'
|
||||||
import defu from 'defu'
|
import defu from 'defu'
|
||||||
import { join } from 'pathe'
|
import { basename, join, relative } from 'pathe'
|
||||||
import { isWindows } from 'std-env'
|
import { isWindows } from 'std-env'
|
||||||
import { tryResolveModule } from '../internal/esm'
|
import { tryResolveModule } from '../internal/esm'
|
||||||
|
|
||||||
@ -18,14 +18,11 @@ export interface LoadNuxtConfigOptions extends Omit<LoadConfigOptions<NuxtConfig
|
|||||||
|
|
||||||
export async function loadNuxtConfig (opts: LoadNuxtConfigOptions): Promise<NuxtOptions> {
|
export async function loadNuxtConfig (opts: LoadNuxtConfigOptions): Promise<NuxtOptions> {
|
||||||
// Automatically detect and import layers from `~~/layers/` directory
|
// Automatically detect and import layers from `~~/layers/` directory
|
||||||
opts.overrides = defu(opts.overrides, {
|
const localLayers = await globby('layers/*', { onlyDirectories: true, cwd: opts.cwd || process.cwd() })
|
||||||
_extends: await globby('layers/*', {
|
opts.overrides = defu(opts.overrides, { _extends: localLayers });
|
||||||
onlyDirectories: true,
|
|
||||||
cwd: opts.cwd || process.cwd(),
|
|
||||||
}),
|
|
||||||
});
|
|
||||||
(globalThis as any).defineNuxtConfig = (c: any) => c
|
(globalThis as any).defineNuxtConfig = (c: any) => c
|
||||||
const result = await loadConfig<NuxtConfig>({
|
const { configFile, layers = [], cwd, config: nuxtConfig, meta } = await loadConfig<NuxtConfig>({
|
||||||
name: 'nuxt',
|
name: 'nuxt',
|
||||||
configFile: 'nuxt.config',
|
configFile: 'nuxt.config',
|
||||||
rcFile: '.nuxtrc',
|
rcFile: '.nuxtrc',
|
||||||
@ -35,13 +32,17 @@ export async function loadNuxtConfig (opts: LoadNuxtConfigOptions): Promise<Nuxt
|
|||||||
...opts,
|
...opts,
|
||||||
})
|
})
|
||||||
delete (globalThis as any).defineNuxtConfig
|
delete (globalThis as any).defineNuxtConfig
|
||||||
const { configFile, layers = [], cwd } = result
|
|
||||||
const nuxtConfig = result.config!
|
|
||||||
|
|
||||||
// Fill config
|
// Fill config
|
||||||
nuxtConfig.rootDir = nuxtConfig.rootDir || cwd
|
nuxtConfig.rootDir = nuxtConfig.rootDir || cwd
|
||||||
nuxtConfig._nuxtConfigFile = configFile
|
nuxtConfig._nuxtConfigFile = configFile
|
||||||
nuxtConfig._nuxtConfigFiles = [configFile]
|
nuxtConfig._nuxtConfigFiles = [configFile]
|
||||||
|
nuxtConfig.alias ||= {}
|
||||||
|
|
||||||
|
if (meta?.name) {
|
||||||
|
const alias = `#layers/${meta.name}`
|
||||||
|
nuxtConfig.alias[alias] ||= nuxtConfig.rootDir
|
||||||
|
}
|
||||||
|
|
||||||
const defaultBuildDir = join(nuxtConfig.rootDir!, '.nuxt')
|
const defaultBuildDir = join(nuxtConfig.rootDir!, '.nuxt')
|
||||||
if (!opts.overrides?._prepare && !nuxtConfig.dev && !nuxtConfig.buildDir && existsSync(defaultBuildDir)) {
|
if (!opts.overrides?._prepare && !nuxtConfig.dev && !nuxtConfig.buildDir && existsSync(defaultBuildDir)) {
|
||||||
@ -74,6 +75,18 @@ export async function loadNuxtConfig (opts: LoadNuxtConfigOptions): Promise<Nuxt
|
|||||||
|
|
||||||
// Filter layers
|
// Filter layers
|
||||||
if (!layer.configFile || layer.configFile.endsWith('.nuxtrc')) { continue }
|
if (!layer.configFile || layer.configFile.endsWith('.nuxtrc')) { continue }
|
||||||
|
|
||||||
|
// Add layer name for local layers
|
||||||
|
if (layer.cwd && cwd && localLayers.includes(relative(cwd, layer.cwd))) {
|
||||||
|
layer.meta ||= {}
|
||||||
|
layer.meta.name ||= basename(layer.cwd)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add layer alias
|
||||||
|
if (layer.meta?.name) {
|
||||||
|
const alias = `#layers/${layer.meta.name}`
|
||||||
|
nuxtConfig.alias[alias] ||= layer.config.rootDir || layer.cwd
|
||||||
|
}
|
||||||
_layers.push(layer)
|
_layers.push(layer)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3,6 +3,7 @@ import { readPackageJSON, resolvePackageJSON } from 'pkg-types'
|
|||||||
import type { Nuxt, NuxtConfig } from '@nuxt/schema'
|
import type { Nuxt, NuxtConfig } from '@nuxt/schema'
|
||||||
import { resolve } from 'pathe'
|
import { resolve } from 'pathe'
|
||||||
import { importModule, tryImportModule } from '../internal/esm'
|
import { importModule, tryImportModule } from '../internal/esm'
|
||||||
|
import { runWithNuxtContext } from '../context'
|
||||||
import type { LoadNuxtConfigOptions } from './config'
|
import type { LoadNuxtConfigOptions } from './config'
|
||||||
|
|
||||||
export interface LoadNuxtOptions extends LoadNuxtConfigOptions {
|
export interface LoadNuxtOptions extends LoadNuxtConfigOptions {
|
||||||
@ -40,5 +41,5 @@ export async function buildNuxt (nuxt: Nuxt): Promise<any> {
|
|||||||
const rootDir = pathToFileURL(nuxt.options.rootDir).href
|
const rootDir = pathToFileURL(nuxt.options.rootDir).href
|
||||||
|
|
||||||
const { build } = await tryImportModule<typeof import('nuxt')>('nuxt-nightly', { paths: rootDir }) || await importModule<typeof import('nuxt')>('nuxt', { paths: rootDir })
|
const { build } = await tryImportModule<typeof import('nuxt')>('nuxt-nightly', { paths: rootDir }) || await importModule<typeof import('nuxt')>('nuxt', { paths: rootDir })
|
||||||
return build(nuxt)
|
return runWithNuxtContext(nuxt, () => build(nuxt))
|
||||||
}
|
}
|
||||||
|
@ -20,7 +20,7 @@ const nuxt = await loadNuxt({
|
|||||||
|
|
||||||
describe('resolvePath', () => {
|
describe('resolvePath', () => {
|
||||||
it('should resolve paths correctly', async () => {
|
it('should resolve paths correctly', async () => {
|
||||||
expect(await resolvePath('.nuxt/app.config')).toBe(resolve(nuxt.options.buildDir, 'app.config'))
|
expect(await resolvePath('.nuxt/app.config')).toBe(resolve('.nuxt/app.config.mjs'))
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -180,6 +180,8 @@ export async function _generateTypes (nuxt: Nuxt) {
|
|||||||
.then(r => r?.version && gte(r.version, '5.4.0'))
|
.then(r => r?.version && gte(r.version, '5.4.0'))
|
||||||
.catch(() => isV4)
|
.catch(() => isV4)
|
||||||
|
|
||||||
|
const useDecorators = Boolean(nuxt.options.experimental?.decorators)
|
||||||
|
|
||||||
// https://www.totaltypescript.com/tsconfig-cheat-sheet
|
// https://www.totaltypescript.com/tsconfig-cheat-sheet
|
||||||
const tsConfig: TSConfig = defu(nuxt.options.typescript?.tsConfig, {
|
const tsConfig: TSConfig = defu(nuxt.options.typescript?.tsConfig, {
|
||||||
compilerOptions: {
|
compilerOptions: {
|
||||||
@ -197,12 +199,20 @@ export async function _generateTypes (nuxt: Nuxt) {
|
|||||||
noUncheckedIndexedAccess: isV4,
|
noUncheckedIndexedAccess: isV4,
|
||||||
forceConsistentCasingInFileNames: true,
|
forceConsistentCasingInFileNames: true,
|
||||||
noImplicitOverride: true,
|
noImplicitOverride: true,
|
||||||
|
/* Decorator support */
|
||||||
|
...useDecorators
|
||||||
|
? {
|
||||||
|
useDefineForClassFields: false,
|
||||||
|
experimentalDecorators: false,
|
||||||
|
}
|
||||||
|
: {},
|
||||||
/* If NOT transpiling with TypeScript: */
|
/* If NOT transpiling with TypeScript: */
|
||||||
module: hasTypescriptVersionWithModulePreserve ? 'preserve' : 'ESNext',
|
module: hasTypescriptVersionWithModulePreserve ? 'preserve' : 'ESNext',
|
||||||
noEmit: true,
|
noEmit: true,
|
||||||
/* If your code runs in the DOM: */
|
/* If your code runs in the DOM: */
|
||||||
lib: [
|
lib: [
|
||||||
'ESNext',
|
'ESNext',
|
||||||
|
...useDecorators ? ['esnext.decorators'] : [],
|
||||||
'dom',
|
'dom',
|
||||||
'dom.iterable',
|
'dom.iterable',
|
||||||
'webworker',
|
'webworker',
|
||||||
|
@ -0,0 +1 @@
|
|||||||
|
export const foo = 'bar'
|
@ -0,0 +1 @@
|
|||||||
|
export default defineNuxtConfig({})
|
5
packages/kit/test/layer-fixture/nuxt.config.ts
Normal file
5
packages/kit/test/layer-fixture/nuxt.config.ts
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
export default defineNuxtConfig({
|
||||||
|
$meta: {
|
||||||
|
name: 'layer-fixture',
|
||||||
|
},
|
||||||
|
})
|
28
packages/kit/test/load-nuxt-config.spec.ts
Normal file
28
packages/kit/test/load-nuxt-config.spec.ts
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
import { fileURLToPath } from 'node:url'
|
||||||
|
import { describe, expect, it } from 'vitest'
|
||||||
|
import { loadNuxtConfig } from '@nuxt/kit'
|
||||||
|
|
||||||
|
describe('loadNuxtConfig', () => {
|
||||||
|
it('should add named aliases for local layers', async () => {
|
||||||
|
const cwd = fileURLToPath(new URL('./layer-fixture', import.meta.url))
|
||||||
|
const config = await loadNuxtConfig({ cwd })
|
||||||
|
for (const alias in config.alias) {
|
||||||
|
config.alias[alias] = config.alias[alias]!.replace(cwd, '<rootDir>')
|
||||||
|
}
|
||||||
|
expect(config.alias).toMatchInlineSnapshot(`
|
||||||
|
{
|
||||||
|
"#build": "<rootDir>/.nuxt",
|
||||||
|
"#internal/nuxt/paths": "<rootDir>/.nuxt/paths.mjs",
|
||||||
|
"#layers/layer-fixture": "<rootDir>",
|
||||||
|
"#layers/test": "<rootDir>/layers/test",
|
||||||
|
"#shared": "<rootDir>/shared",
|
||||||
|
"@": "<rootDir>",
|
||||||
|
"@@": "<rootDir>",
|
||||||
|
"assets": "<rootDir>/assets",
|
||||||
|
"public": "<rootDir>/public",
|
||||||
|
"~": "<rootDir>",
|
||||||
|
"~~": "<rootDir>",
|
||||||
|
}
|
||||||
|
`)
|
||||||
|
})
|
||||||
|
})
|
@ -69,12 +69,12 @@
|
|||||||
"@nuxt/devtools": "^2.0.0",
|
"@nuxt/devtools": "^2.0.0",
|
||||||
"@nuxt/kit": "workspace:*",
|
"@nuxt/kit": "workspace:*",
|
||||||
"@nuxt/schema": "workspace:*",
|
"@nuxt/schema": "workspace:*",
|
||||||
"@nuxt/telemetry": "^2.6.4",
|
"@nuxt/telemetry": "^2.6.5",
|
||||||
"@nuxt/vite-builder": "workspace:*",
|
"@nuxt/vite-builder": "workspace:*",
|
||||||
"@unhead/vue": "^2.0.0-alpha.8",
|
"@unhead/vue": "^2.0.0-alpha.8",
|
||||||
"@vue/shared": "^3.5.13",
|
"@vue/shared": "^3.5.13",
|
||||||
"acorn": "8.14.0",
|
"acorn": "8.14.0",
|
||||||
"c12": "^2.0.1",
|
"c12": "^2.0.2",
|
||||||
"chokidar": "^4.0.3",
|
"chokidar": "^4.0.3",
|
||||||
"compatx": "^0.1.8",
|
"compatx": "^0.1.8",
|
||||||
"consola": "^3.4.0",
|
"consola": "^3.4.0",
|
||||||
@ -116,8 +116,8 @@
|
|||||||
"uncrypto": "^0.1.3",
|
"uncrypto": "^0.1.3",
|
||||||
"unctx": "^2.4.1",
|
"unctx": "^2.4.1",
|
||||||
"unenv": "^1.10.0",
|
"unenv": "^1.10.0",
|
||||||
"unimport": "^4.1.0",
|
"unimport": "^4.1.1",
|
||||||
"unplugin": "^2.1.2",
|
"unplugin": "^2.2.0",
|
||||||
"unplugin-vue-router": "^0.11.2",
|
"unplugin-vue-router": "^0.11.2",
|
||||||
"unstorage": "^1.14.4",
|
"unstorage": "^1.14.4",
|
||||||
"untyped": "^1.5.2",
|
"untyped": "^1.5.2",
|
||||||
@ -149,6 +149,6 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": "^18.20.6 || ^20.9.0 || >=22.0.0"
|
"node": "^18.12.0 || ^20.9.0 || >=22.0.0"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -54,6 +54,13 @@ export function useAppConfig (): AppConfig {
|
|||||||
return nuxtApp._appConfig
|
return nuxtApp._appConfig
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function _replaceAppConfig (newConfig: AppConfig) {
|
||||||
|
const appConfig = useAppConfig()
|
||||||
|
|
||||||
|
deepAssign(appConfig, newConfig)
|
||||||
|
deepDelete(appConfig, newConfig)
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Deep assign the current appConfig with the new one.
|
* Deep assign the current appConfig with the new one.
|
||||||
*
|
*
|
||||||
|
@ -1,11 +1,12 @@
|
|||||||
import { existsSync } from 'node:fs'
|
import { existsSync } from 'node:fs'
|
||||||
import { rm } from 'node:fs/promises'
|
import { rm } from 'node:fs/promises'
|
||||||
|
import { randomUUID } from 'node:crypto'
|
||||||
import { AsyncLocalStorage } from 'node:async_hooks'
|
import { AsyncLocalStorage } from 'node:async_hooks'
|
||||||
import { join, normalize, relative, resolve } from 'pathe'
|
import { join, normalize, relative, resolve } from 'pathe'
|
||||||
import { createDebugger, createHooks } from 'hookable'
|
import { createDebugger, createHooks } from 'hookable'
|
||||||
import ignore from 'ignore'
|
import ignore from 'ignore'
|
||||||
import type { LoadNuxtOptions } from '@nuxt/kit'
|
import type { LoadNuxtOptions } from '@nuxt/kit'
|
||||||
import { addBuildPlugin, addComponent, addPlugin, addPluginTemplate, addRouteMiddleware, addServerPlugin, addTypeTemplate, addVitePlugin, addWebpackPlugin, installModule, loadNuxtConfig, nuxtCtx, resolveAlias, resolveFiles, resolveIgnorePatterns, resolvePath, tryResolveModule, useNitro } from '@nuxt/kit'
|
import { addBuildPlugin, addComponent, addPlugin, addPluginTemplate, addRouteMiddleware, addServerPlugin, addTypeTemplate, addVitePlugin, addWebpackPlugin, installModule, loadNuxtConfig, nuxtCtx, resolveAlias, resolveFiles, resolveIgnorePatterns, resolvePath, runWithNuxtContext, tryResolveModule, useNitro } from '@nuxt/kit'
|
||||||
import type { Nuxt, NuxtHooks, NuxtModule, NuxtOptions } from 'nuxt/schema'
|
import type { Nuxt, NuxtHooks, NuxtModule, NuxtOptions } from 'nuxt/schema'
|
||||||
import type { PackageJson } from 'pkg-types'
|
import type { PackageJson } from 'pkg-types'
|
||||||
import { readPackageJSON } from 'pkg-types'
|
import { readPackageJSON } from 'pkg-types'
|
||||||
@ -53,17 +54,24 @@ import { VirtualFSPlugin } from './plugins/virtual'
|
|||||||
export function createNuxt (options: NuxtOptions): Nuxt {
|
export function createNuxt (options: NuxtOptions): Nuxt {
|
||||||
const hooks = createHooks<NuxtHooks>()
|
const hooks = createHooks<NuxtHooks>()
|
||||||
|
|
||||||
|
const { callHook, callHookParallel, callHookWith } = hooks
|
||||||
|
hooks.callHook = (...args) => runWithNuxtContext(nuxt, () => callHook(...args))
|
||||||
|
hooks.callHookParallel = (...args) => runWithNuxtContext(nuxt, () => callHookParallel(...args))
|
||||||
|
hooks.callHookWith = (...args) => runWithNuxtContext(nuxt, () => callHookWith(...args))
|
||||||
|
|
||||||
const nuxt: Nuxt = {
|
const nuxt: Nuxt = {
|
||||||
|
__name: randomUUID(),
|
||||||
_version: version,
|
_version: version,
|
||||||
_asyncLocalStorageModule: options.experimental.debugModuleMutation ? new AsyncLocalStorage() : undefined,
|
_asyncLocalStorageModule: options.experimental.debugModuleMutation ? new AsyncLocalStorage() : undefined,
|
||||||
hooks,
|
hooks,
|
||||||
callHook: hooks.callHook,
|
callHook: hooks.callHook,
|
||||||
addHooks: hooks.addHooks,
|
addHooks: hooks.addHooks,
|
||||||
hook: hooks.hook,
|
hook: hooks.hook,
|
||||||
ready: () => initNuxt(nuxt),
|
ready: () => runWithNuxtContext(nuxt, () => initNuxt(nuxt)),
|
||||||
close: () => hooks.callHook('close', nuxt),
|
close: () => hooks.callHook('close', nuxt),
|
||||||
vfs: {},
|
vfs: {},
|
||||||
apps: {},
|
apps: {},
|
||||||
|
runWithContext: fn => runWithNuxtContext(nuxt, fn),
|
||||||
options,
|
options,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -115,6 +123,14 @@ export function createNuxt (options: NuxtOptions): Nuxt {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!nuxtCtx.tryUse()) {
|
||||||
|
// backward compatibility with 3.x
|
||||||
|
nuxtCtx.set(nuxt)
|
||||||
|
nuxt.hook('close', () => {
|
||||||
|
nuxtCtx.unset()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
hooks.hookOnce('close', () => { hooks.removeAllHooks() })
|
hooks.hookOnce('close', () => { hooks.removeAllHooks() })
|
||||||
|
|
||||||
return nuxt
|
return nuxt
|
||||||
@ -223,11 +239,6 @@ async function initNuxt (nuxt: Nuxt) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
// Set nuxt instance for useNuxt
|
|
||||||
nuxtCtx.set(nuxt)
|
|
||||||
nuxt.hook('close', () => nuxtCtx.unset())
|
|
||||||
|
|
||||||
const coreTypePackages = nuxt.options.typescript.hoist || []
|
const coreTypePackages = nuxt.options.typescript.hoist || []
|
||||||
|
|
||||||
// Disable environment types entirely if `typescript.builder` is false
|
// Disable environment types entirely if `typescript.builder` is false
|
||||||
@ -454,7 +465,7 @@ async function initNuxt (nuxt: Nuxt) {
|
|||||||
...nuxt.options._layers.filter(i => i.cwd.includes('node_modules')).map(i => i.cwd as string),
|
...nuxt.options._layers.filter(i => i.cwd.includes('node_modules')).map(i => i.cwd as string),
|
||||||
)
|
)
|
||||||
|
|
||||||
// Ensure we can resolve dependencies within layers - filtering out local `~/layers` directories
|
// Ensure we can resolve dependencies within layers - filtering out local `~~/layers` directories
|
||||||
const locallyScannedLayersDirs = nuxt.options._layers.map(l => resolve(l.cwd, 'layers').replace(/\/?$/, '/'))
|
const locallyScannedLayersDirs = nuxt.options._layers.map(l => resolve(l.cwd, 'layers').replace(/\/?$/, '/'))
|
||||||
nuxt.options.modulesDir.push(...nuxt.options._layers
|
nuxt.options.modulesDir.push(...nuxt.options._layers
|
||||||
.filter(l => l.cwd !== nuxt.options.rootDir && locallyScannedLayersDirs.every(dir => !l.cwd.startsWith(dir)))
|
.filter(l => l.cwd !== nuxt.options.rootDir && locallyScannedLayersDirs.every(dir => !l.cwd.startsWith(dir)))
|
||||||
@ -861,27 +872,29 @@ export async function loadNuxt (opts: LoadNuxtOptions): Promise<Nuxt> {
|
|||||||
|
|
||||||
const nuxt = createNuxt(options)
|
const nuxt = createNuxt(options)
|
||||||
|
|
||||||
if (nuxt.options.dev && !nuxt.options.test) {
|
nuxt.runWithContext(() => {
|
||||||
nuxt.hooks.hookOnce('build:done', () => {
|
if (nuxt.options.dev && !nuxt.options.test) {
|
||||||
for (const dep of keyDependencies) {
|
nuxt.hooks.hookOnce('build:done', () => {
|
||||||
checkDependencyVersion(dep, nuxt._version)
|
for (const dep of keyDependencies) {
|
||||||
.catch(e => logger.warn(`Problem checking \`${dep}\` version.`, e))
|
checkDependencyVersion(dep, nuxt._version)
|
||||||
}
|
.catch(e => logger.warn(`Problem checking \`${dep}\` version.`, e))
|
||||||
})
|
}
|
||||||
}
|
})
|
||||||
|
}
|
||||||
|
|
||||||
// We register hooks layer-by-layer so any overrides need to be registered separately
|
// We register hooks layer-by-layer so any overrides need to be registered separately
|
||||||
if (opts.overrides?.hooks) {
|
if (opts.overrides?.hooks) {
|
||||||
nuxt.hooks.addHooks(opts.overrides.hooks)
|
nuxt.hooks.addHooks(opts.overrides.hooks)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (
|
if (
|
||||||
nuxt.options.debug
|
nuxt.options.debug
|
||||||
&& nuxt.options.debug.hooks
|
&& nuxt.options.debug.hooks
|
||||||
&& (nuxt.options.debug.hooks === true || nuxt.options.debug.hooks.server)
|
&& (nuxt.options.debug.hooks === true || nuxt.options.debug.hooks.server)
|
||||||
) {
|
) {
|
||||||
createDebugger(nuxt.hooks, { tag: 'nuxt' })
|
createDebugger(nuxt.hooks, { tag: 'nuxt' })
|
||||||
}
|
}
|
||||||
|
})
|
||||||
|
|
||||||
if (opts.ready !== false) {
|
if (opts.ready !== false) {
|
||||||
await nuxt.ready()
|
await nuxt.ready()
|
||||||
|
@ -1,5 +1,4 @@
|
|||||||
import type { Literal, Property, SpreadElement } from 'estree'
|
import type { Literal, Property, SpreadElement } from 'estree'
|
||||||
import { transform } from 'esbuild'
|
|
||||||
import { defu } from 'defu'
|
import { defu } from 'defu'
|
||||||
import { findExports } from 'mlly'
|
import { findExports } from 'mlly'
|
||||||
import type { Nuxt } from '@nuxt/schema'
|
import type { Nuxt } from '@nuxt/schema'
|
||||||
@ -8,7 +7,7 @@ import MagicString from 'magic-string'
|
|||||||
import { normalize } from 'pathe'
|
import { normalize } from 'pathe'
|
||||||
import type { ObjectPlugin, PluginMeta } from 'nuxt/app'
|
import type { ObjectPlugin, PluginMeta } from 'nuxt/app'
|
||||||
|
|
||||||
import { parseAndWalk, withLocations } from '../../core/utils/parse'
|
import { parseAndWalk, transform, withLocations } from '../../core/utils/parse'
|
||||||
import { logger } from '../../utils'
|
import { logger } from '../../utils'
|
||||||
|
|
||||||
const internalOrderMap = {
|
const internalOrderMap = {
|
||||||
|
@ -1,9 +1,8 @@
|
|||||||
import { transform } from 'esbuild'
|
|
||||||
import { createUnplugin } from 'unplugin'
|
import { createUnplugin } from 'unplugin'
|
||||||
import MagicString from 'magic-string'
|
import MagicString from 'magic-string'
|
||||||
import { hash } from 'ohash'
|
import { hash } from 'ohash'
|
||||||
|
|
||||||
import { parseAndWalk, withLocations } from '../../core/utils/parse'
|
import { parseAndWalk, transform, withLocations } from '../../core/utils/parse'
|
||||||
import { isJS, isVue } from '../utils'
|
import { isJS, isVue } from '../utils'
|
||||||
|
|
||||||
export function PrehydrateTransformPlugin (options: { sourcemap?: boolean } = {}) {
|
export function PrehydrateTransformPlugin (options: { sourcemap?: boolean } = {}) {
|
||||||
|
@ -7,7 +7,7 @@ import escapeRE from 'escape-string-regexp'
|
|||||||
import { hash } from 'ohash'
|
import { hash } from 'ohash'
|
||||||
import { camelCase } from 'scule'
|
import { camelCase } from 'scule'
|
||||||
import { filename } from 'pathe/utils'
|
import { filename } from 'pathe/utils'
|
||||||
import type { NuxtTemplate } from 'nuxt/schema'
|
import type { NuxtOptions, NuxtTemplate } from 'nuxt/schema'
|
||||||
import type { Nitro } from 'nitro/types'
|
import type { Nitro } from 'nitro/types'
|
||||||
|
|
||||||
import { annotatePlugins, checkForCircularDependencies } from './app'
|
import { annotatePlugins, checkForCircularDependencies } from './app'
|
||||||
@ -185,9 +185,18 @@ export const schemaTemplate: NuxtTemplate = {
|
|||||||
const relativeRoot = relative(resolve(nuxt.options.buildDir, 'types'), nuxt.options.rootDir)
|
const relativeRoot = relative(resolve(nuxt.options.buildDir, 'types'), nuxt.options.rootDir)
|
||||||
const getImportName = (name: string) => (name[0] === '.' ? './' + join(relativeRoot, name) : name).replace(IMPORT_NAME_RE, '')
|
const getImportName = (name: string) => (name[0] === '.' ? './' + join(relativeRoot, name) : name).replace(IMPORT_NAME_RE, '')
|
||||||
|
|
||||||
const modules = nuxt.options._installedModules
|
const modules: [string, string, NuxtOptions['_installedModules'][number]][] = []
|
||||||
.filter(m => m.meta && m.meta.configKey && m.meta.name && !m.meta.name.startsWith('nuxt:') && m.meta.name !== 'nuxt-config-schema')
|
for (const m of nuxt.options._installedModules) {
|
||||||
.map(m => [genString(m.meta.configKey), getImportName(m.entryPath || m.meta.name), m] as const)
|
// modules without sufficient metadata
|
||||||
|
if (!m.meta || !m.meta.configKey || !m.meta.name) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
// core nuxt modules
|
||||||
|
if (m.meta.name.startsWith('nuxt:') || m.meta.name === 'nuxt-config-schema') {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
modules.push([genString(m.meta.configKey), getImportName(m.entryPath || m.meta.name), m])
|
||||||
|
}
|
||||||
|
|
||||||
const privateRuntimeConfig = Object.create(null)
|
const privateRuntimeConfig = Object.create(null)
|
||||||
for (const key in nuxt.options.runtimeConfig) {
|
for (const key in nuxt.options.runtimeConfig) {
|
||||||
@ -210,7 +219,7 @@ export const schemaTemplate: NuxtTemplate = {
|
|||||||
} else if (mod.meta?.repository) {
|
} else if (mod.meta?.repository) {
|
||||||
if (typeof mod.meta.repository === 'string') {
|
if (typeof mod.meta.repository === 'string') {
|
||||||
link = mod.meta.repository
|
link = mod.meta.repository
|
||||||
} else if (typeof mod.meta.repository.url === 'string') {
|
} else if (typeof mod.meta.repository === 'object' && 'url' in mod.meta.repository && typeof mod.meta.repository.url === 'string') {
|
||||||
link = mod.meta.repository.url
|
link = mod.meta.repository.url
|
||||||
}
|
}
|
||||||
if (link) {
|
if (link) {
|
||||||
@ -428,12 +437,12 @@ import { defuFn } from 'defu'
|
|||||||
const inlineConfig = ${JSON.stringify(nuxt.options.appConfig, null, 2)}
|
const inlineConfig = ${JSON.stringify(nuxt.options.appConfig, null, 2)}
|
||||||
|
|
||||||
/** client **/
|
/** client **/
|
||||||
import { updateAppConfig } from '#app/config'
|
import { _replaceAppConfig } from '#app/config'
|
||||||
|
|
||||||
// Vite - webpack is handled directly in #app/config
|
// Vite - webpack is handled directly in #app/config
|
||||||
if (import.meta.dev && !import.meta.nitro && import.meta.hot) {
|
if (import.meta.dev && !import.meta.nitro && import.meta.hot) {
|
||||||
import.meta.hot.accept((newModule) => {
|
import.meta.hot.accept((newModule) => {
|
||||||
updateAppConfig(newModule.default)
|
_replaceAppConfig(newModule.default)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
/** client-end **/
|
/** client-end **/
|
||||||
|
@ -1,22 +1,17 @@
|
|||||||
import { walk as _walk } from 'estree-walker'
|
import { walk as _walk } from 'estree-walker'
|
||||||
import type { Node, SyncHandler } from 'estree-walker'
|
import type { Node, SyncHandler } from 'estree-walker'
|
||||||
import type {
|
import type { ArrowFunctionExpression, CatchClause, Program as ESTreeProgram, FunctionDeclaration, FunctionExpression, Identifier, ImportDefaultSpecifier, ImportNamespaceSpecifier, ImportSpecifier, VariableDeclaration } from 'estree'
|
||||||
ArrowFunctionExpression,
|
|
||||||
CatchClause,
|
|
||||||
Program as ESTreeProgram,
|
|
||||||
FunctionDeclaration,
|
|
||||||
FunctionExpression,
|
|
||||||
Identifier,
|
|
||||||
ImportDefaultSpecifier,
|
|
||||||
ImportNamespaceSpecifier,
|
|
||||||
ImportSpecifier,
|
|
||||||
VariableDeclaration,
|
|
||||||
} from 'estree'
|
|
||||||
import { parse } from 'acorn'
|
import { parse } from 'acorn'
|
||||||
import type { Program } from 'acorn'
|
import type { Program } from 'acorn'
|
||||||
|
import { type SameShape, type TransformOptions, type TransformResult, transform as esbuildTransform } from 'esbuild'
|
||||||
|
import { tryUseNuxt } from '@nuxt/kit'
|
||||||
|
|
||||||
export type { Node }
|
export type { Node }
|
||||||
|
|
||||||
|
export async function transform<T extends TransformOptions> (input: string | Uint8Array, options?: SameShape<TransformOptions, T>): Promise<TransformResult<T>> {
|
||||||
|
return await esbuildTransform(input, { ...tryUseNuxt()?.options.esbuild.options, ...options })
|
||||||
|
}
|
||||||
|
|
||||||
type WithLocations<T> = T & { start: number, end: number }
|
type WithLocations<T> = T & { start: number, end: number }
|
||||||
type WalkerCallback = (this: ThisParameterType<SyncHandler>, node: WithLocations<Node>, parent: WithLocations<Node> | null, ctx: { key: string | number | symbol | null | undefined, index: number | null | undefined, ast: Program | Node }) => void
|
type WalkerCallback = (this: ThisParameterType<SyncHandler>, node: WithLocations<Node>, parent: WithLocations<Node> | null, ctx: { key: string | number | symbol | null | undefined, index: number | null | undefined, ast: Program | Node }) => void
|
||||||
|
|
||||||
|
@ -9,7 +9,7 @@ import escapeRE from 'escape-string-regexp'
|
|||||||
import { lookupNodeModuleSubpath, parseNodeModulePath } from 'mlly'
|
import { lookupNodeModuleSubpath, parseNodeModulePath } from 'mlly'
|
||||||
import { isDirectory, logger } from '../utils'
|
import { isDirectory, logger } from '../utils'
|
||||||
import { TransformPlugin } from './transform'
|
import { TransformPlugin } from './transform'
|
||||||
import { defaultPresets } from './presets'
|
import { appCompatPresets, defaultPresets } from './presets'
|
||||||
|
|
||||||
export default defineNuxtModule<Partial<ImportsOptions>>({
|
export default defineNuxtModule<Partial<ImportsOptions>>({
|
||||||
meta: {
|
meta: {
|
||||||
@ -30,11 +30,16 @@ export default defineNuxtModule<Partial<ImportsOptions>>({
|
|||||||
exclude: undefined,
|
exclude: undefined,
|
||||||
},
|
},
|
||||||
virtualImports: ['#imports'],
|
virtualImports: ['#imports'],
|
||||||
|
polyfills: true,
|
||||||
}),
|
}),
|
||||||
async setup (options, nuxt) {
|
async setup (options, nuxt) {
|
||||||
// TODO: fix sharing of defaults between invocations of modules
|
// TODO: fix sharing of defaults between invocations of modules
|
||||||
const presets = JSON.parse(JSON.stringify(options.presets)) as ImportPresetWithDeprecation[]
|
const presets = JSON.parse(JSON.stringify(options.presets)) as ImportPresetWithDeprecation[]
|
||||||
|
|
||||||
|
if (nuxt.options.imports.polyfills) {
|
||||||
|
presets.push(...appCompatPresets)
|
||||||
|
}
|
||||||
|
|
||||||
// Allow modules extending sources
|
// Allow modules extending sources
|
||||||
await nuxt.callHook('imports:sources', presets)
|
await nuxt.callHook('imports:sources', presets)
|
||||||
|
|
||||||
|
@ -21,14 +21,6 @@ const granularAppPresets: InlinePreset[] = [
|
|||||||
imports: ['useNuxtApp', 'tryUseNuxtApp', 'defineNuxtPlugin', 'definePayloadPlugin', 'useRuntimeConfig', 'defineAppConfig'],
|
imports: ['useNuxtApp', 'tryUseNuxtApp', 'defineNuxtPlugin', 'definePayloadPlugin', 'useRuntimeConfig', 'defineAppConfig'],
|
||||||
from: '#app/nuxt',
|
from: '#app/nuxt',
|
||||||
},
|
},
|
||||||
{
|
|
||||||
imports: ['requestIdleCallback', 'cancelIdleCallback'],
|
|
||||||
from: '#app/compat/idle-callback',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
imports: ['setInterval'],
|
|
||||||
from: '#app/compat/interval',
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
imports: ['useAppConfig', 'updateAppConfig'],
|
imports: ['useAppConfig', 'updateAppConfig'],
|
||||||
from: '#app/config',
|
from: '#app/config',
|
||||||
@ -262,6 +254,17 @@ const vueTypesPreset = defineUnimportPreset({
|
|||||||
],
|
],
|
||||||
})
|
})
|
||||||
|
|
||||||
|
export const appCompatPresets: InlinePreset[] = [
|
||||||
|
{
|
||||||
|
imports: ['requestIdleCallback', 'cancelIdleCallback'],
|
||||||
|
from: '#app/compat/idle-callback',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
imports: ['setInterval'],
|
||||||
|
from: '#app/compat/interval',
|
||||||
|
},
|
||||||
|
]
|
||||||
|
|
||||||
export const defaultPresets: InlinePreset[] = [
|
export const defaultPresets: InlinePreset[] = [
|
||||||
...commonPresets,
|
...commonPresets,
|
||||||
...granularAppPresets,
|
...granularAppPresets,
|
||||||
|
@ -1,11 +1,10 @@
|
|||||||
import { runInNewContext } from 'node:vm'
|
import { runInNewContext } from 'node:vm'
|
||||||
import { transform } from 'esbuild'
|
|
||||||
import type { NuxtPage } from '@nuxt/schema'
|
import type { NuxtPage } from '@nuxt/schema'
|
||||||
import type { NitroRouteConfig } from 'nitro/types'
|
import type { NitroRouteConfig } from 'nitro/types'
|
||||||
import { normalize } from 'pathe'
|
import { normalize } from 'pathe'
|
||||||
|
|
||||||
import { getLoader } from '../core/utils'
|
import { getLoader } from '../core/utils'
|
||||||
import { parseAndWalk } from '../core/utils/parse'
|
import { parseAndWalk, transform } from '../core/utils/parse'
|
||||||
import { extractScriptContent, pathToNitroGlob } from './utils'
|
import { extractScriptContent, pathToNitroGlob } from './utils'
|
||||||
|
|
||||||
const ROUTE_RULE_RE = /\bdefineRouteRules\(/
|
const ROUTE_RULE_RE = /\bdefineRouteRules\(/
|
||||||
|
@ -7,12 +7,11 @@ import { genArrayFromRaw, genDynamicImport, genImport, genSafeVariableName } fro
|
|||||||
import escapeRE from 'escape-string-regexp'
|
import escapeRE from 'escape-string-regexp'
|
||||||
import { filename } from 'pathe/utils'
|
import { filename } from 'pathe/utils'
|
||||||
import { hash } from 'ohash'
|
import { hash } from 'ohash'
|
||||||
import { transform } from 'esbuild'
|
|
||||||
import type { Property } from 'estree'
|
import type { Property } from 'estree'
|
||||||
import type { NuxtPage } from 'nuxt/schema'
|
import type { NuxtPage } from 'nuxt/schema'
|
||||||
|
|
||||||
import { klona } from 'klona'
|
import { klona } from 'klona'
|
||||||
import { parseAndWalk, withLocations } from '../core/utils/parse'
|
import { parseAndWalk, transform, withLocations } from '../core/utils/parse'
|
||||||
import { getLoader, uniqueBy } from '../core/utils'
|
import { getLoader, uniqueBy } from '../core/utils'
|
||||||
import { logger, toArray } from '../utils'
|
import { logger, toArray } from '../utils'
|
||||||
|
|
||||||
|
@ -1,7 +1,8 @@
|
|||||||
import { fileURLToPath } from 'node:url'
|
import { fileURLToPath } from 'node:url'
|
||||||
import { afterEach, describe, expect, it, vi } from 'vitest'
|
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'
|
||||||
import { normalize } from 'pathe'
|
import { normalize } from 'pathe'
|
||||||
import { withoutTrailingSlash } from 'ufo'
|
import { withoutTrailingSlash } from 'ufo'
|
||||||
|
import { logger, tryUseNuxt, useNuxt } from '@nuxt/kit'
|
||||||
import { loadNuxt } from '../src'
|
import { loadNuxt } from '../src'
|
||||||
|
|
||||||
const repoRoot = withoutTrailingSlash(normalize(fileURLToPath(new URL('../../../', import.meta.url))))
|
const repoRoot = withoutTrailingSlash(normalize(fileURLToPath(new URL('../../../', import.meta.url))))
|
||||||
@ -12,6 +13,7 @@ vi.stubGlobal('console', {
|
|||||||
warn: vi.fn(console.warn),
|
warn: vi.fn(console.warn),
|
||||||
})
|
})
|
||||||
|
|
||||||
|
const loggerWarn = vi.spyOn(logger, 'warn')
|
||||||
vi.mock('pkg-types', async (og) => {
|
vi.mock('pkg-types', async (og) => {
|
||||||
const originalPkgTypes = (await og<typeof import('pkg-types')>())
|
const originalPkgTypes = (await og<typeof import('pkg-types')>())
|
||||||
return {
|
return {
|
||||||
@ -20,6 +22,9 @@ vi.mock('pkg-types', async (og) => {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
loggerWarn.mockClear()
|
||||||
|
})
|
||||||
afterEach(() => {
|
afterEach(() => {
|
||||||
vi.clearAllMocks()
|
vi.clearAllMocks()
|
||||||
})
|
})
|
||||||
@ -41,4 +46,41 @@ describe('loadNuxt', () => {
|
|||||||
await nuxt.close()
|
await nuxt.close()
|
||||||
expect(hookRan).toBe(true)
|
expect(hookRan).toBe(true)
|
||||||
})
|
})
|
||||||
|
it('load multiple nuxt', async () => {
|
||||||
|
await Promise.all([
|
||||||
|
loadNuxt({
|
||||||
|
cwd: repoRoot,
|
||||||
|
}),
|
||||||
|
loadNuxt({
|
||||||
|
cwd: repoRoot,
|
||||||
|
}),
|
||||||
|
])
|
||||||
|
expect(loggerWarn).not.toHaveBeenCalled()
|
||||||
|
})
|
||||||
|
|
||||||
|
it('expect hooks to get the correct context outside of initNuxt', async () => {
|
||||||
|
const nuxt = await loadNuxt({
|
||||||
|
cwd: repoRoot,
|
||||||
|
})
|
||||||
|
|
||||||
|
// @ts-expect-error - random hook
|
||||||
|
nuxt.hook('test', () => {
|
||||||
|
expect(useNuxt().__name).toBe(nuxt.__name)
|
||||||
|
})
|
||||||
|
|
||||||
|
expect(tryUseNuxt()?.__name).not.toBe(nuxt.__name)
|
||||||
|
|
||||||
|
// second nuxt context
|
||||||
|
const second = await loadNuxt({
|
||||||
|
cwd: repoRoot,
|
||||||
|
})
|
||||||
|
|
||||||
|
expect(second.__name).not.toBe(nuxt.__name)
|
||||||
|
expect(tryUseNuxt()?.__name).not.toBe(nuxt.__name)
|
||||||
|
|
||||||
|
// @ts-expect-error - random hook
|
||||||
|
await nuxt.callHook('test')
|
||||||
|
|
||||||
|
expect(loggerWarn).not.toHaveBeenCalled()
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
@ -32,13 +32,13 @@
|
|||||||
"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.2.2",
|
"@rspack/core": "^1.2.3",
|
||||||
"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",
|
||||||
"cssnano": "^7.0.6",
|
"cssnano": "^7.0.6",
|
||||||
"defu": "^6.1.4",
|
"defu": "^6.1.4",
|
||||||
"esbuild-loader": "^4.2.2",
|
"esbuild-loader": "^4.3.0",
|
||||||
"escape-string-regexp": "^5.0.0",
|
"escape-string-regexp": "^5.0.0",
|
||||||
"file-loader": "^6.2.0",
|
"file-loader": "^6.2.0",
|
||||||
"fork-ts-checker-webpack-plugin": "^9.0.2",
|
"fork-ts-checker-webpack-plugin": "^9.0.2",
|
||||||
@ -51,7 +51,7 @@
|
|||||||
"ohash": "^1.1.4",
|
"ohash": "^1.1.4",
|
||||||
"pathe": "^2.0.2",
|
"pathe": "^2.0.2",
|
||||||
"pify": "^6.1.0",
|
"pify": "^6.1.0",
|
||||||
"postcss": "^8.5.1",
|
"postcss": "^8.5.2",
|
||||||
"postcss-import": "^16.1.0",
|
"postcss-import": "^16.1.0",
|
||||||
"postcss-import-resolver": "^2.0.0",
|
"postcss-import-resolver": "^2.0.0",
|
||||||
"postcss-loader": "^8.1.1",
|
"postcss-loader": "^8.1.1",
|
||||||
@ -61,7 +61,7 @@
|
|||||||
"time-fix-plugin": "^2.0.7",
|
"time-fix-plugin": "^2.0.7",
|
||||||
"ufo": "^1.5.4",
|
"ufo": "^1.5.4",
|
||||||
"unenv": "^1.10.0",
|
"unenv": "^1.10.0",
|
||||||
"unplugin": "^2.1.2",
|
"unplugin": "^2.2.0",
|
||||||
"url-loader": "^4.1.1",
|
"url-loader": "^4.1.1",
|
||||||
"vue-bundle-renderer": "^2.1.1",
|
"vue-bundle-renderer": "^2.1.1",
|
||||||
"vue-loader": "^17.4.2",
|
"vue-loader": "^17.4.2",
|
||||||
@ -83,6 +83,6 @@
|
|||||||
"vue": "^3.3.4"
|
"vue": "^3.3.4"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": "^18.20.6 || ^20.9.0 || >=22.0.0"
|
"node": "^18.12.0 || ^20.9.0 || >=22.0.0"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -39,6 +39,7 @@ export default defineBuildConfig({
|
|||||||
'consola',
|
'consola',
|
||||||
'css-minimizer-webpack-plugin',
|
'css-minimizer-webpack-plugin',
|
||||||
'cssnano',
|
'cssnano',
|
||||||
|
'esbuild',
|
||||||
'esbuild-loader',
|
'esbuild-loader',
|
||||||
'file-loader',
|
'file-loader',
|
||||||
'h3',
|
'h3',
|
||||||
|
@ -37,28 +37,35 @@
|
|||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/pug": "2.0.10",
|
"@types/pug": "2.0.10",
|
||||||
|
"@types/rollup-plugin-visualizer": "4.2.4",
|
||||||
|
"@types/webpack-bundle-analyzer": "4.7.0",
|
||||||
|
"@types/webpack-hot-middleware": "2.25.9",
|
||||||
"@unhead/schema": "2.0.0-alpha.9",
|
"@unhead/schema": "2.0.0-alpha.9",
|
||||||
"@vitejs/plugin-vue": "5.2.1",
|
"@vitejs/plugin-vue": "5.2.1",
|
||||||
"@vitejs/plugin-vue-jsx": "4.1.1",
|
"@vitejs/plugin-vue-jsx": "4.1.1",
|
||||||
"@vue/compiler-core": "3.5.13",
|
"@vue/compiler-core": "3.5.13",
|
||||||
"@vue/compiler-sfc": "3.5.13",
|
"@vue/compiler-sfc": "3.5.13",
|
||||||
"@vue/language-core": "2.2.0",
|
"@vue/language-core": "2.2.0",
|
||||||
"c12": "2.0.1",
|
"c12": "2.0.2",
|
||||||
"chokidar": "4.0.3",
|
"chokidar": "4.0.3",
|
||||||
"compatx": "0.1.8",
|
"compatx": "0.1.8",
|
||||||
"esbuild-loader": "4.2.2",
|
"css-minimizer-webpack-plugin": "7.0.0",
|
||||||
|
"esbuild": "0.25.0",
|
||||||
|
"esbuild-loader": "4.3.0",
|
||||||
"file-loader": "6.2.0",
|
"file-loader": "6.2.0",
|
||||||
"h3": "npm:h3-nightly@1.14.0-20250122-114730-3f9e703",
|
"h3": "npm:h3-nightly@1.14.0-20250122-114730-3f9e703",
|
||||||
"hookable": "5.5.3",
|
"hookable": "5.5.3",
|
||||||
"ignore": "7.0.3",
|
"ignore": "7.0.3",
|
||||||
|
"mini-css-extract-plugin": "2.9.2",
|
||||||
"nitro": "npm:nitro-nightly@3.0.0-beta-28969273.f7aa9de6",
|
"nitro": "npm:nitro-nightly@3.0.0-beta-28969273.f7aa9de6",
|
||||||
"ofetch": "1.4.1",
|
"ofetch": "1.4.1",
|
||||||
"pkg-types": "1.3.1",
|
"pkg-types": "1.3.1",
|
||||||
|
"postcss": "8.5.2",
|
||||||
"sass-loader": "16.0.4",
|
"sass-loader": "16.0.4",
|
||||||
"scule": "1.3.0",
|
"scule": "1.3.0",
|
||||||
"unbuild": "3.3.1",
|
"unbuild": "3.3.1",
|
||||||
"unctx": "2.4.1",
|
"unctx": "2.4.1",
|
||||||
"unimport": "4.1.0",
|
"unimport": "4.1.1",
|
||||||
"untyped": "1.5.2",
|
"untyped": "1.5.2",
|
||||||
"vite": "6.1.0",
|
"vite": "6.1.0",
|
||||||
"vue": "3.5.13",
|
"vue": "3.5.13",
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import { defineUntypedSchema } from 'untyped'
|
import { defineResolvers } from '../utils/definition'
|
||||||
|
|
||||||
export default defineUntypedSchema({
|
export default defineResolvers({
|
||||||
/**
|
/**
|
||||||
* Configure Nuxt component auto-registration.
|
* Configure Nuxt component auto-registration.
|
||||||
*
|
*
|
||||||
@ -14,10 +14,13 @@ export default defineUntypedSchema({
|
|||||||
if (Array.isArray(val)) {
|
if (Array.isArray(val)) {
|
||||||
return { dirs: val }
|
return { dirs: val }
|
||||||
}
|
}
|
||||||
if (val === undefined || val === true) {
|
if (val === false) {
|
||||||
return { dirs: [{ path: '~/components/global', global: true }, '~/components'] }
|
return { dirs: [] }
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
dirs: [{ path: '~/components/global', global: true }, '~/components'],
|
||||||
|
...typeof val === 'object' ? val : {},
|
||||||
}
|
}
|
||||||
return val
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -1,9 +1,10 @@
|
|||||||
import { defineUntypedSchema } from 'untyped'
|
|
||||||
import { defu } from 'defu'
|
import { defu } from 'defu'
|
||||||
import { resolve } from 'pathe'
|
import { resolve } from 'pathe'
|
||||||
|
import { defineResolvers } from '../utils/definition'
|
||||||
import type { AppHeadMetaObject } from '../types/head'
|
import type { AppHeadMetaObject } from '../types/head'
|
||||||
|
import type { NuxtAppConfig } from '../types/config'
|
||||||
|
|
||||||
export default defineUntypedSchema({
|
export default defineResolvers({
|
||||||
/**
|
/**
|
||||||
* Vue.js config
|
* Vue.js config
|
||||||
*/
|
*/
|
||||||
@ -27,7 +28,18 @@ export default defineUntypedSchema({
|
|||||||
* Include Vue compiler in runtime bundle.
|
* Include Vue compiler in runtime bundle.
|
||||||
*/
|
*/
|
||||||
runtimeCompiler: {
|
runtimeCompiler: {
|
||||||
$resolve: async (val, get) => val ?? await get('experimental.runtimeVueCompiler') ?? false,
|
$resolve: async (val, get) => {
|
||||||
|
if (typeof val === 'boolean') {
|
||||||
|
return val
|
||||||
|
}
|
||||||
|
// @ts-expect-error TODO: formally deprecate in v4
|
||||||
|
const legacyProperty = await get('experimental.runtimeVueCompiler') as unknown
|
||||||
|
if (typeof legacyProperty === 'boolean') {
|
||||||
|
return legacyProperty
|
||||||
|
}
|
||||||
|
|
||||||
|
return false
|
||||||
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -41,7 +53,7 @@ export default defineUntypedSchema({
|
|||||||
* may be set in your `nuxt.config`. All other options should be set at runtime in a Nuxt plugin..
|
* may be set in your `nuxt.config`. All other options should be set at runtime in a Nuxt plugin..
|
||||||
* @see [Vue app config documentation](https://vuejs.org/api/application.html#app-config)
|
* @see [Vue app config documentation](https://vuejs.org/api/application.html#app-config)
|
||||||
*/
|
*/
|
||||||
config: undefined,
|
config: {},
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -68,12 +80,22 @@ export default defineUntypedSchema({
|
|||||||
* ```
|
* ```
|
||||||
*/
|
*/
|
||||||
baseURL: {
|
baseURL: {
|
||||||
$resolve: val => val || process.env.NUXT_APP_BASE_URL || '/',
|
$resolve: (val) => {
|
||||||
|
if (typeof val === 'string') {
|
||||||
|
return val
|
||||||
|
}
|
||||||
|
return process.env.NUXT_APP_BASE_URL || '/'
|
||||||
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
/** The folder name for the built site assets, relative to `baseURL` (or `cdnURL` if set). This is set at build time and should not be customized at runtime. */
|
/** The folder name for the built site assets, relative to `baseURL` (or `cdnURL` if set). This is set at build time and should not be customized at runtime. */
|
||||||
buildAssetsDir: {
|
buildAssetsDir: {
|
||||||
$resolve: val => val || process.env.NUXT_APP_BUILD_ASSETS_DIR || '/_nuxt/',
|
$resolve: (val) => {
|
||||||
|
if (typeof val === 'string') {
|
||||||
|
return val
|
||||||
|
}
|
||||||
|
return process.env.NUXT_APP_BUILD_ASSETS_DIR || '/_nuxt/'
|
||||||
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -96,7 +118,12 @@ export default defineUntypedSchema({
|
|||||||
* ```
|
* ```
|
||||||
*/
|
*/
|
||||||
cdnURL: {
|
cdnURL: {
|
||||||
$resolve: async (val, get) => (await get('dev')) ? '' : (process.env.NUXT_APP_CDN_URL ?? val) || '',
|
$resolve: async (val, get) => {
|
||||||
|
if (await get('dev')) {
|
||||||
|
return ''
|
||||||
|
}
|
||||||
|
return process.env.NUXT_APP_CDN_URL || (typeof val === 'string' ? val : '')
|
||||||
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -132,14 +159,20 @@ export default defineUntypedSchema({
|
|||||||
* @type {typeof import('../src/types/config').NuxtAppConfig['head']}
|
* @type {typeof import('../src/types/config').NuxtAppConfig['head']}
|
||||||
*/
|
*/
|
||||||
head: {
|
head: {
|
||||||
$resolve: async (val: Partial<AppHeadMetaObject> | undefined, get) => {
|
$resolve: async (_val, get) => {
|
||||||
const resolved = defu(val, await get('meta') as Partial<AppHeadMetaObject>, {
|
// @ts-expect-error TODO: remove in Nuxt v4
|
||||||
|
const legacyMetaValues = await get('meta') as Record<string, unknown>
|
||||||
|
const val: Partial<NuxtAppConfig['head']> = _val && typeof _val === 'object' ? _val : {}
|
||||||
|
|
||||||
|
type NormalizedMetaObject = Required<Pick<AppHeadMetaObject, 'meta' | 'link' | 'style' | 'script' | 'noscript'>>
|
||||||
|
|
||||||
|
const resolved: NuxtAppConfig['head'] & NormalizedMetaObject = defu(val, legacyMetaValues, {
|
||||||
meta: [],
|
meta: [],
|
||||||
link: [],
|
link: [],
|
||||||
style: [],
|
style: [],
|
||||||
script: [],
|
script: [],
|
||||||
noscript: [],
|
noscript: [],
|
||||||
} as Required<Pick<AppHeadMetaObject, 'meta' | 'link' | 'style' | 'script' | 'noscript'>>)
|
} satisfies NormalizedMetaObject)
|
||||||
|
|
||||||
// provides default charset and viewport if not set
|
// provides default charset and viewport if not set
|
||||||
if (!resolved.meta.find(m => m.charset)?.charset) {
|
if (!resolved.meta.find(m => m.charset)?.charset) {
|
||||||
@ -190,9 +223,13 @@ export default defineUntypedSchema({
|
|||||||
* @type {typeof import('../src/types/config').NuxtAppConfig['viewTransition']}
|
* @type {typeof import('../src/types/config').NuxtAppConfig['viewTransition']}
|
||||||
*/
|
*/
|
||||||
viewTransition: {
|
viewTransition: {
|
||||||
$resolve: async (val, get) => val ?? await (get('experimental') as Promise<Record<string, any>>).then(
|
$resolve: async (val, get) => {
|
||||||
e => e?.viewTransition,
|
if (val === 'always' || typeof val === 'boolean') {
|
||||||
) ?? false,
|
return val
|
||||||
|
}
|
||||||
|
|
||||||
|
return await get('experimental').then(e => e.viewTransition) ?? false
|
||||||
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -211,14 +248,14 @@ export default defineUntypedSchema({
|
|||||||
* @deprecated Prefer `rootAttrs.id` instead
|
* @deprecated Prefer `rootAttrs.id` instead
|
||||||
*/
|
*/
|
||||||
rootId: {
|
rootId: {
|
||||||
$resolve: val => val === false ? false : (val || '__nuxt'),
|
$resolve: val => val === false ? false : (val && typeof val === 'string' ? val : '__nuxt'),
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Customize Nuxt root element tag.
|
* Customize Nuxt root element tag.
|
||||||
*/
|
*/
|
||||||
rootTag: {
|
rootTag: {
|
||||||
$resolve: val => val || 'div',
|
$resolve: val => val && typeof val === 'string' ? val : 'div',
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -226,11 +263,12 @@ export default defineUntypedSchema({
|
|||||||
* @type {typeof import('@unhead/schema').HtmlAttributes}
|
* @type {typeof import('@unhead/schema').HtmlAttributes}
|
||||||
*/
|
*/
|
||||||
rootAttrs: {
|
rootAttrs: {
|
||||||
$resolve: async (val: undefined | null | Record<string, unknown>, get) => {
|
$resolve: async (val, get) => {
|
||||||
const rootId = await get('app.rootId')
|
const rootId = await get('app.rootId')
|
||||||
return defu(val, {
|
return {
|
||||||
id: rootId === false ? undefined : (rootId || '__nuxt'),
|
id: rootId === false ? undefined : (rootId || '__nuxt'),
|
||||||
})
|
...typeof val === 'object' ? val : {},
|
||||||
|
}
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
@ -238,7 +276,7 @@ export default defineUntypedSchema({
|
|||||||
* Customize Nuxt Teleport element tag.
|
* Customize Nuxt Teleport element tag.
|
||||||
*/
|
*/
|
||||||
teleportTag: {
|
teleportTag: {
|
||||||
$resolve: val => val || 'div',
|
$resolve: val => val && typeof val === 'string' ? val : 'div',
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -247,7 +285,7 @@ export default defineUntypedSchema({
|
|||||||
* @deprecated Prefer `teleportAttrs.id` instead
|
* @deprecated Prefer `teleportAttrs.id` instead
|
||||||
*/
|
*/
|
||||||
teleportId: {
|
teleportId: {
|
||||||
$resolve: val => val === false ? false : (val || 'teleports'),
|
$resolve: val => val === false ? false : (val && typeof val === 'string' ? val : 'teleports'),
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -255,11 +293,12 @@ export default defineUntypedSchema({
|
|||||||
* @type {typeof import('@unhead/schema').HtmlAttributes}
|
* @type {typeof import('@unhead/schema').HtmlAttributes}
|
||||||
*/
|
*/
|
||||||
teleportAttrs: {
|
teleportAttrs: {
|
||||||
$resolve: async (val: undefined | null | Record<string, unknown>, get) => {
|
$resolve: async (val, get) => {
|
||||||
const teleportId = await get('app.teleportId')
|
const teleportId = await get('app.teleportId')
|
||||||
return defu(val, {
|
return {
|
||||||
id: teleportId === false ? undefined : (teleportId || 'teleports'),
|
id: teleportId === false ? undefined : (teleportId || 'teleports'),
|
||||||
})
|
...typeof val === 'object' ? val : {},
|
||||||
|
}
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
@ -267,12 +306,12 @@ export default defineUntypedSchema({
|
|||||||
* Customize Nuxt SpaLoader element tag.
|
* Customize Nuxt SpaLoader element tag.
|
||||||
*/
|
*/
|
||||||
spaLoaderTag: {
|
spaLoaderTag: {
|
||||||
$resolve: val => val || 'div',
|
$resolve: val => val && typeof val === 'string' ? val : 'div',
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Customize Nuxt Nuxt SpaLoader element attributes.
|
* Customize Nuxt Nuxt SpaLoader element attributes.
|
||||||
* @type {typeof import('@unhead/schema').HtmlAttributes}
|
* @type {Partial<typeof import('@unhead/schema').HtmlAttributes>}
|
||||||
*/
|
*/
|
||||||
spaLoaderAttrs: {
|
spaLoaderAttrs: {
|
||||||
id: '__nuxt-loader',
|
id: '__nuxt-loader',
|
||||||
@ -332,10 +371,18 @@ export default defineUntypedSchema({
|
|||||||
* }
|
* }
|
||||||
* </style>
|
* </style>
|
||||||
* ```
|
* ```
|
||||||
* @type {string | boolean}
|
* @type {string | boolean | undefined | null}
|
||||||
*/
|
*/
|
||||||
spaLoadingTemplate: {
|
spaLoadingTemplate: {
|
||||||
$resolve: async (val: string | boolean | undefined, get) => typeof val === 'string' ? resolve(await get('srcDir') as string, val) : val ?? null,
|
$resolve: async (val, get) => {
|
||||||
|
if (typeof val === 'string') {
|
||||||
|
return resolve(await get('srcDir'), val)
|
||||||
|
}
|
||||||
|
if (typeof val === 'boolean') {
|
||||||
|
return val
|
||||||
|
}
|
||||||
|
return null
|
||||||
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -386,7 +433,21 @@ export default defineUntypedSchema({
|
|||||||
* @type {string[]}
|
* @type {string[]}
|
||||||
*/
|
*/
|
||||||
css: {
|
css: {
|
||||||
$resolve: (val: string[] | undefined) => (val ?? []).map((c: any) => c.src || c),
|
$resolve: (val) => {
|
||||||
|
if (!Array.isArray(val)) {
|
||||||
|
return []
|
||||||
|
}
|
||||||
|
const css: string[] = []
|
||||||
|
for (const item of val) {
|
||||||
|
if (typeof item === 'string') {
|
||||||
|
css.push(item)
|
||||||
|
} else if (item && 'src' in item) {
|
||||||
|
// TODO: remove in Nuxt v4
|
||||||
|
css.push(item.src)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return css
|
||||||
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -410,12 +471,13 @@ export default defineUntypedSchema({
|
|||||||
* @type {typeof import('@unhead/schema').RenderSSRHeadOptions}
|
* @type {typeof import('@unhead/schema').RenderSSRHeadOptions}
|
||||||
*/
|
*/
|
||||||
renderSSRHeadOptions: {
|
renderSSRHeadOptions: {
|
||||||
$resolve: async (val: Record<string, unknown> | undefined, get) => {
|
$resolve: async (val, get) => {
|
||||||
const isV4 = ((await get('future') as Record<string, unknown>).compatibilityVersion === 4)
|
const isV4 = (await get('future')).compatibilityVersion === 4
|
||||||
|
|
||||||
return defu(val, {
|
return {
|
||||||
|
...typeof val === 'object' ? val : {},
|
||||||
omitLineBreaks: isV4,
|
omitLineBreaks: isV4,
|
||||||
})
|
}
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@ -1,25 +1,35 @@
|
|||||||
import { defineUntypedSchema } from 'untyped'
|
|
||||||
import { defu } from 'defu'
|
import { defu } from 'defu'
|
||||||
import { join } from 'pathe'
|
import { join } from 'pathe'
|
||||||
import { isTest } from 'std-env'
|
import { isTest } from 'std-env'
|
||||||
import { consola } from 'consola'
|
import { consola } from 'consola'
|
||||||
|
import type { Nuxt } from 'nuxt/schema'
|
||||||
|
import { defineResolvers } from '../utils/definition'
|
||||||
|
|
||||||
export default defineUntypedSchema({
|
export default defineResolvers({
|
||||||
/**
|
/**
|
||||||
* The builder to use for bundling the Vue part of your application.
|
* The builder to use for bundling the Vue part of your application.
|
||||||
* @type {'vite' | 'webpack' | 'rspack' | { bundle: (nuxt: typeof import('../src/types/nuxt').Nuxt) => Promise<void> }}
|
* @type {'vite' | 'webpack' | 'rspack' | { bundle: (nuxt: typeof import('../src/types/nuxt').Nuxt) => Promise<void> }}
|
||||||
*/
|
*/
|
||||||
builder: {
|
builder: {
|
||||||
$resolve: async (val: 'vite' | 'webpack' | 'rspack' | { bundle: (nuxt: unknown) => Promise<void> } | undefined = 'vite', get) => {
|
$resolve: async (val, get) => {
|
||||||
if (typeof val === 'object') {
|
if (val && typeof val === 'object' && 'bundle' in val) {
|
||||||
return val
|
return val as { bundle: (nuxt: Nuxt) => Promise<void> }
|
||||||
}
|
}
|
||||||
const map: Record<string, string> = {
|
const map = {
|
||||||
rspack: '@nuxt/rspack-builder',
|
rspack: '@nuxt/rspack-builder',
|
||||||
vite: '@nuxt/vite-builder',
|
vite: '@nuxt/vite-builder',
|
||||||
webpack: '@nuxt/webpack-builder',
|
webpack: '@nuxt/webpack-builder',
|
||||||
}
|
}
|
||||||
return map[val] || val || (await get('vite') === false ? map.webpack : map.vite)
|
type Builder = 'vite' | 'webpack' | 'rspack'
|
||||||
|
if (typeof val === 'string' && val in map) {
|
||||||
|
// TODO: improve normalisation inference
|
||||||
|
return map[val as keyof typeof map] as Builder
|
||||||
|
}
|
||||||
|
// @ts-expect-error TODO: remove old, unsupported config in v4
|
||||||
|
if (await get('vite') === false) {
|
||||||
|
return map.webpack as Builder
|
||||||
|
}
|
||||||
|
return map.vite as Builder
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
@ -37,14 +47,15 @@ export default defineUntypedSchema({
|
|||||||
* @type {boolean | { server?: boolean | 'hidden', client?: boolean | 'hidden' }}
|
* @type {boolean | { server?: boolean | 'hidden', client?: boolean | 'hidden' }}
|
||||||
*/
|
*/
|
||||||
sourcemap: {
|
sourcemap: {
|
||||||
$resolve: async (val: boolean | { server?: boolean | 'hidden', client?: boolean | 'hidden' } | undefined, get) => {
|
$resolve: async (val, get) => {
|
||||||
if (typeof val === 'boolean') {
|
if (typeof val === 'boolean') {
|
||||||
return { server: val, client: val }
|
return { server: val, client: val }
|
||||||
}
|
}
|
||||||
return defu(val, {
|
return {
|
||||||
server: true,
|
server: true,
|
||||||
client: await get('dev'),
|
client: await get('dev'),
|
||||||
})
|
...typeof val === 'object' ? val : {},
|
||||||
|
}
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
@ -56,11 +67,11 @@ export default defineUntypedSchema({
|
|||||||
* @type {'silent' | 'info' | 'verbose'}
|
* @type {'silent' | 'info' | 'verbose'}
|
||||||
*/
|
*/
|
||||||
logLevel: {
|
logLevel: {
|
||||||
$resolve: (val: string | undefined) => {
|
$resolve: (val) => {
|
||||||
if (val && !['silent', 'info', 'verbose'].includes(val)) {
|
if (val && typeof val === 'string' && !['silent', 'info', 'verbose'].includes(val)) {
|
||||||
consola.warn(`Invalid \`logLevel\` option: \`${val}\`. Must be one of: \`silent\`, \`info\`, \`verbose\`.`)
|
consola.warn(`Invalid \`logLevel\` option: \`${val}\`. Must be one of: \`silent\`, \`info\`, \`verbose\`.`)
|
||||||
}
|
}
|
||||||
return val ?? (isTest ? 'silent' : 'info')
|
return val && typeof val === 'string' ? val as 'silent' | 'info' | 'verbose' : (isTest ? 'silent' : 'info')
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
@ -81,7 +92,20 @@ export default defineUntypedSchema({
|
|||||||
* @type {Array<string | RegExp | ((ctx: { isClient?: boolean; isServer?: boolean; isDev: boolean }) => string | RegExp | false)>}
|
* @type {Array<string | RegExp | ((ctx: { isClient?: boolean; isServer?: boolean; isDev: boolean }) => string | RegExp | false)>}
|
||||||
*/
|
*/
|
||||||
transpile: {
|
transpile: {
|
||||||
$resolve: (val: Array<string | RegExp | ((ctx: { isClient?: boolean, isServer?: boolean, isDev: boolean }) => string | RegExp | false)> | undefined) => (val || []).filter(Boolean),
|
$resolve: (val) => {
|
||||||
|
const transpile: Array<string | RegExp | ((ctx: { isClient?: boolean, isServer?: boolean, isDev: boolean }) => string | RegExp | false)> = []
|
||||||
|
if (Array.isArray(val)) {
|
||||||
|
for (const pattern of val) {
|
||||||
|
if (!pattern) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if (typeof pattern === 'string' || typeof pattern === 'function' || pattern instanceof RegExp) {
|
||||||
|
transpile.push(pattern)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return transpile
|
||||||
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -110,16 +134,17 @@ export default defineUntypedSchema({
|
|||||||
* analyzerMode: 'static'
|
* analyzerMode: 'static'
|
||||||
* }
|
* }
|
||||||
* ```
|
* ```
|
||||||
* @type {boolean | { enabled?: boolean } & ((0 extends 1 & typeof import('webpack-bundle-analyzer').BundleAnalyzerPlugin.Options ? {} : typeof import('webpack-bundle-analyzer').BundleAnalyzerPlugin.Options) | typeof import('rollup-plugin-visualizer').PluginVisualizerOptions)}
|
* @type {boolean | { enabled?: boolean } & ((0 extends 1 & typeof import('webpack-bundle-analyzer').BundleAnalyzerPlugin.Options ? Record<string, unknown> : typeof import('webpack-bundle-analyzer').BundleAnalyzerPlugin.Options) | typeof import('rollup-plugin-visualizer').PluginVisualizerOptions)}
|
||||||
*/
|
*/
|
||||||
analyze: {
|
analyze: {
|
||||||
$resolve: async (val: boolean | { enabled?: boolean } | Record<string, unknown>, get) => {
|
$resolve: async (val, get) => {
|
||||||
const [rootDir, analyzeDir] = await Promise.all([get('rootDir'), get('analyzeDir')]) as [string, string]
|
const [rootDir, analyzeDir] = await Promise.all([get('rootDir'), get('analyzeDir')])
|
||||||
return defu(typeof val === 'boolean' ? { enabled: val } : val, {
|
return {
|
||||||
template: 'treemap',
|
template: 'treemap',
|
||||||
projectRoot: rootDir,
|
projectRoot: rootDir,
|
||||||
filename: join(analyzeDir, '{name}.html'),
|
filename: join(analyzeDir, '{name}.html'),
|
||||||
})
|
...typeof val === 'boolean' ? { enabled: val } : typeof val === 'object' ? val : {},
|
||||||
|
}
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@ -139,7 +164,7 @@ export default defineUntypedSchema({
|
|||||||
* @type {Array<{ name: string, source?: string | RegExp, argumentLength: number }>}
|
* @type {Array<{ name: string, source?: string | RegExp, argumentLength: number }>}
|
||||||
*/
|
*/
|
||||||
keyedComposables: {
|
keyedComposables: {
|
||||||
$resolve: (val: Array<{ name: string, argumentLength: string }> | undefined) => [
|
$resolve: val => [
|
||||||
{ name: 'callOnce', argumentLength: 3 },
|
{ name: 'callOnce', argumentLength: 3 },
|
||||||
{ name: 'defineNuxtComponent', argumentLength: 2 },
|
{ name: 'defineNuxtComponent', argumentLength: 2 },
|
||||||
{ name: 'useState', argumentLength: 2 },
|
{ name: 'useState', argumentLength: 2 },
|
||||||
@ -147,7 +172,7 @@ export default defineUntypedSchema({
|
|||||||
{ name: 'useAsyncData', argumentLength: 3 },
|
{ name: 'useAsyncData', argumentLength: 3 },
|
||||||
{ name: 'useLazyAsyncData', argumentLength: 3 },
|
{ name: 'useLazyAsyncData', argumentLength: 3 },
|
||||||
{ name: 'useLazyFetch', argumentLength: 3 },
|
{ name: 'useLazyFetch', argumentLength: 3 },
|
||||||
...val || [],
|
...Array.isArray(val) ? val : [],
|
||||||
].filter(Boolean),
|
].filter(Boolean),
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -1,16 +1,16 @@
|
|||||||
import { existsSync } from 'node:fs'
|
import { existsSync } from 'node:fs'
|
||||||
import { readdir } from 'node:fs/promises'
|
import { readdir } from 'node:fs/promises'
|
||||||
import { randomUUID } from 'node:crypto'
|
import { randomUUID } from 'node:crypto'
|
||||||
import { defineUntypedSchema } from 'untyped'
|
|
||||||
import { basename, join, relative, resolve } from 'pathe'
|
import { basename, join, relative, resolve } from 'pathe'
|
||||||
import { isDebug, isDevelopment, isTest } from 'std-env'
|
import { isDebug, isDevelopment, isTest } from 'std-env'
|
||||||
import { defu } from 'defu'
|
import { defu } from 'defu'
|
||||||
import { findWorkspaceDir } from 'pkg-types'
|
import { findWorkspaceDir } from 'pkg-types'
|
||||||
|
|
||||||
import type { RuntimeConfig } from '../types/config'
|
|
||||||
import type { NuxtDebugOptions } from '../types/debug'
|
import type { NuxtDebugOptions } from '../types/debug'
|
||||||
|
import type { NuxtModule } from '../types/module'
|
||||||
|
import { defineResolvers } from '../utils/definition'
|
||||||
|
|
||||||
export default defineUntypedSchema({
|
export default defineResolvers({
|
||||||
/**
|
/**
|
||||||
* Extend project from multiple local or remote sources.
|
* Extend project from multiple local or remote sources.
|
||||||
*
|
*
|
||||||
@ -21,7 +21,7 @@ export default defineUntypedSchema({
|
|||||||
* @see [`giget` documentation](https://github.com/unjs/giget)
|
* @see [`giget` documentation](https://github.com/unjs/giget)
|
||||||
* @type {string | [string, typeof import('c12').SourceOptions?] | (string | [string, typeof import('c12').SourceOptions?])[]}
|
* @type {string | [string, typeof import('c12').SourceOptions?] | (string | [string, typeof import('c12').SourceOptions?])[]}
|
||||||
*/
|
*/
|
||||||
extends: null,
|
extends: undefined,
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Specify a compatibility date for your app.
|
* Specify a compatibility date for your app.
|
||||||
@ -43,7 +43,7 @@ export default defineUntypedSchema({
|
|||||||
* You can use `github:`, `gitlab:`, `bitbucket:` or `https://` to extend from a remote git repository.
|
* You can use `github:`, `gitlab:`, `bitbucket:` or `https://` to extend from a remote git repository.
|
||||||
* @type {string}
|
* @type {string}
|
||||||
*/
|
*/
|
||||||
theme: null,
|
theme: undefined,
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Define the root directory of your application.
|
* Define the root directory of your application.
|
||||||
@ -67,9 +67,9 @@ export default defineUntypedSchema({
|
|||||||
* It is normally not needed to configure this option.
|
* It is normally not needed to configure this option.
|
||||||
*/
|
*/
|
||||||
workspaceDir: {
|
workspaceDir: {
|
||||||
$resolve: async (val: string | undefined, get): Promise<string> => {
|
$resolve: async (val, get) => {
|
||||||
const rootDir = await get('rootDir') as string
|
const rootDir = await get('rootDir')
|
||||||
return val ? resolve(rootDir, val) : await findWorkspaceDir(rootDir).catch(() => rootDir)
|
return val && typeof val === 'string' ? resolve(rootDir, val) : await findWorkspaceDir(rootDir).catch(() => rootDir)
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
@ -105,14 +105,14 @@ export default defineUntypedSchema({
|
|||||||
* ```
|
* ```
|
||||||
*/
|
*/
|
||||||
srcDir: {
|
srcDir: {
|
||||||
$resolve: async (val: string | undefined, get): Promise<string> => {
|
$resolve: async (val, get) => {
|
||||||
if (val) {
|
if (val && typeof val === 'string') {
|
||||||
return resolve(await get('rootDir') as string, val)
|
return resolve(await get('rootDir'), val)
|
||||||
}
|
}
|
||||||
|
|
||||||
const [rootDir, isV4] = await Promise.all([
|
const [rootDir, isV4] = await Promise.all([
|
||||||
get('rootDir') as Promise<string>,
|
get('rootDir'),
|
||||||
(get('future') as Promise<Record<string, unknown>>).then(r => r.compatibilityVersion === 4),
|
get('future').then(r => r.compatibilityVersion === 4),
|
||||||
])
|
])
|
||||||
|
|
||||||
if (!isV4) {
|
if (!isV4) {
|
||||||
@ -138,7 +138,7 @@ export default defineUntypedSchema({
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
const keys = ['assets', 'layouts', 'middleware', 'pages', 'plugins'] as const
|
const keys = ['assets', 'layouts', 'middleware', 'pages', 'plugins'] as const
|
||||||
const dirs = await Promise.all(keys.map(key => get(`dir.${key}`) as Promise<string>))
|
const dirs = await Promise.all(keys.map(key => get(`dir.${key}`)))
|
||||||
for (const dir of dirs) {
|
for (const dir of dirs) {
|
||||||
if (existsSync(resolve(rootDir, dir))) {
|
if (existsSync(resolve(rootDir, dir))) {
|
||||||
return rootDir
|
return rootDir
|
||||||
@ -157,13 +157,13 @@ export default defineUntypedSchema({
|
|||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
serverDir: {
|
serverDir: {
|
||||||
$resolve: async (val: string | undefined, get): Promise<string> => {
|
$resolve: async (val, get) => {
|
||||||
if (val) {
|
if (val && typeof val === 'string') {
|
||||||
const rootDir = await get('rootDir') as string
|
const rootDir = await get('rootDir')
|
||||||
return resolve(rootDir, val)
|
return resolve(rootDir, val)
|
||||||
}
|
}
|
||||||
const isV4 = (await get('future') as Record<string, unknown>).compatibilityVersion === 4
|
const isV4 = (await get('future')).compatibilityVersion === 4
|
||||||
return join(isV4 ? await get('rootDir') as string : await get('srcDir') as string, 'server')
|
return join(isV4 ? await get('rootDir') : await get('srcDir'), 'server')
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
@ -180,9 +180,9 @@ export default defineUntypedSchema({
|
|||||||
* ```
|
* ```
|
||||||
*/
|
*/
|
||||||
buildDir: {
|
buildDir: {
|
||||||
$resolve: async (val: string | undefined, get) => {
|
$resolve: async (val, get) => {
|
||||||
const rootDir = await get('rootDir') as string
|
const rootDir = await get('rootDir')
|
||||||
return resolve(rootDir, val ?? '.nuxt')
|
return resolve(rootDir, val && typeof val === 'string' ? val : '.nuxt')
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
@ -192,14 +192,14 @@ export default defineUntypedSchema({
|
|||||||
* Defaults to `nuxt-app`.
|
* Defaults to `nuxt-app`.
|
||||||
*/
|
*/
|
||||||
appId: {
|
appId: {
|
||||||
$resolve: (val: string) => val ?? 'nuxt-app',
|
$resolve: val => val && typeof val === 'string' ? val : 'nuxt-app',
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A unique identifier matching the build. This may contain the hash of the current state of the project.
|
* A unique identifier matching the build. This may contain the hash of the current state of the project.
|
||||||
*/
|
*/
|
||||||
buildId: {
|
buildId: {
|
||||||
$resolve: async (val: string | undefined, get): Promise<string> => {
|
$resolve: async (val, get): Promise<string> => {
|
||||||
if (typeof val === 'string') { return val }
|
if (typeof val === 'string') { return val }
|
||||||
|
|
||||||
const [isDev, isTest] = await Promise.all([get('dev') as Promise<boolean>, get('test') as Promise<boolean>])
|
const [isDev, isTest] = await Promise.all([get('dev') as Promise<boolean>, get('test') as Promise<boolean>])
|
||||||
@ -223,12 +223,17 @@ export default defineUntypedSchema({
|
|||||||
*/
|
*/
|
||||||
modulesDir: {
|
modulesDir: {
|
||||||
$default: ['node_modules'],
|
$default: ['node_modules'],
|
||||||
$resolve: async (val: string[] | undefined, get): Promise<string[]> => {
|
$resolve: async (val, get) => {
|
||||||
const rootDir = await get('rootDir') as string
|
const rootDir = await get('rootDir')
|
||||||
return [...new Set([
|
const modulesDir = new Set<string>([resolve(rootDir, 'node_modules')])
|
||||||
...(val || []).map((dir: string) => resolve(rootDir, dir)),
|
if (Array.isArray(val)) {
|
||||||
resolve(rootDir, 'node_modules'),
|
for (const dir of val) {
|
||||||
])]
|
if (dir && typeof dir === 'string') {
|
||||||
|
modulesDir.add(resolve(rootDir, dir))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return [...modulesDir]
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
@ -238,9 +243,9 @@ export default defineUntypedSchema({
|
|||||||
* If a relative path is specified, it will be relative to your `rootDir`.
|
* If a relative path is specified, it will be relative to your `rootDir`.
|
||||||
*/
|
*/
|
||||||
analyzeDir: {
|
analyzeDir: {
|
||||||
$resolve: async (val: string | undefined, get): Promise<string> => val
|
$resolve: async (val, get) => val && typeof val === 'string'
|
||||||
? resolve(await get('rootDir') as string, val)
|
? resolve(await get('rootDir'), val)
|
||||||
: resolve(await get('buildDir') as string, 'analyze'),
|
: resolve(await get('buildDir'), 'analyze'),
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -249,14 +254,14 @@ export default defineUntypedSchema({
|
|||||||
* Normally, you should not need to set this.
|
* Normally, you should not need to set this.
|
||||||
*/
|
*/
|
||||||
dev: {
|
dev: {
|
||||||
$resolve: val => val ?? Boolean(isDevelopment),
|
$resolve: val => typeof val === 'boolean' ? val : Boolean(isDevelopment),
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Whether your app is being unit tested.
|
* Whether your app is being unit tested.
|
||||||
*/
|
*/
|
||||||
test: {
|
test: {
|
||||||
$resolve: val => val ?? Boolean(isTest),
|
$resolve: val => typeof val === 'boolean' ? val : Boolean(isTest),
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -270,11 +275,8 @@ export default defineUntypedSchema({
|
|||||||
* @type {boolean | (typeof import('../src/types/debug').NuxtDebugOptions) | undefined}
|
* @type {boolean | (typeof import('../src/types/debug').NuxtDebugOptions) | undefined}
|
||||||
*/
|
*/
|
||||||
debug: {
|
debug: {
|
||||||
$resolve: (val: boolean | NuxtDebugOptions | undefined) => {
|
$resolve: (val) => {
|
||||||
val ??= isDebug
|
val ??= isDebug
|
||||||
if (val === false) {
|
|
||||||
return val
|
|
||||||
}
|
|
||||||
if (val === true) {
|
if (val === true) {
|
||||||
return {
|
return {
|
||||||
templates: true,
|
templates: true,
|
||||||
@ -289,7 +291,10 @@ export default defineUntypedSchema({
|
|||||||
hydration: true,
|
hydration: true,
|
||||||
} satisfies Required<NuxtDebugOptions>
|
} satisfies Required<NuxtDebugOptions>
|
||||||
}
|
}
|
||||||
return val
|
if (val && typeof val === 'object') {
|
||||||
|
return val
|
||||||
|
}
|
||||||
|
return false
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
@ -298,7 +303,7 @@ export default defineUntypedSchema({
|
|||||||
* If set to `false` generated pages will have no content.
|
* If set to `false` generated pages will have no content.
|
||||||
*/
|
*/
|
||||||
ssr: {
|
ssr: {
|
||||||
$resolve: val => val ?? true,
|
$resolve: val => typeof val === 'boolean' ? val : true,
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -327,7 +332,20 @@ export default defineUntypedSchema({
|
|||||||
* @type {(typeof import('../src/types/module').NuxtModule<any> | string | [typeof import('../src/types/module').NuxtModule | string, Record<string, any>] | undefined | null | false)[]}
|
* @type {(typeof import('../src/types/module').NuxtModule<any> | string | [typeof import('../src/types/module').NuxtModule | string, Record<string, any>] | undefined | null | false)[]}
|
||||||
*/
|
*/
|
||||||
modules: {
|
modules: {
|
||||||
$resolve: (val: string[] | undefined): string[] => (val || []).filter(Boolean),
|
$resolve: (val) => {
|
||||||
|
const modules: Array<string | NuxtModule | [NuxtModule | string, Record<string, any>]> = []
|
||||||
|
if (Array.isArray(val)) {
|
||||||
|
for (const mod of val) {
|
||||||
|
if (!mod) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if (typeof mod === 'string' || typeof mod === 'function' || (Array.isArray(mod) && mod[0])) {
|
||||||
|
modules.push(mod)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return modules
|
||||||
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -337,13 +355,13 @@ export default defineUntypedSchema({
|
|||||||
*/
|
*/
|
||||||
dir: {
|
dir: {
|
||||||
app: {
|
app: {
|
||||||
$resolve: async (val: string | undefined, get) => {
|
$resolve: async (val, get) => {
|
||||||
const isV4 = (await get('future') as Record<string, unknown>).compatibilityVersion === 4
|
const isV4 = (await get('future')).compatibilityVersion === 4
|
||||||
if (isV4) {
|
if (isV4) {
|
||||||
const [srcDir, rootDir] = await Promise.all([get('srcDir') as Promise<string>, get('rootDir') as Promise<string>])
|
const [srcDir, rootDir] = await Promise.all([get('srcDir'), get('rootDir')])
|
||||||
return resolve(await get('srcDir') as string, val || (srcDir === rootDir ? 'app' : '.'))
|
return resolve(await get('srcDir'), val && typeof val === 'string' ? val : (srcDir === rootDir ? 'app' : '.'))
|
||||||
}
|
}
|
||||||
return val || 'app'
|
return val && typeof val === 'string' ? val : 'app'
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
/**
|
/**
|
||||||
@ -365,12 +383,12 @@ export default defineUntypedSchema({
|
|||||||
* The modules directory, each file in which will be auto-registered as a Nuxt module.
|
* The modules directory, each file in which will be auto-registered as a Nuxt module.
|
||||||
*/
|
*/
|
||||||
modules: {
|
modules: {
|
||||||
$resolve: async (val: string | undefined, get) => {
|
$resolve: async (val, get) => {
|
||||||
const isV4 = (await get('future') as Record<string, unknown>).compatibilityVersion === 4
|
const isV4 = (await get('future')).compatibilityVersion === 4
|
||||||
if (isV4) {
|
if (isV4) {
|
||||||
return resolve(await get('rootDir') as string, val || 'modules')
|
return resolve(await get('rootDir'), val && typeof val === 'string' ? val : 'modules')
|
||||||
}
|
}
|
||||||
return val || 'modules'
|
return val && typeof val === 'string' ? val : 'modules'
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
@ -394,18 +412,25 @@ export default defineUntypedSchema({
|
|||||||
* and copied across into your `dist` folder when your app is generated.
|
* and copied across into your `dist` folder when your app is generated.
|
||||||
*/
|
*/
|
||||||
public: {
|
public: {
|
||||||
$resolve: async (val: string | undefined, get) => {
|
$resolve: async (val, get) => {
|
||||||
const isV4 = (await get('future') as Record<string, unknown>).compatibilityVersion === 4
|
const isV4 = (await get('future')).compatibilityVersion === 4
|
||||||
if (isV4) {
|
if (isV4) {
|
||||||
return resolve(await get('rootDir') as string, val || await get('dir.static') as string || 'public')
|
return resolve(await get('rootDir'), val && typeof val === 'string' ? val : (await get('dir.static') || 'public'))
|
||||||
}
|
}
|
||||||
return val || await get('dir.static') as string || 'public'
|
return val && typeof val === 'string' ? val : (await get('dir.static') || 'public')
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
|
// TODO: remove in v4
|
||||||
static: {
|
static: {
|
||||||
|
// @ts-expect-error schema has invalid types
|
||||||
$schema: { deprecated: 'use `dir.public` option instead' },
|
$schema: { deprecated: 'use `dir.public` option instead' },
|
||||||
$resolve: async (val, get) => val || await get('dir.public') || 'public',
|
$resolve: async (val, get) => {
|
||||||
|
if (val && typeof val === 'string') {
|
||||||
|
return val
|
||||||
|
}
|
||||||
|
return await get('dir.public') || 'public'
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
@ -413,7 +438,17 @@ export default defineUntypedSchema({
|
|||||||
* The extensions that should be resolved by the Nuxt resolver.
|
* The extensions that should be resolved by the Nuxt resolver.
|
||||||
*/
|
*/
|
||||||
extensions: {
|
extensions: {
|
||||||
$resolve: (val: string[] | undefined): string[] => ['.js', '.jsx', '.mjs', '.ts', '.tsx', '.vue', ...val || []].filter(Boolean),
|
$resolve: (val): string[] => {
|
||||||
|
const extensions = ['.js', '.jsx', '.mjs', '.ts', '.tsx', '.vue']
|
||||||
|
if (Array.isArray(val)) {
|
||||||
|
for (const item of val) {
|
||||||
|
if (item && typeof item === 'string') {
|
||||||
|
extensions.push(item)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return extensions
|
||||||
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -457,8 +492,8 @@ export default defineUntypedSchema({
|
|||||||
* @type {Record<string, string>}
|
* @type {Record<string, string>}
|
||||||
*/
|
*/
|
||||||
alias: {
|
alias: {
|
||||||
$resolve: async (val: Record<string, string>, get): Promise<Record<string, string>> => {
|
$resolve: async (val, get) => {
|
||||||
const [srcDir, rootDir, assetsDir, publicDir, buildDir, sharedDir] = await Promise.all([get('srcDir'), get('rootDir'), get('dir.assets'), get('dir.public'), get('buildDir'), get('dir.shared')]) as [string, string, string, string, string, string]
|
const [srcDir, rootDir, assetsDir, publicDir, buildDir, sharedDir] = await Promise.all([get('srcDir'), get('rootDir'), get('dir.assets'), get('dir.public'), get('buildDir'), get('dir.shared')])
|
||||||
return {
|
return {
|
||||||
'~': srcDir,
|
'~': srcDir,
|
||||||
'@': srcDir,
|
'@': srcDir,
|
||||||
@ -469,7 +504,7 @@ export default defineUntypedSchema({
|
|||||||
[basename(publicDir)]: resolve(srcDir, publicDir),
|
[basename(publicDir)]: resolve(srcDir, publicDir),
|
||||||
'#build': buildDir,
|
'#build': buildDir,
|
||||||
'#internal/nuxt/paths': resolve(buildDir, 'paths.mjs'),
|
'#internal/nuxt/paths': resolve(buildDir, 'paths.mjs'),
|
||||||
...val,
|
...typeof val === 'object' ? val : {},
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@ -494,7 +529,7 @@ export default defineUntypedSchema({
|
|||||||
* By default, the `ignorePrefix` is set to '-', ignoring any files starting with '-'.
|
* By default, the `ignorePrefix` is set to '-', ignoring any files starting with '-'.
|
||||||
*/
|
*/
|
||||||
ignorePrefix: {
|
ignorePrefix: {
|
||||||
$resolve: val => val ?? '-',
|
$resolve: val => val && typeof val === 'string' ? val : '-',
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -502,18 +537,27 @@ export default defineUntypedSchema({
|
|||||||
* inside the `ignore` array will be ignored in building.
|
* inside the `ignore` array will be ignored in building.
|
||||||
*/
|
*/
|
||||||
ignore: {
|
ignore: {
|
||||||
$resolve: async (val: string[] | undefined, get): Promise<string[]> => {
|
$resolve: async (val, get): Promise<string[]> => {
|
||||||
const [rootDir, ignorePrefix, analyzeDir, buildDir] = await Promise.all([get('rootDir'), get('ignorePrefix'), get('analyzeDir'), get('buildDir')]) as [string, string, string, string]
|
const [rootDir, ignorePrefix, analyzeDir, buildDir] = await Promise.all([get('rootDir'), get('ignorePrefix'), get('analyzeDir'), get('buildDir')])
|
||||||
return [
|
const ignore = new Set<string>([
|
||||||
'**/*.stories.{js,cts,mts,ts,jsx,tsx}', // ignore storybook files
|
'**/*.stories.{js,cts,mts,ts,jsx,tsx}', // ignore storybook files
|
||||||
'**/*.{spec,test}.{js,cts,mts,ts,jsx,tsx}', // ignore tests
|
'**/*.{spec,test}.{js,cts,mts,ts,jsx,tsx}', // ignore tests
|
||||||
'**/*.d.{cts,mts,ts}', // ignore type declarations
|
'**/*.d.{cts,mts,ts}', // ignore type declarations
|
||||||
'**/.{pnpm-store,vercel,netlify,output,git,cache,data}',
|
'**/.{pnpm-store,vercel,netlify,output,git,cache,data}',
|
||||||
relative(rootDir, analyzeDir),
|
relative(rootDir, analyzeDir),
|
||||||
relative(rootDir, buildDir),
|
relative(rootDir, buildDir),
|
||||||
ignorePrefix && `**/${ignorePrefix}*.*`,
|
])
|
||||||
...val || [],
|
if (ignorePrefix) {
|
||||||
].filter(Boolean)
|
ignore.add(`**/${ignorePrefix}*.*`)
|
||||||
|
}
|
||||||
|
if (Array.isArray(val)) {
|
||||||
|
for (const pattern in val) {
|
||||||
|
if (pattern) {
|
||||||
|
ignore.add(pattern)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return [...ignore]
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
@ -526,8 +570,11 @@ export default defineUntypedSchema({
|
|||||||
* @type {Array<string | RegExp>}
|
* @type {Array<string | RegExp>}
|
||||||
*/
|
*/
|
||||||
watch: {
|
watch: {
|
||||||
$resolve: (val: Array<unknown> | undefined) => {
|
$resolve: (val) => {
|
||||||
return (val || []).filter((b: unknown) => typeof b === 'string' || b instanceof RegExp)
|
if (Array.isArray(val)) {
|
||||||
|
return val.filter((b: unknown) => typeof b === 'string' || b instanceof RegExp)
|
||||||
|
}
|
||||||
|
return []
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
@ -583,7 +630,7 @@ export default defineUntypedSchema({
|
|||||||
* ```
|
* ```
|
||||||
* @type {typeof import('../src/types/hooks').NuxtHooks}
|
* @type {typeof import('../src/types/hooks').NuxtHooks}
|
||||||
*/
|
*/
|
||||||
hooks: null,
|
hooks: undefined,
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Runtime config allows passing dynamic config and environment variables to the Nuxt app context.
|
* Runtime config allows passing dynamic config and environment variables to the Nuxt app context.
|
||||||
@ -611,8 +658,9 @@ export default defineUntypedSchema({
|
|||||||
* @type {typeof import('../src/types/config').RuntimeConfig}
|
* @type {typeof import('../src/types/config').RuntimeConfig}
|
||||||
*/
|
*/
|
||||||
runtimeConfig: {
|
runtimeConfig: {
|
||||||
$resolve: async (val: RuntimeConfig, get): Promise<Record<string, unknown>> => {
|
$resolve: async (_val, get) => {
|
||||||
const [app, buildId] = await Promise.all([get('app') as Promise<Record<string, string>>, get('buildId') as Promise<string>])
|
const val = _val && typeof _val === 'object' ? _val : {}
|
||||||
|
const [app, buildId] = await Promise.all([get('app'), get('buildId')])
|
||||||
provideFallbackValues(val)
|
provideFallbackValues(val)
|
||||||
return defu(val, {
|
return defu(val, {
|
||||||
public: {},
|
public: {},
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import { defineUntypedSchema } from 'untyped'
|
import { defineResolvers } from '../utils/definition'
|
||||||
import { template as loadingTemplate } from '../../../ui-templates/dist/templates/loading'
|
import { template as loadingTemplate } from '../../../ui-templates/dist/templates/loading'
|
||||||
|
|
||||||
export default defineUntypedSchema({
|
export default defineResolvers({
|
||||||
devServer: {
|
devServer: {
|
||||||
/**
|
/**
|
||||||
* Whether to enable HTTPS.
|
* Whether to enable HTTPS.
|
||||||
@ -21,9 +21,12 @@ export default defineUntypedSchema({
|
|||||||
https: false,
|
https: false,
|
||||||
|
|
||||||
/** Dev server listening port */
|
/** Dev server listening port */
|
||||||
port: process.env.NUXT_PORT || process.env.NITRO_PORT || process.env.PORT || 3000,
|
port: Number(process.env.NUXT_PORT || process.env.NITRO_PORT || process.env.PORT || 3000),
|
||||||
|
|
||||||
/** Dev server listening host */
|
/**
|
||||||
|
* Dev server listening host
|
||||||
|
* @type {string | undefined}
|
||||||
|
*/
|
||||||
host: process.env.NUXT_HOST || process.env.NITRO_HOST || process.env.HOST || undefined,
|
host: process.env.NUXT_HOST || process.env.NITRO_HOST || process.env.HOST || undefined,
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
32
packages/schema/src/config/esbuild.ts
Normal file
32
packages/schema/src/config/esbuild.ts
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
import { defu } from 'defu'
|
||||||
|
import type { TransformOptions } from 'esbuild'
|
||||||
|
import { defineResolvers } from '../utils/definition'
|
||||||
|
|
||||||
|
export default defineResolvers({
|
||||||
|
esbuild: {
|
||||||
|
/**
|
||||||
|
* Configure shared esbuild options used within Nuxt and passed to other builders, such as Vite or Webpack.
|
||||||
|
* @type {import('esbuild').TransformOptions}
|
||||||
|
*/
|
||||||
|
options: {
|
||||||
|
jsxFactory: 'h',
|
||||||
|
jsxFragment: 'Fragment',
|
||||||
|
tsconfigRaw: {
|
||||||
|
$resolve: async (_val, get) => {
|
||||||
|
const val: NonNullable<Exclude<TransformOptions['tsconfigRaw'], string>> = typeof _val === 'string' ? JSON.parse(_val) : (_val && typeof _val === 'object' ? _val : {})
|
||||||
|
|
||||||
|
const useDecorators = await get('experimental').then(r => r?.decorators === true)
|
||||||
|
if (!useDecorators) {
|
||||||
|
return val
|
||||||
|
}
|
||||||
|
return defu(val, {
|
||||||
|
compilerOptions: {
|
||||||
|
useDefineForClassFields: false,
|
||||||
|
experimentalDecorators: false,
|
||||||
|
},
|
||||||
|
} satisfies TransformOptions['tsconfigRaw'])
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
@ -1,6 +1,6 @@
|
|||||||
import { defineUntypedSchema } from 'untyped'
|
import { defineResolvers } from '../utils/definition'
|
||||||
|
|
||||||
export default defineUntypedSchema({
|
export default defineResolvers({
|
||||||
/**
|
/**
|
||||||
* `future` is for early opting-in to new features that will become default in a future
|
* `future` is for early opting-in to new features that will become default in a future
|
||||||
* (possibly major) version of the framework.
|
* (possibly major) version of the framework.
|
||||||
@ -30,10 +30,10 @@ export default defineUntypedSchema({
|
|||||||
*/
|
*/
|
||||||
typescriptBundlerResolution: {
|
typescriptBundlerResolution: {
|
||||||
async $resolve (val, get) {
|
async $resolve (val, get) {
|
||||||
// TODO: remove in v3.10
|
// @ts-expect-error TODO: remove in v3.10
|
||||||
val = val ?? await (get('experimental') as Promise<Record<string, any>>).then(e => e?.typescriptBundlerResolution)
|
val = typeof val === 'boolean' ? val : await (get('experimental')).then(e => e?.typescriptBundlerResolution as string | undefined)
|
||||||
if (typeof val === 'boolean') { return val }
|
if (typeof val === 'boolean') { return val }
|
||||||
const setting = await get('typescript.tsConfig.compilerOptions.moduleResolution') as string | undefined
|
const setting = await get('typescript.tsConfig').then(r => r?.compilerOptions?.moduleResolution)
|
||||||
if (setting) {
|
if (setting) {
|
||||||
return setting.toLowerCase() === 'bundler'
|
return setting.toLowerCase() === 'bundler'
|
||||||
}
|
}
|
||||||
@ -53,14 +53,22 @@ export default defineUntypedSchema({
|
|||||||
* @type {boolean | ((id?: string) => boolean)}
|
* @type {boolean | ((id?: string) => boolean)}
|
||||||
*/
|
*/
|
||||||
inlineStyles: {
|
inlineStyles: {
|
||||||
async $resolve (val, get) {
|
async $resolve (_val, get) {
|
||||||
// TODO: remove in v3.10
|
const val = typeof _val === 'boolean' || typeof _val === 'function'
|
||||||
val = val ?? await (get('experimental') as Promise<Record<string, any>>).then((e: Record<string, any>) => e?.inlineSSRStyles)
|
? _val
|
||||||
if (val === false || (await get('dev')) || (await get('ssr')) === false || (await get('builder')) === '@nuxt/webpack-builder') {
|
// @ts-expect-error TODO: legacy property - remove in v3.10
|
||||||
|
: await (get('experimental')).then(e => e?.inlineSSRStyles) as undefined | boolean
|
||||||
|
if (
|
||||||
|
val === false ||
|
||||||
|
(await get('dev')) ||
|
||||||
|
(await get('ssr')) === false ||
|
||||||
|
// @ts-expect-error TODO: handled normalised types
|
||||||
|
(await get('builder')) === '@nuxt/webpack-builder'
|
||||||
|
) {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
// Enabled by default for vite prod with ssr (for vue components)
|
// Enabled by default for vite prod with ssr (for vue components)
|
||||||
return val ?? ((await get('future') as Record<string, unknown>).compatibilityVersion === 4 ? (id: string) => id && id.includes('.vue') : true)
|
return val ?? ((await get('future')).compatibilityVersion === 4 ? (id?: string) => !!id && id.includes('.vue') : true)
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
@ -73,7 +81,9 @@ export default defineUntypedSchema({
|
|||||||
*/
|
*/
|
||||||
devLogs: {
|
devLogs: {
|
||||||
async $resolve (val, get) {
|
async $resolve (val, get) {
|
||||||
if (val !== undefined) { return val }
|
if (typeof val === 'boolean' || val === 'silent') {
|
||||||
|
return val
|
||||||
|
}
|
||||||
const [isDev, isTest] = await Promise.all([get('dev'), get('test')])
|
const [isDev, isTest] = await Promise.all([get('dev'), get('test')])
|
||||||
return isDev && !isTest
|
return isDev && !isTest
|
||||||
},
|
},
|
||||||
@ -85,17 +95,25 @@ export default defineUntypedSchema({
|
|||||||
*/
|
*/
|
||||||
noScripts: {
|
noScripts: {
|
||||||
async $resolve (val, get) {
|
async $resolve (val, get) {
|
||||||
// TODO: remove in v3.10
|
return typeof val === 'boolean'
|
||||||
return val ?? await (get('experimental') as Promise<Record<string, any>>).then((e: Record<string, any>) => e?.noScripts) ?? false
|
? val
|
||||||
|
// @ts-expect-error TODO: legacy property - remove in v3.10
|
||||||
|
: (await (get('experimental')).then(e => e?.noScripts as boolean | undefined) ?? false)
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
experimental: {
|
experimental: {
|
||||||
|
/**
|
||||||
|
* Enable to use experimental decorators in Nuxt and Nitro.
|
||||||
|
*
|
||||||
|
* @see https://github.com/tc39/proposal-decorators
|
||||||
|
*/
|
||||||
|
decorators: false,
|
||||||
/**
|
/**
|
||||||
* Set to true to generate an async entry point for the Vue bundle (for module federation support).
|
* Set to true to generate an async entry point for the Vue bundle (for module federation support).
|
||||||
*/
|
*/
|
||||||
asyncEntry: {
|
asyncEntry: {
|
||||||
$resolve: val => val ?? false,
|
$resolve: val => typeof val === 'boolean' ? val : false,
|
||||||
},
|
},
|
||||||
|
|
||||||
// TODO: Remove when nitro has support for mocking traced dependencies
|
// TODO: Remove when nitro has support for mocking traced dependencies
|
||||||
@ -135,7 +153,17 @@ export default defineUntypedSchema({
|
|||||||
if (val === 'reload') {
|
if (val === 'reload') {
|
||||||
return 'automatic'
|
return 'automatic'
|
||||||
}
|
}
|
||||||
return val ?? 'automatic'
|
if (val === false) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
const validOptions = ['manual', 'automatic', 'automatic-immediate'] as const
|
||||||
|
type EmitRouteChunkError = typeof validOptions[number]
|
||||||
|
if (typeof val === 'string' && validOptions.includes(val as EmitRouteChunkError)) {
|
||||||
|
return val as EmitRouteChunkError
|
||||||
|
}
|
||||||
|
|
||||||
|
return 'automatic'
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
@ -255,14 +283,16 @@ export default defineUntypedSchema({
|
|||||||
*/
|
*/
|
||||||
watcher: {
|
watcher: {
|
||||||
$resolve: async (val, get) => {
|
$resolve: async (val, get) => {
|
||||||
if (val) {
|
const validOptions = ['chokidar', 'parcel', 'chokidar-granular'] as const
|
||||||
return val
|
type WatcherOption = typeof validOptions[number]
|
||||||
|
if (typeof val === 'string' && validOptions.includes(val as WatcherOption)) {
|
||||||
|
return val as WatcherOption
|
||||||
}
|
}
|
||||||
const [srcDir, rootDir] = await Promise.all([get('srcDir'), get('rootDir')]) as [string, string]
|
const [srcDir, rootDir] = await Promise.all([get('srcDir'), get('rootDir')])
|
||||||
if (srcDir === rootDir) {
|
if (srcDir === rootDir) {
|
||||||
return 'chokidar-granular'
|
return 'chokidar-granular' as const
|
||||||
}
|
}
|
||||||
return 'chokidar'
|
return 'chokidar' as const
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
@ -304,7 +334,7 @@ export default defineUntypedSchema({
|
|||||||
*/
|
*/
|
||||||
scanPageMeta: {
|
scanPageMeta: {
|
||||||
async $resolve (val, get) {
|
async $resolve (val, get) {
|
||||||
return val ?? ((await get('future') as Record<string, unknown>).compatibilityVersion === 4 ? 'after-resolve' : true)
|
return typeof val === 'boolean' || val === 'after-resolve' ? val : ((await get('future')).compatibilityVersion === 4 ? 'after-resolve' : true)
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
@ -343,7 +373,7 @@ export default defineUntypedSchema({
|
|||||||
*/
|
*/
|
||||||
sharedPrerenderData: {
|
sharedPrerenderData: {
|
||||||
async $resolve (val, get) {
|
async $resolve (val, get) {
|
||||||
return val ?? ((await get('future') as Record<string, unknown>).compatibilityVersion === 4)
|
return typeof val === 'boolean' ? val : ((await get('future')).compatibilityVersion === 4)
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
@ -414,7 +444,7 @@ export default defineUntypedSchema({
|
|||||||
*/
|
*/
|
||||||
normalizeComponentNames: {
|
normalizeComponentNames: {
|
||||||
$resolve: async (val, get) => {
|
$resolve: async (val, get) => {
|
||||||
return val ?? ((await get('future') as Record<string, unknown>).compatibilityVersion === 4)
|
return typeof val === 'boolean' ? val : ((await get('future')).compatibilityVersion === 4)
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
@ -425,7 +455,9 @@ export default defineUntypedSchema({
|
|||||||
*/
|
*/
|
||||||
spaLoadingTemplateLocation: {
|
spaLoadingTemplateLocation: {
|
||||||
$resolve: async (val, get) => {
|
$resolve: async (val, get) => {
|
||||||
return val ?? (((await get('future') as Record<string, unknown>).compatibilityVersion === 4) ? 'body' : 'within')
|
const validOptions = ['body', 'within'] as const
|
||||||
|
type SpaLoadingTemplateLocation = typeof validOptions[number]
|
||||||
|
return typeof val === 'string' && validOptions.includes(val as SpaLoadingTemplateLocation) ? val as SpaLoadingTemplateLocation : (((await get('future')).compatibilityVersion === 4) ? 'body' : 'within')
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
@ -435,7 +467,7 @@ export default defineUntypedSchema({
|
|||||||
* @see [the Chrome DevTools extensibility API](https://developer.chrome.com/docs/devtools/performance/extension#tracks)
|
* @see [the Chrome DevTools extensibility API](https://developer.chrome.com/docs/devtools/performance/extension#tracks)
|
||||||
*/
|
*/
|
||||||
browserDevtoolsTiming: {
|
browserDevtoolsTiming: {
|
||||||
$resolve: async (val, get) => val ?? await get('dev'),
|
$resolve: async (val, get) => typeof val === 'boolean' ? val : await get('dev'),
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -443,7 +475,7 @@ export default defineUntypedSchema({
|
|||||||
*/
|
*/
|
||||||
debugModuleMutation: {
|
debugModuleMutation: {
|
||||||
$resolve: async (val, get) => {
|
$resolve: async (val, get) => {
|
||||||
return val ?? Boolean(await get('debug'))
|
return typeof val === 'boolean' ? val : Boolean(await get('debug'))
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import { defineUntypedSchema } from 'untyped'
|
import { defineResolvers } from '../utils/definition'
|
||||||
|
|
||||||
export default defineUntypedSchema({
|
export default defineResolvers({
|
||||||
generate: {
|
generate: {
|
||||||
/**
|
/**
|
||||||
* The routes to generate.
|
* The routes to generate.
|
||||||
|
@ -1,8 +1,11 @@
|
|||||||
|
import type { ResolvableConfigSchema } from '../utils/definition'
|
||||||
|
|
||||||
import adhoc from './adhoc'
|
import adhoc from './adhoc'
|
||||||
import app from './app'
|
import app from './app'
|
||||||
import build from './build'
|
import build from './build'
|
||||||
import common from './common'
|
import common from './common'
|
||||||
import dev from './dev'
|
import dev from './dev'
|
||||||
|
import esbuild from './esbuild'
|
||||||
import experimental from './experimental'
|
import experimental from './experimental'
|
||||||
import generate from './generate'
|
import generate from './generate'
|
||||||
import internal from './internal'
|
import internal from './internal'
|
||||||
@ -26,6 +29,7 @@ export default {
|
|||||||
...postcss,
|
...postcss,
|
||||||
...router,
|
...router,
|
||||||
...typescript,
|
...typescript,
|
||||||
|
...esbuild,
|
||||||
...vite,
|
...vite,
|
||||||
...webpack,
|
...webpack,
|
||||||
}
|
} satisfies ResolvableConfigSchema
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import { defineUntypedSchema } from 'untyped'
|
import { defineResolvers } from '../utils/definition'
|
||||||
|
|
||||||
export default defineUntypedSchema({
|
export default defineResolvers({
|
||||||
/** @private */
|
/** @private */
|
||||||
_majorVersion: 4,
|
_majorVersion: 4,
|
||||||
/** @private */
|
/** @private */
|
||||||
@ -25,7 +25,7 @@ export default defineUntypedSchema({
|
|||||||
appDir: '',
|
appDir: '',
|
||||||
/**
|
/**
|
||||||
* @private
|
* @private
|
||||||
* @type {Array<{ meta: ModuleMeta; module: NuxtModule, timings?: Record<string, number | undefined>; entryPath?: string }>}
|
* @type {Array<{ meta: typeof import('../src/types/module').ModuleMeta; module: typeof import('../src/types/module').NuxtModule, timings?: Record<string, number | undefined>; entryPath?: string }>}
|
||||||
*/
|
*/
|
||||||
_installedModules: [],
|
_installedModules: [],
|
||||||
/** @private */
|
/** @private */
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
import { defineUntypedSchema } from 'untyped'
|
import { defineResolvers } from '../utils/definition'
|
||||||
import type { RuntimeConfig } from '../types/config'
|
|
||||||
|
|
||||||
export default defineUntypedSchema({
|
export default defineResolvers({
|
||||||
/**
|
/**
|
||||||
* Configuration for Nitro.
|
* Configuration for Nitro.
|
||||||
* @see [Nitro configuration docs](https://nitro.unjs.io/config/)
|
* @see [Nitro configuration docs](https://nitro.unjs.io/config/)
|
||||||
@ -9,8 +8,8 @@ export default defineUntypedSchema({
|
|||||||
*/
|
*/
|
||||||
nitro: {
|
nitro: {
|
||||||
runtimeConfig: {
|
runtimeConfig: {
|
||||||
$resolve: async (val: Record<string, any> | undefined, get) => {
|
$resolve: async (val, get) => {
|
||||||
const runtimeConfig = await get('runtimeConfig') as RuntimeConfig
|
const runtimeConfig = await get('runtimeConfig')
|
||||||
return {
|
return {
|
||||||
...runtimeConfig,
|
...runtimeConfig,
|
||||||
app: {
|
app: {
|
||||||
@ -27,10 +26,12 @@ export default defineUntypedSchema({
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
routeRules: {
|
routeRules: {
|
||||||
$resolve: async (val: Record<string, any> | undefined, get) => ({
|
$resolve: async (val, get) => {
|
||||||
...await get('routeRules') as Record<string, any>,
|
return {
|
||||||
...val,
|
...await get('routeRules'),
|
||||||
}),
|
...(val && typeof val === 'object' ? val : {}),
|
||||||
|
}
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import { defineUntypedSchema } from 'untyped'
|
import { defineResolvers } from '../utils/definition'
|
||||||
|
|
||||||
const ensureItemIsLast = (item: string) => (arr: string[]) => {
|
const ensureItemIsLast = (item: string) => (arr: string[]) => {
|
||||||
const index = arr.indexOf(item)
|
const index = arr.indexOf(item)
|
||||||
@ -17,7 +17,7 @@ const orderPresets = {
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
export default defineUntypedSchema({
|
export default defineResolvers({
|
||||||
postcss: {
|
postcss: {
|
||||||
/**
|
/**
|
||||||
* A strategy for ordering PostCSS plugins.
|
* A strategy for ordering PostCSS plugins.
|
||||||
@ -25,14 +25,20 @@ export default defineUntypedSchema({
|
|||||||
* @type {'cssnanoLast' | 'autoprefixerLast' | 'autoprefixerAndCssnanoLast' | string[] | ((names: string[]) => string[])}
|
* @type {'cssnanoLast' | 'autoprefixerLast' | 'autoprefixerAndCssnanoLast' | string[] | ((names: string[]) => string[])}
|
||||||
*/
|
*/
|
||||||
order: {
|
order: {
|
||||||
$resolve: (val: string | string[] | ((plugins: string[]) => string[])): string[] | ((plugins: string[]) => string[]) => {
|
$resolve: (val) => {
|
||||||
if (typeof val === 'string') {
|
if (typeof val === 'string') {
|
||||||
if (!(val in orderPresets)) {
|
if (!(val in orderPresets)) {
|
||||||
throw new Error(`[nuxt] Unknown PostCSS order preset: ${val}`)
|
throw new Error(`[nuxt] Unknown PostCSS order preset: ${val}`)
|
||||||
}
|
}
|
||||||
return orderPresets[val as keyof typeof orderPresets]
|
return orderPresets[val as keyof typeof orderPresets]
|
||||||
}
|
}
|
||||||
return val ?? orderPresets.autoprefixerAndCssnanoLast
|
if (typeof val === 'function') {
|
||||||
|
return val as (names: string[]) => string[]
|
||||||
|
}
|
||||||
|
if (Array.isArray(val)) {
|
||||||
|
return val
|
||||||
|
}
|
||||||
|
return orderPresets.autoprefixerAndCssnanoLast
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
/**
|
/**
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import { defineUntypedSchema } from 'untyped'
|
import { defineResolvers } from '../utils/definition'
|
||||||
|
|
||||||
export default defineUntypedSchema({
|
export default defineResolvers({
|
||||||
router: {
|
router: {
|
||||||
/**
|
/**
|
||||||
* Additional router options passed to `vue-router`. On top of the options for `vue-router`,
|
* Additional router options passed to `vue-router`. On top of the options for `vue-router`,
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import { defineUntypedSchema } from 'untyped'
|
import { defineResolvers } from '../utils/definition'
|
||||||
|
|
||||||
export default defineUntypedSchema({
|
export default defineResolvers({
|
||||||
/**
|
/**
|
||||||
* Configuration for Nuxt's TypeScript integration.
|
* Configuration for Nuxt's TypeScript integration.
|
||||||
*
|
*
|
||||||
@ -20,10 +20,20 @@ export default defineUntypedSchema({
|
|||||||
* builder environment types (with `false`) to handle this fully yourself, or opt for a 'shared' option.
|
* builder environment types (with `false`) to handle this fully yourself, or opt for a 'shared' option.
|
||||||
*
|
*
|
||||||
* The 'shared' option is advised for module authors, who will want to support multiple possible builders.
|
* The 'shared' option is advised for module authors, who will want to support multiple possible builders.
|
||||||
* @type {'vite' | 'webpack' | 'rspack' | 'shared' | false | undefined}
|
* @type {'vite' | 'webpack' | 'rspack' | 'shared' | false | undefined | null}
|
||||||
*/
|
*/
|
||||||
builder: {
|
builder: {
|
||||||
$resolve: val => val ?? null,
|
$resolve: (val) => {
|
||||||
|
const validBuilderTypes = ['vite', 'webpack', 'rspack', 'shared'] as const
|
||||||
|
type ValidBuilderType = typeof validBuilderTypes[number]
|
||||||
|
if (typeof val === 'string' && validBuilderTypes.includes(val as ValidBuilderType)) {
|
||||||
|
return val as ValidBuilderType
|
||||||
|
}
|
||||||
|
if (val === false) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return null
|
||||||
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -1,10 +1,10 @@
|
|||||||
import { consola } from 'consola'
|
import { consola } from 'consola'
|
||||||
|
import defu from 'defu'
|
||||||
import { resolve } from 'pathe'
|
import { resolve } from 'pathe'
|
||||||
import { isTest } from 'std-env'
|
import { isTest } from 'std-env'
|
||||||
import { defineUntypedSchema } from 'untyped'
|
import { defineResolvers } from '../utils/definition'
|
||||||
import type { NuxtDebugOptions } from '../types/debug'
|
|
||||||
|
|
||||||
export default defineUntypedSchema({
|
export default defineResolvers({
|
||||||
/**
|
/**
|
||||||
* Configuration that will be passed directly to Vite.
|
* Configuration that will be passed directly to Vite.
|
||||||
*
|
*
|
||||||
@ -14,22 +14,21 @@ export default defineUntypedSchema({
|
|||||||
*/
|
*/
|
||||||
vite: {
|
vite: {
|
||||||
root: {
|
root: {
|
||||||
$resolve: async (val, get) => val ?? (await get('srcDir')),
|
$resolve: async (val, get) => typeof val === 'string' ? val : (await get('srcDir')),
|
||||||
},
|
},
|
||||||
mode: {
|
mode: {
|
||||||
$resolve: async (val, get) => val ?? (await get('dev') ? 'development' : 'production'),
|
$resolve: async (val, get) => typeof val === 'string' ? val : (await get('dev') ? 'development' : 'production'),
|
||||||
},
|
},
|
||||||
define: {
|
define: {
|
||||||
$resolve: async (val: Record<string, any> | undefined, get) => {
|
$resolve: async (_val, get) => {
|
||||||
const [isDev, debug] = await Promise.all([get('dev'), get('debug')]) as [boolean, boolean | NuxtDebugOptions]
|
const [isDev, isDebug] = await Promise.all([get('dev'), get('debug')])
|
||||||
|
|
||||||
return {
|
return {
|
||||||
'__VUE_PROD_HYDRATION_MISMATCH_DETAILS__': Boolean(debug && (debug === true || debug.hydration)),
|
'__VUE_PROD_HYDRATION_MISMATCH_DETAILS__': Boolean(isDebug && (isDebug === true || isDebug.hydration)),
|
||||||
'process.dev': isDev,
|
'process.dev': isDev,
|
||||||
'import.meta.dev': isDev,
|
'import.meta.dev': isDev,
|
||||||
'process.test': isTest,
|
'process.test': isTest,
|
||||||
'import.meta.test': isTest,
|
'import.meta.test': isTest,
|
||||||
...val,
|
..._val && typeof _val === 'object' ? _val : {},
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@ -37,6 +36,7 @@ export default defineUntypedSchema({
|
|||||||
extensions: ['.mjs', '.js', '.ts', '.jsx', '.tsx', '.json', '.vue'],
|
extensions: ['.mjs', '.js', '.ts', '.jsx', '.tsx', '.json', '.vue'],
|
||||||
},
|
},
|
||||||
publicDir: {
|
publicDir: {
|
||||||
|
// @ts-expect-error this is missing from our `vite` types deliberately, so users do not configure it
|
||||||
$resolve: (val) => {
|
$resolve: (val) => {
|
||||||
if (val) {
|
if (val) {
|
||||||
consola.warn('Directly configuring the `vite.publicDir` option is not supported. Instead, set `dir.public`. You can read more in `https://nuxt.com/docs/api/nuxt-config#public`.')
|
consola.warn('Directly configuring the `vite.publicDir` option is not supported. Instead, set `dir.public`. You can read more in `https://nuxt.com/docs/api/nuxt-config#public`.')
|
||||||
@ -46,81 +46,89 @@ export default defineUntypedSchema({
|
|||||||
},
|
},
|
||||||
vue: {
|
vue: {
|
||||||
isProduction: {
|
isProduction: {
|
||||||
$resolve: async (val, get) => val ?? !(await get('dev')),
|
$resolve: async (val, get) => typeof val === 'boolean' ? val : !(await get('dev')),
|
||||||
},
|
},
|
||||||
template: {
|
template: {
|
||||||
compilerOptions: {
|
compilerOptions: {
|
||||||
$resolve: async (val, get) => val ?? (await get('vue') as Record<string, any>).compilerOptions,
|
$resolve: async (val, get) => val ?? (await get('vue')).compilerOptions,
|
||||||
},
|
},
|
||||||
transformAssetUrls: {
|
transformAssetUrls: {
|
||||||
$resolve: async (val, get) => val ?? (await get('vue') as Record<string, any>).transformAssetUrls,
|
$resolve: async (val, get) => val ?? (await get('vue')).transformAssetUrls,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
script: {
|
script: {
|
||||||
hoistStatic: {
|
hoistStatic: {
|
||||||
$resolve: async (val, get) => val ?? (await get('vue') as Record<string, any>).compilerOptions?.hoistStatic,
|
$resolve: async (val, get) => typeof val === 'boolean' ? val : (await get('vue')).compilerOptions?.hoistStatic,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
features: {
|
features: {
|
||||||
propsDestructure: {
|
propsDestructure: {
|
||||||
$resolve: async (val, get) => {
|
$resolve: async (val, get) => {
|
||||||
if (val !== undefined && val !== null) {
|
if (typeof val === 'boolean') {
|
||||||
return val
|
return val
|
||||||
}
|
}
|
||||||
const vueOptions = await get('vue') as Record<string, any> || {}
|
const vueOptions = await get('vue') || {}
|
||||||
return Boolean(vueOptions.script?.propsDestructure ?? vueOptions.propsDestructure)
|
return Boolean(
|
||||||
|
// @ts-expect-error TODO: remove in future: supporting a legacy schema
|
||||||
|
vueOptions.script?.propsDestructure
|
||||||
|
?? vueOptions.propsDestructure,
|
||||||
|
)
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
vueJsx: {
|
vueJsx: {
|
||||||
$resolve: async (val: Record<string, any>, get) => {
|
$resolve: async (val, get) => {
|
||||||
return {
|
return {
|
||||||
isCustomElement: (await get('vue') as Record<string, any>).compilerOptions?.isCustomElement,
|
// TODO: investigate type divergence between types for @vue/compiler-core and @vue/babel-plugin-jsx
|
||||||
...val,
|
isCustomElement: (await get('vue')).compilerOptions?.isCustomElement as undefined | ((tag: string) => boolean),
|
||||||
|
...typeof val === 'object' ? val : {},
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
optimizeDeps: {
|
optimizeDeps: {
|
||||||
|
esbuildOptions: {
|
||||||
|
$resolve: async (val, get) => defu(val && typeof val === 'object' ? val : {}, await get('esbuild.options')),
|
||||||
|
},
|
||||||
exclude: {
|
exclude: {
|
||||||
$resolve: async (val: string[] | undefined, get) => [
|
$resolve: async (val, get) => [
|
||||||
...val || [],
|
...Array.isArray(val) ? val : [],
|
||||||
...(await get('build.transpile') as Array<string | RegExp | ((ctx: { isClient?: boolean, isServer?: boolean, isDev: boolean }) => string | RegExp | false)>).filter(i => typeof i === 'string'),
|
...(await get('build.transpile')).filter(i => typeof i === 'string'),
|
||||||
'vue-demi',
|
'vue-demi',
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
esbuild: {
|
esbuild: {
|
||||||
jsxFactory: 'h',
|
$resolve: async (val, get) => {
|
||||||
jsxFragment: 'Fragment',
|
return defu(val && typeof val === 'object' ? val : {}, await get('esbuild.options'))
|
||||||
tsconfigRaw: '{}',
|
},
|
||||||
},
|
},
|
||||||
clearScreen: true,
|
clearScreen: true,
|
||||||
build: {
|
build: {
|
||||||
assetsDir: {
|
assetsDir: {
|
||||||
$resolve: async (val, get) => val ?? (await get('app') as Record<string, string>).buildAssetsDir?.replace(/^\/+/, ''),
|
$resolve: async (val, get) => typeof val === 'string' ? val : (await get('app')).buildAssetsDir?.replace(/^\/+/, ''),
|
||||||
},
|
},
|
||||||
emptyOutDir: false,
|
emptyOutDir: false,
|
||||||
},
|
},
|
||||||
server: {
|
server: {
|
||||||
fs: {
|
fs: {
|
||||||
allow: {
|
allow: {
|
||||||
$resolve: async (val: string[] | undefined, get) => {
|
$resolve: async (val, get) => {
|
||||||
const [buildDir, srcDir, rootDir, workspaceDir, modulesDir] = await Promise.all([get('buildDir'), get('srcDir'), get('rootDir'), get('workspaceDir'), get('modulesDir')]) as [string, string, string, string, string]
|
const [buildDir, srcDir, rootDir, workspaceDir, modulesDir] = await Promise.all([get('buildDir'), get('srcDir'), get('rootDir'), get('workspaceDir'), get('modulesDir')])
|
||||||
return [...new Set([
|
return [...new Set([
|
||||||
buildDir,
|
buildDir,
|
||||||
srcDir,
|
srcDir,
|
||||||
rootDir,
|
rootDir,
|
||||||
workspaceDir,
|
workspaceDir,
|
||||||
...(modulesDir),
|
...(modulesDir),
|
||||||
...val ?? [],
|
...Array.isArray(val) ? val : [],
|
||||||
])]
|
])]
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
cacheDir: {
|
cacheDir: {
|
||||||
$resolve: async (val, get) => val ?? resolve(await get('rootDir') as string, 'node_modules/.cache/vite'),
|
$resolve: async (val, get) => typeof val === 'string' ? val : resolve(await get('rootDir'), 'node_modules/.cache/vite'),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
@ -1,8 +1,7 @@
|
|||||||
import { defu } from 'defu'
|
import { defu } from 'defu'
|
||||||
import { defineUntypedSchema } from 'untyped'
|
import { defineResolvers } from '../utils/definition'
|
||||||
import type { VueLoaderOptions } from 'vue-loader'
|
|
||||||
|
|
||||||
export default defineUntypedSchema({
|
export default defineResolvers({
|
||||||
webpack: {
|
webpack: {
|
||||||
/**
|
/**
|
||||||
* Nuxt uses `webpack-bundle-analyzer` to visualize your bundles and how to optimize them.
|
* Nuxt uses `webpack-bundle-analyzer` to visualize your bundles and how to optimize them.
|
||||||
@ -17,8 +16,8 @@ export default defineUntypedSchema({
|
|||||||
* @type {boolean | { enabled?: boolean } & typeof import('webpack-bundle-analyzer').BundleAnalyzerPlugin.Options}
|
* @type {boolean | { enabled?: boolean } & typeof import('webpack-bundle-analyzer').BundleAnalyzerPlugin.Options}
|
||||||
*/
|
*/
|
||||||
analyze: {
|
analyze: {
|
||||||
$resolve: async (val: boolean | { enabled?: boolean } | Record<string, unknown>, get) => {
|
$resolve: async (val, get) => {
|
||||||
const value = typeof val === 'boolean' ? { enabled: val } : val
|
const value = typeof val === 'boolean' ? { enabled: val } : (val && typeof val === 'object' ? val : {})
|
||||||
return defu(value, await get('build.analyze') as { enabled?: boolean } | Record<string, unknown>)
|
return defu(value, await get('build.analyze') as { enabled?: boolean } | Record<string, unknown>)
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@ -83,7 +82,7 @@ export default defineUntypedSchema({
|
|||||||
* Enables CSS source map support (defaults to `true` in development).
|
* Enables CSS source map support (defaults to `true` in development).
|
||||||
*/
|
*/
|
||||||
cssSourceMap: {
|
cssSourceMap: {
|
||||||
$resolve: async (val, get) => val ?? await get('dev'),
|
$resolve: async (val, get) => typeof val === 'boolean' ? val : await get('dev'),
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -147,7 +146,10 @@ export default defineUntypedSchema({
|
|||||||
for (const name of styleLoaders) {
|
for (const name of styleLoaders) {
|
||||||
const loader = loaders[name]
|
const loader = loaders[name]
|
||||||
if (loader && loader.sourceMap === undefined) {
|
if (loader && loader.sourceMap === undefined) {
|
||||||
loader.sourceMap = Boolean(await get('build.cssSourceMap'))
|
loader.sourceMap = Boolean(
|
||||||
|
// @ts-expect-error TODO: remove legacay configuration
|
||||||
|
await get('build.cssSourceMap'),
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return loaders
|
return loaders
|
||||||
@ -158,37 +160,34 @@ export default defineUntypedSchema({
|
|||||||
* @type {Omit<typeof import('esbuild-loader')['LoaderOptions'], 'loader'>}
|
* @type {Omit<typeof import('esbuild-loader')['LoaderOptions'], 'loader'>}
|
||||||
*/
|
*/
|
||||||
esbuild: {
|
esbuild: {
|
||||||
jsxFactory: 'h',
|
$resolve: async (val, get) => {
|
||||||
jsxFragment: 'Fragment',
|
return defu(val && typeof val === 'object' ? val : {}, await get('esbuild.options'))
|
||||||
tsconfigRaw: '{}',
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @see [`file-loader` Options](https://github.com/webpack-contrib/file-loader#options)
|
* @see [`file-loader` Options](https://github.com/webpack-contrib/file-loader#options)
|
||||||
* @type {Omit<typeof import('file-loader')['Options'], 'name'>}
|
|
||||||
* @default
|
* @default
|
||||||
* ```ts
|
* ```ts
|
||||||
* { esModule: false }
|
* { esModule: false }
|
||||||
* ```
|
* ```
|
||||||
*/
|
*/
|
||||||
file: { esModule: false },
|
file: { esModule: false, limit: 1000 },
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @see [`file-loader` Options](https://github.com/webpack-contrib/file-loader#options)
|
* @see [`file-loader` Options](https://github.com/webpack-contrib/file-loader#options)
|
||||||
* @type {Omit<typeof import('file-loader')['Options'], 'name'>}
|
|
||||||
* @default
|
* @default
|
||||||
* ```ts
|
* ```ts
|
||||||
* { esModule: false, limit: 1000 }
|
* { esModule: false }
|
||||||
* ```
|
* ```
|
||||||
*/
|
*/
|
||||||
fontUrl: { esModule: false, limit: 1000 },
|
fontUrl: { esModule: false, limit: 1000 },
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @see [`file-loader` Options](https://github.com/webpack-contrib/file-loader#options)
|
* @see [`file-loader` Options](https://github.com/webpack-contrib/file-loader#options)
|
||||||
* @type {Omit<typeof import('file-loader')['Options'], 'name'>}
|
|
||||||
* @default
|
* @default
|
||||||
* ```ts
|
* ```ts
|
||||||
* { esModule: false, limit: 1000 }
|
* { esModule: false }
|
||||||
* ```
|
* ```
|
||||||
*/
|
*/
|
||||||
imgUrl: { esModule: false, limit: 1000 },
|
imgUrl: { esModule: false, limit: 1000 },
|
||||||
@ -205,26 +204,38 @@ export default defineUntypedSchema({
|
|||||||
*/
|
*/
|
||||||
vue: {
|
vue: {
|
||||||
transformAssetUrls: {
|
transformAssetUrls: {
|
||||||
$resolve: async (val, get) => (val ?? (await get('vue.transformAssetUrls'))) as VueLoaderOptions['transformAssetUrls'],
|
$resolve: async (val, get) => (val ?? (await get('vue.transformAssetUrls'))),
|
||||||
},
|
},
|
||||||
compilerOptions: {
|
compilerOptions: {
|
||||||
$resolve: async (val, get) => (val ?? (await get('vue.compilerOptions'))) as VueLoaderOptions['compilerOptions'],
|
$resolve: async (val, get) => (val ?? (await get('vue.compilerOptions'))),
|
||||||
},
|
},
|
||||||
propsDestructure: {
|
propsDestructure: {
|
||||||
$resolve: async (val, get) => Boolean(val ?? await get('vue.propsDestructure')),
|
$resolve: async (val, get) => Boolean(val ?? await get('vue.propsDestructure')),
|
||||||
},
|
},
|
||||||
} satisfies { [K in keyof VueLoaderOptions]: { $resolve: (val: unknown, get: (id: string) => Promise<unknown>) => Promise<VueLoaderOptions[K]> } },
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* See [css-loader](https://github.com/webpack-contrib/css-loader) for available options.
|
||||||
|
*/
|
||||||
css: {
|
css: {
|
||||||
importLoaders: 0,
|
importLoaders: 0,
|
||||||
|
/**
|
||||||
|
* @type {boolean | { filter: (url: string, resourcePath: string) => boolean }}
|
||||||
|
*/
|
||||||
url: {
|
url: {
|
||||||
filter: (url: string, _resourcePath: string) => url[0] !== '/',
|
filter: (url: string, _resourcePath: string) => url[0] !== '/',
|
||||||
},
|
},
|
||||||
esModule: false,
|
esModule: false,
|
||||||
},
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* See [css-loader](https://github.com/webpack-contrib/css-loader) for available options.
|
||||||
|
*/
|
||||||
cssModules: {
|
cssModules: {
|
||||||
importLoaders: 0,
|
importLoaders: 0,
|
||||||
|
/**
|
||||||
|
* @type {boolean | { filter: (url: string, resourcePath: string) => boolean }}
|
||||||
|
*/
|
||||||
url: {
|
url: {
|
||||||
filter: (url: string, _resourcePath: string) => url[0] !== '/',
|
filter: (url: string, _resourcePath: string) => url[0] !== '/',
|
||||||
},
|
},
|
||||||
@ -241,7 +252,6 @@ export default defineUntypedSchema({
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* @see [`sass-loader` Options](https://github.com/webpack-contrib/sass-loader#options)
|
* @see [`sass-loader` Options](https://github.com/webpack-contrib/sass-loader#options)
|
||||||
* @type {typeof import('sass-loader')['Options']}
|
|
||||||
* @default
|
* @default
|
||||||
* ```ts
|
* ```ts
|
||||||
* {
|
* {
|
||||||
@ -259,7 +269,6 @@ export default defineUntypedSchema({
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* @see [`sass-loader` Options](https://github.com/webpack-contrib/sass-loader#options)
|
* @see [`sass-loader` Options](https://github.com/webpack-contrib/sass-loader#options)
|
||||||
* @type {typeof import('sass-loader')['Options']}
|
|
||||||
*/
|
*/
|
||||||
scss: {},
|
scss: {},
|
||||||
|
|
||||||
@ -300,7 +309,14 @@ export default defineUntypedSchema({
|
|||||||
* @type {false | typeof import('css-minimizer-webpack-plugin').BasePluginOptions & typeof import('css-minimizer-webpack-plugin').DefinedDefaultMinimizerAndOptions<any>}
|
* @type {false | typeof import('css-minimizer-webpack-plugin').BasePluginOptions & typeof import('css-minimizer-webpack-plugin').DefinedDefaultMinimizerAndOptions<any>}
|
||||||
*/
|
*/
|
||||||
optimizeCSS: {
|
optimizeCSS: {
|
||||||
$resolve: async (val, get) => val ?? (await get('build.extractCSS') ? {} : false),
|
$resolve: async (val, get) => {
|
||||||
|
if (val === false || (val && typeof val === 'object')) {
|
||||||
|
return val
|
||||||
|
}
|
||||||
|
// @ts-expect-error TODO: remove legacy configuration
|
||||||
|
const extractCSS = await get('build.extractCSS')
|
||||||
|
return extractCSS ? {} : false
|
||||||
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -310,7 +326,9 @@ export default defineUntypedSchema({
|
|||||||
optimization: {
|
optimization: {
|
||||||
runtimeChunk: 'single',
|
runtimeChunk: 'single',
|
||||||
/** Set minimize to `false` to disable all minimizers. (It is disabled in development by default). */
|
/** Set minimize to `false` to disable all minimizers. (It is disabled in development by default). */
|
||||||
minimize: { $resolve: async (val, get) => val ?? !(await get('dev')) },
|
minimize: {
|
||||||
|
$resolve: async (val, get) => typeof val === 'boolean' ? val : !(await get('dev')),
|
||||||
|
},
|
||||||
/** You can set minimizer to a customized array of plugins. */
|
/** You can set minimizer to a customized array of plugins. */
|
||||||
minimizer: undefined,
|
minimizer: undefined,
|
||||||
splitChunks: {
|
splitChunks: {
|
||||||
@ -323,15 +341,12 @@ export default defineUntypedSchema({
|
|||||||
/**
|
/**
|
||||||
* Customize PostCSS Loader.
|
* Customize PostCSS Loader.
|
||||||
* same options as [`postcss-loader` options](https://github.com/webpack-contrib/postcss-loader#options)
|
* same options as [`postcss-loader` options](https://github.com/webpack-contrib/postcss-loader#options)
|
||||||
* @type {{ execute?: boolean, postcssOptions: typeof import('postcss').ProcessOptions, sourceMap?: boolean, implementation?: any }}
|
* @type {{ execute?: boolean, postcssOptions: typeof import('postcss').ProcessOptions & { plugins: Record<string, unknown> & { autoprefixer?: typeof import('autoprefixer').Options; cssnano?: typeof import('cssnano').Options } }, sourceMap?: boolean, implementation?: any }}
|
||||||
*/
|
*/
|
||||||
postcss: {
|
postcss: {
|
||||||
postcssOptions: {
|
postcssOptions: {
|
||||||
config: {
|
|
||||||
$resolve: async (val, get) => val ?? (await get('postcss.config')),
|
|
||||||
},
|
|
||||||
plugins: {
|
plugins: {
|
||||||
$resolve: async (val, get) => val ?? (await get('postcss.plugins')),
|
$resolve: async (val, get) => val && typeof val === 'object' ? val : (await get('postcss.plugins')),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@ -32,4 +32,10 @@ export interface ImportsOptions extends UnimportOptions {
|
|||||||
exclude?: RegExp[]
|
exclude?: RegExp[]
|
||||||
include?: RegExp[]
|
include?: RegExp[]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add polyfills for setInterval, requestIdleCallback, and others
|
||||||
|
* @default true
|
||||||
|
*/
|
||||||
|
polyfills?: boolean
|
||||||
}
|
}
|
||||||
|
@ -21,6 +21,12 @@ export interface ModuleMeta {
|
|||||||
*/
|
*/
|
||||||
compatibility?: NuxtCompatibility
|
compatibility?: NuxtCompatibility
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fully resolved path used internally by Nuxt. Do not depend on this value.
|
||||||
|
* @internal
|
||||||
|
*/
|
||||||
|
rawPath?: string
|
||||||
|
|
||||||
[key: string]: unknown
|
[key: string]: unknown
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -83,6 +83,7 @@ export interface NuxtApp {
|
|||||||
|
|
||||||
export interface Nuxt {
|
export interface Nuxt {
|
||||||
// Private fields.
|
// Private fields.
|
||||||
|
__name: string
|
||||||
_version: string
|
_version: string
|
||||||
_ignore?: Ignore
|
_ignore?: Ignore
|
||||||
_dependencies?: Set<string>
|
_dependencies?: Set<string>
|
||||||
@ -96,6 +97,7 @@ export interface Nuxt {
|
|||||||
hook: Nuxt['hooks']['hook']
|
hook: Nuxt['hooks']['hook']
|
||||||
callHook: Nuxt['hooks']['callHook']
|
callHook: Nuxt['hooks']['callHook']
|
||||||
addHooks: Nuxt['hooks']['addHooks']
|
addHooks: Nuxt['hooks']['addHooks']
|
||||||
|
runWithContext: <T extends (...args: any[]) => any>(fn: T) => ReturnType<T>
|
||||||
|
|
||||||
ready: () => Promise<void>
|
ready: () => Promise<void>
|
||||||
close: () => Promise<void>
|
close: () => Promise<void>
|
||||||
|
56
packages/schema/src/utils/definition.ts
Normal file
56
packages/schema/src/utils/definition.ts
Normal file
@ -0,0 +1,56 @@
|
|||||||
|
import type { InputObject } from 'untyped'
|
||||||
|
|
||||||
|
import { defineUntypedSchema } from 'untyped'
|
||||||
|
|
||||||
|
import type { ConfigSchema } from '../../schema/config'
|
||||||
|
|
||||||
|
type KeysOf<T, Prefix extends string | unknown = unknown> = keyof T extends string
|
||||||
|
?
|
||||||
|
{
|
||||||
|
[K in keyof T]: K extends string
|
||||||
|
? string extends K
|
||||||
|
? never // exclude generic 'string' type
|
||||||
|
: unknown extends Prefix
|
||||||
|
? `${K | KeysOf<T[K], K>}`
|
||||||
|
: Prefix extends string
|
||||||
|
? `${Prefix}.${K | KeysOf<T[K], `hey.${Prefix}.${K}`>}`
|
||||||
|
: never
|
||||||
|
: never
|
||||||
|
}[keyof T]
|
||||||
|
: never
|
||||||
|
|
||||||
|
type ReturnFromKey<T, K extends string> = keyof T extends string
|
||||||
|
? K extends keyof T
|
||||||
|
? T[K]
|
||||||
|
: K extends `${keyof T}.${string}`
|
||||||
|
? K extends `${infer Prefix}.${string}`
|
||||||
|
? Prefix extends keyof T
|
||||||
|
? K extends `${Prefix}.${infer Suffix}`
|
||||||
|
? ReturnFromKey<T[Prefix], Suffix>
|
||||||
|
: never
|
||||||
|
: never
|
||||||
|
: never
|
||||||
|
: never
|
||||||
|
: never
|
||||||
|
|
||||||
|
type Awaitable<T> = T | Promise<T>
|
||||||
|
|
||||||
|
interface Resolvers<ReturnValue> {
|
||||||
|
$resolve: (val: unknown, get: <K extends KeysOf<ConfigSchema>>(key: K) => Promise<ReturnFromKey<ConfigSchema, K>>) => Awaitable<ReturnValue>
|
||||||
|
$schema?: InputObject['$schema']
|
||||||
|
$default?: ReturnValue
|
||||||
|
}
|
||||||
|
|
||||||
|
type Resolvable<Namespace> = keyof Exclude<NonNullable<Namespace>, boolean | string | (() => any)> extends string
|
||||||
|
? {
|
||||||
|
[K in keyof Namespace]: Partial<Resolvable<Namespace[K]>> | Resolvers<Namespace[K]>
|
||||||
|
} | Namespace
|
||||||
|
: Namespace | Resolvers<Namespace>
|
||||||
|
|
||||||
|
export function defineResolvers<C extends Partial<Resolvable<ConfigSchema>>> (config: C) {
|
||||||
|
return defineUntypedSchema(config) /* as C */
|
||||||
|
}
|
||||||
|
|
||||||
|
export type ResolvableConfigSchema = Partial<Resolvable<ConfigSchema>>
|
||||||
|
|
||||||
|
export { defineUntypedSchema } from 'untyped'
|
@ -24,7 +24,7 @@
|
|||||||
"jiti": "2.4.2",
|
"jiti": "2.4.2",
|
||||||
"knitwork": "1.2.0",
|
"knitwork": "1.2.0",
|
||||||
"pathe": "2.0.2",
|
"pathe": "2.0.2",
|
||||||
"prettier": "3.4.2",
|
"prettier": "3.5.0",
|
||||||
"scule": "1.3.0",
|
"scule": "1.3.0",
|
||||||
"svgo": "3.3.2",
|
"svgo": "3.3.2",
|
||||||
"tinyexec": "0.3.2",
|
"tinyexec": "0.3.2",
|
||||||
|
@ -1243,8 +1243,8 @@ exports[`template > produces correct output for welcome template 1`] = `
|
|||||||
--un-ring-shadow: var(--un-ring-inset) 0 0 0
|
--un-ring-shadow: var(--un-ring-inset) 0 0 0
|
||||||
calc(var(--un-ring-width) + var(--un-ring-offset-width))
|
calc(var(--un-ring-width) + var(--un-ring-offset-width))
|
||||||
var(--un-ring-color);
|
var(--un-ring-color);
|
||||||
box-shadow: var(--un-ring-offset-shadow), var(--un-ring-shadow),
|
box-shadow:
|
||||||
var(--un-shadow);
|
var(--un-ring-offset-shadow), var(--un-ring-shadow), var(--un-shadow);
|
||||||
}
|
}
|
||||||
.transition-all {
|
.transition-all {
|
||||||
transition-duration: 0.15s;
|
transition-duration: 0.15s;
|
||||||
|
@ -41,7 +41,6 @@
|
|||||||
"defu": "^6.1.4",
|
"defu": "^6.1.4",
|
||||||
"esbuild": "^0.25.0",
|
"esbuild": "^0.25.0",
|
||||||
"escape-string-regexp": "^5.0.0",
|
"escape-string-regexp": "^5.0.0",
|
||||||
"externality": "^1.0.2",
|
|
||||||
"get-port-please": "^3.1.2",
|
"get-port-please": "^3.1.2",
|
||||||
"h3": "npm:h3-nightly@1.14.0-20250122-114730-3f9e703",
|
"h3": "npm:h3-nightly@1.14.0-20250122-114730-3f9e703",
|
||||||
"jiti": "^2.4.2",
|
"jiti": "^2.4.2",
|
||||||
@ -50,12 +49,12 @@
|
|||||||
"mlly": "^1.7.4",
|
"mlly": "^1.7.4",
|
||||||
"pathe": "^2.0.2",
|
"pathe": "^2.0.2",
|
||||||
"pkg-types": "^1.3.1",
|
"pkg-types": "^1.3.1",
|
||||||
"postcss": "^8.5.1",
|
"postcss": "^8.5.2",
|
||||||
"rollup-plugin-visualizer": "^5.14.0",
|
"rollup-plugin-visualizer": "^5.14.0",
|
||||||
"std-env": "^3.8.0",
|
"std-env": "^3.8.0",
|
||||||
"ufo": "^1.5.4",
|
"ufo": "^1.5.4",
|
||||||
"unenv": "^1.10.0",
|
"unenv": "^1.10.0",
|
||||||
"unplugin": "^2.1.2",
|
"unplugin": "^2.2.0",
|
||||||
"vite": "^6.1.0",
|
"vite": "^6.1.0",
|
||||||
"vite-node": "^3.0.5",
|
"vite-node": "^3.0.5",
|
||||||
"vite-plugin-checker": "^0.8.0",
|
"vite-plugin-checker": "^0.8.0",
|
||||||
@ -65,6 +64,6 @@
|
|||||||
"vue": "^3.3.4"
|
"vue": "^3.3.4"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": "^18.20.6 || ^20.9.0 || >=22.0.0"
|
"node": "^18.12.0 || ^20.9.0 || >=22.0.0"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -39,6 +39,9 @@ function createRunner () {
|
|||||||
return new ViteNodeRunner({
|
return new ViteNodeRunner({
|
||||||
root: viteNodeOptions.root, // Equals to Nuxt `srcDir`
|
root: viteNodeOptions.root, // Equals to Nuxt `srcDir`
|
||||||
base: viteNodeOptions.base,
|
base: viteNodeOptions.base,
|
||||||
|
async resolveId (id, importer) {
|
||||||
|
return await viteNodeFetch('/resolve/' + encodeURIComponent(id) + (importer ? '?importer=' + encodeURIComponent(importer) : '')) ?? undefined
|
||||||
|
},
|
||||||
async fetchModule (id) {
|
async fetchModule (id) {
|
||||||
id = id.replace(/\/\//g, '/') // TODO: fix in vite-node
|
id = id.replace(/\/\//g, '/') // TODO: fix in vite-node
|
||||||
return await viteNodeFetch('/module/' + encodeURI(id)).catch((err) => {
|
return await viteNodeFetch('/module/' + encodeURI(id)).catch((err) => {
|
||||||
|
@ -1,36 +0,0 @@
|
|||||||
import type { ExternalsOptions } from 'externality'
|
|
||||||
import { ExternalsDefaults, isExternal } from 'externality'
|
|
||||||
import type { ViteDevServer } from 'vite'
|
|
||||||
import escapeStringRegexp from 'escape-string-regexp'
|
|
||||||
import { withTrailingSlash } from 'ufo'
|
|
||||||
import type { Nuxt } from 'nuxt/schema'
|
|
||||||
import { resolve } from 'pathe'
|
|
||||||
import { toArray } from '.'
|
|
||||||
|
|
||||||
export function createIsExternal (viteServer: ViteDevServer, nuxt: Nuxt) {
|
|
||||||
const externalOpts: ExternalsOptions = {
|
|
||||||
inline: [
|
|
||||||
/virtual:/,
|
|
||||||
/\.ts$/,
|
|
||||||
...ExternalsDefaults.inline || [],
|
|
||||||
...(
|
|
||||||
viteServer.config.ssr.noExternal && viteServer.config.ssr.noExternal !== true
|
|
||||||
? toArray(viteServer.config.ssr.noExternal)
|
|
||||||
: []
|
|
||||||
),
|
|
||||||
],
|
|
||||||
external: [
|
|
||||||
'#shared',
|
|
||||||
new RegExp('^' + escapeStringRegexp(withTrailingSlash(resolve(nuxt.options.rootDir, nuxt.options.dir.shared)))),
|
|
||||||
...(viteServer.config.ssr.external as string[]) || [],
|
|
||||||
/node_modules/,
|
|
||||||
],
|
|
||||||
resolve: {
|
|
||||||
modules: nuxt.options.modulesDir,
|
|
||||||
type: 'module',
|
|
||||||
extensions: ['.ts', '.js', '.json', '.vue', '.mjs', '.jsx', '.tsx', '.wasm'],
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
return (id: string) => isExternal(id, nuxt.options.rootDir, externalOpts)
|
|
||||||
}
|
|
@ -1,18 +1,16 @@
|
|||||||
import { mkdir, writeFile } from 'node:fs/promises'
|
import { mkdir, writeFile } from 'node:fs/promises'
|
||||||
import { pathToFileURL } from 'node:url'
|
import { pathToFileURL } from 'node:url'
|
||||||
import { createApp, createError, defineEventHandler, defineLazyEventHandler, eventHandler, toNodeListener } from 'h3'
|
import { createApp, createError, defineEventHandler, toNodeListener } from 'h3'
|
||||||
import { ViteNodeServer } from 'vite-node/server'
|
import { ViteNodeServer } from 'vite-node/server'
|
||||||
import { isAbsolute, join, normalize, resolve } from 'pathe'
|
import { isAbsolute, join, normalize, resolve } from 'pathe'
|
||||||
// import { addDevServerHandler } from '@nuxt/kit'
|
// import { addDevServerHandler } from '@nuxt/kit'
|
||||||
import { isFileServingAllowed } from 'vite'
|
import { isFileServingAllowed } from 'vite'
|
||||||
import type { ModuleNode, Plugin as VitePlugin } from 'vite'
|
import type { ModuleNode, ViteDevServer, Plugin as VitePlugin } from 'vite'
|
||||||
import { getQuery } from 'ufo'
|
import { getQuery } from 'ufo'
|
||||||
import { normalizeViteManifest } from 'vue-bundle-renderer'
|
import { normalizeViteManifest } from 'vue-bundle-renderer'
|
||||||
import { resolve as resolveModule } from 'mlly'
|
|
||||||
import { distDir } from './dirs'
|
import { distDir } from './dirs'
|
||||||
import type { ViteBuildContext } from './vite'
|
import type { ViteBuildContext } from './vite'
|
||||||
import { isCSS } from './utils'
|
import { isCSS } from './utils'
|
||||||
import { createIsExternal } from './utils/external'
|
|
||||||
|
|
||||||
// TODO: Remove this in favor of registerViteNodeMiddleware
|
// TODO: Remove this in favor of registerViteNodeMiddleware
|
||||||
// after Nitropack or h3 allows adding middleware after setup
|
// after Nitropack or h3 allows adding middleware after setup
|
||||||
@ -101,6 +99,19 @@ function getManifest (ctx: ViteBuildContext) {
|
|||||||
function createViteNodeApp (ctx: ViteBuildContext, invalidates: Set<string> = new Set()) {
|
function createViteNodeApp (ctx: ViteBuildContext, invalidates: Set<string> = new Set()) {
|
||||||
const app = createApp()
|
const app = createApp()
|
||||||
|
|
||||||
|
let _node: ViteNodeServer | undefined
|
||||||
|
function getNode (server: ViteDevServer) {
|
||||||
|
return _node ||= new ViteNodeServer(server, {
|
||||||
|
deps: {
|
||||||
|
inline: [/^#/, /\?/],
|
||||||
|
},
|
||||||
|
transformMode: {
|
||||||
|
ssr: [/.*/],
|
||||||
|
web: [],
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
app.use('/manifest', defineEventHandler(() => {
|
app.use('/manifest', defineEventHandler(() => {
|
||||||
const manifest = getManifest(ctx)
|
const manifest = getManifest(ctx)
|
||||||
return manifest
|
return manifest
|
||||||
@ -112,54 +123,38 @@ function createViteNodeApp (ctx: ViteBuildContext, invalidates: Set<string> = ne
|
|||||||
return ids
|
return ids
|
||||||
}))
|
}))
|
||||||
|
|
||||||
app.use('/module', defineLazyEventHandler(() => {
|
const RESOLVE_RE = /^\/(?<id>[^?]+)(?:\?importer=(?<importer>.*))?$/
|
||||||
const viteServer = ctx.ssrServer!
|
app.use('/resolve', defineEventHandler(async (event) => {
|
||||||
const node = new ViteNodeServer(viteServer, {
|
const { id, importer } = event.path.match(RESOLVE_RE)?.groups || {}
|
||||||
deps: {
|
if (!id || !ctx.ssrServer) {
|
||||||
inline: [
|
throw createError({ statusCode: 400 })
|
||||||
// Common
|
|
||||||
/^#/,
|
|
||||||
/\?/,
|
|
||||||
],
|
|
||||||
},
|
|
||||||
transformMode: {
|
|
||||||
ssr: [/.*/],
|
|
||||||
web: [],
|
|
||||||
},
|
|
||||||
})
|
|
||||||
|
|
||||||
const isExternal = createIsExternal(viteServer, ctx.nuxt)
|
|
||||||
node.shouldExternalize = async (id: string) => {
|
|
||||||
const result = await isExternal(id)
|
|
||||||
if (result?.external) {
|
|
||||||
return resolveModule(result.id, { url: ctx.nuxt.options.modulesDir }).catch(() => false)
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
}
|
||||||
|
return await getNode(ctx.ssrServer).resolveId(decodeURIComponent(id), importer ? decodeURIComponent(importer) : undefined).catch(() => null)
|
||||||
|
}))
|
||||||
|
|
||||||
return eventHandler(async (event) => {
|
app.use('/module', defineEventHandler(async (event) => {
|
||||||
const moduleId = decodeURI(event.path).substring(1)
|
const moduleId = decodeURI(event.path).substring(1)
|
||||||
if (moduleId === '/') {
|
if (moduleId === '/' || !ctx.ssrServer) {
|
||||||
throw createError({ statusCode: 400 })
|
throw createError({ statusCode: 400 })
|
||||||
|
}
|
||||||
|
if (isAbsolute(moduleId) && !isFileServingAllowed(ctx.ssrServer.config, moduleId)) {
|
||||||
|
throw createError({ statusCode: 403 /* Restricted */ })
|
||||||
|
}
|
||||||
|
const node = getNode(ctx.ssrServer)
|
||||||
|
const module = await node.fetchModule(moduleId).catch(async (err) => {
|
||||||
|
const errorData = {
|
||||||
|
code: 'VITE_ERROR',
|
||||||
|
id: moduleId,
|
||||||
|
stack: '',
|
||||||
|
...err,
|
||||||
}
|
}
|
||||||
if (isAbsolute(moduleId) && !isFileServingAllowed(moduleId, viteServer)) {
|
|
||||||
throw createError({ statusCode: 403 /* Restricted */ })
|
|
||||||
}
|
|
||||||
const module = await node.fetchModule(moduleId).catch(async (err) => {
|
|
||||||
const errorData = {
|
|
||||||
code: 'VITE_ERROR',
|
|
||||||
id: moduleId,
|
|
||||||
stack: '',
|
|
||||||
...err,
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!errorData.frame && errorData.code === 'PARSE_ERROR') {
|
if (!errorData.frame && errorData.code === 'PARSE_ERROR') {
|
||||||
errorData.frame = await node.transformModule(moduleId, 'web').then(({ code }) => `${err.message || ''}\n${code}`).catch(() => undefined)
|
errorData.frame = await node.transformModule(moduleId, 'web').then(({ code }) => `${err.message || ''}\n${code}`).catch(() => undefined)
|
||||||
}
|
}
|
||||||
throw createError({ data: errorData })
|
throw createError({ data: errorData })
|
||||||
})
|
|
||||||
return module
|
|
||||||
})
|
})
|
||||||
|
return module
|
||||||
}))
|
}))
|
||||||
|
|
||||||
return app
|
return app
|
||||||
|
@ -37,7 +37,7 @@
|
|||||||
"css-minimizer-webpack-plugin": "^7.0.0",
|
"css-minimizer-webpack-plugin": "^7.0.0",
|
||||||
"cssnano": "^7.0.6",
|
"cssnano": "^7.0.6",
|
||||||
"defu": "^6.1.4",
|
"defu": "^6.1.4",
|
||||||
"esbuild-loader": "^4.2.2",
|
"esbuild-loader": "^4.3.0",
|
||||||
"escape-string-regexp": "^5.0.0",
|
"escape-string-regexp": "^5.0.0",
|
||||||
"file-loader": "^6.2.0",
|
"file-loader": "^6.2.0",
|
||||||
"fork-ts-checker-webpack-plugin": "^9.0.2",
|
"fork-ts-checker-webpack-plugin": "^9.0.2",
|
||||||
@ -51,7 +51,7 @@
|
|||||||
"ohash": "^1.1.4",
|
"ohash": "^1.1.4",
|
||||||
"pathe": "^2.0.2",
|
"pathe": "^2.0.2",
|
||||||
"pify": "^6.1.0",
|
"pify": "^6.1.0",
|
||||||
"postcss": "^8.5.1",
|
"postcss": "^8.5.2",
|
||||||
"postcss-import": "^16.1.0",
|
"postcss-import": "^16.1.0",
|
||||||
"postcss-import-resolver": "^2.0.0",
|
"postcss-import-resolver": "^2.0.0",
|
||||||
"postcss-loader": "^8.1.1",
|
"postcss-loader": "^8.1.1",
|
||||||
@ -61,7 +61,7 @@
|
|||||||
"time-fix-plugin": "^2.0.7",
|
"time-fix-plugin": "^2.0.7",
|
||||||
"ufo": "^1.5.4",
|
"ufo": "^1.5.4",
|
||||||
"unenv": "^1.10.0",
|
"unenv": "^1.10.0",
|
||||||
"unplugin": "^2.1.2",
|
"unplugin": "^2.2.0",
|
||||||
"url-loader": "^4.1.1",
|
"url-loader": "^4.1.1",
|
||||||
"vue-bundle-renderer": "^2.1.1",
|
"vue-bundle-renderer": "^2.1.1",
|
||||||
"vue-loader": "^17.4.2",
|
"vue-loader": "^17.4.2",
|
||||||
@ -73,7 +73,7 @@
|
|||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@nuxt/schema": "workspace:*",
|
"@nuxt/schema": "workspace:*",
|
||||||
"@rspack/core": "1.2.2",
|
"@rspack/core": "1.2.3",
|
||||||
"@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",
|
||||||
@ -85,6 +85,6 @@
|
|||||||
"vue": "^3.3.4"
|
"vue": "^3.3.4"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": "^18.20.6 || ^20.9.0 || >=22.0.0"
|
"node": "^18.12.0 || ^20.9.0 || >=22.0.0"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -10,6 +10,6 @@
|
|||||||
"nuxt": "workspace:*"
|
"nuxt": "workspace:*"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": "^18.20.6 || ^20.9.0 || >=22.0.0"
|
"node": "^18.12.0 || ^20.9.0 || >=22.0.0"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
1428
pnpm-lock.yaml
1428
pnpm-lock.yaml
File diff suppressed because it is too large
Load Diff
@ -2781,8 +2781,14 @@ describe('teleports', () => {
|
|||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
describe('Node.js compatibility for client-side', () => {
|
describe('experimental', () => {
|
||||||
it('should work', async () => {
|
it('decorators support works', async () => {
|
||||||
|
const html = await $fetch('/experimental/decorators')
|
||||||
|
expect(html).toContain('decorated-decorated')
|
||||||
|
expectNoClientErrors('/experimental/decorators')
|
||||||
|
})
|
||||||
|
|
||||||
|
it('Node.js compatibility for client-side', async () => {
|
||||||
const { page } = await renderPage('/experimental/node-compat')
|
const { page } = await renderPage('/experimental/node-compat')
|
||||||
await page.locator('body').getByText('Nuxt is Awesome!').waitFor()
|
await page.locator('body').getByText('Nuxt is Awesome!').waitFor()
|
||||||
expect(await page.innerHTML('body')).toContain('CWD: [available]')
|
expect(await page.innerHTML('body')).toContain('CWD: [available]')
|
||||||
|
2
test/fixtures/basic-types/package.json
vendored
2
test/fixtures/basic-types/package.json
vendored
@ -16,6 +16,6 @@
|
|||||||
"vue-router": "latest"
|
"vue-router": "latest"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": "^18.20.6 || ^20.9.0 || >=22.0.0"
|
"node": "^18.12.0 || ^20.9.0 || >=22.0.0"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
1
test/fixtures/basic/nuxt.config.ts
vendored
1
test/fixtures/basic/nuxt.config.ts
vendored
@ -159,6 +159,7 @@ export default defineNuxtConfig({
|
|||||||
inlineStyles: id => !!id && !id.includes('assets.vue'),
|
inlineStyles: id => !!id && !id.includes('assets.vue'),
|
||||||
},
|
},
|
||||||
experimental: {
|
experimental: {
|
||||||
|
decorators: true,
|
||||||
serverAppConfig: true,
|
serverAppConfig: true,
|
||||||
typedPages: true,
|
typedPages: true,
|
||||||
clientFallback: true,
|
clientFallback: true,
|
||||||
|
2
test/fixtures/basic/package.json
vendored
2
test/fixtures/basic/package.json
vendored
@ -17,6 +17,6 @@
|
|||||||
"vue": "latest"
|
"vue": "latest"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": "^18.20.6 || ^20.9.0 || >=22.0.0"
|
"node": "^18.12.0 || ^20.9.0 || >=22.0.0"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
26
test/fixtures/basic/pages/experimental/decorators.vue
vendored
Normal file
26
test/fixtures/basic/pages/experimental/decorators.vue
vendored
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
function something (_method: () => unknown) {
|
||||||
|
return () => 'decorated'
|
||||||
|
}
|
||||||
|
|
||||||
|
class SomeClass {
|
||||||
|
@something
|
||||||
|
public someMethod () {
|
||||||
|
return 'initial'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const value = new SomeClass().someMethod()
|
||||||
|
|
||||||
|
const { data } = await useFetch('/api/experimental/decorators')
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
{{ value }}-{{ data }}
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
|
||||||
|
</style>
|
14
test/fixtures/basic/server/api/experimental/decorators.ts
vendored
Normal file
14
test/fixtures/basic/server/api/experimental/decorators.ts
vendored
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
export default eventHandler((_event) => {
|
||||||
|
function something (_method: () => unknown) {
|
||||||
|
return () => 'decorated'
|
||||||
|
}
|
||||||
|
|
||||||
|
class SomeClass {
|
||||||
|
@something
|
||||||
|
public someMethod () {
|
||||||
|
return 'initial'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return new SomeClass().someMethod()
|
||||||
|
})
|
2
test/fixtures/hmr/package.json
vendored
2
test/fixtures/hmr/package.json
vendored
@ -8,6 +8,6 @@
|
|||||||
"nuxt": "workspace:*"
|
"nuxt": "workspace:*"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": "^18.20.6 || ^20.9.0 || >=22.0.0"
|
"node": "^18.12.0 || ^20.9.0 || >=22.0.0"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
2
test/fixtures/minimal-pages/package.json
vendored
2
test/fixtures/minimal-pages/package.json
vendored
@ -8,6 +8,6 @@
|
|||||||
"nuxt": "workspace:*"
|
"nuxt": "workspace:*"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": "^18.20.6 || ^20.9.0 || >=22.0.0"
|
"node": "^18.12.0 || ^20.9.0 || >=22.0.0"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
2
test/fixtures/minimal-types/package.json
vendored
2
test/fixtures/minimal-types/package.json
vendored
@ -9,6 +9,6 @@
|
|||||||
"nuxt": "workspace:*"
|
"nuxt": "workspace:*"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": "^18.20.6 || ^20.9.0 || >=22.0.0"
|
"node": "^18.12.0 || ^20.9.0 || >=22.0.0"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
2
test/fixtures/minimal/package.json
vendored
2
test/fixtures/minimal/package.json
vendored
@ -8,6 +8,6 @@
|
|||||||
"nuxt": "workspace:*"
|
"nuxt": "workspace:*"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": "^18.20.6 || ^20.9.0 || >=22.0.0"
|
"node": "^18.12.0 || ^20.9.0 || >=22.0.0"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
2
test/fixtures/runtime-compiler/package.json
vendored
2
test/fixtures/runtime-compiler/package.json
vendored
@ -8,6 +8,6 @@
|
|||||||
"nuxt": "workspace:*"
|
"nuxt": "workspace:*"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": "^18.20.6 || ^20.9.0 || >=22.0.0"
|
"node": "^18.12.0 || ^20.9.0 || >=22.0.0"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
2
test/fixtures/spa-loader/package.json
vendored
2
test/fixtures/spa-loader/package.json
vendored
@ -10,6 +10,6 @@
|
|||||||
"nuxt": "workspace:*"
|
"nuxt": "workspace:*"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": "^18.20.6 || ^20.9.0 || >=22.0.0"
|
"node": "^18.12.0 || ^20.9.0 || >=22.0.0"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
2
test/fixtures/suspense/package.json
vendored
2
test/fixtures/suspense/package.json
vendored
@ -11,6 +11,6 @@
|
|||||||
"typescript": "latest"
|
"typescript": "latest"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": "^18.20.6 || ^20.9.0 || >=22.0.0"
|
"node": "^18.12.0 || ^20.9.0 || >=22.0.0"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
29
test/nuxt/polyfills.test.ts
Normal file
29
test/nuxt/polyfills.test.ts
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
import { mount } from '@vue/test-utils'
|
||||||
|
import { describe, expect, it, vi } from 'vitest'
|
||||||
|
import { defineComponent, h } from 'vue'
|
||||||
|
|
||||||
|
describe('app/compat', () => {
|
||||||
|
const Component = defineComponent({
|
||||||
|
setup () {
|
||||||
|
const visible = ref(false)
|
||||||
|
setInterval(() => {
|
||||||
|
visible.value = true
|
||||||
|
}, 1000)
|
||||||
|
|
||||||
|
return () => h('div', {}, visible.value ? h('span', { id: 'child' }) : {})
|
||||||
|
},
|
||||||
|
})
|
||||||
|
it('setInterval is not auto-imported', async () => {
|
||||||
|
vi.useFakeTimers()
|
||||||
|
|
||||||
|
const wrapper = mount(Component)
|
||||||
|
|
||||||
|
vi.advanceTimersByTime(1000)
|
||||||
|
|
||||||
|
await wrapper.vm.$nextTick()
|
||||||
|
|
||||||
|
expect(wrapper.find('#child').exists()).toBe(true)
|
||||||
|
|
||||||
|
vi.useRealTimers()
|
||||||
|
})
|
||||||
|
})
|
@ -22,6 +22,9 @@ export default defineVitestConfig({
|
|||||||
experimental: {
|
experimental: {
|
||||||
appManifest: process.env.TEST_MANIFEST !== 'manifest-off',
|
appManifest: process.env.TEST_MANIFEST !== 'manifest-off',
|
||||||
},
|
},
|
||||||
|
imports: {
|
||||||
|
polyfills: false,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
Loading…
Reference in New Issue
Block a user