@nx/nuxt 22.1.2 → 22.2.0-beta.1
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 +34 -2
- package/package.json +11 -10
- package/src/generators/application/application.d.ts.map +1 -1
- package/src/generators/application/application.js +13 -5
- package/src/generators/application/files/app-dir/__dot__npmrc +2 -0
- package/src/generators/application/files/app-dir/app/app.vue__tmpl__ +48 -0
- package/src/generators/application/files/app-dir/app/assets/css/styles.__style__ +41 -0
- package/src/generators/application/files/app-dir/app/pages/about.vue__tmpl__ +16 -0
- package/src/generators/application/files/app-dir/app/pages/index.vue__tmpl__ +3 -0
- package/src/generators/application/files/app-dir/nuxt.config.ts__tmpl__ +29 -0
- package/src/generators/application/files/app-dir/public/__dot__gitkeep +0 -0
- package/src/generators/application/files/app-dir/public/favicon.ico__tmpl__ +0 -0
- package/src/generators/application/files/app-dir/server/api/greet.ts__tmpl__ +10 -0
- package/src/generators/application/files/app-dir/server/tsconfig.json__tmpl__ +3 -0
- package/src/generators/application/files/nx-welcome-app-dir/claimed/app/components/NxWelcome.vue__tmpl__ +791 -0
- package/src/generators/application/files/nx-welcome-app-dir/not-configured/app/components/NxWelcome.vue__tmpl__ +795 -0
- package/src/generators/application/files/nx-welcome-app-dir/unclaimed/app/components/NxWelcome.vue__tmpl__ +792 -0
- package/src/generators/application/lib/ensure-dependencies.d.ts +1 -1
- package/src/generators/application/lib/ensure-dependencies.d.ts.map +1 -1
- package/src/generators/application/lib/ensure-dependencies.js +8 -6
- package/src/generators/application/lib/normalize-options.d.ts.map +1 -1
- package/src/generators/application/lib/normalize-options.js +8 -0
- package/src/generators/application/schema.d.ts +3 -0
- package/src/generators/application/schema.json +5 -0
- package/src/generators/init/init.js +1 -1
- package/src/generators/init/lib/utils.d.ts +1 -1
- package/src/generators/init/lib/utils.d.ts.map +1 -1
- package/src/generators/init/lib/utils.js +4 -2
- package/src/generators/storybook-configuration/configuration.d.ts.map +1 -1
- package/src/generators/storybook-configuration/configuration.js +12 -2
- package/src/migrations/update-22-2-0/create-ai-instructions-for-nuxt-4.d.ts +3 -0
- package/src/migrations/update-22-2-0/create-ai-instructions-for-nuxt-4.d.ts.map +1 -0
- package/src/migrations/update-22-2-0/create-ai-instructions-for-nuxt-4.js +16 -0
- package/src/migrations/update-22-2-0/files/ai-instructions-for-nuxt-4.md +531 -0
- package/src/utils/add-linting.d.ts.map +1 -1
- package/src/utils/add-linting.js +82 -21
- package/src/utils/create-ts-config.d.ts +1 -0
- package/src/utils/create-ts-config.d.ts.map +1 -1
- package/src/utils/create-ts-config.js +27 -2
- package/src/utils/version-utils.d.ts +15 -0
- package/src/utils/version-utils.d.ts.map +1 -0
- package/src/utils/version-utils.js +85 -0
- package/src/utils/versions.d.ts +11 -3
- package/src/utils/versions.d.ts.map +1 -1
- package/src/utils/versions.js +15 -5
|
@@ -0,0 +1,531 @@
|
|
|
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)
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"add-linting.d.ts","sourceRoot":"","sources":["../../../../../packages/nuxt/src/utils/add-linting.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,wBAAwB,CAAC;AAE9C,OAAO,EAAE,MAAM,EAAE,UAAU,EAAwB,MAAM,YAAY,CAAC;
|
|
1
|
+
{"version":3,"file":"add-linting.d.ts","sourceRoot":"","sources":["../../../../../packages/nuxt/src/utils/add-linting.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,wBAAwB,CAAC;AAE9C,OAAO,EAAE,MAAM,EAAE,UAAU,EAAwB,MAAM,YAAY,CAAC;AAEtE,OAAO,EAEL,iBAAiB,EAElB,MAAM,YAAY,CAAC;AAgBpB,wBAAsB,UAAU,CAC9B,IAAI,EAAE,IAAI,EACV,OAAO,EAAE;IACP,MAAM,EAAE,MAAM,GAAG,UAAU,CAAC;IAC5B,WAAW,EAAE,MAAM,CAAC;IACpB,WAAW,EAAE,MAAM,CAAC;IACpB,cAAc,CAAC,EAAE,QAAQ,GAAG,MAAM,CAAC;IACnC,WAAW,CAAC,EAAE,OAAO,CAAC;CACvB,8BAsDF"}
|
package/src/utils/add-linting.js
CHANGED
|
@@ -2,13 +2,11 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.addLinting = addLinting;
|
|
4
4
|
const eslint_1 = require("@nx/eslint");
|
|
5
|
-
const version_utils_1 = require("@nx/eslint/src/utils/version-utils");
|
|
6
5
|
const path_1 = require("nx/src/utils/path");
|
|
7
6
|
const devkit_1 = require("@nx/devkit");
|
|
8
7
|
const eslint_file_1 = require("@nx/eslint/src/generators/utils/eslint-file");
|
|
9
8
|
const versions_1 = require("./versions");
|
|
10
9
|
const flat_config_1 = require("@nx/eslint/src/utils/flat-config");
|
|
11
|
-
// TODO(colum): Look into the recommended set up using `withNuxt` inside eslint.config.mjs. https://eslint.nuxt.com/packages/config
|
|
12
10
|
async function addLinting(host, options) {
|
|
13
11
|
const tasks = [];
|
|
14
12
|
if (options.linter === 'eslint') {
|
|
@@ -22,35 +20,98 @@ async function addLinting(host, options) {
|
|
|
22
20
|
addPlugin: true,
|
|
23
21
|
});
|
|
24
22
|
tasks.push(lintTask);
|
|
23
|
+
const isFlatConfig = (0, flat_config_1.useFlatConfig)(host);
|
|
24
|
+
// Version-aware dependencies:
|
|
25
|
+
// - Flat config (v4+): use @nuxt/eslint-config ^1.10.0 with createConfigForNuxt
|
|
26
|
+
// - Legacy (.eslintrc.json): use @nuxt/eslint-config ~0.5.6 with extends
|
|
25
27
|
const devDependencies = {
|
|
26
|
-
'@nuxt/eslint-config':
|
|
28
|
+
'@nuxt/eslint-config': isFlatConfig
|
|
29
|
+
? versions_1.nuxtEslintConfigVersion
|
|
30
|
+
: versions_1.nuxtEslintConfigLegacyVersion,
|
|
27
31
|
};
|
|
28
32
|
if ((0, eslint_file_1.isEslintConfigSupported)(host, options.projectRoot)) {
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
33
|
+
if (isFlatConfig) {
|
|
34
|
+
// For flat config: Generate eslint.config.mjs using createConfigForNuxt
|
|
35
|
+
generateNuxtFlatEslintConfig(host, options.projectRoot);
|
|
36
|
+
}
|
|
37
|
+
else {
|
|
38
|
+
// For legacy: Use extends with the old @nuxt/eslint-config
|
|
39
|
+
editEslintConfigFiles(host, options.projectRoot);
|
|
40
|
+
const addExtendsTask = (0, eslint_file_1.addExtendsToLintConfig)(host, options.projectRoot, ['@nuxt/eslint-config'], true);
|
|
41
|
+
tasks.push(addExtendsTask);
|
|
42
|
+
(0, eslint_file_1.addIgnoresToLintConfig)(host, options.projectRoot, [
|
|
43
|
+
'.nuxt/**',
|
|
44
|
+
'.output/**',
|
|
45
|
+
'node_modules',
|
|
46
|
+
]);
|
|
42
47
|
}
|
|
43
|
-
(0, eslint_file_1.addIgnoresToLintConfig)(host, options.projectRoot, [
|
|
44
|
-
'.nuxt/**',
|
|
45
|
-
'.output/**',
|
|
46
|
-
'node_modules',
|
|
47
|
-
]);
|
|
48
48
|
}
|
|
49
49
|
const installTask = (0, devkit_1.addDependenciesToPackageJson)(host, {}, devDependencies);
|
|
50
50
|
tasks.push(installTask);
|
|
51
51
|
}
|
|
52
52
|
return (0, devkit_1.runTasksInSerial)(...tasks);
|
|
53
53
|
}
|
|
54
|
+
/**
|
|
55
|
+
* Generates a flat ESLint config for Nuxt using createConfigForNuxt from @nuxt/eslint-config/flat.
|
|
56
|
+
* This is the recommended approach for Nuxt v4+ and ESLint flat config.
|
|
57
|
+
*/
|
|
58
|
+
function generateNuxtFlatEslintConfig(tree, projectRoot) {
|
|
59
|
+
const eslintFile = (0, eslint_file_1.findEslintFile)(tree, projectRoot);
|
|
60
|
+
if (!eslintFile)
|
|
61
|
+
return;
|
|
62
|
+
const configPath = (0, path_1.joinPathFragments)(projectRoot, eslintFile);
|
|
63
|
+
const isMjs = eslintFile.endsWith('.mjs');
|
|
64
|
+
const isCjs = eslintFile.endsWith('.cjs');
|
|
65
|
+
// Determine the relative path to root config
|
|
66
|
+
const depth = projectRoot.split('/').filter(Boolean).length;
|
|
67
|
+
const rootConfigRelativePath = depth > 0 ? '../'.repeat(depth) : './';
|
|
68
|
+
let configContent;
|
|
69
|
+
if (isCjs) {
|
|
70
|
+
// CJS flat config
|
|
71
|
+
configContent = `const { createConfigForNuxt } = require('@nuxt/eslint-config/flat');
|
|
72
|
+
const baseConfig = require('${rootConfigRelativePath}eslint.config.cjs');
|
|
73
|
+
|
|
74
|
+
module.exports = createConfigForNuxt({
|
|
75
|
+
features: {
|
|
76
|
+
typescript: true,
|
|
77
|
+
},
|
|
78
|
+
})
|
|
79
|
+
.prepend(...baseConfig)
|
|
80
|
+
.append(
|
|
81
|
+
{
|
|
82
|
+
files: ['**/*.ts', '**/*.tsx', '**/*.js', '**/*.jsx', '**/*.vue'],
|
|
83
|
+
rules: {},
|
|
84
|
+
},
|
|
85
|
+
{
|
|
86
|
+
ignores: ['.nuxt/**', '.output/**', 'node_modules'],
|
|
87
|
+
}
|
|
88
|
+
);
|
|
89
|
+
`;
|
|
90
|
+
}
|
|
91
|
+
else {
|
|
92
|
+
// ESM flat config (default)
|
|
93
|
+
configContent = `import { createConfigForNuxt } from '@nuxt/eslint-config/flat';
|
|
94
|
+
import baseConfig from '${rootConfigRelativePath}eslint.config.${isMjs ? 'mjs' : 'js'}';
|
|
95
|
+
|
|
96
|
+
export default createConfigForNuxt({
|
|
97
|
+
features: {
|
|
98
|
+
typescript: true,
|
|
99
|
+
},
|
|
100
|
+
})
|
|
101
|
+
.prepend(...baseConfig)
|
|
102
|
+
.append(
|
|
103
|
+
{
|
|
104
|
+
files: ['**/*.ts', '**/*.tsx', '**/*.js', '**/*.jsx', '**/*.vue'],
|
|
105
|
+
rules: {},
|
|
106
|
+
},
|
|
107
|
+
{
|
|
108
|
+
ignores: ['.nuxt/**', '.output/**', 'node_modules'],
|
|
109
|
+
}
|
|
110
|
+
);
|
|
111
|
+
`;
|
|
112
|
+
}
|
|
113
|
+
tree.write(configPath, configContent);
|
|
114
|
+
}
|
|
54
115
|
function editEslintConfigFiles(tree, projectRoot) {
|
|
55
116
|
const hasVueFiles = (o) => o.files &&
|
|
56
117
|
(Array.isArray(o.files)
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"create-ts-config.d.ts","sourceRoot":"","sources":["../../../../../packages/nuxt/src/utils/create-ts-config.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAa,MAAM,YAAY,CAAC;AAG7C,wBAAgB,cAAc,CAC5B,IAAI,EAAE,IAAI,EACV,OAAO,EAAE;IACP,WAAW,EAAE,MAAM,CAAC;IACpB,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,uBAAuB,EAAE,OAAO,CAAC;
|
|
1
|
+
{"version":3,"file":"create-ts-config.d.ts","sourceRoot":"","sources":["../../../../../packages/nuxt/src/utils/create-ts-config.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAa,MAAM,YAAY,CAAC;AAG7C,wBAAgB,cAAc,CAC5B,IAAI,EAAE,IAAI,EACV,OAAO,EAAE;IACP,WAAW,EAAE,MAAM,CAAC;IACpB,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,uBAAuB,EAAE,OAAO,CAAC;IACjC,SAAS,CAAC,EAAE,OAAO,CAAC;CACrB,EACD,0BAA0B,EAAE,MAAM,QAgCnC"}
|