mirror of
https://github.com/nuxt/nuxt.git
synced 2024-11-30 01:17:16 +00:00
Merge branch 'main' into docs/kit
This commit is contained in:
commit
a19074b205
2
.github/workflows/autofix-docs.yml
vendored
2
.github/workflows/autofix-docs.yml
vendored
@ -17,7 +17,7 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@c85c95e3d7251135ab7dc9ce3241c5835cc595a9 # v3.5.3
|
||||
- uses: actions/checkout@f43a0e5ff2bd294095638e18286ca9a3d1956744 # v3.6.0
|
||||
- run: corepack enable
|
||||
- uses: actions/setup-node@5e21ff4d9bc1a8cf6de233a3057d20ec6b3fb69d # v3.8.1
|
||||
with:
|
||||
|
2
.github/workflows/autofix.yml
vendored
2
.github/workflows/autofix.yml
vendored
@ -13,7 +13,7 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@c85c95e3d7251135ab7dc9ce3241c5835cc595a9 # v3.5.3
|
||||
- uses: actions/checkout@f43a0e5ff2bd294095638e18286ca9a3d1956744 # v3.6.0
|
||||
- run: corepack enable
|
||||
- uses: actions/setup-node@5e21ff4d9bc1a8cf6de233a3057d20ec6b3fb69d # v3.8.1
|
||||
with:
|
||||
|
2
.github/workflows/changelogensets.yml
vendored
2
.github/workflows/changelogensets.yml
vendored
@ -19,7 +19,7 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@c85c95e3d7251135ab7dc9ce3241c5835cc595a9 # v3.5.3
|
||||
- uses: actions/checkout@f43a0e5ff2bd294095638e18286ca9a3d1956744 # v3.6.0
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- run: corepack enable
|
||||
|
14
.github/workflows/ci.yml
vendored
14
.github/workflows/ci.yml
vendored
@ -38,7 +38,7 @@ jobs:
|
||||
timeout-minutes: 10
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@c85c95e3d7251135ab7dc9ce3241c5835cc595a9 # v3.5.3
|
||||
- uses: actions/checkout@f43a0e5ff2bd294095638e18286ca9a3d1956744 # v3.6.0
|
||||
- run: corepack enable
|
||||
- uses: actions/setup-node@5e21ff4d9bc1a8cf6de233a3057d20ec6b3fb69d # v3.8.1
|
||||
with:
|
||||
@ -75,7 +75,7 @@ jobs:
|
||||
- build
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@c85c95e3d7251135ab7dc9ce3241c5835cc595a9 # v3.5.3
|
||||
- uses: actions/checkout@f43a0e5ff2bd294095638e18286ca9a3d1956744 # v3.6.0
|
||||
- run: corepack enable
|
||||
- uses: actions/setup-node@5e21ff4d9bc1a8cf6de233a3057d20ec6b3fb69d # v3.8.1
|
||||
with:
|
||||
@ -114,7 +114,7 @@ jobs:
|
||||
module: ['bundler', 'node']
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@c85c95e3d7251135ab7dc9ce3241c5835cc595a9 # v3.5.3
|
||||
- uses: actions/checkout@f43a0e5ff2bd294095638e18286ca9a3d1956744 # v3.6.0
|
||||
- run: corepack enable
|
||||
- uses: actions/setup-node@5e21ff4d9bc1a8cf6de233a3057d20ec6b3fb69d # v3.8.1
|
||||
with:
|
||||
@ -142,7 +142,7 @@ jobs:
|
||||
timeout-minutes: 10
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@c85c95e3d7251135ab7dc9ce3241c5835cc595a9 # v3.5.3
|
||||
- uses: actions/checkout@f43a0e5ff2bd294095638e18286ca9a3d1956744 # v3.6.0
|
||||
- run: corepack enable
|
||||
- uses: actions/setup-node@5e21ff4d9bc1a8cf6de233a3057d20ec6b3fb69d # v3.8.1
|
||||
with:
|
||||
@ -178,7 +178,7 @@ jobs:
|
||||
timeout-minutes: 15
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@c85c95e3d7251135ab7dc9ce3241c5835cc595a9 # v3.5.3
|
||||
- uses: actions/checkout@f43a0e5ff2bd294095638e18286ca9a3d1956744 # v3.6.0
|
||||
- run: corepack enable
|
||||
- uses: actions/setup-node@5e21ff4d9bc1a8cf6de233a3057d20ec6b3fb69d # v3.8.1
|
||||
with:
|
||||
@ -250,7 +250,7 @@ jobs:
|
||||
timeout-minutes: 20
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@c85c95e3d7251135ab7dc9ce3241c5835cc595a9 # v3.5.3
|
||||
- uses: actions/checkout@f43a0e5ff2bd294095638e18286ca9a3d1956744 # v3.6.0
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- run: corepack enable
|
||||
@ -289,7 +289,7 @@ jobs:
|
||||
timeout-minutes: 20
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@c85c95e3d7251135ab7dc9ce3241c5835cc595a9 # v3.5.3
|
||||
- uses: actions/checkout@f43a0e5ff2bd294095638e18286ca9a3d1956744 # v3.6.0
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- run: corepack enable
|
||||
|
2
.github/workflows/dependency-review.yml
vendored
2
.github/workflows/dependency-review.yml
vendored
@ -17,6 +17,6 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: 'Checkout Repository'
|
||||
uses: actions/checkout@c85c95e3d7251135ab7dc9ce3241c5835cc595a9 # v3.5.3
|
||||
uses: actions/checkout@f43a0e5ff2bd294095638e18286ca9a3d1956744 # v3.6.0
|
||||
- name: 'Dependency Review'
|
||||
uses: actions/dependency-review-action@f6fff72a3217f580d5afd49a46826795305b63c7 # v3.0.8
|
||||
|
2
.github/workflows/docs-e2e.yml
vendored
2
.github/workflows/docs-e2e.yml
vendored
@ -21,7 +21,7 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@c85c95e3d7251135ab7dc9ce3241c5835cc595a9 # v3.5.3
|
||||
- uses: actions/checkout@f43a0e5ff2bd294095638e18286ca9a3d1956744 # v3.6.0
|
||||
- run: corepack enable
|
||||
- uses: actions/setup-node@5e21ff4d9bc1a8cf6de233a3057d20ec6b3fb69d # v3.8.1
|
||||
with:
|
||||
|
2
.github/workflows/docs.yml
vendored
2
.github/workflows/docs.yml
vendored
@ -20,7 +20,7 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@c85c95e3d7251135ab7dc9ce3241c5835cc595a9 # v3.5.3
|
||||
- uses: actions/checkout@f43a0e5ff2bd294095638e18286ca9a3d1956744 # v3.6.0
|
||||
- run: corepack enable
|
||||
- uses: actions/setup-node@5e21ff4d9bc1a8cf6de233a3057d20ec6b3fb69d # v3.8.1
|
||||
with:
|
||||
|
2
.github/workflows/introspect.yml
vendored
2
.github/workflows/introspect.yml
vendored
@ -21,7 +21,7 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@c85c95e3d7251135ab7dc9ce3241c5835cc595a9 # v3.5.3
|
||||
- uses: actions/checkout@f43a0e5ff2bd294095638e18286ca9a3d1956744 # v3.6.0
|
||||
# From https://github.com/rhysd/actionlint/blob/main/docs/usage.md#use-actionlint-on-github-actions
|
||||
- name: Check workflow files
|
||||
run: |
|
||||
|
2
.github/workflows/nuxt2-edge.yml
vendored
2
.github/workflows/nuxt2-edge.yml
vendored
@ -21,7 +21,7 @@ jobs:
|
||||
permissions:
|
||||
id-token: write
|
||||
steps:
|
||||
- uses: actions/checkout@c85c95e3d7251135ab7dc9ce3241c5835cc595a9 # v3.5.3
|
||||
- uses: actions/checkout@f43a0e5ff2bd294095638e18286ca9a3d1956744 # v3.6.0
|
||||
with:
|
||||
ref: '2.x'
|
||||
fetch-depth: 0 # All history
|
||||
|
2
.github/workflows/release-pr.yml
vendored
2
.github/workflows/release-pr.yml
vendored
@ -29,7 +29,7 @@ jobs:
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- uses: actions/checkout@c85c95e3d7251135ab7dc9ce3241c5835cc595a9 # v3.5.3
|
||||
- uses: actions/checkout@f43a0e5ff2bd294095638e18286ca9a3d1956744 # v3.6.0
|
||||
with:
|
||||
ref: refs/pull/${{ github.event.issue.number }}/merge
|
||||
fetch-depth: 0
|
||||
|
2
.github/workflows/reproduire.yml
vendored
2
.github/workflows/reproduire.yml
vendored
@ -10,7 +10,7 @@ jobs:
|
||||
reproduire:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@c85c95e3d7251135ab7dc9ce3241c5835cc595a9 # v3.5.3
|
||||
- uses: actions/checkout@f43a0e5ff2bd294095638e18286ca9a3d1956744 # v3.6.0
|
||||
- uses: Hebilicious/reproduire@4b686ae9cbb72dad60f001d278b6e3b2ce40a9ac # v0.0.9-mp
|
||||
with:
|
||||
label: needs reproduction
|
||||
|
2
.github/workflows/scorecards.yml
vendored
2
.github/workflows/scorecards.yml
vendored
@ -31,7 +31,7 @@ jobs:
|
||||
|
||||
steps:
|
||||
- name: "Checkout code"
|
||||
uses: actions/checkout@c85c95e3d7251135ab7dc9ce3241c5835cc595a9 # v3.5.3
|
||||
uses: actions/checkout@f43a0e5ff2bd294095638e18286ca9a3d1956744 # v3.6.0
|
||||
with:
|
||||
persist-credentials: false
|
||||
|
||||
|
1
.gitignore
vendored
1
.gitignore
vendored
@ -4,6 +4,7 @@ jspm_packages
|
||||
|
||||
package-lock.json
|
||||
packages/*/README.md
|
||||
!packages/nuxi/README.md
|
||||
packages/*/LICENSE
|
||||
*/**/yarn.lock
|
||||
/.yarn
|
||||
|
@ -6,7 +6,6 @@
|
||||
"@nuxt/test-utils": "./packages/test-utils",
|
||||
"@nuxt/vite": "./packages/vite",
|
||||
"@nuxt/webpack": "./packages/webpack",
|
||||
"nuxi": "./packages/nuxi",
|
||||
"nuxt": "./packages/nuxt"
|
||||
}
|
||||
}
|
||||
|
@ -88,6 +88,10 @@ npm install
|
||||
pnpm install
|
||||
```
|
||||
|
||||
```bash [bun]
|
||||
bun install
|
||||
```
|
||||
|
||||
::
|
||||
|
||||
## Development Server
|
||||
@ -108,6 +112,10 @@ npm run dev -- -o
|
||||
pnpm dev -o
|
||||
```
|
||||
|
||||
```bash [bun]
|
||||
bun run dev -o
|
||||
```
|
||||
|
||||
::
|
||||
|
||||
::alert{type=success icon=✨ .font-bold}
|
||||
|
@ -7,4 +7,4 @@ head.title: "node_modules/"
|
||||
|
||||
# Node modules Directory
|
||||
|
||||
The package manager ([`npm`](https://docs.npmjs.com/cli/v7/commands/npm) or [`yarn`](https://yarnpkg.com/) or [`pnpm`](https://pnpm.io/cli/install)) creates the [`node_modules/` directory](/docs/guide/directory-structure/node_modules) to store the dependencies of your project.
|
||||
The package manager ([`npm`](https://docs.npmjs.com/cli/v7/commands/npm) or [`yarn`](https://yarnpkg.com/) or [`pnpm`](https://pnpm.io/cli/install) or [`bun`](https://bun.sh/package-manager)) creates the [`node_modules/` directory](/docs/guide/directory-structure/node_modules) to store the dependencies of your project.
|
||||
|
@ -14,7 +14,7 @@ Nuxt automatically scans files inside these directories to register API and serv
|
||||
|
||||
Each file should export a default function defined with `defineEventHandler()` or `eventHandler()` (alias).
|
||||
|
||||
The handler can directly return JSON data, a `Promise` or use `event.node.res.end()` to send a response.
|
||||
The handler can directly return JSON data, a `Promise`, or use `event.node.res.end()` to send a response.
|
||||
|
||||
**Example:** Create the `/api/hello` route with `server/api/hello.ts` file:
|
||||
|
||||
|
@ -30,7 +30,7 @@ Update `nuxt` dependency inside `package.json`:
|
||||
}
|
||||
```
|
||||
|
||||
Remove lockfile (`package-lock.json`, `yarn.lock`, or `pnpm-lock.yaml`) and reinstall dependencies.
|
||||
Remove lockfile (`package-lock.json`, `yarn.lock`, `pnpm-lock.yaml`, or `bun.lockb`) and reinstall dependencies.
|
||||
|
||||
## Opting Out From the Edge Channel
|
||||
|
||||
@ -45,7 +45,7 @@ Update `nuxt` dependency inside `package.json`:
|
||||
}
|
||||
```
|
||||
|
||||
Remove lockfile (`package-lock.json`, `yarn.lock`, or `pnpm-lock.yaml`) and reinstall dependencies.
|
||||
Remove lockfile (`package-lock.json`, `yarn.lock`, `pnpm-lock.yaml`, or `bun.lockb`) and reinstall dependencies.
|
||||
|
||||
## Using Latest `nuxi` CLI From Edge
|
||||
|
||||
|
@ -12,24 +12,24 @@ Within your pages, components, and plugins you can use useAsyncData to get acces
|
||||
## Type
|
||||
|
||||
```ts [Signature]
|
||||
function useAsyncData(
|
||||
function useAsyncData<DataT, DataE>(
|
||||
handler: (nuxtApp?: NuxtApp) => Promise<DataT>,
|
||||
options?: AsyncDataOptions<DataT>
|
||||
): AsyncData<DataT>
|
||||
function useAsyncData(
|
||||
): AsyncData<DataT, DataE>
|
||||
function useAsyncData<DataT, DataE>(
|
||||
key: string,
|
||||
handler: (nuxtApp?: NuxtApp) => Promise<DataT>,
|
||||
options?: AsyncDataOptions<DataT>
|
||||
): Promise<AsyncData<DataT>>
|
||||
): Promise<AsyncData<DataT, DataE>
|
||||
|
||||
type AsyncDataOptions<DataT> = {
|
||||
server?: boolean
|
||||
lazy?: boolean
|
||||
immediate?: boolean
|
||||
default?: () => DataT | Ref<DataT> | null
|
||||
transform?: (input: DataT) => DataT
|
||||
pick?: string[]
|
||||
watch?: WatchSource[]
|
||||
immediate?: boolean
|
||||
}
|
||||
|
||||
type AsyncData<DataT, ErrorT> = {
|
||||
@ -53,13 +53,13 @@ type AsyncDataRequestStatus = 'idle' | 'pending' | 'success' | 'error'
|
||||
* **key**: a unique key to ensure that data fetching can be properly de-duplicated across requests. If you do not provide a key, then a key that is unique to the file name and line number of the instance of [`useAsyncData`](/docs/api/composables/use-async-data) will be generated for you.
|
||||
* **handler**: an asynchronous function that returns a value
|
||||
* **options**:
|
||||
* _lazy_: whether to resolve the async function after loading the route, instead of blocking client-side navigation (defaults to `false`)
|
||||
* _default_: a factory function to set the default value of the data, before the async function resolves - particularly useful with the `lazy: true` option
|
||||
* _server_: whether to fetch the data on the server (defaults to `true`)
|
||||
* _lazy_: whether to resolve the async function after loading the route, instead of blocking client-side navigation (defaults to `false`)
|
||||
* _immediate_: when set to `false`, will prevent the request from firing immediately. (defaults to `true`)
|
||||
* _default_: a factory function to set the default value of the `data`, before the async function resolves - useful with the `lazy: true` or `immediate: false` option
|
||||
* _transform_: a function that can be used to alter `handler` function result after resolving
|
||||
* _pick_: only pick specified keys in this array from the `handler` function result
|
||||
* _watch_: watch reactive sources to auto-refresh
|
||||
* _immediate_: When set to `false`, will prevent the request from firing immediately. (defaults to `true`)
|
||||
|
||||
Under the hood, `lazy: false` uses `<Suspense>` to block the loading of the route before the data has been fetched. Consider using `lazy: true` and implementing a loading state instead for a snappier user experience.
|
||||
|
||||
|
@ -10,12 +10,12 @@ It automatically generates a key based on URL and fetch options, provides type h
|
||||
## Type
|
||||
|
||||
```ts [Signature]
|
||||
function useFetch(
|
||||
function useFetch<DataT, ErrorT>(
|
||||
url: string | Request | Ref<string | Request> | () => string | Request,
|
||||
options?: UseFetchOptions<DataT>
|
||||
): Promise<AsyncData<DataT>>
|
||||
): Promise<AsyncData<DataT, ErrorT>>
|
||||
|
||||
type UseFetchOptions = {
|
||||
type UseFetchOptions<DataT> = {
|
||||
key?: string
|
||||
method?: string
|
||||
query?: SearchParams
|
||||
@ -29,7 +29,7 @@ type UseFetchOptions = {
|
||||
default?: () => DataT
|
||||
transform?: (input: DataT) => DataT
|
||||
pick?: string[]
|
||||
watch?: WatchSource[]
|
||||
watch?: WatchSource[] | false
|
||||
}
|
||||
|
||||
type AsyncData<DataT, ErrorT> = {
|
||||
@ -63,14 +63,15 @@ type AsyncDataRequestStatus = 'idle' | 'pending' | 'success' | 'error'
|
||||
All fetch options can be given a `computed` or `ref` value. These will be watched and new requests made automatically with any new values if they are updated.
|
||||
::
|
||||
|
||||
* **Options (from [`useAsyncData`](/docs/api/composables/use-async-data) )**:
|
||||
* `key`: a unique key to ensure that data fetching can be properly de-duplicated across requests, if not provided, it will be generated based on the static code location where [`useAsyncData`](/docs/api/composables/use-async-data) is used.
|
||||
* `server`: Whether to fetch the data on the server (defaults to `true`).
|
||||
* `default`: A factory function to set the default value of the data, before the async function resolves - particularly useful with the `lazy: true` option.
|
||||
* `pick`: Only pick specified keys in this array from the `handler` function result.
|
||||
* `watch`: Watch an array of reactive sources and auto-refresh the fetch result when they change. Fetch options and URL are watched by default. You can completely ignore reactive sources by using `watch: false`. Together with `immediate: false`, this allows for a fully-manual `useFetch`.
|
||||
* `transform`: A function that can be used to alter `handler` function result after resolving.
|
||||
* `immediate`: When set to `false`, will prevent the request from firing immediately. (defaults to `true`)
|
||||
* **Options (from `useAsyncData`)**:
|
||||
* `key`: a unique key to ensure that data fetching can be properly de-duplicated across requests, if not provided, it will be generated based on the static code location where `useAsyncData` is used.
|
||||
* `server`: whether to fetch the data on the server (defaults to `true`)
|
||||
* `lazy`: whether to resolve the async function after loading the route, instead of blocking client-side navigation (defaults to `false`)
|
||||
* `immediate`: when set to `false`, will prevent the request from firing immediately. (defaults to `true`)
|
||||
* `default`: a factory function to set the default value of the `data`, before the async function resolves - useful with the `lazy: true` or `immediate: false` option
|
||||
* `transform`: a function that can be used to alter `handler` function result after resolving
|
||||
* `pick`: only pick specified keys in this array from the `handler` function result
|
||||
* `watch`: watch an array of reactive sources and auto-refresh the fetch result when they change. Fetch options and URL are watched by default. You can completely ignore reactive sources by using `watch: false`. Together with `immediate: false`, this allows for a fully-manual `useFetch`.
|
||||
|
||||
::alert{type=warning}
|
||||
If you provide a function or ref as the `url` parameter, or if you provide functions as arguments to the `options` parameter, then the [`useFetch`](/docs/api/composables/use-fetch) call will not match other [`useFetch`](/docs/api/composables/use-fetch) calls elsewhere in your codebase, even if the options seem to be identical. If you wish to force a match, you may provide your own key in `options`.
|
||||
|
@ -12,7 +12,7 @@ Within your pages, components, and plugins you can use `useRequestEvent` to acce
|
||||
const event = useRequestEvent()
|
||||
|
||||
// Get the URL
|
||||
const url = event.node.req.url
|
||||
const url = event.path
|
||||
```
|
||||
|
||||
::alert{icon=👉}
|
||||
|
@ -157,7 +157,7 @@ We recommend using [VS Code](https://code.visualstudio.com/) along with the [ESL
|
||||
|
||||
#### No Prettier
|
||||
|
||||
Since ESLint is already configured to format the code, there is no need to duplicate the functionality with Prettier. To format the code, you can run `yarn lint --fix` or `pnpm lint --fix` or referring the [ESLint section](#use-eslint) for IDE Setup.
|
||||
Since ESLint is already configured to format the code, there is no need to duplicate the functionality with Prettier. To format the code, you can run `yarn lint --fix`, `pnpm lint --fix`, or `bun run lint --fix` or referring the [ESLint section](#use-eslint) for IDE Setup.
|
||||
|
||||
If you have Prettier installed in your editor, we recommend you disable it when working on the project to avoid conflict.
|
||||
|
||||
|
21
package.json
21
package.json
@ -33,11 +33,10 @@
|
||||
"@nuxt/test-utils": "workspace:*",
|
||||
"@nuxt/vite-builder": "workspace:*",
|
||||
"@nuxt/webpack-builder": "workspace:*",
|
||||
"nuxi": "workspace:*",
|
||||
"nuxt": "workspace:*",
|
||||
"vite": "4.4.9",
|
||||
"vue": "3.3.4",
|
||||
"magic-string": "^0.30.2"
|
||||
"magic-string": "^0.30.3"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@actions/core": "1.10.0",
|
||||
@ -45,11 +44,11 @@
|
||||
"@nuxt/webpack-builder": "workspace:*",
|
||||
"@nuxtjs/eslint-config-typescript": "12.0.0",
|
||||
"@types/fs-extra": "11.0.1",
|
||||
"@types/node": "18.17.6",
|
||||
"@types/node": "18.17.11",
|
||||
"@types/semver": "7.5.0",
|
||||
"case-police": "0.6.1",
|
||||
"chalk": "5.3.0",
|
||||
"changelogen": "0.5.4",
|
||||
"changelogen": "0.5.5",
|
||||
"cheerio": "1.0.0-rc.12",
|
||||
"consola": "3.2.3",
|
||||
"devalue": "4.3.2",
|
||||
@ -61,21 +60,21 @@
|
||||
"fs-extra": "11.1.1",
|
||||
"globby": "13.2.2",
|
||||
"h3": "1.8.0",
|
||||
"happy-dom": "10.10.4",
|
||||
"happy-dom": "10.11.0",
|
||||
"jiti": "1.19.3",
|
||||
"markdownlint-cli": "^0.33.0",
|
||||
"nitropack": "2.5.2",
|
||||
"nuxi": "workspace:*",
|
||||
"nitropack": "2.6.0",
|
||||
"nuxi": "npm:nuxi-ng@0.3.0-1692970235.c259efa",
|
||||
"nuxt": "workspace:*",
|
||||
"nuxt-vitest": "0.10.2",
|
||||
"ofetch": "1.1.1",
|
||||
"ofetch": "1.3.3",
|
||||
"pathe": "1.1.1",
|
||||
"playwright-core": "1.37.1",
|
||||
"rimraf": "5.0.1",
|
||||
"semver": "7.5.4",
|
||||
"std-env": "3.4.0",
|
||||
"typescript": "5.1.6",
|
||||
"ufo": "1.2.0",
|
||||
"std-env": "3.4.3",
|
||||
"typescript": "5.2.2",
|
||||
"ufo": "1.3.0",
|
||||
"vite": "4.4.9",
|
||||
"vitest": "0.33.0",
|
||||
"vitest-environment-nuxt": "0.10.2",
|
||||
|
@ -34,9 +34,9 @@
|
||||
"pkg-types": "^1.0.3",
|
||||
"scule": "^1.0.0",
|
||||
"semver": "^7.5.4",
|
||||
"ufo": "^1.2.0",
|
||||
"ufo": "^1.3.0",
|
||||
"unctx": "^2.3.1",
|
||||
"unimport": "^3.1.3",
|
||||
"unimport": "^3.2.0",
|
||||
"untyped": "^1.4.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
@ -44,7 +44,7 @@
|
||||
"@types/lodash-es": "4.17.8",
|
||||
"@types/semver": "7.5.0",
|
||||
"lodash-es": "4.17.21",
|
||||
"nitropack": "2.5.2",
|
||||
"nitropack": "2.6.0",
|
||||
"unbuild": "latest",
|
||||
"vite": "4.4.9",
|
||||
"vitest": "0.33.0",
|
||||
|
@ -1,6 +1,6 @@
|
||||
import { existsSync, readFileSync } from 'node:fs'
|
||||
import ignore from 'ignore'
|
||||
import { join, relative } from 'pathe'
|
||||
import { join, relative, resolve } from 'pathe'
|
||||
import { tryUseNuxt } from './context'
|
||||
|
||||
/**
|
||||
@ -16,14 +16,7 @@ export function isIgnored (pathname: string): boolean {
|
||||
|
||||
if (!nuxt._ignore) {
|
||||
nuxt._ignore = ignore(nuxt.options.ignoreOptions)
|
||||
const resolvedIgnore = nuxt.options.ignore.flatMap(s => resolveGroupSyntax(s))
|
||||
|
||||
nuxt._ignore.add(resolvedIgnore)
|
||||
|
||||
const nuxtignoreFile = join(nuxt.options.rootDir, '.nuxtignore')
|
||||
if (existsSync(nuxtignoreFile)) {
|
||||
nuxt._ignore.add(readFileSync(nuxtignoreFile, 'utf-8'))
|
||||
}
|
||||
nuxt._ignore.add(resolveIgnorePatterns())
|
||||
}
|
||||
|
||||
const cwds = nuxt.options._layers?.map(layer => layer.cwd).sort((a, b) => b.length - a.length)
|
||||
@ -35,6 +28,31 @@ export function isIgnored (pathname: string): boolean {
|
||||
return !!(relativePath && nuxt._ignore.ignores(relativePath))
|
||||
}
|
||||
|
||||
export function resolveIgnorePatterns (relativePath?: string): string[] {
|
||||
const nuxt = tryUseNuxt()
|
||||
|
||||
// Happens with CLI reloads
|
||||
if (!nuxt) {
|
||||
return []
|
||||
}
|
||||
|
||||
if (!nuxt._ignorePatterns) {
|
||||
nuxt._ignorePatterns = nuxt.options.ignore.flatMap(s => resolveGroupSyntax(s))
|
||||
|
||||
const nuxtignoreFile = join(nuxt.options.rootDir, '.nuxtignore')
|
||||
if (existsSync(nuxtignoreFile)) {
|
||||
const contents = readFileSync(nuxtignoreFile, 'utf-8')
|
||||
nuxt._ignorePatterns.push(...contents.trim().split(/\r?\n/))
|
||||
}
|
||||
}
|
||||
|
||||
if (relativePath) {
|
||||
return nuxt._ignorePatterns.map(p => p.startsWith('*') || p.startsWith('!*') ? p : relative(relativePath, resolve(nuxt.options.rootDir, p)))
|
||||
}
|
||||
|
||||
return nuxt._ignorePatterns
|
||||
}
|
||||
|
||||
/**
|
||||
* This function turns string containing groups '**\/*.{spec,test}.{js,ts}' into an array of strings.
|
||||
* For example will '**\/*.{spec,test}.{js,ts}' be resolved to:
|
||||
|
@ -14,7 +14,7 @@ export * from './build'
|
||||
export * from './compatibility'
|
||||
export * from './components'
|
||||
export * from './context'
|
||||
export { isIgnored } from './ignore'
|
||||
export { isIgnored, resolveIgnorePatterns } from './ignore'
|
||||
export * from './layout'
|
||||
export * from './pages'
|
||||
export * from './plugin'
|
||||
|
5
packages/nuxi/README.md
Normal file
5
packages/nuxi/README.md
Normal file
@ -0,0 +1,5 @@
|
||||
# Nuxt CLI (nuxi)
|
||||
|
||||
⚡️ Next Generation CLI Experience for [Nuxt](https://nuxt.com/).
|
||||
|
||||
- 👉 View on GitHub at https://github.com/nuxt/cli
|
@ -1,2 +0,0 @@
|
||||
#!/usr/bin/env node
|
||||
import('../dist/cli-wrapper.mjs')
|
@ -1,31 +0,0 @@
|
||||
import { defineBuildConfig } from 'unbuild'
|
||||
|
||||
export default defineBuildConfig({
|
||||
declaration: true,
|
||||
rollup: {
|
||||
inlineDependencies: true,
|
||||
resolve: {
|
||||
exportConditions: ['production', 'node'] as any
|
||||
}
|
||||
},
|
||||
entries: [
|
||||
'src/cli',
|
||||
'src/cli-run',
|
||||
'src/cli-wrapper',
|
||||
'src/index'
|
||||
],
|
||||
externals: [
|
||||
'@nuxt/kit',
|
||||
'@nuxt/schema',
|
||||
'@nuxt/test-utils',
|
||||
'fsevents',
|
||||
// TODO: Fix rollup/unbuild issue
|
||||
'node:url',
|
||||
'node:buffer',
|
||||
'node:path',
|
||||
'node:child_process',
|
||||
'node:process',
|
||||
'node:path',
|
||||
'node:os'
|
||||
]
|
||||
})
|
@ -1,60 +0,0 @@
|
||||
{
|
||||
"name": "nuxi",
|
||||
"version": "3.6.5",
|
||||
"repository": "nuxt/nuxt",
|
||||
"license": "MIT",
|
||||
"type": "module",
|
||||
"types": "./dist/index.d.ts",
|
||||
"exports": {
|
||||
".": "./dist/index.mjs",
|
||||
"./cli": "./bin/nuxi.mjs"
|
||||
},
|
||||
"bin": "./bin/nuxi.mjs",
|
||||
"files": [
|
||||
"bin",
|
||||
"dist"
|
||||
],
|
||||
"scripts": {
|
||||
"prepack": "unbuild"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@nuxt/kit": "workspace:../kit",
|
||||
"@nuxt/schema": "workspace:../schema",
|
||||
"@types/clear": "0.1.2",
|
||||
"@types/flat": "5.0.2",
|
||||
"@types/mri": "1.1.1",
|
||||
"@types/semver": "7.5.0",
|
||||
"c12": "1.4.2",
|
||||
"chokidar": "3.5.3",
|
||||
"clear": "0.1.0",
|
||||
"clipboardy": "3.0.0",
|
||||
"colorette": "2.0.20",
|
||||
"consola": "3.2.3",
|
||||
"deep-object-diff": "1.1.9",
|
||||
"defu": "6.1.2",
|
||||
"destr": "2.0.1",
|
||||
"execa": "7.2.0",
|
||||
"flat": "5.0.2",
|
||||
"giget": "1.1.2",
|
||||
"h3": "1.8.0",
|
||||
"jiti": "1.19.3",
|
||||
"listhen": "1.3.0",
|
||||
"mlly": "1.4.0",
|
||||
"mri": "1.2.0",
|
||||
"nitropack": "2.5.2",
|
||||
"ohash": "1.1.3",
|
||||
"pathe": "1.1.1",
|
||||
"perfect-debounce": "1.0.0",
|
||||
"pkg-types": "1.0.3",
|
||||
"scule": "1.0.0",
|
||||
"semver": "7.5.4",
|
||||
"ufo": "1.2.0",
|
||||
"unbuild": "latest"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"fsevents": "~2.3.2"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^14.18.0 || >=16.10.0"
|
||||
}
|
||||
}
|
@ -1,5 +0,0 @@
|
||||
// @ts-expect-error internal property for tracking start time
|
||||
process._startTime = Date.now()
|
||||
|
||||
// @ts-expect-error `default` property is not declared
|
||||
import('./cli').then(r => (r.default || r).main())
|
@ -1,58 +0,0 @@
|
||||
/**
|
||||
* This file is used to wrap the CLI entrypoint in a restartable process.
|
||||
*/
|
||||
import { fileURLToPath } from 'node:url'
|
||||
import { fork } from 'node:child_process'
|
||||
import type { ChildProcess } from 'node:child_process'
|
||||
|
||||
const cliEntry = new URL('../dist/cli-run.mjs', import.meta.url)
|
||||
|
||||
// Only enable wrapper for nuxt dev command
|
||||
if (process.argv[2] === 'dev') {
|
||||
process.env.__CLI_ARGV__ = JSON.stringify(process.argv)
|
||||
startSubprocess()
|
||||
} else {
|
||||
import(cliEntry.href)
|
||||
}
|
||||
|
||||
function startSubprocess () {
|
||||
let childProc: ChildProcess | undefined
|
||||
|
||||
const onShutdown = () => {
|
||||
if (childProc) {
|
||||
childProc.kill()
|
||||
childProc = undefined
|
||||
}
|
||||
}
|
||||
|
||||
process.on('exit', onShutdown)
|
||||
process.on('SIGTERM', onShutdown) // Graceful shutdown
|
||||
process.on('SIGINT', onShutdown) // Ctrl-C
|
||||
process.on('SIGQUIT', onShutdown) // Ctrl-\
|
||||
|
||||
start()
|
||||
|
||||
function start () {
|
||||
const _argv: string[] = (process.env.__CLI_ARGV__ ? JSON.parse(process.env.__CLI_ARGV__) : process.argv).slice(2)
|
||||
const execArguments: string[] = getInspectArgs()
|
||||
|
||||
childProc = fork(fileURLToPath(cliEntry), [], { execArgv: execArguments })
|
||||
childProc.on('close', (code) => { if (code) { process.exit(code) } })
|
||||
childProc.on('message', (message) => {
|
||||
if ((message as { type: string })?.type === 'nuxt:restart') {
|
||||
childProc?.kill()
|
||||
startSubprocess()
|
||||
}
|
||||
})
|
||||
|
||||
function getInspectArgs (): string[] {
|
||||
const inspectArgv = _argv.find(argvItem => argvItem.includes('--inspect'))
|
||||
|
||||
if (!inspectArgv) {
|
||||
return []
|
||||
}
|
||||
|
||||
return [inspectArgv]
|
||||
}
|
||||
}
|
||||
}
|
@ -1,87 +0,0 @@
|
||||
import mri from 'mri'
|
||||
import { red } from 'colorette'
|
||||
import type { ConsolaReporter } from 'consola'
|
||||
import { consola } from 'consola'
|
||||
import { checkEngines } from './utils/engines'
|
||||
import type { Command, NuxtCommand } from './commands'
|
||||
import { commands } from './commands'
|
||||
import { showHelp } from './utils/help'
|
||||
import { showBanner } from './utils/banner'
|
||||
|
||||
async function _main () {
|
||||
const _argv = (process.env.__CLI_ARGV__ ? JSON.parse(process.env.__CLI_ARGV__) : process.argv).slice(2)
|
||||
const args = mri(_argv, {
|
||||
boolean: [
|
||||
'no-clear'
|
||||
]
|
||||
})
|
||||
const command = args._.shift() || 'usage'
|
||||
|
||||
showBanner(command === 'dev' && args.clear !== false && !args.help)
|
||||
|
||||
if (!(command in commands)) {
|
||||
console.log('\n' + red('Invalid command ' + command))
|
||||
|
||||
await commands.usage().then(r => r.invoke())
|
||||
process.exit(1)
|
||||
}
|
||||
|
||||
// Check Node.js version in background
|
||||
setTimeout(() => { checkEngines().catch(() => {}) }, 1000)
|
||||
|
||||
const cmd = await commands[command as Command]() as NuxtCommand
|
||||
if (args.h || args.help) {
|
||||
showHelp(cmd.meta)
|
||||
} else {
|
||||
const result = await cmd.invoke(args)
|
||||
return result
|
||||
}
|
||||
}
|
||||
|
||||
// Wrap all console logs with consola for better DX
|
||||
consola.wrapAll()
|
||||
|
||||
// Filter out unwanted logs
|
||||
// TODO: Use better API from consola for intercepting logs
|
||||
const wrapReporter = (reporter: ConsolaReporter) => ({
|
||||
log (logObj, ctx) {
|
||||
if (!logObj.args || !logObj.args.length) { return }
|
||||
const msg = logObj.args[0]
|
||||
if (typeof msg === 'string' && !process.env.DEBUG) {
|
||||
// Hide vue-router 404 warnings
|
||||
if (msg.startsWith('[Vue Router warn]: No match found for location with path')) {
|
||||
return
|
||||
}
|
||||
// Suppress warning about native Node.js fetch
|
||||
if (msg.includes('ExperimentalWarning: The Fetch API is an experimental feature')) {
|
||||
return
|
||||
}
|
||||
// TODO: resolve upstream in Vite
|
||||
// Hide sourcemap warnings related to node_modules
|
||||
if (msg.startsWith('Sourcemap') && msg.includes('node_modules')) {
|
||||
return
|
||||
}
|
||||
}
|
||||
return reporter.log(logObj, ctx)
|
||||
}
|
||||
}) satisfies ConsolaReporter
|
||||
|
||||
consola.options.reporters = consola.options.reporters.map(wrapReporter)
|
||||
|
||||
process.on('unhandledRejection', err => consola.error('[unhandledRejection]', err))
|
||||
process.on('uncaughtException', err => consola.error('[uncaughtException]', err))
|
||||
|
||||
export function main () {
|
||||
_main()
|
||||
.then((result) => {
|
||||
if (result === 'error') {
|
||||
process.exit(1)
|
||||
} else if (result !== 'wait') {
|
||||
process.exit()
|
||||
}
|
||||
})
|
||||
.catch((error) => {
|
||||
consola.error(error)
|
||||
process.exit(1)
|
||||
})
|
||||
}
|
@ -1,62 +0,0 @@
|
||||
import { existsSync, promises as fsp } from 'node:fs'
|
||||
import { dirname, resolve } from 'pathe'
|
||||
import { consola } from 'consola'
|
||||
import { loadKit } from '../utils/kit'
|
||||
import { templates } from '../utils/templates'
|
||||
import { defineNuxtCommand } from './index'
|
||||
|
||||
export default defineNuxtCommand({
|
||||
meta: {
|
||||
name: 'add',
|
||||
usage: `npx nuxi add [--cwd] [--force] ${Object.keys(templates).join('|')} <name>`,
|
||||
description: 'Create a new template file.'
|
||||
},
|
||||
async invoke (args) {
|
||||
const cwd = resolve(args.cwd || '.')
|
||||
|
||||
const template = args._[0]
|
||||
const name = args._[1]
|
||||
|
||||
// Validate template name
|
||||
if (!templates[template]) {
|
||||
consola.error(`Template ${template} is not supported. Possible values: ${Object.keys(templates).join(', ')}`)
|
||||
process.exit(1)
|
||||
}
|
||||
|
||||
// Validate options
|
||||
if (!name) {
|
||||
consola.error('name argument is missing!')
|
||||
process.exit(1)
|
||||
}
|
||||
|
||||
// Load config in order to respect srcDir
|
||||
const kit = await loadKit(cwd)
|
||||
const config = await kit.loadNuxtConfig({ cwd })
|
||||
|
||||
// Resolve template
|
||||
const res = templates[template]({ name, args })
|
||||
|
||||
// Resolve full path to generated file
|
||||
const path = resolve(config.srcDir, res.path)
|
||||
|
||||
// Ensure not overriding user code
|
||||
if (!args.force && existsSync(path)) {
|
||||
consola.error(`File exists: ${path} . Use --force to override or use a different name.`)
|
||||
process.exit(1)
|
||||
}
|
||||
|
||||
// Ensure parent directory exists
|
||||
const parentDir = dirname(path)
|
||||
if (!existsSync(parentDir)) {
|
||||
consola.info('Creating directory', parentDir)
|
||||
if (template === 'page') {
|
||||
consola.info('This enables vue-router functionality!')
|
||||
}
|
||||
await fsp.mkdir(parentDir, { recursive: true })
|
||||
}
|
||||
|
||||
// Write file
|
||||
await fsp.writeFile(path, res.contents.trim() + '\n')
|
||||
consola.info(`🪄 Generated a new ${template} in ${path}`)
|
||||
}
|
||||
})
|
@ -1,110 +0,0 @@
|
||||
import { promises as fsp } from 'node:fs'
|
||||
import { join, resolve } from 'pathe'
|
||||
import { createApp, eventHandler, lazyEventHandler, toNodeListener } from 'h3'
|
||||
import { listen } from 'listhen'
|
||||
import type { NuxtAnalyzeMeta } from '@nuxt/schema'
|
||||
import { defu } from 'defu'
|
||||
import { loadKit } from '../utils/kit'
|
||||
import { clearDir } from '../utils/fs'
|
||||
import { overrideEnv } from '../utils/env'
|
||||
import { defineNuxtCommand } from './index'
|
||||
|
||||
export default defineNuxtCommand({
|
||||
meta: {
|
||||
name: 'analyze',
|
||||
usage: 'npx nuxi analyze [--log-level] [--name] [--no-serve] [rootDir]',
|
||||
description: 'Build nuxt and analyze production bundle (experimental)'
|
||||
},
|
||||
async invoke (args, options = {}) {
|
||||
overrideEnv('production')
|
||||
|
||||
const name = args.name || 'default'
|
||||
const slug = name.trim().replace(/[^a-z0-9_-]/gi, '_')
|
||||
const rootDir = resolve(args._[0] || '.')
|
||||
|
||||
let analyzeDir = join(rootDir, '.nuxt/analyze', slug)
|
||||
let buildDir = join(analyzeDir, '.nuxt')
|
||||
let outDir = join(analyzeDir, '.output')
|
||||
|
||||
const startTime = Date.now()
|
||||
|
||||
const { loadNuxt, buildNuxt } = await loadKit(rootDir)
|
||||
|
||||
const nuxt = await loadNuxt({
|
||||
rootDir,
|
||||
overrides: defu(options.overrides, {
|
||||
build: {
|
||||
analyze: true
|
||||
},
|
||||
analyzeDir,
|
||||
buildDir,
|
||||
nitro: {
|
||||
output: {
|
||||
dir: outDir
|
||||
}
|
||||
},
|
||||
logLevel: args['log-level']
|
||||
})
|
||||
})
|
||||
|
||||
analyzeDir = nuxt.options.analyzeDir
|
||||
buildDir = nuxt.options.buildDir
|
||||
outDir = nuxt.options.nitro.output?.dir || outDir
|
||||
|
||||
await clearDir(analyzeDir)
|
||||
await buildNuxt(nuxt)
|
||||
|
||||
const endTime = Date.now()
|
||||
|
||||
const meta: NuxtAnalyzeMeta = {
|
||||
name,
|
||||
slug,
|
||||
startTime,
|
||||
endTime,
|
||||
analyzeDir,
|
||||
buildDir,
|
||||
outDir
|
||||
}
|
||||
|
||||
await nuxt.callHook('build:analyze:done', meta)
|
||||
await fsp.writeFile(join(analyzeDir, 'meta.json'), JSON.stringify(meta, null, 2), 'utf-8')
|
||||
|
||||
console.info('Analyze results are available at: `' + analyzeDir + '`')
|
||||
console.warn('Do not deploy analyze results! Use `nuxi build` before deploying.')
|
||||
|
||||
if (args.serve !== false && !process.env.CI) {
|
||||
const app = createApp()
|
||||
|
||||
const serveFile = (filePath: string) => lazyEventHandler(async () => {
|
||||
const contents = await fsp.readFile(filePath, 'utf-8')
|
||||
return eventHandler((event) => { event.node.res.end(contents) })
|
||||
})
|
||||
|
||||
console.info('Starting stats server...')
|
||||
|
||||
app.use('/client', serveFile(join(analyzeDir, 'client.html')))
|
||||
app.use('/nitro', serveFile(join(analyzeDir, 'nitro.html')))
|
||||
app.use(eventHandler(() => `<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Nuxt Bundle Stats (experimental)</title>
|
||||
</head>
|
||||
<h1>Nuxt Bundle Stats (experimental)</h1>
|
||||
<ul>
|
||||
<li>
|
||||
<a href="/nitro">Nitro server bundle stats</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="/client">Client bundle stats</a>
|
||||
</li>
|
||||
</ul>
|
||||
</html>
|
||||
`))
|
||||
|
||||
await listen(toNodeListener(app))
|
||||
|
||||
return 'wait' as const
|
||||
}
|
||||
}
|
||||
})
|
@ -1,34 +0,0 @@
|
||||
import { execa } from 'execa'
|
||||
import { consola } from 'consola'
|
||||
import { resolve } from 'pathe'
|
||||
import { tryResolveModule } from '../utils/esm'
|
||||
import { defineNuxtCommand } from './index'
|
||||
|
||||
const MODULE_BUILDER_PKG = '@nuxt/module-builder'
|
||||
|
||||
export default defineNuxtCommand({
|
||||
meta: {
|
||||
name: 'build-module',
|
||||
usage: 'npx nuxi build-module [--stub] [rootDir]',
|
||||
description: `Helper command for using ${MODULE_BUILDER_PKG}`
|
||||
},
|
||||
async invoke (args) {
|
||||
// Find local installed version
|
||||
const rootDir = resolve(args._[0] || '.')
|
||||
const hasLocal = await tryResolveModule(`${MODULE_BUILDER_PKG}/package.json`, rootDir)
|
||||
|
||||
const execArgs = Object.entries({
|
||||
'--stub': args.stub,
|
||||
'--prepare': args.prepare
|
||||
}).filter(([, value]) => value).map(([key]) => key)
|
||||
|
||||
let cmd = 'nuxt-module-build'
|
||||
if (!hasLocal) {
|
||||
consola.warn(`Cannot find locally installed version of \`${MODULE_BUILDER_PKG}\` (>=0.2.0). Falling back to \`npx ${MODULE_BUILDER_PKG}\``)
|
||||
cmd = 'npx'
|
||||
execArgs.unshift(MODULE_BUILDER_PKG)
|
||||
}
|
||||
|
||||
await execa(cmd, execArgs, { preferLocal: true, stdio: 'inherit', cwd: rootDir })
|
||||
}
|
||||
})
|
@ -1,70 +0,0 @@
|
||||
import { relative, resolve } from 'pathe'
|
||||
import { consola } from 'consola'
|
||||
import type { Nitro } from 'nitropack'
|
||||
|
||||
// we are deliberately inlining this code as a backup in case user has `@nuxt/schema<3.7`
|
||||
import { writeTypes as writeTypesLegacy } from '../../../kit/src/template'
|
||||
import { loadKit } from '../utils/kit'
|
||||
import { clearBuildDir } from '../utils/fs'
|
||||
import { overrideEnv } from '../utils/env'
|
||||
import { showVersions } from '../utils/banner'
|
||||
import { defineNuxtCommand } from './index'
|
||||
|
||||
export default defineNuxtCommand({
|
||||
meta: {
|
||||
name: 'build',
|
||||
usage: 'npx nuxi build [--prerender] [--dotenv] [--log-level] [rootDir]',
|
||||
description: 'Build nuxt for production deployment'
|
||||
},
|
||||
async invoke (args, options = {}) {
|
||||
overrideEnv('production')
|
||||
|
||||
const rootDir = resolve(args._[0] || '.')
|
||||
showVersions(rootDir)
|
||||
|
||||
const { loadNuxt, buildNuxt, useNitro, writeTypes = writeTypesLegacy } = await loadKit(rootDir)
|
||||
|
||||
const nuxt = await loadNuxt({
|
||||
rootDir,
|
||||
dotenv: {
|
||||
cwd: rootDir,
|
||||
fileName: args.dotenv
|
||||
},
|
||||
overrides: {
|
||||
logLevel: args['log-level'],
|
||||
// TODO: remove in 3.8
|
||||
_generate: args.prerender,
|
||||
...(args.prerender ? { nitro: { static: true } } : {}),
|
||||
...(options?.overrides || {})
|
||||
}
|
||||
})
|
||||
|
||||
let nitro: Nitro | undefined
|
||||
// In Bridge, if nitro is not enabled, useNitro will throw an error
|
||||
try {
|
||||
// Use ? for backward compatibility for Nuxt <= RC.10
|
||||
nitro = useNitro?.()
|
||||
} catch {}
|
||||
|
||||
await clearBuildDir(nuxt.options.buildDir)
|
||||
|
||||
await writeTypes(nuxt)
|
||||
|
||||
nuxt.hook('build:error', (err) => {
|
||||
consola.error('Nuxt Build Error:', err)
|
||||
process.exit(1)
|
||||
})
|
||||
|
||||
await buildNuxt(nuxt)
|
||||
|
||||
if (args.prerender) {
|
||||
if (!nuxt.options.ssr) {
|
||||
consola.warn('HTML content not prerendered because `ssr: false` was set. You can read more in `https://nuxt.com/docs/getting-started/deployment#static-hosting`.')
|
||||
}
|
||||
// TODO: revisit later if/when nuxt build --prerender will output hybrid
|
||||
const dir = nitro?.options.output.publicDir
|
||||
const publicDir = dir ? relative(process.cwd(), dir) : '.output/public'
|
||||
consola.success(`You can now deploy \`${publicDir}\` to any static hosting!`)
|
||||
}
|
||||
}
|
||||
})
|
@ -1,15 +0,0 @@
|
||||
import { resolve } from 'pathe'
|
||||
import { cleanupNuxtDirs } from '../utils/nuxt'
|
||||
import { defineNuxtCommand } from './index'
|
||||
|
||||
export default defineNuxtCommand({
|
||||
meta: {
|
||||
name: 'cleanup',
|
||||
usage: 'npx nuxi clean|cleanup',
|
||||
description: 'Cleanup generated nuxt files and caches'
|
||||
},
|
||||
async invoke (args) {
|
||||
const rootDir = resolve(args._[0] || '.')
|
||||
await cleanupNuxtDirs(rootDir)
|
||||
}
|
||||
})
|
@ -1,190 +0,0 @@
|
||||
import type { AddressInfo } from 'node:net'
|
||||
import type { RequestListener } from 'node:http'
|
||||
import { relative, resolve } from 'pathe'
|
||||
import chokidar from 'chokidar'
|
||||
import { debounce } from 'perfect-debounce'
|
||||
import type { Nuxt } from '@nuxt/schema'
|
||||
import { consola } from 'consola'
|
||||
import { withTrailingSlash } from 'ufo'
|
||||
import { setupDotenv } from 'c12'
|
||||
|
||||
// we are deliberately inlining this code as a backup in case user has `@nuxt/schema<3.7`
|
||||
import { writeTypes as writeTypesLegacy } from '../../../kit/src/template'
|
||||
import { showBanner, showVersions } from '../utils/banner'
|
||||
import { loadKit } from '../utils/kit'
|
||||
import { importModule } from '../utils/esm'
|
||||
import { overrideEnv } from '../utils/env'
|
||||
import { loadNuxtManifest, writeNuxtManifest } from '../utils/nuxt'
|
||||
import { clearBuildDir } from '../utils/fs'
|
||||
import { defineNuxtCommand } from './index'
|
||||
|
||||
export default defineNuxtCommand({
|
||||
meta: {
|
||||
name: 'dev',
|
||||
usage: 'npx nuxi dev [rootDir] [--dotenv] [--log-level] [--clipboard] [--open, -o] [--port, -p] [--host, -h] [--https] [--ssl-cert] [--ssl-key]',
|
||||
description: 'Run nuxt development server'
|
||||
},
|
||||
async invoke (args, options = {}) {
|
||||
overrideEnv('development')
|
||||
|
||||
const rootDir = resolve(args._[0] || '.')
|
||||
showVersions(rootDir)
|
||||
|
||||
await setupDotenv({ cwd: rootDir, fileName: args.dotenv })
|
||||
|
||||
const { loadNuxt, loadNuxtConfig, buildNuxt, writeTypes = writeTypesLegacy } = await loadKit(rootDir)
|
||||
|
||||
const config = await loadNuxtConfig({
|
||||
cwd: rootDir,
|
||||
overrides: {
|
||||
dev: true,
|
||||
logLevel: args['log-level'],
|
||||
...(options.overrides || {})
|
||||
}
|
||||
})
|
||||
|
||||
const { listen } = await import('listhen')
|
||||
const { toNodeListener } = await import('h3')
|
||||
let currentHandler: RequestListener | undefined
|
||||
let loadingMessage = 'Nuxt is starting...'
|
||||
const loadingHandler: RequestListener = async (_req, res) => {
|
||||
const loadingTemplate = config.devServer.loadingTemplate ?? await importModule('@nuxt/ui-templates', config.modulesDir).then(r => r.loading)
|
||||
res.setHeader('Content-Type', 'text/html; charset=UTF-8')
|
||||
res.statusCode = 503 // Service Unavailable
|
||||
res.end(loadingTemplate({ loading: loadingMessage }))
|
||||
}
|
||||
const serverHandler: RequestListener = (req, res) => {
|
||||
return currentHandler ? currentHandler(req, res) : loadingHandler(req, res)
|
||||
}
|
||||
|
||||
const listener = await listen(serverHandler, {
|
||||
showURL: false,
|
||||
clipboard: args.clipboard,
|
||||
open: args.open || args.o,
|
||||
port: args.port || args.p || process.env.NUXT_PORT || process.env.NITRO_PORT || config.devServer.port,
|
||||
hostname: args.host || args.h || process.env.NUXT_HOST || process.env.NITRO_HOST || config.devServer.host,
|
||||
https: (args.https !== false && (args.https || config.devServer.https))
|
||||
? {
|
||||
cert: args['ssl-cert'] || process.env.NUXT_SSL_CERT || process.env.NITRO_SSL_CERT || (typeof config.devServer.https !== 'boolean' && config.devServer.https.cert) || undefined,
|
||||
key: args['ssl-key'] || process.env.NUXT_SSL_KEY || process.env.NITRO_SSL_KEY || (typeof config.devServer.https !== 'boolean' && config.devServer.https.key) || undefined
|
||||
}
|
||||
: false
|
||||
})
|
||||
|
||||
let currentNuxt: Nuxt
|
||||
let distWatcher: chokidar.FSWatcher
|
||||
|
||||
const showURL = () => {
|
||||
listener.showURL({
|
||||
// TODO: Normalize URL with trailing slash within schema
|
||||
baseURL: withTrailingSlash(currentNuxt?.options.app.baseURL) || '/'
|
||||
})
|
||||
}
|
||||
async function hardRestart (reason?: string) {
|
||||
if (process.send) {
|
||||
await listener.close().catch(() => {})
|
||||
await currentNuxt.close().catch(() => {})
|
||||
await watcher.close().catch(() => {})
|
||||
await distWatcher.close().catch(() => {})
|
||||
consola.info(`${reason ? reason + '. ' : ''}Restarting nuxt...`)
|
||||
process.send({ type: 'nuxt:restart' })
|
||||
} else {
|
||||
await load(true, reason)
|
||||
}
|
||||
}
|
||||
const load = async (isRestart: boolean, reason?: string) => {
|
||||
try {
|
||||
loadingMessage = `${reason ? reason + '. ' : ''}${isRestart ? 'Restarting' : 'Starting'} nuxt...`
|
||||
currentHandler = undefined
|
||||
if (isRestart) {
|
||||
consola.info(loadingMessage)
|
||||
}
|
||||
if (currentNuxt) {
|
||||
await currentNuxt.close()
|
||||
}
|
||||
if (distWatcher) {
|
||||
await distWatcher.close()
|
||||
}
|
||||
|
||||
currentNuxt = await loadNuxt({
|
||||
rootDir,
|
||||
dev: true,
|
||||
ready: false,
|
||||
overrides: {
|
||||
logLevel: args['log-level'],
|
||||
vite: {
|
||||
clearScreen: args.clear
|
||||
},
|
||||
...(options.overrides || {})
|
||||
}
|
||||
})
|
||||
|
||||
if (!isRestart) {
|
||||
showURL()
|
||||
}
|
||||
|
||||
// Write manifest and also check if we need cache invalidation
|
||||
if (!isRestart) {
|
||||
const previousManifest = await loadNuxtManifest(currentNuxt.options.buildDir)
|
||||
const newManifest = await writeNuxtManifest(currentNuxt)
|
||||
if (previousManifest && newManifest && previousManifest._hash !== newManifest._hash) {
|
||||
await clearBuildDir(currentNuxt.options.buildDir)
|
||||
}
|
||||
}
|
||||
|
||||
await currentNuxt.ready()
|
||||
|
||||
const unsub = currentNuxt.hooks.hook('restart', async (options) => {
|
||||
unsub() // we use this instead of `hookOnce` for Nuxt Bridge support
|
||||
if (options?.hard) { return hardRestart() }
|
||||
await load(true)
|
||||
})
|
||||
|
||||
await currentNuxt.hooks.callHook('listen', listener.server, listener)
|
||||
const address = (listener.server.address() || {}) as AddressInfo
|
||||
currentNuxt.options.devServer.url = listener.url
|
||||
currentNuxt.options.devServer.port = address.port
|
||||
currentNuxt.options.devServer.host = address.address
|
||||
currentNuxt.options.devServer.https = listener.https
|
||||
|
||||
await Promise.all([
|
||||
writeTypes(currentNuxt).catch(console.error),
|
||||
buildNuxt(currentNuxt)
|
||||
])
|
||||
|
||||
distWatcher = chokidar.watch(resolve(currentNuxt.options.buildDir, 'dist'), { ignoreInitial: true, depth: 0 })
|
||||
distWatcher.on('unlinkDir', () => {
|
||||
dLoad(true, '.nuxt/dist directory has been removed')
|
||||
})
|
||||
|
||||
currentHandler = toNodeListener(currentNuxt.server.app)
|
||||
if (isRestart && args.clear !== false) {
|
||||
showBanner()
|
||||
showURL()
|
||||
}
|
||||
} catch (err) {
|
||||
consola.error(`Cannot ${isRestart ? 'restart' : 'start'} nuxt: `, err)
|
||||
currentHandler = undefined
|
||||
loadingMessage = 'Error while loading nuxt. Please check console and fix errors.'
|
||||
}
|
||||
}
|
||||
|
||||
// Watch for config changes
|
||||
// TODO: Watcher service, modules, and requireTree
|
||||
const dLoad = debounce(load)
|
||||
const watcher = chokidar.watch([rootDir], { ignoreInitial: true, depth: 0 })
|
||||
watcher.on('all', (_event, _file) => {
|
||||
const file = relative(rootDir, _file)
|
||||
if (file === (args.dotenv || '.env')) { return hardRestart('.env updated') }
|
||||
if (RESTART_RE.test(file)) {
|
||||
dLoad(true, `${file} updated`)
|
||||
}
|
||||
})
|
||||
|
||||
await load(false)
|
||||
|
||||
return 'wait' as const
|
||||
}
|
||||
})
|
||||
|
||||
const RESTART_RE = /^(nuxt\.config\.(js|ts|mjs|cjs)|\.nuxtignore|\.nuxtrc)$/
|
@ -1,24 +0,0 @@
|
||||
import { resolve } from 'pathe'
|
||||
import { execa } from 'execa'
|
||||
import { showHelp } from '../utils/help'
|
||||
import { defineNuxtCommand } from './index'
|
||||
|
||||
export default defineNuxtCommand({
|
||||
meta: {
|
||||
name: 'enable',
|
||||
usage: 'npx nuxi devtools enable|disable [rootDir]',
|
||||
description: 'Enable or disable features in a Nuxt project'
|
||||
},
|
||||
async invoke (args) {
|
||||
const [command, _rootDir = '.'] = args._
|
||||
const rootDir = resolve(_rootDir)
|
||||
|
||||
if (!['enable', 'disable'].includes(command)) {
|
||||
console.error(`Unknown command \`${command}\`.`)
|
||||
showHelp(this.meta)
|
||||
process.exit(1)
|
||||
}
|
||||
|
||||
await execa('npx', ['@nuxt/devtools-wizard@latest', command, rootDir], { stdio: 'inherit', cwd: rootDir })
|
||||
}
|
||||
})
|
@ -1,14 +0,0 @@
|
||||
import buildCommand from './build'
|
||||
import { defineNuxtCommand } from './index'
|
||||
|
||||
export default defineNuxtCommand({
|
||||
meta: {
|
||||
name: 'generate',
|
||||
usage: 'npx nuxi generate [rootDir] [--dotenv]',
|
||||
description: 'Build Nuxt and prerender static routes'
|
||||
},
|
||||
async invoke (args, options = {}) {
|
||||
args.prerender = true
|
||||
await buildCommand.invoke(args, options)
|
||||
}
|
||||
})
|
@ -1,46 +0,0 @@
|
||||
import type { Argv } from 'mri'
|
||||
|
||||
const _rDefault = (r: any) => r.default || r
|
||||
|
||||
export const commands = {
|
||||
dev: () => import('./dev').then(_rDefault),
|
||||
build: () => import('./build').then(_rDefault),
|
||||
'build-module': () => import('./build-module').then(_rDefault),
|
||||
cleanup: () => import('./cleanup').then(_rDefault),
|
||||
clean: () => import('./cleanup').then(_rDefault),
|
||||
preview: () => import('./preview').then(_rDefault),
|
||||
start: () => import('./preview').then(_rDefault),
|
||||
analyze: () => import('./analyze').then(_rDefault),
|
||||
generate: () => import('./generate').then(_rDefault),
|
||||
prepare: () => import('./prepare').then(_rDefault),
|
||||
typecheck: () => import('./typecheck').then(_rDefault),
|
||||
usage: () => import('./usage').then(_rDefault),
|
||||
info: () => import('./info').then(_rDefault),
|
||||
init: () => import('./init').then(_rDefault),
|
||||
create: () => import('./init').then(_rDefault),
|
||||
devtools: () => import('./devtools').then(_rDefault),
|
||||
upgrade: () => import('./upgrade').then(_rDefault),
|
||||
test: () => import('./test').then(_rDefault),
|
||||
add: () => import('./add').then(_rDefault),
|
||||
new: () => import('./add').then(_rDefault)
|
||||
}
|
||||
|
||||
export type Command = keyof typeof commands
|
||||
|
||||
export interface NuxtCommandMeta {
|
||||
name: string;
|
||||
usage: string;
|
||||
description: string;
|
||||
[key: string]: any;
|
||||
}
|
||||
|
||||
export type CLIInvokeResult = void | 'error' | 'wait'
|
||||
|
||||
export interface NuxtCommand {
|
||||
invoke(args: Argv, options?: Record<string, any>): Promise<CLIInvokeResult> | CLIInvokeResult
|
||||
meta: NuxtCommandMeta
|
||||
}
|
||||
|
||||
export function defineNuxtCommand (command: NuxtCommand): NuxtCommand {
|
||||
return command
|
||||
}
|
@ -1,161 +0,0 @@
|
||||
import os from 'node:os'
|
||||
import { existsSync, readFileSync } from 'node:fs'
|
||||
import { createRequire } from 'node:module'
|
||||
import { resolve } from 'pathe'
|
||||
import jiti from 'jiti'
|
||||
import destr from 'destr'
|
||||
import type { PackageJson } from 'pkg-types'
|
||||
import { splitByCase } from 'scule'
|
||||
import clipboardy from 'clipboardy'
|
||||
import type { NuxtModule } from '@nuxt/schema'
|
||||
import type { packageManagerLocks } from '../utils/packageManagers'
|
||||
import { getPackageManager, getPackageManagerVersion } from '../utils/packageManagers'
|
||||
import { findup } from '../utils/fs'
|
||||
import { defineNuxtCommand } from './index'
|
||||
|
||||
export default defineNuxtCommand({
|
||||
meta: {
|
||||
name: 'info',
|
||||
usage: 'npx nuxi info [rootDir]',
|
||||
description: 'Get information about nuxt project'
|
||||
},
|
||||
async invoke (args) {
|
||||
// Resolve rootDir
|
||||
const rootDir = resolve(args._[0] || '.')
|
||||
|
||||
// Load nuxt.config
|
||||
const nuxtConfig = getNuxtConfig(rootDir)
|
||||
|
||||
// Find nearest package.json
|
||||
const { dependencies = {}, devDependencies = {} } = findPackage(rootDir)
|
||||
|
||||
// Utils to query a dependency version
|
||||
const getDepVersion = (name: string) => getPkg(name, rootDir)?.version || dependencies[name] || devDependencies[name]
|
||||
|
||||
const listModules = (arr = []) => arr
|
||||
.map(m => normalizeConfigModule(m, rootDir))
|
||||
.filter(Boolean)
|
||||
.map((name) => {
|
||||
const npmName = name!.split('/').splice(0, 2).join('/') // @foo/bar/baz => @foo/bar
|
||||
const v = getDepVersion(npmName)
|
||||
return '`' + (v ? `${name}@${v}` : name) + '`'
|
||||
})
|
||||
.join(', ')
|
||||
|
||||
// Check nuxt version
|
||||
const nuxtVersion = getDepVersion('nuxt') || getDepVersion('nuxt-edge') || getDepVersion('nuxt3') || '0.0.0'
|
||||
const isNuxt3 = nuxtVersion.startsWith('3')
|
||||
const builder = isNuxt3
|
||||
? nuxtConfig.builder /* latest schema */ || (nuxtConfig.vite !== false ? 'vite' : 'webpack') /* previous schema */
|
||||
: nuxtConfig.bridge?.vite
|
||||
? 'vite' /* bridge vite implementation */
|
||||
: (nuxtConfig.buildModules?.includes('nuxt-vite')
|
||||
? 'vite' /* nuxt-vite */
|
||||
: 'webpack')
|
||||
|
||||
let packageManager: keyof typeof packageManagerLocks | 'unknown' | null = getPackageManager(rootDir)
|
||||
if (packageManager) {
|
||||
packageManager += '@' + getPackageManagerVersion(packageManager)
|
||||
} else {
|
||||
packageManager = 'unknown'
|
||||
}
|
||||
|
||||
const infoObj = {
|
||||
OperatingSystem: os.type(),
|
||||
NodeVersion: process.version,
|
||||
NuxtVersion: nuxtVersion,
|
||||
NitroVersion: getDepVersion('nitropack'),
|
||||
PackageManager: packageManager,
|
||||
Builder: builder,
|
||||
UserConfig: Object.keys(nuxtConfig).map(key => '`' + key + '`').join(', '),
|
||||
RuntimeModules: listModules(nuxtConfig.modules),
|
||||
BuildModules: listModules(nuxtConfig.buildModules || [])
|
||||
}
|
||||
|
||||
console.log('RootDir:', rootDir)
|
||||
|
||||
let maxLength = 0
|
||||
const entries = Object.entries(infoObj).map(([key, val]) => {
|
||||
const label = splitByCase(key).join(' ')
|
||||
if (label.length > maxLength) { maxLength = label.length }
|
||||
return [label, val || '-']
|
||||
})
|
||||
let infoStr = ''
|
||||
for (const [label, value] of entries) {
|
||||
infoStr += '- ' + (label + ': ').padEnd(maxLength + 2) + (value.includes('`') ? value : '`' + value + '`') + '\n'
|
||||
}
|
||||
|
||||
const copied = await clipboardy.write(infoStr).then(() => true).catch(() => false)
|
||||
const splitter = '------------------------------'
|
||||
console.log(`Nuxt project info: ${copied ? '(copied to clipboard)' : ''}\n\n${splitter}\n${infoStr}${splitter}\n`)
|
||||
|
||||
const isNuxt3OrBridge = infoObj.NuxtVersion.startsWith('3') || infoObj.BuildModules.includes('bridge')
|
||||
console.log([
|
||||
'👉 Report an issue: https://github.com/nuxt/nuxt/issues/new',
|
||||
'👉 Suggest an improvement: https://github.com/nuxt/nuxt/discussions/new',
|
||||
`👉 Read documentation: ${isNuxt3OrBridge ? 'https://nuxt.com' : 'https://v2.nuxt.com'}`
|
||||
].join('\n\n') + '\n')
|
||||
}
|
||||
})
|
||||
|
||||
function normalizeConfigModule (module: NuxtModule | string | null | undefined, rootDir: string): string | null {
|
||||
if (!module) {
|
||||
return null
|
||||
}
|
||||
if (typeof module === 'string') {
|
||||
return module
|
||||
.split(rootDir).pop()! // Strip rootDir
|
||||
.split('node_modules').pop()! // Strip node_modules
|
||||
.replace(/^\//, '')
|
||||
}
|
||||
if (typeof module === 'function') {
|
||||
return `${module.name}()`
|
||||
}
|
||||
if (Array.isArray(module)) {
|
||||
return normalizeConfigModule(module[0], rootDir)
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
function getNuxtConfig (rootDir: string) {
|
||||
try {
|
||||
(globalThis as any).defineNuxtConfig = (c: any) => c
|
||||
const result = jiti(rootDir, { interopDefault: true, esmResolve: true })('./nuxt.config')
|
||||
delete (globalThis as any).defineNuxtConfig
|
||||
return result
|
||||
} catch (err) {
|
||||
// TODO: Show error as warning if it is not 404
|
||||
return {}
|
||||
}
|
||||
}
|
||||
|
||||
function getPkg (name: string, rootDir: string) {
|
||||
// Assume it is in {rootDir}/node_modules/${name}/package.json
|
||||
let pkgPath = resolve(rootDir, 'node_modules', name, 'package.json')
|
||||
|
||||
// Try to resolve for more accuracy
|
||||
const _require = createRequire(rootDir)
|
||||
try { pkgPath = _require.resolve(name + '/package.json') } catch (_err) {
|
||||
// console.log('not found:', name)
|
||||
}
|
||||
|
||||
return readJSONSync(pkgPath) as PackageJson
|
||||
}
|
||||
|
||||
function findPackage (rootDir: string) {
|
||||
return findup(rootDir, (dir) => {
|
||||
const p = resolve(dir, 'package.json')
|
||||
if (existsSync(p)) {
|
||||
return readJSONSync(p) as PackageJson
|
||||
}
|
||||
}) || {}
|
||||
}
|
||||
|
||||
function readJSONSync (filePath: string) {
|
||||
try {
|
||||
return destr(readFileSync(filePath, 'utf-8'))
|
||||
} catch (err) {
|
||||
// TODO: Warn error
|
||||
return null
|
||||
}
|
||||
}
|
@ -1,68 +0,0 @@
|
||||
import { writeFile } from 'node:fs/promises'
|
||||
import { downloadTemplate, startShell } from 'giget'
|
||||
import { relative } from 'pathe'
|
||||
import { consola } from 'consola'
|
||||
import { defineNuxtCommand } from './index'
|
||||
|
||||
const rpath = (p: string) => relative(process.cwd(), p)
|
||||
|
||||
const DEFAULT_REGISTRY = 'https://raw.githubusercontent.com/nuxt/starter/templates/templates'
|
||||
|
||||
export default defineNuxtCommand({
|
||||
meta: {
|
||||
name: 'init',
|
||||
usage: 'npx nuxi init|create [--template,-t] [--force] [--offline] [--prefer-offline] [--shell] [dir]',
|
||||
description: 'Initialize a fresh project'
|
||||
},
|
||||
async invoke (args) {
|
||||
// Clone template
|
||||
const template = args.template || args.t || 'v3'
|
||||
|
||||
if (typeof template === 'boolean') {
|
||||
consola.error('Please specify a template!')
|
||||
process.exit(1)
|
||||
}
|
||||
|
||||
let t
|
||||
|
||||
try {
|
||||
t = await downloadTemplate(template, {
|
||||
dir: args._[0] as string,
|
||||
force: args.force,
|
||||
offline: args.offline,
|
||||
preferOffline: args['prefer-offline'],
|
||||
registry: process.env.NUXI_INIT_REGISTRY || DEFAULT_REGISTRY
|
||||
})
|
||||
} catch (err) {
|
||||
if (process.env.DEBUG) {
|
||||
throw err
|
||||
}
|
||||
consola.error((err as Error).toString())
|
||||
process.exit(1)
|
||||
}
|
||||
|
||||
// Show next steps
|
||||
const relativeDist = rpath(t.dir)
|
||||
|
||||
// Write .nuxtrc with `shamefully-hoist=true` for pnpm
|
||||
const usingPnpm = (process.env.npm_config_user_agent || '').includes('pnpm')
|
||||
if (usingPnpm) {
|
||||
await writeFile(`${relativeDist}/.npmrc`, 'shamefully-hoist=true')
|
||||
}
|
||||
|
||||
const nextSteps = [
|
||||
!args.shell && relativeDist.length > 1 && `\`cd ${relativeDist}\``,
|
||||
'Install dependencies with `npm install` or `yarn install` or `pnpm install`',
|
||||
'Start development server with `npm run dev` or `yarn dev` or `pnpm run dev`'
|
||||
].filter(Boolean)
|
||||
|
||||
consola.log(`✨ Nuxt project is created with \`${t.name}\` template. Next steps:`)
|
||||
for (const step of nextSteps) {
|
||||
consola.log(` › ${step}`)
|
||||
}
|
||||
|
||||
if (args.shell) {
|
||||
startShell(t.dir)
|
||||
}
|
||||
}
|
||||
})
|
@ -1,35 +0,0 @@
|
||||
import { relative, resolve } from 'pathe'
|
||||
import { consola } from 'consola'
|
||||
|
||||
// we are deliberately inlining this code as a backup in case user has `@nuxt/schema<3.7`
|
||||
import { writeTypes as writeTypesLegacy } from '../../../kit/src/template'
|
||||
import { clearBuildDir } from '../utils/fs'
|
||||
import { loadKit } from '../utils/kit'
|
||||
import { defineNuxtCommand } from './index'
|
||||
|
||||
export default defineNuxtCommand({
|
||||
meta: {
|
||||
name: 'prepare',
|
||||
usage: 'npx nuxi prepare [--log-level] [rootDir]',
|
||||
description: 'Prepare nuxt for development/build'
|
||||
},
|
||||
async invoke (args, options = {}) {
|
||||
process.env.NODE_ENV = process.env.NODE_ENV || 'production'
|
||||
const rootDir = resolve(args._[0] || '.')
|
||||
|
||||
const { loadNuxt, buildNuxt, writeTypes = writeTypesLegacy } = await loadKit(rootDir)
|
||||
const nuxt = await loadNuxt({
|
||||
rootDir,
|
||||
overrides: {
|
||||
_prepare: true,
|
||||
logLevel: args['log-level'],
|
||||
...(options.overrides || {})
|
||||
}
|
||||
})
|
||||
await clearBuildDir(nuxt.options.buildDir)
|
||||
|
||||
await buildNuxt(nuxt)
|
||||
await writeTypes(nuxt)
|
||||
consola.success('Types generated in', relative(process.cwd(), nuxt.options.buildDir))
|
||||
}
|
||||
})
|
@ -1,55 +0,0 @@
|
||||
import { existsSync, promises as fsp } from 'node:fs'
|
||||
import { dirname, relative } from 'node:path'
|
||||
import { execa } from 'execa'
|
||||
import { setupDotenv } from 'c12'
|
||||
import { resolve } from 'pathe'
|
||||
import { consola } from 'consola'
|
||||
import { loadKit } from '../utils/kit'
|
||||
|
||||
import { defineNuxtCommand } from './index'
|
||||
|
||||
export default defineNuxtCommand({
|
||||
meta: {
|
||||
name: 'preview',
|
||||
usage: 'npx nuxi preview|start [--dotenv] [rootDir]',
|
||||
description: 'Launches nitro server for local testing after `nuxi build`.'
|
||||
},
|
||||
async invoke (args, options = {}) {
|
||||
process.env.NODE_ENV = process.env.NODE_ENV || 'production'
|
||||
const rootDir = resolve(args._[0] || '.')
|
||||
const { loadNuxtConfig } = await loadKit(rootDir)
|
||||
const config = await loadNuxtConfig({ cwd: rootDir, overrides: options?.overrides || {} })
|
||||
|
||||
const resolvedOutputDir = resolve(config.srcDir || rootDir, config.nitro.srcDir || 'server', config.nitro.output?.dir || '.output', 'nitro.json')
|
||||
const defaultOutput = resolve(rootDir, '.output', 'nitro.json') // for backwards compatibility
|
||||
|
||||
const nitroJSONPaths = [resolvedOutputDir, defaultOutput]
|
||||
const nitroJSONPath = nitroJSONPaths.find(p => existsSync(p))
|
||||
if (!nitroJSONPath) {
|
||||
consola.error('Cannot find `nitro.json`. Did you run `nuxi build` first? Search path:\n', nitroJSONPaths)
|
||||
process.exit(1)
|
||||
}
|
||||
const outputPath = dirname(nitroJSONPath)
|
||||
const nitroJSON = JSON.parse(await fsp.readFile(nitroJSONPath, 'utf-8'))
|
||||
|
||||
consola.info('Node.js version:', process.versions.node)
|
||||
consola.info('Preset:', nitroJSON.preset)
|
||||
consola.info('Working dir:', relative(process.cwd(), outputPath))
|
||||
|
||||
if (!nitroJSON.commands.preview) {
|
||||
consola.error('Preview is not supported for this build.')
|
||||
process.exit(1)
|
||||
}
|
||||
|
||||
const envExists = args.dotenv ? existsSync(resolve(rootDir, args.dotenv)) : existsSync(rootDir)
|
||||
if (envExists) {
|
||||
consola.info('Loading `.env`. This will not be loaded when running the server in production.')
|
||||
await setupDotenv({ cwd: rootDir, fileName: args.dotenv })
|
||||
}
|
||||
|
||||
consola.info('Starting preview command:', nitroJSON.commands.preview)
|
||||
const [command, ...commandArgs] = nitroJSON.commands.preview.split(' ')
|
||||
consola.log('')
|
||||
await execa(command, commandArgs, { stdio: 'inherit', cwd: outputPath })
|
||||
}
|
||||
})
|
@ -1,43 +0,0 @@
|
||||
import { resolve } from 'pathe'
|
||||
import { defineNuxtCommand } from './index'
|
||||
|
||||
export default defineNuxtCommand({
|
||||
meta: {
|
||||
name: 'test',
|
||||
usage: 'npx nuxi test [--dev] [--watch] [rootDir]',
|
||||
description: 'Run tests'
|
||||
},
|
||||
async invoke (args, options = {}) {
|
||||
process.env.NODE_ENV = process.env.NODE_ENV || 'test'
|
||||
const rootDir = resolve(args._[0] || '.')
|
||||
const { runTests } = await importTestUtils()
|
||||
await runTests({
|
||||
rootDir,
|
||||
dev: !!args.dev,
|
||||
watch: !!args.watch,
|
||||
...(options || {})
|
||||
})
|
||||
|
||||
if (args.watch) {
|
||||
return 'wait' as const
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
async function importTestUtils (): Promise<typeof import('@nuxt/test-utils')> {
|
||||
let err
|
||||
for (const pkg of ['@nuxt/test-utils-edge', '@nuxt/test-utils']) {
|
||||
try {
|
||||
const exports = await import(pkg)
|
||||
// Detect old @nuxt/test-utils
|
||||
if (!exports.runTests) {
|
||||
throw new Error('Invalid version of `@nuxt/test-utils` is installed!')
|
||||
}
|
||||
return exports
|
||||
} catch (_err) {
|
||||
err = _err
|
||||
}
|
||||
}
|
||||
console.error(err)
|
||||
throw new Error('`@nuxt/test-utils-edge` seems missing. Run `npm i -D @nuxt/test-utils-edge` or `yarn add -D @nuxt/test-utils-edge` to install.')
|
||||
}
|
@ -1,43 +0,0 @@
|
||||
import { execa } from 'execa'
|
||||
import { resolve } from 'pathe'
|
||||
|
||||
// we are deliberately inlining this code as a backup in case user has `@nuxt/schema<3.7`
|
||||
import { writeTypes as writeTypesLegacy } from '../../../kit/src/template'
|
||||
import { tryResolveModule } from '../utils/esm'
|
||||
import { loadKit } from '../utils/kit'
|
||||
import { defineNuxtCommand } from './index'
|
||||
|
||||
export default defineNuxtCommand({
|
||||
meta: {
|
||||
name: 'typecheck',
|
||||
usage: 'npx nuxi typecheck [--log-level] [rootDir]',
|
||||
description: 'Runs `vue-tsc` to check types throughout your app.'
|
||||
},
|
||||
async invoke (args, options = {}) {
|
||||
process.env.NODE_ENV = process.env.NODE_ENV || 'production'
|
||||
const rootDir = resolve(args._[0] || '.')
|
||||
|
||||
const { loadNuxt, buildNuxt, writeTypes = writeTypesLegacy } = await loadKit(rootDir)
|
||||
const nuxt = await loadNuxt({
|
||||
rootDir,
|
||||
overrides: {
|
||||
_prepare: true,
|
||||
logLevel: args['log-level'],
|
||||
...(options?.overrides || {})
|
||||
}
|
||||
})
|
||||
|
||||
// Generate types and build nuxt instance
|
||||
await writeTypes(nuxt)
|
||||
await buildNuxt(nuxt)
|
||||
await nuxt.close()
|
||||
|
||||
// Prefer local install if possible
|
||||
const hasLocalInstall = await tryResolveModule('typescript', rootDir) && await tryResolveModule('vue-tsc/package.json', rootDir)
|
||||
if (hasLocalInstall) {
|
||||
await execa('vue-tsc', ['--noEmit'], { preferLocal: true, stdio: 'inherit', cwd: rootDir })
|
||||
} else {
|
||||
await execa('npx', '-p vue-tsc -p typescript vue-tsc --noEmit'.split(' '), { stdio: 'inherit', cwd: rootDir })
|
||||
}
|
||||
}
|
||||
})
|
@ -1,74 +0,0 @@
|
||||
import { execSync } from 'node:child_process'
|
||||
import { consola } from 'consola'
|
||||
import { resolve } from 'pathe'
|
||||
import { readPackageJSON } from 'pkg-types'
|
||||
import { getPackageManager, packageManagerLocks } from '../utils/packageManagers'
|
||||
import { rmRecursive, touchFile } from '../utils/fs'
|
||||
import { cleanupNuxtDirs, nuxtVersionToGitIdentifier } from '../utils/nuxt'
|
||||
import { defineNuxtCommand } from './index'
|
||||
|
||||
async function getNuxtVersion (path: string): Promise<string | null> {
|
||||
try {
|
||||
const pkg = await readPackageJSON('nuxt', { url: path })
|
||||
if (!pkg.version) {
|
||||
consola.warn('Cannot find any installed nuxt versions in ', path)
|
||||
}
|
||||
return pkg.version || null
|
||||
} catch {
|
||||
return null
|
||||
}
|
||||
}
|
||||
|
||||
export default defineNuxtCommand({
|
||||
meta: {
|
||||
name: 'upgrade',
|
||||
usage: 'npx nuxi upgrade [--force|-f]',
|
||||
description: 'Upgrade nuxt'
|
||||
},
|
||||
async invoke (args) {
|
||||
const rootDir = resolve(args._[0] || '.')
|
||||
|
||||
// Check package manager
|
||||
const packageManager = getPackageManager(rootDir)
|
||||
if (!packageManager) {
|
||||
console.error('Cannot detect Package Manager in', rootDir)
|
||||
process.exit(1)
|
||||
}
|
||||
const packageManagerVersion = execSync(`${packageManager} --version`).toString('utf8').trim()
|
||||
consola.info('Package Manager:', packageManager, packageManagerVersion)
|
||||
|
||||
// Check currently installed nuxt version
|
||||
const currentVersion = await getNuxtVersion(rootDir) || '[unknown]'
|
||||
consola.info('Current nuxt version:', currentVersion)
|
||||
|
||||
// Force install
|
||||
if (args.force || args.f) {
|
||||
consola.info('Removing lock-file and node_modules...')
|
||||
const pmLockFile = resolve(rootDir, packageManagerLocks[packageManager])
|
||||
await rmRecursive([pmLockFile, resolve(rootDir, 'node_modules')])
|
||||
await touchFile(pmLockFile)
|
||||
}
|
||||
|
||||
// Install latest version
|
||||
consola.info('Installing latest Nuxt 3 release...')
|
||||
execSync(`${packageManager} ${packageManager === 'yarn' ? 'add' : 'install'} -D nuxt`, { stdio: 'inherit', cwd: rootDir })
|
||||
|
||||
// Cleanup after upgrade
|
||||
await cleanupNuxtDirs(rootDir)
|
||||
|
||||
// Check installed nuxt version again
|
||||
const upgradedVersion = await getNuxtVersion(rootDir) || '[unknown]'
|
||||
consola.info('Upgraded nuxt version:', upgradedVersion)
|
||||
|
||||
if (upgradedVersion === currentVersion) {
|
||||
consola.success('You\'re already using the latest version of nuxt.')
|
||||
} else {
|
||||
consola.success('Successfully upgraded nuxt from', currentVersion, 'to', upgradedVersion)
|
||||
const commitA = nuxtVersionToGitIdentifier(currentVersion)
|
||||
const commitB = nuxtVersionToGitIdentifier(upgradedVersion)
|
||||
if (commitA && commitB) {
|
||||
consola.info('Changelog:', `https://github.com/nuxt/nuxt/compare/${commitA}...${commitB}`)
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
@ -1,21 +0,0 @@
|
||||
import { cyan } from 'colorette'
|
||||
import { showHelp } from '../utils/help'
|
||||
import { commands, defineNuxtCommand } from './index'
|
||||
|
||||
export default defineNuxtCommand({
|
||||
meta: {
|
||||
name: 'help',
|
||||
usage: 'nuxt help',
|
||||
description: 'Show help'
|
||||
},
|
||||
invoke (_args) {
|
||||
const sections: string[] = []
|
||||
|
||||
sections.push(`Usage: ${cyan(`npx nuxi ${Object.keys(commands).join('|')} [args]`)}`)
|
||||
|
||||
console.log(sections.join('\n\n') + '\n')
|
||||
|
||||
// Reuse the same wording as in `-h` commands
|
||||
showHelp({})
|
||||
}
|
||||
})
|
@ -1 +0,0 @@
|
||||
export * from './run'
|
@ -1,13 +0,0 @@
|
||||
import mri from 'mri'
|
||||
import type { Command, NuxtCommand } from './commands'
|
||||
import { commands } from './commands'
|
||||
|
||||
export async function runCommand (command: string, argv = process.argv.slice(2), options: Record<string, any> = {}) {
|
||||
const args = mri(argv)
|
||||
args.clear = false // used by dev
|
||||
const cmd = await commands[command as Command]() as NuxtCommand
|
||||
if (!cmd) {
|
||||
throw new Error(`Invalid command ${command}`)
|
||||
}
|
||||
await cmd.invoke(args, options)
|
||||
}
|
@ -1,21 +0,0 @@
|
||||
import clear from 'clear'
|
||||
import { bold, gray, green } from 'colorette'
|
||||
import { version } from '../../package.json'
|
||||
import { tryRequireModule } from './cjs'
|
||||
|
||||
export function showBanner (_clear?: boolean) {
|
||||
if (_clear) { clear() }
|
||||
console.log(gray(`Nuxi ${(bold(version))}`))
|
||||
}
|
||||
|
||||
export function showVersions (cwd: string) {
|
||||
const getPkgVersion = (pkg: string) => {
|
||||
return tryRequireModule(`${pkg}/package.json`, cwd)?.version || ''
|
||||
}
|
||||
const nuxtVersion = getPkgVersion('nuxt') || getPkgVersion('nuxt-edge')
|
||||
const nitroVersion = getPkgVersion('nitropack')
|
||||
console.log(gray(
|
||||
green(`Nuxt ${bold(nuxtVersion)}`) +
|
||||
(nitroVersion ? ` with Nitro ${(bold(nitroVersion))}` : '')
|
||||
))
|
||||
}
|
@ -1,27 +0,0 @@
|
||||
import { createRequire } from 'node:module'
|
||||
import { normalize } from 'pathe'
|
||||
|
||||
function getModulePaths (paths?: string | string[]): string[] {
|
||||
return ([] as Array<string | undefined>)
|
||||
.concat(
|
||||
global.__NUXT_PREPATHS__,
|
||||
paths,
|
||||
process.cwd(),
|
||||
global.__NUXT_PATHS__
|
||||
)
|
||||
.filter(Boolean) as string[]
|
||||
}
|
||||
|
||||
const _require = createRequire(process.cwd())
|
||||
|
||||
function resolveModule (id: string, paths?: string | string[]) {
|
||||
return normalize(_require.resolve(id, { paths: getModulePaths(paths) }))
|
||||
}
|
||||
|
||||
function requireModule (id: string, paths?: string | string[]) {
|
||||
return _require(resolveModule(id, paths))
|
||||
}
|
||||
|
||||
export function tryRequireModule (id: string, paths?: string | string[]) {
|
||||
try { return requireModule(id, paths) } catch { return null }
|
||||
}
|
@ -1,31 +0,0 @@
|
||||
import flatten from 'flat'
|
||||
import { detailedDiff } from 'deep-object-diff'
|
||||
import { blue, cyan, green, red } from 'colorette'
|
||||
|
||||
function normalizeDiff (diffObj: any, type: 'added' | 'deleted' | 'updated', ignore: string[]) {
|
||||
return Object.entries(flatten(diffObj) as Record<string, any>)
|
||||
.map(([key, value]) => ({ key, value, type }))
|
||||
.filter(item => !ignore.includes(item.key) && typeof item.value !== 'function')
|
||||
}
|
||||
|
||||
export function diff (a: any, b: any, ignore: string[]) {
|
||||
const _diff: any = detailedDiff(a, b)
|
||||
return [
|
||||
...normalizeDiff(_diff.added, 'added', ignore),
|
||||
...normalizeDiff(_diff.deleted, 'deleted', ignore),
|
||||
...normalizeDiff(_diff.updated, 'updated', ignore)
|
||||
]
|
||||
}
|
||||
|
||||
const typeMap = {
|
||||
added: green('added'),
|
||||
deleted: red('deleted'),
|
||||
updated: blue('updated')
|
||||
}
|
||||
|
||||
export function printDiff (diff: any) {
|
||||
for (const item of diff) {
|
||||
console.log(' ', typeMap[item.type as keyof typeof typeMap] || item.type, cyan(item.key), item.value ? `~> ${cyan(item.value)}` : '')
|
||||
}
|
||||
console.log()
|
||||
}
|
@ -1,12 +0,0 @@
|
||||
import { engines } from '../../package.json'
|
||||
|
||||
export async function checkEngines () {
|
||||
const satisfies = await import('semver/functions/satisfies.js')
|
||||
.then(r => r.default || r as any as typeof import('semver/functions/satisfies.js')) // npm/node-semver#381
|
||||
const currentNode = process.versions.node
|
||||
const nodeRange = engines.node
|
||||
|
||||
if (!satisfies(currentNode, nodeRange)) {
|
||||
console.warn(`Current version of Node.js (\`${currentNode}\`) is unsupported and might cause issues.\n Please upgrade to a compatible version (${nodeRange}).`)
|
||||
}
|
||||
}
|
@ -1,8 +0,0 @@
|
||||
export const overrideEnv = (targetEnv: string) => {
|
||||
const currentEnv = process.env.NODE_ENV
|
||||
if (currentEnv && currentEnv !== targetEnv) {
|
||||
console.warn(`Changing \`NODE_ENV\` from \`${currentEnv}\` to \`${targetEnv}\`, to avoid unintended behavior.`)
|
||||
}
|
||||
|
||||
process.env.NODE_ENV = targetEnv
|
||||
}
|
@ -1,13 +0,0 @@
|
||||
import { pathToFileURL } from 'node:url'
|
||||
import { interopDefault, resolvePath } from 'mlly'
|
||||
|
||||
export async function tryResolveModule (id: string, url = import.meta.url) {
|
||||
try {
|
||||
return await resolvePath(id, { url })
|
||||
} catch { }
|
||||
}
|
||||
|
||||
export async function importModule (id: string, url: string | string[] = import.meta.url) {
|
||||
const resolvedPath = await resolvePath(id, { url })
|
||||
return import(pathToFileURL(resolvedPath).href).then(interopDefault)
|
||||
}
|
@ -1,45 +0,0 @@
|
||||
import { existsSync, promises as fsp } from 'node:fs'
|
||||
import { dirname, join } from 'pathe'
|
||||
import { consola } from 'consola'
|
||||
|
||||
export async function clearDir (path: string, exclude?: string[]) {
|
||||
if (!exclude) {
|
||||
await fsp.rm(path, { recursive: true, force: true })
|
||||
} else if (existsSync(path)) {
|
||||
const files = await fsp.readdir(path)
|
||||
await Promise.all(files.map(async (name) => {
|
||||
if (!exclude.includes(name)) {
|
||||
await fsp.rm(join(path, name), { recursive: true, force: true })
|
||||
}
|
||||
}))
|
||||
}
|
||||
await fsp.mkdir(path, { recursive: true })
|
||||
}
|
||||
|
||||
export function clearBuildDir (path: string) {
|
||||
return clearDir(path, ['cache', 'analyze'])
|
||||
}
|
||||
|
||||
export async function rmRecursive (paths: string[]) {
|
||||
await Promise.all(paths.filter(p => typeof p === 'string').map(async (path) => {
|
||||
consola.debug('Removing recursive path', path)
|
||||
await fsp.rm(path, { recursive: true, force: true }).catch(() => {})
|
||||
}))
|
||||
}
|
||||
|
||||
export async function touchFile (path: string) {
|
||||
const time = new Date()
|
||||
await fsp.utimes(path, time, time).catch(() => {})
|
||||
}
|
||||
|
||||
export function findup<T> (rootDir: string, fn: (dir: string) => T | undefined): T | null {
|
||||
let dir = rootDir
|
||||
while (dir !== dirname(dir)) {
|
||||
const res = fn(dir)
|
||||
if (res) {
|
||||
return res
|
||||
}
|
||||
dir = dirname(dir)
|
||||
}
|
||||
return null
|
||||
}
|
@ -1,20 +0,0 @@
|
||||
import { cyan, magenta } from 'colorette'
|
||||
import type { NuxtCommandMeta } from '../commands'
|
||||
|
||||
export function showHelp (meta?: Partial<NuxtCommandMeta>) {
|
||||
const sections: string[] = []
|
||||
|
||||
if (meta) {
|
||||
if (meta.usage) {
|
||||
sections.push(magenta('> ') + 'Usage: ' + cyan(meta.usage))
|
||||
}
|
||||
|
||||
if (meta.description) {
|
||||
sections.push(magenta('⋮ ') + meta.description)
|
||||
}
|
||||
}
|
||||
|
||||
sections.push(`Use ${cyan('npx nuxi [command] --help')} to see help for each command`)
|
||||
|
||||
console.log(sections.join('\n\n') + '\n')
|
||||
}
|
@ -1,24 +0,0 @@
|
||||
import { importModule, tryResolveModule } from './esm'
|
||||
|
||||
export const loadKit = async (rootDir: string): Promise<typeof import('@nuxt/kit')> => {
|
||||
try {
|
||||
// Without PNP (or if users have a local install of kit, we bypass resolving from nuxt)
|
||||
const localKit = await tryResolveModule('@nuxt/kit', rootDir)
|
||||
// Otherwise, we resolve Nuxt _first_ as it is Nuxt's kit dependency that will be used
|
||||
const rootURL = localKit ? rootDir : await tryResolveNuxt() || rootDir
|
||||
return await importModule('@nuxt/kit', rootURL) as typeof import('@nuxt/kit')
|
||||
} catch (e: any) {
|
||||
if (e.toString().includes("Cannot find module '@nuxt/kit'")) {
|
||||
throw new Error('nuxi requires `@nuxt/kit` to be installed in your project. Try installing `nuxt` v3 or `@nuxt/bridge` first.')
|
||||
}
|
||||
throw e
|
||||
}
|
||||
}
|
||||
|
||||
async function tryResolveNuxt () {
|
||||
for (const pkg of ['nuxt3', 'nuxt', 'nuxt-edge']) {
|
||||
const path = await tryResolveModule(pkg)
|
||||
if (path) { return path }
|
||||
}
|
||||
return null
|
||||
}
|
@ -1,68 +0,0 @@
|
||||
import { promises as fsp } from 'node:fs'
|
||||
import { dirname, resolve } from 'pathe'
|
||||
import { consola } from 'consola'
|
||||
import { hash } from 'ohash'
|
||||
import type { Nuxt } from '@nuxt/schema'
|
||||
import { rmRecursive } from './fs'
|
||||
|
||||
interface NuxtProjectManifest {
|
||||
_hash: string | null
|
||||
project: {
|
||||
rootDir: string
|
||||
},
|
||||
versions: {
|
||||
nuxt: string
|
||||
}
|
||||
}
|
||||
|
||||
export async function cleanupNuxtDirs (rootDir: string) {
|
||||
consola.info('Cleaning up generated nuxt files and caches...')
|
||||
|
||||
await rmRecursive([
|
||||
'.nuxt',
|
||||
'.output',
|
||||
'dist',
|
||||
'node_modules/.vite',
|
||||
'node_modules/.cache'
|
||||
].map(dir => resolve(rootDir, dir)))
|
||||
}
|
||||
|
||||
export function nuxtVersionToGitIdentifier (version: string) {
|
||||
// match the git identifier in the release, for example: 3.0.0-rc.8-27677607.a3a8706
|
||||
const id = /\.([0-9a-f]{7,8})$/.exec(version)
|
||||
if (id?.[1]) {
|
||||
return id[1]
|
||||
}
|
||||
// match github tag, for example 3.0.0-rc.8
|
||||
return `v${version}`
|
||||
}
|
||||
|
||||
function resolveNuxtManifest (nuxt: Nuxt): NuxtProjectManifest {
|
||||
const manifest: NuxtProjectManifest = {
|
||||
_hash: null,
|
||||
project: {
|
||||
rootDir: nuxt.options.rootDir
|
||||
},
|
||||
versions: {
|
||||
nuxt: nuxt._version
|
||||
}
|
||||
}
|
||||
manifest._hash = hash(manifest)
|
||||
return manifest
|
||||
}
|
||||
|
||||
export async function writeNuxtManifest (nuxt: Nuxt): Promise<NuxtProjectManifest> {
|
||||
const manifest = resolveNuxtManifest(nuxt)
|
||||
const manifestPath = resolve(nuxt.options.buildDir, 'nuxt.json')
|
||||
await fsp.mkdir(dirname(manifestPath), { recursive: true })
|
||||
await fsp.writeFile(manifestPath, JSON.stringify(manifest, null, 2), 'utf-8')
|
||||
return manifest
|
||||
}
|
||||
|
||||
export async function loadNuxtManifest (buildDir: string): Promise<NuxtProjectManifest | null> {
|
||||
const manifestPath = resolve(buildDir, 'nuxt.json')
|
||||
const manifest: NuxtProjectManifest | null = await fsp.readFile(manifestPath, 'utf-8')
|
||||
.then(data => JSON.parse(data) as NuxtProjectManifest)
|
||||
.catch(() => null)
|
||||
return manifest
|
||||
}
|
@ -1,28 +0,0 @@
|
||||
import { execSync } from 'node:child_process'
|
||||
import { existsSync } from 'node:fs'
|
||||
import { resolve } from 'pathe'
|
||||
import { findup } from './fs'
|
||||
|
||||
export const packageManagerLocks = {
|
||||
yarn: 'yarn.lock',
|
||||
npm: 'package-lock.json',
|
||||
pnpm: 'pnpm-lock.yaml',
|
||||
bun: 'bun.lockb'
|
||||
}
|
||||
|
||||
type PackageManager = keyof typeof packageManagerLocks
|
||||
|
||||
export function getPackageManager (rootDir: string) {
|
||||
return findup(rootDir, (dir) => {
|
||||
for (const name in packageManagerLocks) {
|
||||
const path = packageManagerLocks[name as PackageManager]
|
||||
if (path && existsSync(resolve(dir, path))) {
|
||||
return name
|
||||
}
|
||||
}
|
||||
}) as PackageManager | null
|
||||
}
|
||||
|
||||
export function getPackageManagerVersion (name: string) {
|
||||
return execSync(`${name} --version`).toString('utf8').trim()
|
||||
}
|
@ -1,119 +0,0 @@
|
||||
import { upperFirst } from 'scule'
|
||||
|
||||
interface TemplateOptions {
|
||||
name: string,
|
||||
args: Record<string, any>
|
||||
}
|
||||
|
||||
interface Template {
|
||||
(options: TemplateOptions): { path: string, contents: string }
|
||||
}
|
||||
|
||||
const httpMethods = ['connect', 'delete', 'get', 'head', 'options', 'post', 'put', 'trace', 'patch']
|
||||
const api: Template = ({ name, args }) => ({
|
||||
path: `server/api/${name}${applySuffix(args, httpMethods, 'method')}.ts`,
|
||||
contents: `
|
||||
export default defineEventHandler((event) => {
|
||||
return 'Hello ${name}'
|
||||
})
|
||||
`
|
||||
})
|
||||
|
||||
const plugin: Template = ({ name, args }) => ({
|
||||
path: `plugins/${name}${applySuffix(args, ['client', 'server'], 'mode')}.ts`,
|
||||
contents: `
|
||||
export default defineNuxtPlugin((nuxtApp) => {})
|
||||
`
|
||||
})
|
||||
|
||||
const component: Template = ({ name, args }) => ({
|
||||
path: `components/${name}${applySuffix(args, ['client', 'server'], 'mode')}.vue`,
|
||||
contents: `
|
||||
<script lang="ts" setup></script>
|
||||
|
||||
<template>
|
||||
<div>
|
||||
Component: ${name}
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped></style>
|
||||
`
|
||||
})
|
||||
|
||||
const composable: Template = ({ name }) => {
|
||||
const nameWithUsePrefix = name.startsWith('use') ? name : `use${upperFirst(name)}`
|
||||
return {
|
||||
path: `composables/${name}.ts`,
|
||||
contents: `
|
||||
export const ${nameWithUsePrefix} = () => {
|
||||
return ref()
|
||||
}
|
||||
`
|
||||
}
|
||||
}
|
||||
|
||||
const middleware: Template = ({ name, args }) => ({
|
||||
path: `middleware/${name}${applySuffix(args, ['global'])}.ts`,
|
||||
contents: `
|
||||
export default defineNuxtRouteMiddleware((to, from) => {})
|
||||
`
|
||||
})
|
||||
|
||||
const layout: Template = ({ name }) => ({
|
||||
path: `layouts/${name}.vue`,
|
||||
contents: `
|
||||
<script lang="ts" setup></script>
|
||||
|
||||
<template>
|
||||
<div>
|
||||
Layout: ${name}
|
||||
<slot />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped></style>
|
||||
`
|
||||
})
|
||||
|
||||
const page: Template = ({ name }) => ({
|
||||
path: `pages/${name}.vue`,
|
||||
contents: `
|
||||
<script lang="ts" setup></script>
|
||||
|
||||
<template>
|
||||
<div>
|
||||
Page: foo
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped></style>
|
||||
`
|
||||
})
|
||||
|
||||
export const templates = {
|
||||
api,
|
||||
plugin,
|
||||
component,
|
||||
composable,
|
||||
middleware,
|
||||
layout,
|
||||
page
|
||||
} as Record<string, Template>
|
||||
|
||||
// -- internal utils --
|
||||
|
||||
function applySuffix (args: TemplateOptions['args'], suffixes: string[], unwrapFrom?: string): string {
|
||||
let suffix = ''
|
||||
// --client
|
||||
for (const s of suffixes) {
|
||||
if (args[s]) {
|
||||
suffix += '.' + s
|
||||
}
|
||||
}
|
||||
// --mode=server
|
||||
if (unwrapFrom && args[unwrapFrom] && suffixes.includes(args[unwrapFrom])) {
|
||||
suffix += '.' + args[unwrapFrom]
|
||||
}
|
||||
return suffix
|
||||
}
|
@ -58,9 +58,9 @@
|
||||
"@nuxt/telemetry": "^2.4.1",
|
||||
"@nuxt/ui-templates": "^1.3.1",
|
||||
"@nuxt/vite-builder": "workspace:../vite",
|
||||
"@unhead/dom": "^1.3.5",
|
||||
"@unhead/ssr": "^1.3.5",
|
||||
"@unhead/vue": "^1.3.5",
|
||||
"@unhead/dom": "^1.3.7",
|
||||
"@unhead/ssr": "^1.3.7",
|
||||
"@unhead/vue": "^1.3.7",
|
||||
"@vue/shared": "^3.3.4",
|
||||
"acorn": "8.10.0",
|
||||
"c12": "^1.4.2",
|
||||
@ -79,26 +79,26 @@
|
||||
"jiti": "^1.19.3",
|
||||
"klona": "^2.0.6",
|
||||
"knitwork": "^1.0.0",
|
||||
"magic-string": "^0.30.2",
|
||||
"magic-string": "^0.30.3",
|
||||
"mlly": "^1.4.0",
|
||||
"nitropack": "^2.5.2",
|
||||
"nuxi": "workspace:../nuxi",
|
||||
"nypm": "^0.3.0",
|
||||
"ofetch": "^1.1.1",
|
||||
"nitropack": "^2.6.0",
|
||||
"nuxi": "npm:nuxi-ng@0.3.0-1692970235.c259efa",
|
||||
"nypm": "^0.3.1",
|
||||
"ofetch": "^1.3.3",
|
||||
"ohash": "^1.1.3",
|
||||
"pathe": "^1.1.1",
|
||||
"perfect-debounce": "^1.0.0",
|
||||
"pkg-types": "^1.0.3",
|
||||
"prompts": "^2.4.2",
|
||||
"scule": "^1.0.0",
|
||||
"std-env": "^3.4.0",
|
||||
"std-env": "^3.4.3",
|
||||
"strip-literal": "^1.3.0",
|
||||
"ufo": "^1.2.0",
|
||||
"ufo": "^1.3.0",
|
||||
"ultrahtml": "^1.3.0",
|
||||
"uncrypto": "^0.1.3",
|
||||
"unctx": "^2.3.1",
|
||||
"unenv": "^1.7.1",
|
||||
"unimport": "^3.1.3",
|
||||
"unenv": "^1.7.3",
|
||||
"unimport": "^3.2.0",
|
||||
"unplugin": "^1.4.0",
|
||||
"unplugin-vue-router": "^0.6.4",
|
||||
"untyped": "^1.4.0",
|
||||
@ -108,11 +108,11 @@
|
||||
"vue-router": "^4.2.4"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@parcel/watcher": "2.2.0",
|
||||
"@parcel/watcher": "2.3.0",
|
||||
"@types/estree": "1.0.1",
|
||||
"@types/fs-extra": "11.0.1",
|
||||
"@types/prompts": "2.4.4",
|
||||
"@vitejs/plugin-vue": "4.3.1",
|
||||
"@vitejs/plugin-vue": "4.3.3",
|
||||
"unbuild": "latest",
|
||||
"vite": "4.4.9",
|
||||
"vitest": "0.33.0"
|
||||
|
@ -145,7 +145,7 @@ export function useAsyncData<
|
||||
const hasCachedData = () => getCachedData() !== undefined
|
||||
|
||||
// Create or use a shared asyncData entity
|
||||
if (!nuxt._asyncData[key]) {
|
||||
if (!nuxt._asyncData[key] || !options.immediate) {
|
||||
nuxt._asyncData[key] = {
|
||||
data: ref(getCachedData() ?? options.default!()),
|
||||
pending: ref(!hasCachedData()),
|
||||
|
@ -2,7 +2,7 @@ import type { Ref } from 'vue'
|
||||
import { getCurrentInstance, nextTick, onUnmounted, ref, toRaw, watch } from 'vue'
|
||||
import type { CookieParseOptions, CookieSerializeOptions } from 'cookie-es'
|
||||
import { parse, serialize } from 'cookie-es'
|
||||
import { deleteCookie, getCookie, setCookie } from 'h3'
|
||||
import { deleteCookie, getCookie, getRequestHeader, setCookie } from 'h3'
|
||||
import type { H3Event } from 'h3'
|
||||
import destr from 'destr'
|
||||
import { isEqual } from 'ohash'
|
||||
@ -80,7 +80,7 @@ export function useCookie<T = string | null | undefined> (name: string, _opts?:
|
||||
|
||||
function readRawCookies (opts: CookieOptions = {}): Record<string, string> | undefined {
|
||||
if (import.meta.server) {
|
||||
return parse(useRequestEvent()?.node.req.headers.cookie || '', opts)
|
||||
return parse(getRequestHeader(useRequestEvent(), 'cookie') || '', opts)
|
||||
} else if (import.meta.client) {
|
||||
return parse(document.cookie, opts)
|
||||
}
|
||||
|
@ -1,5 +1,5 @@
|
||||
import type { H3Event } from 'h3'
|
||||
import { setResponseStatus as _setResponseStatus } from 'h3'
|
||||
import { setResponseStatus as _setResponseStatus, getRequestHeaders } from 'h3'
|
||||
import type { NuxtApp } from '../nuxt'
|
||||
import { useNuxtApp } from '../nuxt'
|
||||
|
||||
@ -7,7 +7,8 @@ export function useRequestHeaders<K extends string = string> (include: K[]): { [
|
||||
export function useRequestHeaders (): Readonly<Record<string, string>>
|
||||
export function useRequestHeaders (include?: any[]) {
|
||||
if (import.meta.client) { return {} }
|
||||
const headers = useNuxtApp().ssrContext?.event.node.req.headers ?? {}
|
||||
const event = useNuxtApp().ssrContext?.event
|
||||
const headers = event ? getRequestHeaders(event) : {}
|
||||
if (!include) { return headers }
|
||||
return Object.fromEntries(include.map(key => key.toLowerCase()).filter(key => headers[key]).map(key => [key, headers[key]]))
|
||||
}
|
||||
|
@ -208,16 +208,16 @@ export default defineNuxtModule<ComponentsOptions>({
|
||||
config.plugins = config.plugins || []
|
||||
if (nuxt.options.experimental.treeshakeClientOnly && isServer) {
|
||||
config.plugins.push(TreeShakeTemplatePlugin.vite({
|
||||
sourcemap: nuxt.options.sourcemap[mode],
|
||||
sourcemap: !!nuxt.options.sourcemap[mode],
|
||||
getComponents
|
||||
}))
|
||||
}
|
||||
config.plugins.push(clientFallbackAutoIdPlugin.vite({
|
||||
sourcemap: nuxt.options.sourcemap[mode],
|
||||
sourcemap: !!nuxt.options.sourcemap[mode],
|
||||
rootDir: nuxt.options.rootDir
|
||||
}))
|
||||
config.plugins.push(loaderPlugin.vite({
|
||||
sourcemap: nuxt.options.sourcemap[mode],
|
||||
sourcemap: !!nuxt.options.sourcemap[mode],
|
||||
getComponents,
|
||||
mode,
|
||||
transform: typeof nuxt.options.components === 'object' && !Array.isArray(nuxt.options.components) ? nuxt.options.components.transform : undefined,
|
||||
@ -252,16 +252,16 @@ export default defineNuxtModule<ComponentsOptions>({
|
||||
config.plugins = config.plugins || []
|
||||
if (nuxt.options.experimental.treeshakeClientOnly && mode === 'server') {
|
||||
config.plugins.push(TreeShakeTemplatePlugin.webpack({
|
||||
sourcemap: nuxt.options.sourcemap[mode],
|
||||
sourcemap: !!nuxt.options.sourcemap[mode],
|
||||
getComponents
|
||||
}))
|
||||
}
|
||||
config.plugins.push(clientFallbackAutoIdPlugin.webpack({
|
||||
sourcemap: nuxt.options.sourcemap[mode],
|
||||
sourcemap: !!nuxt.options.sourcemap[mode],
|
||||
rootDir: nuxt.options.rootDir
|
||||
}))
|
||||
config.plugins.push(loaderPlugin.webpack({
|
||||
sourcemap: nuxt.options.sourcemap[mode],
|
||||
sourcemap: !!nuxt.options.sourcemap[mode],
|
||||
getComponents,
|
||||
mode,
|
||||
transform: typeof nuxt.options.components === 'object' && !Array.isArray(nuxt.options.components) ? nuxt.options.components.transform : undefined,
|
||||
|
@ -38,7 +38,8 @@ export async function scanComponents (dirs: ComponentsDir[], srcDir: string): Pr
|
||||
|
||||
const directory = basename(dir.path)
|
||||
if (!siblings.includes(directory)) {
|
||||
const caseCorrected = siblings.find(sibling => sibling.toLowerCase() === directory.toLowerCase())
|
||||
const directoryLowerCase = directory.toLowerCase()
|
||||
const caseCorrected = siblings.find(sibling => sibling.toLowerCase() === directoryLowerCase)
|
||||
if (caseCorrected) {
|
||||
const nuxt = useNuxt()
|
||||
const original = relative(nuxt.options.srcDir, dir.path)
|
||||
@ -95,10 +96,7 @@ export async function scanComponents (dirs: ComponentsDir[], srcDir: string): Pr
|
||||
const componentName = resolveComponentName(fileName, prefixParts)
|
||||
|
||||
if (resolvedNames.has(componentName + suffix) || resolvedNames.has(componentName)) {
|
||||
console.warn(`[nuxt] Two component files resolving to the same name \`${componentName}\`:\n` +
|
||||
`\n - ${filePath}` +
|
||||
`\n - ${resolvedNames.get(componentName)}`
|
||||
)
|
||||
warnAboutDuplicateComponent(componentName, filePath, resolvedNames.get(componentName) || resolvedNames.get(componentName + suffix)!)
|
||||
continue
|
||||
}
|
||||
resolvedNames.set(componentName + suffix, filePath)
|
||||
@ -136,10 +134,14 @@ export async function scanComponents (dirs: ComponentsDir[], srcDir: string): Pr
|
||||
continue
|
||||
}
|
||||
|
||||
// Ignore component if component is already defined (with same mode)
|
||||
if (!components.some(c => c.pascalName === component.pascalName && ['all', component.mode].includes(c.mode))) {
|
||||
components.push(component)
|
||||
const existingComponent = components.find(c => c.pascalName === component.pascalName && ['all', component.mode].includes(c.mode))
|
||||
if (existingComponent) {
|
||||
// Ignore component if component is already defined (with same mode)
|
||||
warnAboutDuplicateComponent(componentName, filePath, existingComponent.filePath)
|
||||
continue
|
||||
}
|
||||
|
||||
components.push(component)
|
||||
}
|
||||
scannedPaths.push(dir.path)
|
||||
}
|
||||
@ -174,3 +176,10 @@ export function resolveComponentName (fileName: string, prefixParts: string[]) {
|
||||
|
||||
return pascalCase(componentNameParts) + pascalCase(fileNameParts)
|
||||
}
|
||||
|
||||
function warnAboutDuplicateComponent (componentName: string, filePath: string, duplicatePath: string) {
|
||||
console.warn(`[nuxt] Two component files resolving to the same name \`${componentName}\`:\n` +
|
||||
`\n - ${filePath}` +
|
||||
`\n - ${duplicatePath}`
|
||||
)
|
||||
}
|
||||
|
@ -3,7 +3,7 @@ import { cpus } from 'node:os'
|
||||
import { join, relative, resolve } from 'pathe'
|
||||
import { build, copyPublicAssets, createDevServer, createNitro, prepare, prerender, scanHandlers, writeTypes } from 'nitropack'
|
||||
import type { Nitro, NitroConfig } from 'nitropack'
|
||||
import { logger } from '@nuxt/kit'
|
||||
import { logger, resolveIgnorePatterns } from '@nuxt/kit'
|
||||
import escapeRE from 'escape-string-regexp'
|
||||
import { defu } from 'defu'
|
||||
import fsExtra from 'fs-extra'
|
||||
@ -31,11 +31,10 @@ export async function initNitro (nuxt: Nuxt & { _nitro?: Nitro }) {
|
||||
: [/node_modules/]
|
||||
|
||||
const spaLoadingTemplate = nuxt.options.spaLoadingTemplate ?? resolve(nuxt.options.srcDir, 'app/spa-loading-template.html')
|
||||
if (spaLoadingTemplate && nuxt.options.spaLoadingTemplate && !existsSync(spaLoadingTemplate)) {
|
||||
if (spaLoadingTemplate && nuxt.options.spaLoadingTemplate && typeof spaLoadingTemplate !== 'boolean' && !existsSync(spaLoadingTemplate)) {
|
||||
console.warn(`[nuxt] Could not load custom \`spaLoadingTemplate\` path as it does not exist: \`${spaLoadingTemplate}\`.`)
|
||||
}
|
||||
|
||||
// @ts-expect-error `typescriptBundlerResolution` coming in next nitro version
|
||||
const nitroConfig: NitroConfig = defu(_nitroConfig, {
|
||||
debug: nuxt.options.debug,
|
||||
rootDir: nuxt.options.rootDir,
|
||||
@ -44,9 +43,8 @@ export async function initNitro (nuxt: Nuxt & { _nitro?: Nitro }) {
|
||||
dev: nuxt.options.dev,
|
||||
buildDir: nuxt.options.buildDir,
|
||||
experimental: {
|
||||
// @ts-expect-error `typescriptBundlerResolution` coming in next nitro version
|
||||
typescriptBundlerResolution: nuxt.options.experimental.typescriptBundlerResolution || nuxt.options.typescript?.tsConfig?.compilerOptions?.moduleResolution?.toLowerCase() === 'bundler' || _nitroConfig.typescript?.tsConfig?.compilerOptions?.moduleResolution?.toLowerCase() === 'bundler',
|
||||
asyncContext: nuxt.options.experimental.asyncContext
|
||||
asyncContext: nuxt.options.experimental.asyncContext,
|
||||
typescriptBundlerResolution: nuxt.options.experimental.typescriptBundlerResolution || nuxt.options.typescript?.tsConfig?.compilerOptions?.moduleResolution?.toLowerCase() === 'bundler' || _nitroConfig.typescript?.tsConfig?.compilerOptions?.moduleResolution?.toLowerCase() === 'bundler'
|
||||
},
|
||||
imports: {
|
||||
autoImport: nuxt.options.imports.autoImport as boolean,
|
||||
@ -91,7 +89,9 @@ export async function initNitro (nuxt: Nuxt & { _nitro?: Nitro }) {
|
||||
'#spa-template': () => {
|
||||
if (!spaLoadingTemplate) { return 'export const template = ""' }
|
||||
try {
|
||||
return `export const template = ${JSON.stringify(readFileSync(spaLoadingTemplate, 'utf-8'))}`
|
||||
if (spaLoadingTemplate !== true) {
|
||||
return `export const template = ${JSON.stringify(readFileSync(spaLoadingTemplate, 'utf-8'))}`
|
||||
}
|
||||
} catch {}
|
||||
return `export const template = ${JSON.stringify(defaultSpaLoadingTemplate({}))}`
|
||||
}
|
||||
@ -117,6 +117,11 @@ export async function initNitro (nuxt: Nuxt & { _nitro?: Nitro }) {
|
||||
tsConfig: {
|
||||
include: [
|
||||
join(nuxt.options.buildDir, 'types/nitro-nuxt.d.ts')
|
||||
],
|
||||
exclude: [
|
||||
...nuxt.options.modulesDir.map(m => relativeWithDot(nuxt.options.buildDir, m)),
|
||||
// nitro generate output: https://github.com/nuxt/nuxt/blob/main/packages/nuxt/src/core/nitro.ts#L186
|
||||
relativeWithDot(nuxt.options.buildDir, resolve(nuxt.options.rootDir, 'dist'))
|
||||
]
|
||||
}
|
||||
},
|
||||
@ -204,6 +209,7 @@ export async function initNitro (nuxt: Nuxt & { _nitro?: Nitro }) {
|
||||
|
||||
// Resolve user-provided paths
|
||||
nitroConfig.srcDir = resolve(nuxt.options.rootDir, nuxt.options.srcDir, nitroConfig.srcDir!)
|
||||
nitroConfig.ignore = [...(nitroConfig.ignore || []), ...resolveIgnorePatterns(nitroConfig.srcDir)]
|
||||
|
||||
// Add fallback server for `ssr: false`
|
||||
if (!nuxt.options.ssr) {
|
||||
@ -405,3 +411,7 @@ export async function initNitro (nuxt: Nuxt & { _nitro?: Nitro }) {
|
||||
nuxt.hook('build:done', () => waitUntilCompile)
|
||||
}
|
||||
}
|
||||
|
||||
function relativeWithDot (from: string, to: string) {
|
||||
return relative(from, to).replace(/^([^.])/, './$1') || '.'
|
||||
}
|
||||
|
@ -16,6 +16,7 @@ import importsModule from '../imports/module'
|
||||
import { distDir, pkgDir } from '../dirs'
|
||||
import { version } from '../../package.json'
|
||||
import { ImportProtectionPlugin, vueAppPatterns } from './plugins/import-protection'
|
||||
import type { UnctxTransformPluginOptions } from './plugins/unctx'
|
||||
import { UnctxTransformPlugin } from './plugins/unctx'
|
||||
import type { TreeShakeComposablesPluginOptions } from './plugins/tree-shake'
|
||||
import { TreeShakeComposablesPlugin } from './plugins/tree-shake'
|
||||
@ -94,14 +95,14 @@ async function initNuxt (nuxt: Nuxt) {
|
||||
if (nuxt.options.experimental.localLayerAliases) {
|
||||
// Add layer aliasing support for ~, ~~, @ and @@ aliases
|
||||
addVitePlugin(() => LayerAliasingPlugin.vite({
|
||||
sourcemap: nuxt.options.sourcemap.server || nuxt.options.sourcemap.client,
|
||||
sourcemap: !!nuxt.options.sourcemap.server || !!nuxt.options.sourcemap.client,
|
||||
dev: nuxt.options.dev,
|
||||
root: nuxt.options.srcDir,
|
||||
// skip top-level layer (user's project) as the aliases will already be correctly resolved
|
||||
layers: nuxt.options._layers.slice(1)
|
||||
}))
|
||||
addWebpackPlugin(() => LayerAliasingPlugin.webpack({
|
||||
sourcemap: nuxt.options.sourcemap.server || nuxt.options.sourcemap.client,
|
||||
sourcemap: !!nuxt.options.sourcemap.server || !!nuxt.options.sourcemap.client,
|
||||
dev: nuxt.options.dev,
|
||||
root: nuxt.options.srcDir,
|
||||
// skip top-level layer (user's project) as the aliases will already be correctly resolved
|
||||
@ -110,18 +111,21 @@ async function initNuxt (nuxt: Nuxt) {
|
||||
}))
|
||||
}
|
||||
|
||||
nuxt.hook('modules:done', () => {
|
||||
nuxt.hook('modules:done', async () => {
|
||||
// Add unctx transform
|
||||
const options = {
|
||||
sourcemap: nuxt.options.sourcemap.server || nuxt.options.sourcemap.client,
|
||||
transformerOptions: nuxt.options.optimization.asyncTransforms
|
||||
}
|
||||
sourcemap: !!nuxt.options.sourcemap.server || !!nuxt.options.sourcemap.client,
|
||||
transformerOptions: {
|
||||
...nuxt.options.optimization.asyncTransforms,
|
||||
helperModule: await tryResolveModule('unctx', nuxt.options.modulesDir) ?? 'unctx'
|
||||
}
|
||||
} satisfies UnctxTransformPluginOptions
|
||||
addVitePlugin(() => UnctxTransformPlugin.vite(options))
|
||||
addWebpackPlugin(() => UnctxTransformPlugin.webpack(options))
|
||||
|
||||
// Add composable tree-shaking optimisations
|
||||
const serverTreeShakeOptions: TreeShakeComposablesPluginOptions = {
|
||||
sourcemap: nuxt.options.sourcemap.server,
|
||||
sourcemap: !!nuxt.options.sourcemap.server,
|
||||
composables: nuxt.options.optimization.treeShake.composables.server
|
||||
}
|
||||
if (Object.keys(serverTreeShakeOptions.composables).length) {
|
||||
@ -129,7 +133,7 @@ async function initNuxt (nuxt: Nuxt) {
|
||||
addWebpackPlugin(() => TreeShakeComposablesPlugin.webpack(serverTreeShakeOptions), { client: false })
|
||||
}
|
||||
const clientTreeShakeOptions: TreeShakeComposablesPluginOptions = {
|
||||
sourcemap: nuxt.options.sourcemap.client,
|
||||
sourcemap: !!nuxt.options.sourcemap.client,
|
||||
composables: nuxt.options.optimization.treeShake.composables.client
|
||||
}
|
||||
if (Object.keys(clientTreeShakeOptions.composables).length) {
|
||||
@ -140,8 +144,8 @@ async function initNuxt (nuxt: Nuxt) {
|
||||
|
||||
if (!nuxt.options.dev) {
|
||||
// DevOnly component tree-shaking - build time only
|
||||
addVitePlugin(() => DevOnlyPlugin.vite({ sourcemap: nuxt.options.sourcemap.server || nuxt.options.sourcemap.client }))
|
||||
addWebpackPlugin(() => DevOnlyPlugin.webpack({ sourcemap: nuxt.options.sourcemap.server || nuxt.options.sourcemap.client }))
|
||||
addVitePlugin(() => DevOnlyPlugin.vite({ sourcemap: !!nuxt.options.sourcemap.server || !!nuxt.options.sourcemap.client }))
|
||||
addWebpackPlugin(() => DevOnlyPlugin.webpack({ sourcemap: !!nuxt.options.sourcemap.server || !!nuxt.options.sourcemap.client }))
|
||||
}
|
||||
|
||||
// Transform initial composable call within `<script setup>` to preserve context
|
||||
|
@ -6,7 +6,7 @@ import { isJS, isVue } from '../utils'
|
||||
|
||||
const TRANSFORM_MARKER = '/* _processed_nuxt_unctx_transform */\n'
|
||||
|
||||
interface UnctxTransformPluginOptions {
|
||||
export interface UnctxTransformPluginOptions {
|
||||
sourcemap?: boolean
|
||||
transformerOptions: TransformerOptions
|
||||
}
|
||||
|
@ -1,8 +1,9 @@
|
||||
import { joinURL, withQuery } from 'ufo'
|
||||
import type { NitroErrorHandler } from 'nitropack'
|
||||
import type { H3Error } from 'h3'
|
||||
import { getRequestHeaders, setResponseHeader, setResponseStatus } from 'h3'
|
||||
import { useNitroApp, useRuntimeConfig } from '#internal/nitro'
|
||||
import { getRequestHeaders, send, setResponseHeader, setResponseStatus } from 'h3'
|
||||
import { useRuntimeConfig } from '#internal/nitro'
|
||||
import { useNitroApp } from '#internal/nitro/app'
|
||||
import { isJsonRequest, normalizeError } from '#internal/nitro/utils'
|
||||
|
||||
export default <NitroErrorHandler> async function errorhandler (error: H3Error, event) {
|
||||
@ -11,7 +12,7 @@ export default <NitroErrorHandler> async function errorhandler (error: H3Error,
|
||||
|
||||
// Create an error object
|
||||
const errorObject = {
|
||||
url: event.node.req.url,
|
||||
url: event.path,
|
||||
statusCode,
|
||||
statusMessage,
|
||||
message,
|
||||
@ -41,12 +42,11 @@ export default <NitroErrorHandler> async function errorhandler (error: H3Error,
|
||||
// JSON response
|
||||
if (isJsonRequest(event)) {
|
||||
setResponseHeader(event, 'Content-Type', 'application/json')
|
||||
event.node.res.end(JSON.stringify(errorObject))
|
||||
return
|
||||
return send(event, JSON.stringify(errorObject))
|
||||
}
|
||||
|
||||
// HTML response (via SSR)
|
||||
const isErrorPage = event.node.req.url?.startsWith('/__nuxt_error')
|
||||
const isErrorPage = event.path.startsWith('/__nuxt_error')
|
||||
const res = !isErrorPage
|
||||
? await useNitroApp().localFetch(withQuery(joinURL(useRuntimeConfig().app.baseURL, '/__nuxt_error'), errorObject), {
|
||||
headers: getRequestHeaders(event) as Record<string, string>,
|
||||
@ -67,8 +67,7 @@ export default <NitroErrorHandler> async function errorhandler (error: H3Error,
|
||||
}
|
||||
if (event.handled) { return }
|
||||
setResponseHeader(event, 'Content-Type', 'text/html;charset=UTF-8')
|
||||
event.node.res.end(template(errorObject))
|
||||
return
|
||||
return send(event, template(errorObject))
|
||||
}
|
||||
|
||||
const html = await res.text()
|
||||
@ -79,5 +78,5 @@ export default <NitroErrorHandler> async function errorhandler (error: H3Error,
|
||||
}
|
||||
setResponseStatus(event, res.status && res.status !== 200 ? res.status : undefined, res.statusText)
|
||||
|
||||
event.node.res.end(html)
|
||||
return send(event, html)
|
||||
}
|
||||
|
@ -9,7 +9,7 @@ import {
|
||||
import type { RenderResponse } from 'nitropack'
|
||||
import type { Manifest } from 'vite'
|
||||
import type { H3Event } from 'h3'
|
||||
import { appendResponseHeader, createError, getQuery, readBody, writeEarlyHints } from 'h3'
|
||||
import { appendResponseHeader, createError, getQuery, getResponseStatus, getResponseStatusText, readBody, writeEarlyHints } from 'h3'
|
||||
import devalue from '@nuxt/devalue'
|
||||
import { stringify, uneval } from 'devalue'
|
||||
import destr from 'destr'
|
||||
@ -170,16 +170,16 @@ const islandPropCache = import.meta.prerender ? useStorage('internal:nuxt:preren
|
||||
|
||||
async function getIslandContext (event: H3Event): Promise<NuxtIslandContext> {
|
||||
// TODO: Strict validation for url
|
||||
let url = event.node.req.url || ''
|
||||
if (import.meta.prerender && event.node.req.url && await islandPropCache!.hasItem(event.node.req.url)) {
|
||||
let url = event.path || ''
|
||||
if (import.meta.prerender && event.path && await islandPropCache!.hasItem(event.path)) {
|
||||
// rehydrate props from cache so we can rerender island if cache does not have it any more
|
||||
url = await islandPropCache!.getItem(event.node.req.url) as string
|
||||
url = await islandPropCache!.getItem(event.path) as string
|
||||
}
|
||||
url = url.substring('/__nuxt_island'.length + 1) || ''
|
||||
const [componentName, hashId] = url.split('?')[0].split('_')
|
||||
|
||||
// TODO: Validate context
|
||||
const context = event.node.req.method === 'GET' ? getQuery(event) : await readBody(event)
|
||||
const context = event.method === 'GET' ? getQuery(event) : await readBody(event)
|
||||
|
||||
const ctx: NuxtIslandContext = {
|
||||
url: '/',
|
||||
@ -202,7 +202,7 @@ export default defineRenderHandler(async (event): Promise<Partial<RenderResponse
|
||||
const nitroApp = useNitroApp()
|
||||
|
||||
// Whether we're rendering an error page
|
||||
const ssrError = event.node.req.url?.startsWith('/__nuxt_error')
|
||||
const ssrError = event.path.startsWith('/__nuxt_error')
|
||||
? getQuery(event) as unknown as Exclude<NuxtPayload['error'], Error>
|
||||
: null
|
||||
|
||||
@ -210,7 +210,7 @@ export default defineRenderHandler(async (event): Promise<Partial<RenderResponse
|
||||
ssrError.statusCode = parseInt(ssrError.statusCode as any)
|
||||
}
|
||||
|
||||
if (ssrError && event.node.req.socket.readyState !== 'readOnly' /* direct request */) {
|
||||
if (ssrError && !('__unenv__' in event.node.req) /* allow internal fetch */) {
|
||||
throw createError({
|
||||
statusCode: 404,
|
||||
statusMessage: 'Page Not Found: /__nuxt_error'
|
||||
@ -218,21 +218,23 @@ export default defineRenderHandler(async (event): Promise<Partial<RenderResponse
|
||||
}
|
||||
|
||||
// Check for island component rendering
|
||||
const islandContext = (process.env.NUXT_COMPONENT_ISLANDS && event.node.req.url?.startsWith('/__nuxt_island'))
|
||||
const islandContext = (process.env.NUXT_COMPONENT_ISLANDS && event.path.startsWith('/__nuxt_island'))
|
||||
? await getIslandContext(event)
|
||||
: undefined
|
||||
|
||||
if (import.meta.prerender && islandContext && event.node.req.url && await islandCache!.hasItem(event.node.req.url)) {
|
||||
return islandCache!.getItem(event.node.req.url) as Promise<Partial<RenderResponse>>
|
||||
if (import.meta.prerender && islandContext && event.path && await islandCache!.hasItem(event.path)) {
|
||||
return islandCache!.getItem(event.path) as Promise<Partial<RenderResponse>>
|
||||
}
|
||||
|
||||
// Request url
|
||||
let url = ssrError?.url as string || islandContext?.url || event.node.req.url!
|
||||
let url = ssrError?.url as string || islandContext?.url || event.path
|
||||
|
||||
// Whether we are rendering payload route
|
||||
const isRenderingPayload = PAYLOAD_URL_RE.test(url) && !islandContext
|
||||
if (isRenderingPayload) {
|
||||
url = url.substring(0, url.lastIndexOf('/')) || '/'
|
||||
|
||||
event._path = url
|
||||
event.node.req.url = url
|
||||
if (import.meta.prerender && await payloadCache!.hasItem(url)) {
|
||||
return payloadCache!.getItem(url) as Promise<Partial<RenderResponse>>
|
||||
@ -435,8 +437,8 @@ export default defineRenderHandler(async (event): Promise<Partial<RenderResponse
|
||||
|
||||
const response = {
|
||||
body: JSON.stringify(islandResponse, null, 2),
|
||||
statusCode: event.node.res.statusCode,
|
||||
statusMessage: event.node.res.statusMessage,
|
||||
statusCode: getResponseStatus(event),
|
||||
statusMessage: getResponseStatusText(event),
|
||||
headers: {
|
||||
'content-type': 'application/json;charset=utf-8',
|
||||
'x-powered-by': 'Nuxt'
|
||||
@ -444,7 +446,7 @@ export default defineRenderHandler(async (event): Promise<Partial<RenderResponse
|
||||
} satisfies RenderResponse
|
||||
if (import.meta.prerender) {
|
||||
await islandCache!.setItem(`/__nuxt_island/${islandContext!.name}_${islandContext!.id}`, response)
|
||||
await islandPropCache!.setItem(`/__nuxt_island/${islandContext!.name}_${islandContext!.id}`, event.node.req.url!)
|
||||
await islandPropCache!.setItem(`/__nuxt_island/${islandContext!.name}_${islandContext!.id}`, event.path)
|
||||
}
|
||||
return response
|
||||
}
|
||||
@ -452,8 +454,8 @@ export default defineRenderHandler(async (event): Promise<Partial<RenderResponse
|
||||
// Construct HTML response
|
||||
const response = {
|
||||
body: renderHTMLDocument(htmlContext),
|
||||
statusCode: event.node.res.statusCode,
|
||||
statusMessage: event.node.res.statusMessage,
|
||||
statusCode: getResponseStatus(event),
|
||||
statusMessage: getResponseStatusText(event),
|
||||
headers: {
|
||||
'content-type': 'text/html;charset=utf-8',
|
||||
'x-powered-by': 'Nuxt'
|
||||
@ -511,8 +513,8 @@ function renderPayloadResponse (ssrContext: NuxtSSRContext) {
|
||||
body: process.env.NUXT_JSON_PAYLOADS
|
||||
? stringify(splitPayload(ssrContext).payload, ssrContext._payloadReducers)
|
||||
: `export default ${devalue(splitPayload(ssrContext).payload)}`,
|
||||
statusCode: ssrContext.event.node.res.statusCode,
|
||||
statusMessage: ssrContext.event.node.res.statusMessage,
|
||||
statusCode: getResponseStatus(ssrContext.event),
|
||||
statusMessage: getResponseStatusText(ssrContext.event),
|
||||
headers: {
|
||||
'content-type': process.env.NUXT_JSON_PAYLOADS ? 'application/json;charset=utf-8' : 'text/javascript;charset=utf-8',
|
||||
'x-powered-by': 'Nuxt'
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { addTemplate, addVitePlugin, addWebpackPlugin, defineNuxtModule, resolveAlias, tryResolveModule, updateTemplates, useNuxt } from '@nuxt/kit'
|
||||
import { addTemplate, addVitePlugin, addWebpackPlugin, defineNuxtModule, isIgnored, resolveAlias, tryResolveModule, updateTemplates, useNuxt } from '@nuxt/kit'
|
||||
import { isAbsolute, join, normalize, relative, resolve } from 'pathe'
|
||||
import type { Import, Unimport } from 'unimport'
|
||||
import { createUnimport, scanDirExports } from 'unimport'
|
||||
@ -82,8 +82,8 @@ export default defineNuxtModule<Partial<ImportsOptions>>({
|
||||
nuxt.options.alias['#imports'] = join(nuxt.options.buildDir, 'imports')
|
||||
|
||||
// Transform to inject imports in production mode
|
||||
addVitePlugin(() => TransformPlugin.vite({ ctx, options, sourcemap: nuxt.options.sourcemap.server || nuxt.options.sourcemap.client }))
|
||||
addWebpackPlugin(() => TransformPlugin.webpack({ ctx, options, sourcemap: nuxt.options.sourcemap.server || nuxt.options.sourcemap.client }))
|
||||
addVitePlugin(() => TransformPlugin.vite({ ctx, options, sourcemap: !!nuxt.options.sourcemap.server || !!nuxt.options.sourcemap.client }))
|
||||
addWebpackPlugin(() => TransformPlugin.webpack({ ctx, options, sourcemap: !!nuxt.options.sourcemap.server || !!nuxt.options.sourcemap.client }))
|
||||
|
||||
const priorities = nuxt.options._layers.map((layer, i) => [layer.config.srcDir, -i] as const).sort(([a], [b]) => b.length - a.length)
|
||||
|
||||
@ -92,7 +92,9 @@ export default defineNuxtModule<Partial<ImportsOptions>>({
|
||||
// Clear old imports
|
||||
imports.length = 0
|
||||
// Scan `composables/`
|
||||
const composableImports = await scanDirExports(composablesDirs)
|
||||
const composableImports = await scanDirExports(composablesDirs, {
|
||||
fileFilter: file => !isIgnored(file)
|
||||
})
|
||||
for (const i of composableImports) {
|
||||
i.priority = i.priority || priorities.find(([dir]) => i.from.startsWith(dir))?.[1]
|
||||
}
|
||||
|
@ -9,11 +9,14 @@ import { createRoutesContext } from 'unplugin-vue-router'
|
||||
import { resolveOptions } from 'unplugin-vue-router/options'
|
||||
import type { EditableTreeNode, Options as TypedRouterOptions } from 'unplugin-vue-router'
|
||||
|
||||
import type { NitroRouteConfig } from 'nitropack'
|
||||
import { defu } from 'defu'
|
||||
import { distDir } from '../dirs'
|
||||
import { normalizeRoutes, resolvePagesRoutes } from './utils'
|
||||
import type { PageMetaPluginOptions } from './page-meta'
|
||||
import { PageMetaPlugin } from './page-meta'
|
||||
import { RouteInjectionPlugin } from './route-injection'
|
||||
import { extractRouteRules, getMappedPages } from './route-rules'
|
||||
import type { PageMetaPluginOptions } from './plugins/page-meta'
|
||||
import { PageMetaPlugin } from './plugins/page-meta'
|
||||
import { RouteInjectionPlugin } from './plugins/route-injection'
|
||||
|
||||
const OPTIONAL_PARAM_RE = /^\/?:.*(\?|\(\.\*\)\*)$/
|
||||
|
||||
@ -239,12 +242,73 @@ export default defineNuxtModule({
|
||||
{ name: 'definePageMeta', as: 'definePageMeta', from: resolve(runtimeDir, 'composables') },
|
||||
{ name: 'useLink', as: 'useLink', from: '#vue-router' }
|
||||
)
|
||||
if (nuxt.options.experimental.inlineRouteRules) {
|
||||
imports.push({ name: 'defineRouteRules', as: 'defineRouteRules', from: resolve(runtimeDir, 'composables') })
|
||||
}
|
||||
})
|
||||
|
||||
if (nuxt.options.experimental.inlineRouteRules) {
|
||||
// Track mappings of absolute files to globs
|
||||
let pageToGlobMap = {} as { [absolutePath: string]: string | null }
|
||||
nuxt.hook('pages:extend', (pages) => { pageToGlobMap = getMappedPages(pages) })
|
||||
|
||||
// Extracted route rules defined inline in pages
|
||||
const inlineRules = {} as { [glob: string]: NitroRouteConfig }
|
||||
|
||||
// Allow telling Nitro to reload route rules
|
||||
let updateRouteConfig: () => void | Promise<void>
|
||||
nuxt.hook('nitro:init', (nitro) => {
|
||||
updateRouteConfig = () => nitro.updateConfig({ routeRules: defu(inlineRules, nitro.options._config.routeRules) })
|
||||
})
|
||||
|
||||
async function updatePage (path: string) {
|
||||
const glob = pageToGlobMap[path]
|
||||
const code = path in nuxt.vfs ? nuxt.vfs[path] : await readFile(path!, 'utf-8')
|
||||
try {
|
||||
const extractedRule = await extractRouteRules(code)
|
||||
if (extractedRule) {
|
||||
if (!glob) {
|
||||
const relativePath = relative(nuxt.options.srcDir, path)
|
||||
console.error(`[nuxt] Could not set inline route rules in \`~/${relativePath}\` as it could not be mapped to a Nitro route.`)
|
||||
return
|
||||
}
|
||||
|
||||
inlineRules[glob] = extractedRule
|
||||
} else if (glob) {
|
||||
delete inlineRules[glob]
|
||||
}
|
||||
} catch (e: any) {
|
||||
if (e.toString().includes('Error parsing route rules')) {
|
||||
const relativePath = relative(nuxt.options.srcDir, path)
|
||||
console.error(`[nuxt] Error parsing route rules within \`~/${relativePath}\`. They should be JSON-serializable.`)
|
||||
} else {
|
||||
console.error(e)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
nuxt.hook('builder:watch', async (event, relativePath) => {
|
||||
const path = join(nuxt.options.srcDir, relativePath)
|
||||
if (!(path in pageToGlobMap)) { return }
|
||||
if (event === 'unlink') {
|
||||
delete inlineRules[path]
|
||||
delete pageToGlobMap[path]
|
||||
} else {
|
||||
await updatePage(path)
|
||||
}
|
||||
await updateRouteConfig?.()
|
||||
})
|
||||
|
||||
nuxt.hooks.hookOnce('pages:extend', async () => {
|
||||
for (const page in pageToGlobMap) { await updatePage(page) }
|
||||
await updateRouteConfig?.()
|
||||
})
|
||||
}
|
||||
|
||||
// Extract macros from pages
|
||||
const pageMetaOptions: PageMetaPluginOptions = {
|
||||
dev: nuxt.options.dev,
|
||||
sourcemap: nuxt.options.sourcemap.server || nuxt.options.sourcemap.client
|
||||
sourcemap: !!nuxt.options.sourcemap.server || !!nuxt.options.sourcemap.client
|
||||
}
|
||||
nuxt.hook('modules:done', () => {
|
||||
addVitePlugin(() => PageMetaPlugin.vite(pageMetaOptions))
|
||||
|
@ -1,7 +1,7 @@
|
||||
import { createUnplugin } from 'unplugin'
|
||||
import MagicString from 'magic-string'
|
||||
import type { Nuxt } from '@nuxt/schema'
|
||||
import { isVue } from '../core/utils'
|
||||
import { isVue } from '../../core/utils'
|
||||
|
||||
const INJECTION_RE = /\b_ctx\.\$route\b/g
|
||||
const INJECTION_SINGLE_RE = /\b_ctx\.\$route\b/
|
60
packages/nuxt/src/pages/route-rules.ts
Normal file
60
packages/nuxt/src/pages/route-rules.ts
Normal file
@ -0,0 +1,60 @@
|
||||
import { runInNewContext } from 'node:vm'
|
||||
import type { Node } from 'estree-walker'
|
||||
import type { CallExpression } from 'estree'
|
||||
import { walk } from 'estree-walker'
|
||||
import { transform } from 'esbuild'
|
||||
import { parse } from 'acorn'
|
||||
import type { NuxtPage } from '@nuxt/schema'
|
||||
import type { NitroRouteConfig } from 'nitropack'
|
||||
import { normalize } from 'pathe'
|
||||
import { extractScriptContent, pathToNitroGlob } from './utils'
|
||||
|
||||
const ROUTE_RULE_RE = /\bdefineRouteRules\(/
|
||||
const ruleCache: Record<string, NitroRouteConfig | null> = {}
|
||||
|
||||
export async function extractRouteRules (code: string): Promise<NitroRouteConfig | null> {
|
||||
if (code in ruleCache) {
|
||||
return ruleCache[code]
|
||||
}
|
||||
if (!ROUTE_RULE_RE.test(code)) { return null }
|
||||
|
||||
code = extractScriptContent(code) || code
|
||||
|
||||
let rule: NitroRouteConfig | null = null
|
||||
|
||||
const js = await transform(code, { loader: 'ts' })
|
||||
walk(parse(js.code, {
|
||||
sourceType: 'module',
|
||||
ecmaVersion: 'latest'
|
||||
}) as Node, {
|
||||
enter (_node) {
|
||||
if (_node.type !== 'CallExpression' || (_node as CallExpression).callee.type !== 'Identifier') { return }
|
||||
const node = _node as CallExpression & { start: number, end: number }
|
||||
const name = 'name' in node.callee && node.callee.name
|
||||
if (name === 'defineRouteRules') {
|
||||
const rulesString = js.code.slice(node.start, node.end)
|
||||
try {
|
||||
rule = JSON.parse(runInNewContext(rulesString.replace('defineRouteRules', 'JSON.stringify'), {}))
|
||||
} catch {
|
||||
throw new Error('[nuxt] Error parsing route rules. They should be JSON-serializable.')
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
ruleCache[code] = rule
|
||||
return rule
|
||||
}
|
||||
|
||||
export function getMappedPages (pages: NuxtPage[], paths = {} as { [absolutePath: string]: string | null }, prefix = '') {
|
||||
for (const page of pages) {
|
||||
if (page.file) {
|
||||
const filename = normalize(page.file)
|
||||
paths[filename] = pathToNitroGlob(prefix + page.path)
|
||||
}
|
||||
if (page.children) {
|
||||
getMappedPages(page.children, paths, page.path + '/')
|
||||
}
|
||||
}
|
||||
return paths
|
||||
}
|
@ -2,6 +2,7 @@ import type { KeepAliveProps, TransitionProps, UnwrapRef } from 'vue'
|
||||
import { getCurrentInstance } from 'vue'
|
||||
import type { RouteLocationNormalized, RouteLocationNormalizedLoaded, RouteRecordRedirectOption } from '#vue-router'
|
||||
import { useRoute } from 'vue-router'
|
||||
import type { NitroRouteConfig } from 'nitropack'
|
||||
import type { NuxtError } from '#app'
|
||||
|
||||
export interface PageMeta {
|
||||
@ -64,3 +65,15 @@ export const definePageMeta = (meta: PageMeta): void => {
|
||||
warnRuntimeUsage('definePageMeta')
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* You can define route rules for the current page. Matching route rules will be created, based on the page's _path_.
|
||||
*
|
||||
* For example, a rule defined in `~/pages/foo/bar.vue` will be applied to `/foo/bar` requests. A rule in
|
||||
* `~/pages/foo/[id].vue` will be applied to `/foo/**` requests.
|
||||
*
|
||||
* For more control, such as if you are using a custom `path` or `alias` set in the page's `definePageMeta`, you
|
||||
* should set `routeRules` directly within your `nuxt.config`.
|
||||
*/
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
export const defineRouteRules = (rules: NitroRouteConfig): void => {}
|
||||
|
@ -107,7 +107,7 @@ export async function generateRoutesFromFiles (files: string[], pagesDir: string
|
||||
}
|
||||
|
||||
const SFC_SCRIPT_RE = /<script\s*[^>]*>([\s\S]*?)<\/script\s*[^>]*>/i
|
||||
function extractScriptContent (html: string) {
|
||||
export function extractScriptContent (html: string) {
|
||||
const match = html.match(SFC_SCRIPT_RE)
|
||||
|
||||
if (match && match[1]) {
|
||||
@ -335,3 +335,15 @@ export function normalizeRoutes (routes: NuxtPage[], metaImports: Set<string> =
|
||||
}))
|
||||
}
|
||||
}
|
||||
|
||||
export function pathToNitroGlob (path: string) {
|
||||
if (!path) {
|
||||
return null
|
||||
}
|
||||
// Ignore pages with multiple dynamic parameters.
|
||||
if (path.indexOf(':') !== path.lastIndexOf(':')) {
|
||||
return null
|
||||
}
|
||||
|
||||
return path.replace(/\/(?:[^:/]+)?:\w+.*$/, '/**')
|
||||
}
|
||||
|
@ -1,6 +1,6 @@
|
||||
import { describe, expect, it } from 'vitest'
|
||||
import type { NuxtPage } from 'nuxt/schema'
|
||||
import { generateRoutesFromFiles } from '../src/pages/utils'
|
||||
import { generateRoutesFromFiles, pathToNitroGlob } from '../src/pages/utils'
|
||||
import { generateRouteKey } from '../src/pages/runtime/utils'
|
||||
|
||||
describe('pages:generateRoutesFromFiles', () => {
|
||||
@ -442,3 +442,20 @@ describe('pages:generateRouteKey', () => {
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
const pathToNitroGlobTests = {
|
||||
'/': '/',
|
||||
'/:id': '/**',
|
||||
'/:id()': '/**',
|
||||
'/:id?': '/**',
|
||||
'/some-:id?': '/**',
|
||||
'/other/some-:id?': '/other/**',
|
||||
'/other/some-:id()-more': '/other/**',
|
||||
'/other/nested': '/other/nested'
|
||||
}
|
||||
|
||||
describe('pages:pathToNitroGlob', () => {
|
||||
it.each(Object.entries(pathToNitroGlobTests))('should convert %s to %s', (path, expected) => {
|
||||
expect(pathToNitroGlob(path)).to.equal(expected)
|
||||
})
|
||||
})
|
||||
|
@ -5,6 +5,7 @@
|
||||
"license": "MIT",
|
||||
"type": "module",
|
||||
"types": "./dist/index.d.ts",
|
||||
"sideEffects": false,
|
||||
"exports": {
|
||||
".": {
|
||||
"types": "./dist/index.d.ts",
|
||||
@ -30,14 +31,14 @@
|
||||
"@types/file-loader": "5.0.1",
|
||||
"@types/pug": "2.0.6",
|
||||
"@types/sass-loader": "8.0.5",
|
||||
"@unhead/schema": "1.3.5",
|
||||
"@vitejs/plugin-vue": "4.3.1",
|
||||
"@unhead/schema": "1.3.7",
|
||||
"@vitejs/plugin-vue": "4.3.3",
|
||||
"@vitejs/plugin-vue-jsx": "3.0.2",
|
||||
"@vue/compiler-core": "3.3.4",
|
||||
"esbuild-loader": "4.0.1",
|
||||
"h3": "1.8.0",
|
||||
"ignore": "5.2.4",
|
||||
"nitropack": "2.5.2",
|
||||
"nitropack": "2.6.0",
|
||||
"unbuild": "latest",
|
||||
"unctx": "2.3.1",
|
||||
"vite": "4.4.9",
|
||||
@ -55,9 +56,9 @@
|
||||
"pathe": "^1.1.1",
|
||||
"pkg-types": "^1.0.3",
|
||||
"postcss-import-resolver": "^2.0.0",
|
||||
"std-env": "^3.4.0",
|
||||
"ufo": "^1.2.0",
|
||||
"unimport": "^3.1.3",
|
||||
"std-env": "^3.4.3",
|
||||
"ufo": "^1.3.0",
|
||||
"unimport": "^3.2.0",
|
||||
"untyped": "^1.4.0"
|
||||
},
|
||||
"engines": {
|
||||
|
@ -241,10 +241,10 @@ export default defineUntypedSchema({
|
||||
* }
|
||||
* </style>
|
||||
* ```
|
||||
* @type {string | false}
|
||||
* @type {string | boolean}
|
||||
*/
|
||||
spaLoadingTemplate: {
|
||||
$resolve: async (val, get) => typeof val === 'string' ? resolve(await get('srcDir'), val) : (val ?? null)
|
||||
$resolve: async (val, get) => typeof val === 'string' ? resolve(await get('srcDir'), val) : (val ?? false)
|
||||
},
|
||||
|
||||
/**
|
||||
|
@ -25,7 +25,7 @@ export default defineUntypedSchema({
|
||||
/**
|
||||
* Whether to generate sourcemaps.
|
||||
*
|
||||
* @type {boolean | { server?: boolean, client?: boolean }}
|
||||
* @type {boolean | { server?: boolean | 'hidden', client?: boolean | 'hidden' }}
|
||||
*/
|
||||
sourcemap: {
|
||||
$resolve: async (val, get) => {
|
||||
|
@ -238,6 +238,18 @@ export default defineUntypedSchema({
|
||||
*
|
||||
* @see https://github.com/nuxt/nuxt/discussions/22632
|
||||
*/
|
||||
headNext: false
|
||||
headNext: false,
|
||||
|
||||
/**
|
||||
* Allow defining `routeRules` directly within your `~/pages` directory using `defineRouteRules`.
|
||||
*
|
||||
* Rules are converted (based on the path) and applied for server requests. For example, a rule
|
||||
* defined in `~/pages/foo/bar.vue` will be applied to `/foo/bar` requests. A rule in `~/pages/foo/[id].vue`
|
||||
* will be applied to `/foo/**` requests.
|
||||
*
|
||||
* For more control, such as if you are using a custom `path` or `alias` set in the page's `definePageMeta`, you
|
||||
* should set `routeRules` directly within your `nuxt.config`.
|
||||
*/
|
||||
inlineRouteRules: false
|
||||
}
|
||||
})
|
||||
|
@ -59,6 +59,7 @@ export interface Nuxt {
|
||||
// Private fields.
|
||||
_version: string
|
||||
_ignore?: Ignore
|
||||
_ignorePatterns?: string[]
|
||||
|
||||
/** The resolved Nuxt configuration. */
|
||||
options: NuxtOptions
|
||||
|
@ -28,12 +28,12 @@
|
||||
"defu": "^6.1.2",
|
||||
"execa": "^7.2.0",
|
||||
"get-port-please": "^3.0.1",
|
||||
"ofetch": "^1.1.1",
|
||||
"ofetch": "^1.3.3",
|
||||
"pathe": "^1.1.1",
|
||||
"ufo": "^1.2.0"
|
||||
"ufo": "^1.3.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@jest/globals": "29.6.2",
|
||||
"@jest/globals": "29.6.4",
|
||||
"playwright-core": "1.37.1",
|
||||
"unbuild": "latest",
|
||||
"vitest": "0.33.0"
|
||||
|
@ -17,11 +17,12 @@ export async function startServer () {
|
||||
ctx.url = 'http://127.0.0.1:' + port
|
||||
if (ctx.options.dev) {
|
||||
const nuxiCLI = await kit.resolvePath('nuxi/cli')
|
||||
ctx.serverProcess = execa(nuxiCLI, ['dev'], {
|
||||
ctx.serverProcess = execa(nuxiCLI, ['_dev'], {
|
||||
cwd: ctx.nuxt!.options.rootDir,
|
||||
stdio: 'inherit',
|
||||
env: {
|
||||
...process.env,
|
||||
_PORT: String(port),
|
||||
PORT: String(port),
|
||||
NITRO_PORT: String(port),
|
||||
NODE_ENV: 'development'
|
||||
@ -29,7 +30,7 @@ export async function startServer () {
|
||||
})
|
||||
await waitForPort(port, { retries: 32 })
|
||||
let lastError
|
||||
for (let i = 0; i < 50; i++) {
|
||||
for (let i = 0; i < 150; i++) {
|
||||
await new Promise(resolve => setTimeout(resolve, 100))
|
||||
try {
|
||||
const res = await $fetch(ctx.nuxt!.options.app.baseURL)
|
||||
|
@ -28,7 +28,7 @@
|
||||
"dependencies": {
|
||||
"@nuxt/kit": "workspace:../kit",
|
||||
"@rollup/plugin-replace": "^5.0.2",
|
||||
"@vitejs/plugin-vue": "^4.3.1",
|
||||
"@vitejs/plugin-vue": "^4.3.3",
|
||||
"@vitejs/plugin-vue-jsx": "^3.0.2",
|
||||
"autoprefixer": "^10.4.15",
|
||||
"clear": "^0.1.0",
|
||||
@ -43,7 +43,7 @@
|
||||
"get-port-please": "^3.0.1",
|
||||
"h3": "^1.8.0",
|
||||
"knitwork": "^1.0.0",
|
||||
"magic-string": "^0.30.2",
|
||||
"magic-string": "^0.30.3",
|
||||
"mlly": "^1.4.0",
|
||||
"ohash": "^1.1.3",
|
||||
"pathe": "^1.1.1",
|
||||
@ -53,9 +53,9 @@
|
||||
"postcss-import": "^15.1.0",
|
||||
"postcss-url": "^10.1.3",
|
||||
"rollup-plugin-visualizer": "^5.9.2",
|
||||
"std-env": "^3.4.0",
|
||||
"std-env": "^3.4.3",
|
||||
"strip-literal": "^1.3.0",
|
||||
"ufo": "^1.2.0",
|
||||
"ufo": "^1.3.0",
|
||||
"unplugin": "^1.4.0",
|
||||
"vite": "^4.4.9",
|
||||
"vite-node": "^0.33.0",
|
||||
|
@ -35,7 +35,7 @@ export async function buildClient (ctx: ViteBuildContext) {
|
||||
}
|
||||
},
|
||||
css: {
|
||||
devSourcemap: ctx.nuxt.options.sourcemap.client
|
||||
devSourcemap: !!ctx.nuxt.options.sourcemap.client
|
||||
},
|
||||
define: {
|
||||
'process.env.NODE_ENV': JSON.stringify(ctx.config.mode),
|
||||
@ -78,12 +78,12 @@ export async function buildClient (ctx: ViteBuildContext) {
|
||||
buildAssetsURL: joinURL(ctx.nuxt.options.app.baseURL, ctx.nuxt.options.app.buildAssetsDir)
|
||||
}),
|
||||
runtimePathsPlugin({
|
||||
sourcemap: ctx.nuxt.options.sourcemap.client
|
||||
sourcemap: !!ctx.nuxt.options.sourcemap.client
|
||||
}),
|
||||
viteNodePlugin(ctx),
|
||||
pureAnnotationsPlugin.vite({
|
||||
sourcemap: ctx.nuxt.options.sourcemap.client,
|
||||
functions: ['defineComponent', 'defineAsyncComponent', 'defineNuxtLink', 'createClientOnly', 'defineNuxtPlugin', 'defineNuxtRouteMiddleware', 'defineNuxtComponent', 'useRuntimeConfig']
|
||||
sourcemap: !!ctx.nuxt.options.sourcemap.client,
|
||||
functions: ['defineComponent', 'defineAsyncComponent', 'defineNuxtLink', 'createClientOnly', 'defineNuxtPlugin', 'defineNuxtRouteMiddleware', 'defineNuxtComponent', 'useRuntimeConfig', 'defineRouteRules']
|
||||
})
|
||||
],
|
||||
appType: 'custom',
|
||||
@ -102,7 +102,7 @@ export async function buildClient (ctx: ViteBuildContext) {
|
||||
|
||||
// Emit chunk errors if the user has opted in to `experimental.emitRouteChunkError`
|
||||
if (ctx.nuxt.options.experimental.emitRouteChunkError) {
|
||||
clientConfig.plugins!.push(chunkErrorPlugin({ sourcemap: ctx.nuxt.options.sourcemap.client }))
|
||||
clientConfig.plugins!.push(chunkErrorPlugin({ sourcemap: !!ctx.nuxt.options.sourcemap.client }))
|
||||
}
|
||||
|
||||
// We want to respect users' own rollup output options
|
||||
@ -135,7 +135,7 @@ export async function buildClient (ctx: ViteBuildContext) {
|
||||
|
||||
// Add type checking client panel
|
||||
if (ctx.nuxt.options.typescript.typeCheck && ctx.nuxt.options.dev) {
|
||||
clientConfig.plugins!.push(typeCheckPlugin({ sourcemap: ctx.nuxt.options.sourcemap.client }))
|
||||
clientConfig.plugins!.push(typeCheckPlugin({ sourcemap: !!ctx.nuxt.options.sourcemap.client }))
|
||||
}
|
||||
|
||||
await ctx.nuxt.callHook('vite:extendConfig', clientConfig, { isClient: true, isServer: false })
|
||||
@ -163,18 +163,17 @@ export async function buildClient (ctx: ViteBuildContext) {
|
||||
})
|
||||
|
||||
const viteMiddleware = defineEventHandler(async (event) => {
|
||||
// Workaround: vite devmiddleware modifies req.url
|
||||
const originalURL = event.node.req.url!
|
||||
|
||||
const viteRoutes = viteServer.middlewares.stack.map(m => m.route).filter(r => r.length > 1)
|
||||
if (!originalURL.startsWith(clientConfig.base!) && !viteRoutes.some(route => originalURL.startsWith(route))) {
|
||||
if (!event.path.startsWith(clientConfig.base!) && !viteRoutes.some(route => event.path.startsWith(route))) {
|
||||
// @ts-expect-error _skip_transform is a private property
|
||||
event.node.req._skip_transform = true
|
||||
}
|
||||
|
||||
// Workaround: vite devmiddleware modifies req.url
|
||||
const _originalPath = event.node.req.url
|
||||
await new Promise((resolve, reject) => {
|
||||
viteServer.middlewares.handle(event.node.req, event.node.res, (err: Error) => {
|
||||
event.node.req.url = originalURL
|
||||
event.node.req.url = _originalPath
|
||||
return err ? reject(err) : resolve(null)
|
||||
})
|
||||
})
|
||||
|
@ -75,11 +75,13 @@ export function ssrStylesPlugin (options: SSRStylePluginOptions): Plugin {
|
||||
source: ''
|
||||
})
|
||||
|
||||
const baseDir = dirname(base)
|
||||
|
||||
emitted[file] = this.emitFile({
|
||||
type: 'asset',
|
||||
name: `${filename(file)}-styles.mjs`,
|
||||
source: [
|
||||
...files.map((css, i) => `import style_${i} from './${relative(dirname(base), this.getFileName(css))}';`),
|
||||
...files.map((css, i) => `import style_${i} from './${relative(baseDir, this.getFileName(css))}';`),
|
||||
`export default [${files.map((_, i) => `style_${i}`).join(', ')}]`
|
||||
].join('\n')
|
||||
})
|
||||
|
@ -15,7 +15,6 @@ import { transpile } from './utils/transpile'
|
||||
export async function buildServer (ctx: ViteBuildContext) {
|
||||
const helper = ctx.nuxt.options.nitro.imports !== false ? '' : 'globalThis.'
|
||||
const entry = ctx.nuxt.options.ssr ? ctx.entry : await resolvePath(resolve(ctx.nuxt.options.appDir, 'entry-spa'))
|
||||
const nitroDependencies = await tryResolveModule('nitropack/package.json', ctx.nuxt.options.modulesDir).then(r => import(r!)).then(r => Object.keys(r.dependencies || {})).catch(() => [])
|
||||
const serverConfig: ViteConfig = vite.mergeConfig(ctx.config, vite.mergeConfig({
|
||||
configFile: false,
|
||||
base: ctx.nuxt.options.dev
|
||||
@ -37,7 +36,7 @@ export async function buildServer (ctx: ViteBuildContext) {
|
||||
}
|
||||
},
|
||||
css: {
|
||||
devSourcemap: ctx.nuxt.options.sourcemap.server
|
||||
devSourcemap: !!ctx.nuxt.options.sourcemap.server
|
||||
},
|
||||
define: {
|
||||
'process.server': true,
|
||||
@ -62,11 +61,7 @@ export async function buildServer (ctx: ViteBuildContext) {
|
||||
},
|
||||
ssr: {
|
||||
external: [
|
||||
'#internal/nitro', '#internal/nitro/utils',
|
||||
// explicit dependencies we use in our ssr renderer - these can be inlined (if necessary) in the nitro build
|
||||
'unhead', '@unhead/ssr', '@unhead/vue', 'unctx', 'h3', 'devalue', '@nuxt/devalue', 'radix3', 'unstorage', 'hookable',
|
||||
// dependencies we might share with nitro - these can be inlined (if necessary) in the nitro build
|
||||
...nitroDependencies
|
||||
'#internal/nitro', '#internal/nitro/utils'
|
||||
],
|
||||
noExternal: [
|
||||
...transpile({ isServer: true, isDev: ctx.nuxt.options.dev }),
|
||||
@ -106,12 +101,23 @@ export async function buildServer (ctx: ViteBuildContext) {
|
||||
},
|
||||
plugins: [
|
||||
pureAnnotationsPlugin.vite({
|
||||
sourcemap: ctx.nuxt.options.sourcemap.server,
|
||||
functions: ['defineComponent', 'defineAsyncComponent', 'defineNuxtLink', 'createClientOnly', 'defineNuxtPlugin', 'defineNuxtRouteMiddleware', 'defineNuxtComponent', 'useRuntimeConfig']
|
||||
sourcemap: !!ctx.nuxt.options.sourcemap.server,
|
||||
functions: ['defineComponent', 'defineAsyncComponent', 'defineNuxtLink', 'createClientOnly', 'defineNuxtPlugin', 'defineNuxtRouteMiddleware', 'defineNuxtComponent', 'useRuntimeConfig', 'defineRouteRules']
|
||||
})
|
||||
]
|
||||
} satisfies vite.InlineConfig, ctx.nuxt.options.vite.$server || {}))
|
||||
|
||||
if (!ctx.nuxt.options.dev) {
|
||||
const nitroDependencies = await tryResolveModule('nitropack/package.json', ctx.nuxt.options.modulesDir)
|
||||
.then(r => import(r!)).then(r => Object.keys(r.dependencies || {})).catch(() => [])
|
||||
serverConfig.ssr!.external!.push(
|
||||
// explicit dependencies we use in our ssr renderer - these can be inlined (if necessary) in the nitro build
|
||||
'unhead', '@unhead/ssr', 'unctx', 'h3', 'devalue', '@nuxt/devalue', 'radix3', 'unstorage', 'hookable',
|
||||
// dependencies we might share with nitro - these can be inlined (if necessary) in the nitro build
|
||||
...nitroDependencies
|
||||
)
|
||||
}
|
||||
|
||||
serverConfig.customLogger = createViteLogger(serverConfig)
|
||||
|
||||
await ctx.nuxt.callHook('vite:extendConfig', serverConfig, { isClient: false, isServer: true })
|
||||
|
@ -141,7 +141,7 @@ function createViteNodeApp (ctx: ViteBuildContext, invalidates: Set<string> = ne
|
||||
}
|
||||
|
||||
return eventHandler(async (event) => {
|
||||
const moduleId = decodeURI(event.node.req.url!).substring(1)
|
||||
const moduleId = decodeURI(event.path).substring(1)
|
||||
if (moduleId === '/') {
|
||||
throw createError({ statusCode: 400 })
|
||||
}
|
||||
|
@ -99,7 +99,7 @@ export const bundle: NuxtBuilder['bundle'] = async (nuxt) => {
|
||||
},
|
||||
plugins: [
|
||||
composableKeysPlugin.vite({
|
||||
sourcemap: nuxt.options.sourcemap.server || nuxt.options.sourcemap.client,
|
||||
sourcemap: !!nuxt.options.sourcemap.server || !!nuxt.options.sourcemap.client,
|
||||
rootDir: nuxt.options.rootDir,
|
||||
composables: nuxt.options.optimization.keyedComposables
|
||||
}),
|
||||
|
@ -35,7 +35,7 @@
|
||||
"h3": "^1.8.0",
|
||||
"hash-sum": "^2.0.0",
|
||||
"lodash-es": "^4.17.21",
|
||||
"magic-string": "^0.30.2",
|
||||
"magic-string": "^0.30.3",
|
||||
"memfs": "^4.2.1",
|
||||
"mini-css-extract-plugin": "^2.7.6",
|
||||
"mlly": "^1.4.0",
|
||||
@ -47,9 +47,9 @@
|
||||
"postcss-loader": "^7.3.3",
|
||||
"postcss-url": "^10.1.3",
|
||||
"pug-plain-loader": "^1.1.0",
|
||||
"std-env": "^3.4.0",
|
||||
"std-env": "^3.4.3",
|
||||
"time-fix-plugin": "^2.0.7",
|
||||
"ufo": "^1.2.0",
|
||||
"ufo": "^1.3.0",
|
||||
"unplugin": "^1.4.0",
|
||||
"url-loader": "^4.1.1",
|
||||
"vue-bundle-renderer": "^2.0.0",
|
||||
|
@ -30,12 +30,14 @@ function clientDevtool (ctx: WebpackConfigContext) {
|
||||
return
|
||||
}
|
||||
|
||||
const prefix = ctx.nuxt.options.sourcemap.client === 'hidden' ? 'hidden-' : ''
|
||||
|
||||
if (!ctx.isDev) {
|
||||
ctx.config.devtool = 'source-map'
|
||||
ctx.config.devtool = prefix + 'source-map'
|
||||
return
|
||||
}
|
||||
|
||||
ctx.config.devtool = 'eval-cheap-module-source-map'
|
||||
ctx.config.devtool = prefix + 'eval-cheap-module-source-map'
|
||||
}
|
||||
|
||||
function clientPerformance (ctx: WebpackConfigContext) {
|
||||
|
@ -27,7 +27,12 @@ export function server (ctx: WebpackConfigContext) {
|
||||
function serverPreset (ctx: WebpackConfigContext) {
|
||||
ctx.config.output!.filename = 'server.mjs'
|
||||
|
||||
ctx.config.devtool = ctx.nuxt.options.sourcemap.server ? ctx.isDev ? 'cheap-module-source-map' : 'source-map' : false
|
||||
if (ctx.nuxt.options.sourcemap.server) {
|
||||
const prefix = ctx.nuxt.options.sourcemap.server === 'hidden' ? 'hidden-' : ''
|
||||
ctx.config.devtool = prefix + ctx.isDev ? 'cheap-module-source-map' : 'source-map'
|
||||
} else {
|
||||
ctx.config.devtool = false
|
||||
}
|
||||
|
||||
ctx.config.optimization = {
|
||||
splitChunks: false,
|
||||
|
@ -39,14 +39,14 @@ export const bundle: NuxtBuilder['bundle'] = async (nuxt) => {
|
||||
|
||||
for (const config of webpackConfigs) {
|
||||
config.plugins!.push(DynamicBasePlugin.webpack({
|
||||
sourcemap: nuxt.options.sourcemap[config.name as 'client' | 'server']
|
||||
sourcemap: !!nuxt.options.sourcemap[config.name as 'client' | 'server']
|
||||
}))
|
||||
// Emit chunk errors if the user has opted in to `experimental.emitRouteChunkError`
|
||||
if (config.name === 'client' && nuxt.options.experimental.emitRouteChunkError) {
|
||||
config.plugins!.push(new ChunkErrorPlugin())
|
||||
}
|
||||
config.plugins!.push(composableKeysPlugin.webpack({
|
||||
sourcemap: nuxt.options.sourcemap[config.name as 'client' | 'server'],
|
||||
sourcemap: !!nuxt.options.sourcemap[config.name as 'client' | 'server'],
|
||||
rootDir: nuxt.options.rootDir,
|
||||
composables: nuxt.options.optimization.keyedComposables
|
||||
}))
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user