@nx/nuxt 23.0.0-beta.16 → 23.0.0-beta.17
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/migrations.json +1 -1
- package/package.json +8 -8
- package/src/utils/add-linting.js +11 -12
- package/src/migrations/update-18-1-0/add-include-tsconfig.d.ts +0 -3
- package/src/migrations/update-18-1-0/add-include-tsconfig.d.ts.map +0 -1
- package/src/migrations/update-18-1-0/add-include-tsconfig.js +0 -48
- package/src/migrations/update-22-2-0/create-ai-instructions-for-nuxt-4.d.ts +0 -3
- package/src/migrations/update-22-2-0/create-ai-instructions-for-nuxt-4.d.ts.map +0 -1
- package/src/migrations/update-22-2-0/create-ai-instructions-for-nuxt-4.js +0 -16
- package/src/migrations/update-22-2-0/files/ai-instructions-for-nuxt-4.md +0 -531
package/migrations.json
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
"update-22-2-0-create-ai-instructions-for-nuxt-4": {
|
|
4
4
|
"version": "22.2.0-beta.0",
|
|
5
5
|
"description": "Create AI Instructions to help migrate workspaces to Nuxt 4.",
|
|
6
|
-
"
|
|
6
|
+
"prompt": "./src/migrations/update-22-2-0/ai-instructions-for-nuxt-4.md"
|
|
7
7
|
}
|
|
8
8
|
},
|
|
9
9
|
"packageJsonUpdates": {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@nx/nuxt",
|
|
3
|
-
"version": "23.0.0-beta.
|
|
3
|
+
"version": "23.0.0-beta.17",
|
|
4
4
|
"private": false,
|
|
5
5
|
"description": "The Nuxt plugin for Nx contains executors and generators for managing Nuxt applications and libraries within an Nx workspace. It provides:\n\n\n- Integration with libraries such as Vitest, Playwright, Cypress, and Storybook.\n\n- Generators for applications, libraries, and more.\n\n- Library build support for publishing packages to npm or other registries.\n\n- Utilities for automatic workspace refactoring.",
|
|
6
6
|
"repository": {
|
|
@@ -45,16 +45,16 @@
|
|
|
45
45
|
"dependencies": {
|
|
46
46
|
"tslib": "^2.3.0",
|
|
47
47
|
"@nuxt/kit": "^3.10.0 || ^4.0.0",
|
|
48
|
-
"@nx/devkit": "23.0.0-beta.
|
|
49
|
-
"@nx/js": "23.0.0-beta.
|
|
50
|
-
"@nx/eslint": "23.0.0-beta.
|
|
51
|
-
"@nx/vue": "23.0.0-beta.
|
|
52
|
-
"@nx/vite": "23.0.0-beta.
|
|
53
|
-
"@nx/vitest": "23.0.0-beta.
|
|
48
|
+
"@nx/devkit": "23.0.0-beta.17",
|
|
49
|
+
"@nx/js": "23.0.0-beta.17",
|
|
50
|
+
"@nx/eslint": "23.0.0-beta.17",
|
|
51
|
+
"@nx/vue": "23.0.0-beta.17",
|
|
52
|
+
"@nx/vite": "23.0.0-beta.17",
|
|
53
|
+
"@nx/vitest": "23.0.0-beta.17",
|
|
54
54
|
"semver": "^7.6.3"
|
|
55
55
|
},
|
|
56
56
|
"devDependencies": {
|
|
57
|
-
"nx": "23.0.0-beta.
|
|
57
|
+
"nx": "23.0.0-beta.17"
|
|
58
58
|
},
|
|
59
59
|
"peerDependencies": {
|
|
60
60
|
"@nuxt/schema": "^3.10.0 || ^4.0.0"
|
package/src/utils/add-linting.js
CHANGED
|
@@ -4,9 +4,8 @@ exports.addLinting = addLinting;
|
|
|
4
4
|
const eslint_1 = require("@nx/eslint");
|
|
5
5
|
const path_1 = require("nx/src/utils/path");
|
|
6
6
|
const devkit_1 = require("@nx/devkit");
|
|
7
|
-
const
|
|
7
|
+
const internal_1 = require("@nx/eslint/internal");
|
|
8
8
|
const versions_1 = require("./versions");
|
|
9
|
-
const flat_config_1 = require("@nx/eslint/src/utils/flat-config");
|
|
10
9
|
async function addLinting(host, options) {
|
|
11
10
|
const tasks = [];
|
|
12
11
|
if (options.linter === 'eslint') {
|
|
@@ -20,7 +19,7 @@ async function addLinting(host, options) {
|
|
|
20
19
|
addPlugin: true,
|
|
21
20
|
});
|
|
22
21
|
tasks.push(lintTask);
|
|
23
|
-
const isFlatConfig = (0,
|
|
22
|
+
const isFlatConfig = (0, internal_1.useFlatConfig)(host);
|
|
24
23
|
// Version-aware dependencies:
|
|
25
24
|
// - Flat config (v4+): use @nuxt/eslint-config ^1.10.0 with createConfigForNuxt
|
|
26
25
|
// - Legacy (.eslintrc.json): use @nuxt/eslint-config ~0.5.6 with extends
|
|
@@ -29,7 +28,7 @@ async function addLinting(host, options) {
|
|
|
29
28
|
? versions_1.nuxtEslintConfigVersion
|
|
30
29
|
: versions_1.nuxtEslintConfigLegacyVersion,
|
|
31
30
|
};
|
|
32
|
-
if ((0,
|
|
31
|
+
if ((0, internal_1.isEslintConfigSupported)(host, options.projectRoot)) {
|
|
33
32
|
if (isFlatConfig) {
|
|
34
33
|
// For flat config: Generate eslint.config.mjs using createConfigForNuxt
|
|
35
34
|
generateNuxtFlatEslintConfig(host, options.projectRoot);
|
|
@@ -37,9 +36,9 @@ async function addLinting(host, options) {
|
|
|
37
36
|
else {
|
|
38
37
|
// For legacy: Use extends with the old @nuxt/eslint-config
|
|
39
38
|
editEslintConfigFiles(host, options.projectRoot);
|
|
40
|
-
const addExtendsTask = (0,
|
|
39
|
+
const addExtendsTask = (0, internal_1.addExtendsToLintConfig)(host, options.projectRoot, ['@nuxt/eslint-config'], true);
|
|
41
40
|
tasks.push(addExtendsTask);
|
|
42
|
-
(0,
|
|
41
|
+
(0, internal_1.addIgnoresToLintConfig)(host, options.projectRoot, [
|
|
43
42
|
'.nuxt/**',
|
|
44
43
|
'.output/**',
|
|
45
44
|
'node_modules',
|
|
@@ -56,7 +55,7 @@ async function addLinting(host, options) {
|
|
|
56
55
|
* This is the recommended approach for Nuxt v4+ and ESLint flat config.
|
|
57
56
|
*/
|
|
58
57
|
function generateNuxtFlatEslintConfig(tree, projectRoot) {
|
|
59
|
-
const eslintFile = (0,
|
|
58
|
+
const eslintFile = (0, internal_1.findEslintFile)(tree, projectRoot);
|
|
60
59
|
if (!eslintFile)
|
|
61
60
|
return;
|
|
62
61
|
const configPath = (0, path_1.joinPathFragments)(projectRoot, eslintFile);
|
|
@@ -132,22 +131,22 @@ function editEslintConfigFiles(tree, projectRoot) {
|
|
|
132
131
|
o.files = [o.files, '*.vue'];
|
|
133
132
|
}
|
|
134
133
|
};
|
|
135
|
-
if ((0,
|
|
136
|
-
(0,
|
|
134
|
+
if ((0, internal_1.lintConfigHasOverride)(tree, projectRoot, (o) => o.parserOptions && !hasVueFiles(o), true)) {
|
|
135
|
+
(0, internal_1.updateOverrideInLintConfig)(tree, projectRoot, (o) => !!o.parserOptions, (o) => {
|
|
137
136
|
addVueFiles(o);
|
|
138
137
|
return o;
|
|
139
138
|
});
|
|
140
139
|
}
|
|
141
140
|
else {
|
|
142
|
-
(0,
|
|
141
|
+
(0, internal_1.replaceOverridesInLintConfig)(tree, projectRoot, [
|
|
143
142
|
{
|
|
144
143
|
files: ['*.ts', '*.tsx', '*.js', '*.jsx', '*.vue'],
|
|
145
144
|
rules: {},
|
|
146
145
|
},
|
|
147
146
|
]);
|
|
148
147
|
}
|
|
149
|
-
if ((0,
|
|
150
|
-
(0,
|
|
148
|
+
if ((0, internal_1.lintConfigHasOverride)(tree, '', (o) => o.rules?.['@nx/enforce-module-boundaries'] && !hasVueFiles(o), true)) {
|
|
149
|
+
(0, internal_1.updateOverrideInLintConfig)(tree, '', (o) => !!o.rules?.['@nx/enforce-module-boundaries'], (o) => {
|
|
151
150
|
addVueFiles(o);
|
|
152
151
|
return o;
|
|
153
152
|
});
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"add-include-tsconfig.d.ts","sourceRoot":"","sources":["../../../../../../packages/nuxt/src/migrations/update-18-1-0/add-include-tsconfig.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,IAAI,EAML,MAAM,YAAY,CAAC;AAIpB,yBAA+B,IAAI,EAAE,IAAI,iBAmCxC"}
|
|
@@ -1,48 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.default = default_1;
|
|
4
|
-
const devkit_1 = require("@nx/devkit");
|
|
5
|
-
const executor_utils_1 = require("../../utils/executor-utils");
|
|
6
|
-
const path_1 = require("path");
|
|
7
|
-
async function default_1(tree) {
|
|
8
|
-
const projects = (0, devkit_1.getProjects)(tree);
|
|
9
|
-
for (const project of projects.values()) {
|
|
10
|
-
const nuxtConfigPath = findNuxtConfig(tree, project.root);
|
|
11
|
-
if (!nuxtConfigPath) {
|
|
12
|
-
continue;
|
|
13
|
-
}
|
|
14
|
-
const nuxtConfig = await getInfoFromNuxtConfig(nuxtConfigPath, project.root);
|
|
15
|
-
const buildDir = nuxtConfig.buildDir ?? '.nuxt';
|
|
16
|
-
const tsConfigPath = (0, devkit_1.joinPathFragments)(project.root, 'tsconfig.json');
|
|
17
|
-
if (tree.exists(tsConfigPath)) {
|
|
18
|
-
(0, devkit_1.updateJson)(tree, tsConfigPath, (json) => {
|
|
19
|
-
if (!json.include) {
|
|
20
|
-
json.include = [];
|
|
21
|
-
}
|
|
22
|
-
if (!json.include.includes(buildDir + '/nuxt.d.ts')) {
|
|
23
|
-
json.include.push(buildDir + '/nuxt.d.ts');
|
|
24
|
-
}
|
|
25
|
-
return json;
|
|
26
|
-
});
|
|
27
|
-
}
|
|
28
|
-
}
|
|
29
|
-
await (0, devkit_1.formatFiles)(tree);
|
|
30
|
-
}
|
|
31
|
-
function findNuxtConfig(tree, projectRoot) {
|
|
32
|
-
const allowsExt = ['js', 'mjs', 'ts', 'cjs', 'mts', 'cts'];
|
|
33
|
-
for (const ext of allowsExt) {
|
|
34
|
-
if (tree.exists((0, devkit_1.joinPathFragments)(projectRoot, `nuxt.config.${ext}`))) {
|
|
35
|
-
return (0, devkit_1.joinPathFragments)(projectRoot, `nuxt.config.${ext}`);
|
|
36
|
-
}
|
|
37
|
-
}
|
|
38
|
-
}
|
|
39
|
-
async function getInfoFromNuxtConfig(configFilePath, projectRoot) {
|
|
40
|
-
const { loadNuxtConfig } = await (0, executor_utils_1.loadNuxtKitDynamicImport)();
|
|
41
|
-
const config = await loadNuxtConfig({
|
|
42
|
-
cwd: (0, devkit_1.joinPathFragments)(devkit_1.workspaceRoot, projectRoot),
|
|
43
|
-
configFile: (0, path_1.basename)(configFilePath),
|
|
44
|
-
});
|
|
45
|
-
return {
|
|
46
|
-
buildDir: config?.buildDir,
|
|
47
|
-
};
|
|
48
|
-
}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"create-ai-instructions-for-nuxt-4.d.ts","sourceRoot":"","sources":["../../../../../../packages/nuxt/src/migrations/update-22-2-0/create-ai-instructions-for-nuxt-4.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,IAAI,EAAE,MAAM,YAAY,CAAC;AAElC,wBAA8B,2BAA2B,CAAC,IAAI,EAAE,IAAI,qBAenE"}
|
|
@@ -1,16 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.default = createAiInstructionsForNuxt;
|
|
4
|
-
const path_1 = require("path");
|
|
5
|
-
const fs_1 = require("fs");
|
|
6
|
-
async function createAiInstructionsForNuxt(tree) {
|
|
7
|
-
const pathToAiInstructions = (0, path_1.join)(__dirname, 'files', 'ai-instructions-for-nuxt-4.md');
|
|
8
|
-
if (!(0, fs_1.existsSync)(pathToAiInstructions)) {
|
|
9
|
-
return;
|
|
10
|
-
}
|
|
11
|
-
const contents = (0, fs_1.readFileSync)(pathToAiInstructions);
|
|
12
|
-
tree.write('tools/ai-migrations/MIGRATE_NUXT_4.md', contents);
|
|
13
|
-
return [
|
|
14
|
-
`We created 'tools/ai-migrations/MIGRATE_NUXT_4.md' with instructions for an AI Agent to help migrate your Nuxt projects to Nuxt 4.`,
|
|
15
|
-
];
|
|
16
|
-
}
|
|
@@ -1,531 +0,0 @@
|
|
|
1
|
-
# Nuxt 3 to Nuxt 4 Migration Instructions
|
|
2
|
-
|
|
3
|
-
This document provides instructions for an AI Agent to assist with migrating Nuxt 3 projects to Nuxt 4.
|
|
4
|
-
|
|
5
|
-
## Pre-Migration Checklist
|
|
6
|
-
|
|
7
|
-
Before starting the migration, run these commands to understand the scope:
|
|
8
|
-
|
|
9
|
-
```bash
|
|
10
|
-
# List all Nuxt projects in the workspace
|
|
11
|
-
nx show projects --with-target build | xargs -I {} sh -c 'cat {}/nuxt.config.ts 2>/dev/null && echo "Project: {}"' | grep -B1 "Project:"
|
|
12
|
-
|
|
13
|
-
# Find all files that may need updates
|
|
14
|
-
find . -name "*.vue" -o -name "*.ts" | head -50
|
|
15
|
-
```
|
|
16
|
-
|
|
17
|
-
---
|
|
18
|
-
|
|
19
|
-
## Section 1: Configuration Updates
|
|
20
|
-
|
|
21
|
-
### 1.1 Directory Structure (Optional but Recommended)
|
|
22
|
-
|
|
23
|
-
**Search Pattern**: Check `nuxt.config.ts` for `srcDir` configuration
|
|
24
|
-
|
|
25
|
-
Nuxt 4 introduces a new default directory structure using `app/` instead of `src/`.
|
|
26
|
-
|
|
27
|
-
**If adopting new structure:**
|
|
28
|
-
|
|
29
|
-
```typescript
|
|
30
|
-
// Before (Nuxt 3 with srcDir)
|
|
31
|
-
export default defineNuxtConfig({
|
|
32
|
-
srcDir: 'src',
|
|
33
|
-
});
|
|
34
|
-
|
|
35
|
-
// After (Nuxt 4 - remove srcDir, move files to app/)
|
|
36
|
-
export default defineNuxtConfig({
|
|
37
|
-
// srcDir removed - app/ is now the default
|
|
38
|
-
});
|
|
39
|
-
```
|
|
40
|
-
|
|
41
|
-
**Action Items:**
|
|
42
|
-
|
|
43
|
-
- [ ] Move `src/` contents to `app/` directory
|
|
44
|
-
- [ ] Keep `server/`, `public/`, `layers/`, `modules/` at project root
|
|
45
|
-
- [ ] Remove `srcDir` from `nuxt.config.ts`
|
|
46
|
-
|
|
47
|
-
**OR to keep existing structure:**
|
|
48
|
-
|
|
49
|
-
```typescript
|
|
50
|
-
export default defineNuxtConfig({
|
|
51
|
-
srcDir: '.',
|
|
52
|
-
dir: { app: 'app' },
|
|
53
|
-
});
|
|
54
|
-
```
|
|
55
|
-
|
|
56
|
-
### 1.2 Remove Deprecated `generate` Configuration
|
|
57
|
-
|
|
58
|
-
**Search Pattern**: `grep -r "generate:" --include="nuxt.config.ts"`
|
|
59
|
-
|
|
60
|
-
```typescript
|
|
61
|
-
// Before
|
|
62
|
-
export default defineNuxtConfig({
|
|
63
|
-
generate: {
|
|
64
|
-
exclude: ['/admin'],
|
|
65
|
-
routes: ['/sitemap.xml'],
|
|
66
|
-
},
|
|
67
|
-
});
|
|
68
|
-
|
|
69
|
-
// After
|
|
70
|
-
export default defineNuxtConfig({
|
|
71
|
-
nitro: {
|
|
72
|
-
prerender: {
|
|
73
|
-
ignore: ['/admin'],
|
|
74
|
-
routes: ['/sitemap.xml'],
|
|
75
|
-
},
|
|
76
|
-
},
|
|
77
|
-
});
|
|
78
|
-
```
|
|
79
|
-
|
|
80
|
-
### 1.3 TypeScript Configuration
|
|
81
|
-
|
|
82
|
-
**Search Pattern**: Check `tsconfig.json` files
|
|
83
|
-
|
|
84
|
-
Nuxt 4 sets `noUncheckedIndexedAccess: true` by default.
|
|
85
|
-
|
|
86
|
-
**To override if needed:**
|
|
87
|
-
|
|
88
|
-
```typescript
|
|
89
|
-
export default defineNuxtConfig({
|
|
90
|
-
typescript: {
|
|
91
|
-
tsConfig: {
|
|
92
|
-
compilerOptions: {
|
|
93
|
-
noUncheckedIndexedAccess: false,
|
|
94
|
-
},
|
|
95
|
-
},
|
|
96
|
-
},
|
|
97
|
-
});
|
|
98
|
-
```
|
|
99
|
-
|
|
100
|
-
---
|
|
101
|
-
|
|
102
|
-
## Section 2: Data Fetching Updates
|
|
103
|
-
|
|
104
|
-
### 2.1 Default Values Changed from `null` to `undefined`
|
|
105
|
-
|
|
106
|
-
**Search Pattern**: `grep -rn "!== null\|=== null" --include="*.vue" --include="*.ts"`
|
|
107
|
-
|
|
108
|
-
```typescript
|
|
109
|
-
// Before (Nuxt 3)
|
|
110
|
-
const { data } = await useAsyncData('key', () => fetch('/api/data'));
|
|
111
|
-
if (data.value !== null) {
|
|
112
|
-
/* ... */
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
// After (Nuxt 4)
|
|
116
|
-
const { data } = await useAsyncData('key', () => fetch('/api/data'));
|
|
117
|
-
if (data.value !== undefined) {
|
|
118
|
-
/* ... */
|
|
119
|
-
}
|
|
120
|
-
```
|
|
121
|
-
|
|
122
|
-
**Automation available:**
|
|
123
|
-
|
|
124
|
-
```bash
|
|
125
|
-
npx codemod@latest nuxt/4/default-data-error-value
|
|
126
|
-
```
|
|
127
|
-
|
|
128
|
-
### 2.2 `getCachedData` Context Parameter
|
|
129
|
-
|
|
130
|
-
**Search Pattern**: `grep -rn "getCachedData" --include="*.vue" --include="*.ts"`
|
|
131
|
-
|
|
132
|
-
```typescript
|
|
133
|
-
// Before
|
|
134
|
-
getCachedData: (key, nuxtApp) => cachedData[key];
|
|
135
|
-
|
|
136
|
-
// After
|
|
137
|
-
getCachedData: (key, nuxtApp, ctx) => {
|
|
138
|
-
// ctx.cause: 'initial' | 'refresh:hook' | 'refresh:manual' | 'watch'
|
|
139
|
-
if (ctx.cause === 'refresh:manual') return undefined;
|
|
140
|
-
return cachedData[key];
|
|
141
|
-
};
|
|
142
|
-
```
|
|
143
|
-
|
|
144
|
-
### 2.3 Shallow Data Reactivity
|
|
145
|
-
|
|
146
|
-
**Search Pattern**: `grep -rn "useAsyncData\|useFetch" --include="*.vue" --include="*.ts"`
|
|
147
|
-
|
|
148
|
-
Data from `useAsyncData`/`useFetch` is now `shallowRef` (not deep reactive).
|
|
149
|
-
|
|
150
|
-
```typescript
|
|
151
|
-
// If deep reactivity is needed:
|
|
152
|
-
const { data } = useFetch('/api/test', { deep: true });
|
|
153
|
-
```
|
|
154
|
-
|
|
155
|
-
**Automation available:**
|
|
156
|
-
|
|
157
|
-
```bash
|
|
158
|
-
npx codemod@latest nuxt/4/shallow-function-reactivity
|
|
159
|
-
```
|
|
160
|
-
|
|
161
|
-
### 2.4 Removed `dedupe` Boolean Values
|
|
162
|
-
|
|
163
|
-
**Search Pattern**: `grep -rn "dedupe: true\|dedupe: false" --include="*.vue" --include="*.ts"`
|
|
164
|
-
|
|
165
|
-
```typescript
|
|
166
|
-
// Before
|
|
167
|
-
refresh({ dedupe: true });
|
|
168
|
-
refresh({ dedupe: false });
|
|
169
|
-
|
|
170
|
-
// After
|
|
171
|
-
refresh({ dedupe: 'cancel' });
|
|
172
|
-
refresh({ dedupe: 'defer' });
|
|
173
|
-
```
|
|
174
|
-
|
|
175
|
-
**Automation available:**
|
|
176
|
-
|
|
177
|
-
```bash
|
|
178
|
-
npx codemod@latest nuxt/4/deprecated-dedupe-value
|
|
179
|
-
```
|
|
180
|
-
|
|
181
|
-
### 2.5 Unique Keys for Shared Prerender Data
|
|
182
|
-
|
|
183
|
-
**Search Pattern**: Check dynamic route files `[*.vue` in `pages/`
|
|
184
|
-
|
|
185
|
-
```typescript
|
|
186
|
-
// Before (unsafe for dynamic routes)
|
|
187
|
-
const { data } = await useAsyncData(async () =>
|
|
188
|
-
$fetch(`/api/page/${route.params.slug}`)
|
|
189
|
-
);
|
|
190
|
-
|
|
191
|
-
// After (safe - key includes slug)
|
|
192
|
-
const { data } = await useAsyncData(route.params.slug, async () =>
|
|
193
|
-
$fetch(`/api/page/${route.params.slug}`)
|
|
194
|
-
);
|
|
195
|
-
```
|
|
196
|
-
|
|
197
|
-
---
|
|
198
|
-
|
|
199
|
-
## Section 3: Unhead v2 Migration
|
|
200
|
-
|
|
201
|
-
### 3.1 Remove Deprecated Props
|
|
202
|
-
|
|
203
|
-
**Search Pattern**: `grep -rn "hid:\|vmid:\|children:\|body:" --include="*.vue" --include="*.ts"`
|
|
204
|
-
|
|
205
|
-
```typescript
|
|
206
|
-
// Before
|
|
207
|
-
useHead({
|
|
208
|
-
meta: [{ name: 'description', hid: 'description', content: 'My page' }],
|
|
209
|
-
script: [{ children: 'console.log("hello")' }],
|
|
210
|
-
});
|
|
211
|
-
|
|
212
|
-
// After
|
|
213
|
-
useHead({
|
|
214
|
-
meta: [{ name: 'description', content: 'My page' }],
|
|
215
|
-
script: [{ innerHTML: 'console.log("hello")' }],
|
|
216
|
-
});
|
|
217
|
-
```
|
|
218
|
-
|
|
219
|
-
---
|
|
220
|
-
|
|
221
|
-
## Section 4: Component and Routing Changes
|
|
222
|
-
|
|
223
|
-
### 4.1 Normalized Component Names
|
|
224
|
-
|
|
225
|
-
**Search Pattern**: Check test files using `findComponent` and templates with `<KeepAlive>`
|
|
226
|
-
|
|
227
|
-
```typescript
|
|
228
|
-
// Component in SomeFolder/MyComponent.vue
|
|
229
|
-
// Before: findComponent({ name: 'MyComponent' })
|
|
230
|
-
// After: findComponent({ name: 'SomeFolderMyComponent' })
|
|
231
|
-
```
|
|
232
|
-
|
|
233
|
-
**To disable if needed:**
|
|
234
|
-
|
|
235
|
-
```typescript
|
|
236
|
-
export default defineNuxtConfig({
|
|
237
|
-
experimental: {
|
|
238
|
-
normalizeComponentNames: false,
|
|
239
|
-
},
|
|
240
|
-
});
|
|
241
|
-
```
|
|
242
|
-
|
|
243
|
-
### 4.2 Route Metadata Deduplication
|
|
244
|
-
|
|
245
|
-
**Search Pattern**: `grep -rn "route.meta.name\|route.meta.path" --include="*.vue" --include="*.ts"`
|
|
246
|
-
|
|
247
|
-
```typescript
|
|
248
|
-
// Before
|
|
249
|
-
const name = route.meta.name;
|
|
250
|
-
|
|
251
|
-
// After
|
|
252
|
-
const name = route.name;
|
|
253
|
-
```
|
|
254
|
-
|
|
255
|
-
---
|
|
256
|
-
|
|
257
|
-
## Section 5: Removed Experimental Flags
|
|
258
|
-
|
|
259
|
-
These flags are now hardcoded and cannot be configured:
|
|
260
|
-
|
|
261
|
-
- `experimental.treeshakeClientOnly` → always `true`
|
|
262
|
-
- `experimental.configSchema` → always `true`
|
|
263
|
-
- `experimental.polyfillVueUseHead` → always `false`
|
|
264
|
-
- `experimental.respectNoSSRHeader` → always `false`
|
|
265
|
-
|
|
266
|
-
**Action Items:**
|
|
267
|
-
|
|
268
|
-
- [ ] Remove these from `nuxt.config.ts` if present
|
|
269
|
-
|
|
270
|
-
---
|
|
271
|
-
|
|
272
|
-
## Section 6: Error Handling
|
|
273
|
-
|
|
274
|
-
### 6.1 Parsed `error.data`
|
|
275
|
-
|
|
276
|
-
**Search Pattern**: `grep -rn "JSON.parse.*error.data\|error.data.*JSON.parse" --include="*.vue" --include="*.ts"`
|
|
277
|
-
|
|
278
|
-
```typescript
|
|
279
|
-
// Before
|
|
280
|
-
const data = JSON.parse(error.data);
|
|
281
|
-
|
|
282
|
-
// After (data is already parsed)
|
|
283
|
-
const data = error.data;
|
|
284
|
-
```
|
|
285
|
-
|
|
286
|
-
---
|
|
287
|
-
|
|
288
|
-
## Section 7: Module and Build Changes
|
|
289
|
-
|
|
290
|
-
### 7.1 Absolute Watch Paths in `builder:watch`
|
|
291
|
-
|
|
292
|
-
**Search Pattern**: Check custom modules using `builder:watch` hook
|
|
293
|
-
|
|
294
|
-
```typescript
|
|
295
|
-
import { relative, resolve } from 'node:fs';
|
|
296
|
-
|
|
297
|
-
nuxt.hook('builder:watch', async (event, path) => {
|
|
298
|
-
// Convert to relative path for backward/forward compatibility
|
|
299
|
-
path = relative(nuxt.options.srcDir, resolve(nuxt.options.srcDir, path));
|
|
300
|
-
});
|
|
301
|
-
```
|
|
302
|
-
|
|
303
|
-
### 7.2 Template Compilation Changes
|
|
304
|
-
|
|
305
|
-
**Search Pattern**: Check modules using `addTemplate` with EJS syntax
|
|
306
|
-
|
|
307
|
-
```typescript
|
|
308
|
-
// Before (using lodash template)
|
|
309
|
-
addTemplate({
|
|
310
|
-
fileName: 'plugin.js',
|
|
311
|
-
src: './runtime/plugin.ejs',
|
|
312
|
-
});
|
|
313
|
-
|
|
314
|
-
// After (using getContents)
|
|
315
|
-
import { template } from 'es-toolkit/compat';
|
|
316
|
-
|
|
317
|
-
addTemplate({
|
|
318
|
-
fileName: 'plugin.js',
|
|
319
|
-
getContents({ options }) {
|
|
320
|
-
const contents = readFileSync('./runtime/plugin.ejs', 'utf-8');
|
|
321
|
-
return template(contents)({ options });
|
|
322
|
-
},
|
|
323
|
-
});
|
|
324
|
-
```
|
|
325
|
-
|
|
326
|
-
---
|
|
327
|
-
|
|
328
|
-
## Section 8: ESLint Configuration (Flat Config Only)
|
|
329
|
-
|
|
330
|
-
> **Note:** This section only applies if your workspace uses ESLint flat config (`eslint.config.js`, `eslint.config.mjs`, or `eslint.config.cjs`). If you're using legacy `.eslintrc.json`, no changes are required.
|
|
331
|
-
|
|
332
|
-
### 8.1 Migrate to `createConfigForNuxt`
|
|
333
|
-
|
|
334
|
-
**Search Pattern**: Check for `eslint.config.js`, `eslint.config.mjs`, or `eslint.config.cjs` in Nuxt project directories
|
|
335
|
-
|
|
336
|
-
For workspaces using ESLint flat config, Nuxt 4 requires updating to `@nuxt/eslint-config` version `^1.10.0` and using `createConfigForNuxt` from `@nuxt/eslint-config/flat`.
|
|
337
|
-
|
|
338
|
-
**Before (Nuxt 3 flat config):**
|
|
339
|
-
|
|
340
|
-
```javascript
|
|
341
|
-
import baseConfig from '../../eslint.config.mjs';
|
|
342
|
-
|
|
343
|
-
export default [
|
|
344
|
-
...baseConfig,
|
|
345
|
-
{
|
|
346
|
-
files: ['**/*.vue'],
|
|
347
|
-
languageOptions: {
|
|
348
|
-
parserOptions: { parser: '@typescript-eslint/parser' },
|
|
349
|
-
},
|
|
350
|
-
},
|
|
351
|
-
{
|
|
352
|
-
ignores: ['.nuxt/**', '.output/**', 'node_modules'],
|
|
353
|
-
},
|
|
354
|
-
];
|
|
355
|
-
```
|
|
356
|
-
|
|
357
|
-
**After (Nuxt 4 flat config - eslint.config.mjs):**
|
|
358
|
-
|
|
359
|
-
```javascript
|
|
360
|
-
import { createConfigForNuxt } from '@nuxt/eslint-config/flat';
|
|
361
|
-
import baseConfig from '../../eslint.config.mjs';
|
|
362
|
-
|
|
363
|
-
export default createConfigForNuxt({
|
|
364
|
-
features: {
|
|
365
|
-
typescript: true,
|
|
366
|
-
},
|
|
367
|
-
})
|
|
368
|
-
.prepend(...baseConfig)
|
|
369
|
-
.append(
|
|
370
|
-
{
|
|
371
|
-
files: ['**/*.ts', '**/*.tsx', '**/*.js', '**/*.jsx', '**/*.vue'],
|
|
372
|
-
rules: {},
|
|
373
|
-
},
|
|
374
|
-
{
|
|
375
|
-
ignores: ['.nuxt/**', '.output/**', 'node_modules'],
|
|
376
|
-
}
|
|
377
|
-
);
|
|
378
|
-
```
|
|
379
|
-
|
|
380
|
-
**For CJS (eslint.config.cjs):**
|
|
381
|
-
|
|
382
|
-
```javascript
|
|
383
|
-
const { createConfigForNuxt } = require('@nuxt/eslint-config/flat');
|
|
384
|
-
const baseConfig = require('../../eslint.config.cjs');
|
|
385
|
-
|
|
386
|
-
module.exports = createConfigForNuxt({
|
|
387
|
-
features: {
|
|
388
|
-
typescript: true,
|
|
389
|
-
},
|
|
390
|
-
})
|
|
391
|
-
.prepend(...baseConfig)
|
|
392
|
-
.append(
|
|
393
|
-
{
|
|
394
|
-
files: ['**/*.ts', '**/*.tsx', '**/*.js', '**/*.jsx', '**/*.vue'],
|
|
395
|
-
rules: {},
|
|
396
|
-
},
|
|
397
|
-
{
|
|
398
|
-
ignores: ['.nuxt/**', '.output/**', 'node_modules'],
|
|
399
|
-
}
|
|
400
|
-
);
|
|
401
|
-
```
|
|
402
|
-
|
|
403
|
-
**Action Items (Flat Config Only):**
|
|
404
|
-
|
|
405
|
-
- [ ] Update `@nuxt/eslint-config` to `^1.10.0` in `package.json`
|
|
406
|
-
- [ ] Replace manual Vue/TypeScript parser config with `createConfigForNuxt`
|
|
407
|
-
- [ ] Use `features.typescript: true` option for TypeScript support
|
|
408
|
-
- [ ] Remove `@typescript-eslint/parser` from devDependencies (handled automatically)
|
|
409
|
-
- [ ] Use `.prepend()` for base configs and `.append()` for project-specific rules/ignores
|
|
410
|
-
|
|
411
|
-
### 8.2 Understanding the New Config Structure
|
|
412
|
-
|
|
413
|
-
The `createConfigForNuxt` function returns a chainable config builder:
|
|
414
|
-
|
|
415
|
-
- **`features.typescript: true`** - Enables TypeScript support with proper Vue file parsing
|
|
416
|
-
- **`.prepend(...configs)`** - Adds configs at the beginning (useful for workspace base configs)
|
|
417
|
-
- **`.append(...configs)`** - Adds configs at the end (for project-specific rules and ignores)
|
|
418
|
-
|
|
419
|
-
---
|
|
420
|
-
|
|
421
|
-
## Post-Migration Validation
|
|
422
|
-
|
|
423
|
-
After completing the migration, run these commands:
|
|
424
|
-
|
|
425
|
-
```bash
|
|
426
|
-
# 1. Install updated dependencies
|
|
427
|
-
npm install
|
|
428
|
-
|
|
429
|
-
# 2. Run Nuxt prepare
|
|
430
|
-
npx nuxi prepare
|
|
431
|
-
|
|
432
|
-
# 3. Type check
|
|
433
|
-
npx nuxi typecheck
|
|
434
|
-
|
|
435
|
-
# 4. Build the application
|
|
436
|
-
nx build <project-name>
|
|
437
|
-
|
|
438
|
-
# 5. Run tests
|
|
439
|
-
nx test <project-name>
|
|
440
|
-
|
|
441
|
-
# 6. Start dev server to verify
|
|
442
|
-
nx serve <project-name>
|
|
443
|
-
```
|
|
444
|
-
|
|
445
|
-
---
|
|
446
|
-
|
|
447
|
-
## Quick Migration Commands
|
|
448
|
-
|
|
449
|
-
Nuxt provides codemods to automate many changes:
|
|
450
|
-
|
|
451
|
-
```bash
|
|
452
|
-
# Run the full migration recipe
|
|
453
|
-
npx codemod@0.18.7 nuxt/4/migration-recipe
|
|
454
|
-
|
|
455
|
-
# Or run individual codemods:
|
|
456
|
-
npx codemod@latest nuxt/4/file-structure
|
|
457
|
-
npx codemod@latest nuxt/4/default-data-error-value
|
|
458
|
-
npx codemod@latest nuxt/4/shallow-function-reactivity
|
|
459
|
-
npx codemod@latest nuxt/4/deprecated-dedupe-value
|
|
460
|
-
npx codemod@latest nuxt/4/template-compilation-changes
|
|
461
|
-
npx codemod@latest nuxt/4/absolute-watch-path
|
|
462
|
-
```
|
|
463
|
-
|
|
464
|
-
---
|
|
465
|
-
|
|
466
|
-
## Common Issues and Solutions
|
|
467
|
-
|
|
468
|
-
### Issue: White flash on initial load
|
|
469
|
-
|
|
470
|
-
**Solution:** This is expected behavior - SPA loading template now renders outside `#__nuxt`. To revert:
|
|
471
|
-
|
|
472
|
-
```typescript
|
|
473
|
-
experimental: {
|
|
474
|
-
spaLoadingTemplateLocation: 'within';
|
|
475
|
-
}
|
|
476
|
-
```
|
|
477
|
-
|
|
478
|
-
### Issue: Global CSS not inlined
|
|
479
|
-
|
|
480
|
-
**Solution:** Only component CSS is inlined by default. To inline all CSS:
|
|
481
|
-
|
|
482
|
-
```typescript
|
|
483
|
-
features: {
|
|
484
|
-
inlineStyles: true;
|
|
485
|
-
}
|
|
486
|
-
```
|
|
487
|
-
|
|
488
|
-
### Issue: Middleware index files being registered
|
|
489
|
-
|
|
490
|
-
**Solution:** Filter unwanted middleware:
|
|
491
|
-
|
|
492
|
-
```typescript
|
|
493
|
-
hooks: {
|
|
494
|
-
'app:resolve'(app) {
|
|
495
|
-
app.middleware = app.middleware.filter(mw => !/\/index\.[^/]+$/.test(mw.path))
|
|
496
|
-
}
|
|
497
|
-
}
|
|
498
|
-
```
|
|
499
|
-
|
|
500
|
-
---
|
|
501
|
-
|
|
502
|
-
## Files to Review
|
|
503
|
-
|
|
504
|
-
```bash
|
|
505
|
-
# Find all Vue files
|
|
506
|
-
find . -name "*.vue" -not -path "./node_modules/*"
|
|
507
|
-
|
|
508
|
-
# Find all nuxt config files
|
|
509
|
-
find . -name "nuxt.config.*" -not -path "./node_modules/*"
|
|
510
|
-
|
|
511
|
-
# Find composables using data fetching
|
|
512
|
-
grep -rn "useAsyncData\|useFetch\|getCachedData" --include="*.vue" --include="*.ts" | grep -v node_modules
|
|
513
|
-
```
|
|
514
|
-
|
|
515
|
-
---
|
|
516
|
-
|
|
517
|
-
## Notes for AI Agent
|
|
518
|
-
|
|
519
|
-
1. **Work systematically** through each section
|
|
520
|
-
2. **Run codemods first** where available, then manually fix remaining issues
|
|
521
|
-
3. **Test incrementally** - run `nx build` and `nx test` after each major change
|
|
522
|
-
4. **Document changes** as you make them for user review
|
|
523
|
-
5. **Handle errors gracefully** - if a file doesn't exist or a pattern isn't found, continue to the next item
|
|
524
|
-
|
|
525
|
-
---
|
|
526
|
-
|
|
527
|
-
## References
|
|
528
|
-
|
|
529
|
-
- [Official Nuxt 4 Upgrade Guide](https://nuxt.com/docs/4.x/getting-started/upgrade)
|
|
530
|
-
- [Nuxt 4 Announcement Blog](https://nuxt.com/blog/v4)
|
|
531
|
-
- [Nuxt GitHub Repository](https://github.com/nuxt/nuxt)
|