@nx/nuxt 23.0.0-beta.2 → 23.0.0-beta.20
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 +9 -9
- package/src/generators/application/application.d.ts.map +1 -1
- package/src/generators/application/application.js +7 -8
- package/src/generators/application/lib/add-e2e.d.ts.map +1 -1
- package/src/generators/application/lib/add-e2e.js +5 -5
- package/src/generators/application/lib/normalize-options.js +5 -5
- package/src/generators/init/init.js +2 -2
- package/src/plugins/plugin.d.ts.map +1 -1
- package/src/plugins/plugin.js +62 -33
- package/src/utils/add-linting.js +11 -12
- package/src/utils/create-ts-config.js +1 -1
- 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.20",
|
|
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.
|
|
54
|
-
"semver": "^7.
|
|
48
|
+
"@nx/devkit": "23.0.0-beta.20",
|
|
49
|
+
"@nx/js": "23.0.0-beta.20",
|
|
50
|
+
"@nx/eslint": "23.0.0-beta.20",
|
|
51
|
+
"@nx/vue": "23.0.0-beta.20",
|
|
52
|
+
"@nx/vite": "23.0.0-beta.20",
|
|
53
|
+
"@nx/vitest": "23.0.0-beta.20",
|
|
54
|
+
"semver": "^7.6.3"
|
|
55
55
|
},
|
|
56
56
|
"devDependencies": {
|
|
57
|
-
"nx": "23.0.0-beta.
|
|
57
|
+
"nx": "23.0.0-beta.20"
|
|
58
58
|
},
|
|
59
59
|
"peerDependencies": {
|
|
60
60
|
"@nuxt/schema": "^3.10.0 || ^4.0.0"
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"application.d.ts","sourceRoot":"","sources":["../../../../../../packages/nuxt/src/generators/application/application.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"application.d.ts","sourceRoot":"","sources":["../../../../../../packages/nuxt/src/generators/application/application.ts"],"names":[],"mappings":"AACA,OAAO,EAKL,iBAAiB,EAKjB,IAAI,EAEL,MAAM,YAAY,CAAC;AACpB,OAAO,EAAE,MAAM,EAAE,MAAM,UAAU,CAAC;AA4BlC,wBAAsB,oBAAoB,CAAC,IAAI,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,8BAKpE;AAED,wBAAsB,4BAA4B,CAAC,IAAI,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,8BAiN5E;AAED,eAAe,oBAAoB,CAAC"}
|
|
@@ -3,6 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.applicationGenerator = applicationGenerator;
|
|
4
4
|
exports.applicationGeneratorInternal = applicationGeneratorInternal;
|
|
5
5
|
const tslib_1 = require("tslib");
|
|
6
|
+
const internal_1 = require("@nx/devkit/internal");
|
|
6
7
|
const devkit_1 = require("@nx/devkit");
|
|
7
8
|
const init_1 = tslib_1.__importDefault(require("../init/init"));
|
|
8
9
|
const normalize_options_1 = require("./lib/normalize-options");
|
|
@@ -14,12 +15,10 @@ const add_linting_1 = require("../../utils/add-linting");
|
|
|
14
15
|
const add_vitest_1 = require("./lib/add-vitest");
|
|
15
16
|
const vue_1 = require("@nx/vue");
|
|
16
17
|
const ensure_dependencies_1 = require("./lib/ensure-dependencies");
|
|
17
|
-
const log_show_project_command_1 = require("@nx/devkit/src/utils/log-show-project-command");
|
|
18
18
|
const node_child_process_1 = require("node:child_process");
|
|
19
19
|
const node_path_1 = require("node:path");
|
|
20
20
|
const onboarding_1 = require("nx/src/nx-cloud/utilities/onboarding");
|
|
21
|
-
const
|
|
22
|
-
const sort_fields_1 = require("@nx/js/src/utils/package-json/sort-fields");
|
|
21
|
+
const internal_2 = require("@nx/js/internal");
|
|
23
22
|
async function applicationGenerator(tree, schema) {
|
|
24
23
|
return await applicationGeneratorInternal(tree, {
|
|
25
24
|
useProjectJson: true,
|
|
@@ -28,7 +27,7 @@ async function applicationGenerator(tree, schema) {
|
|
|
28
27
|
}
|
|
29
28
|
async function applicationGeneratorInternal(tree, schema) {
|
|
30
29
|
const tasks = [];
|
|
31
|
-
const addTsPlugin = (0,
|
|
30
|
+
const addTsPlugin = (0, internal_2.shouldConfigureTsSolutionSetup)(tree, true, // nuxt always adds plugins
|
|
32
31
|
schema.useTsSolution);
|
|
33
32
|
const jsInitTask = await (0, js_1.initGenerator)(tree, {
|
|
34
33
|
...schema,
|
|
@@ -113,7 +112,7 @@ async function applicationGeneratorInternal(tree, schema) {
|
|
|
113
112
|
// If we are using the new TS solution
|
|
114
113
|
// We need to update the workspace file (package.json or pnpm-workspaces.yaml) to include the new project
|
|
115
114
|
if (options.isUsingTsSolutionConfig) {
|
|
116
|
-
await (0,
|
|
115
|
+
await (0, internal_2.addProjectToTsSolutionWorkspace)(tree, options.appProjectRoot);
|
|
117
116
|
}
|
|
118
117
|
tasks.push(await (0, add_linting_1.addLinting)(tree, {
|
|
119
118
|
projectName: options.projectName,
|
|
@@ -138,7 +137,7 @@ async function applicationGeneratorInternal(tree, schema) {
|
|
|
138
137
|
if (options.js)
|
|
139
138
|
(0, devkit_1.toJS)(tree);
|
|
140
139
|
if (options.isUsingTsSolutionConfig) {
|
|
141
|
-
(0,
|
|
140
|
+
(0, internal_2.updateTsconfigFiles)(tree, options.appProjectRoot, 'tsconfig.app.json', {
|
|
142
141
|
jsx: 'preserve',
|
|
143
142
|
jsxImportSource: 'vue',
|
|
144
143
|
module: 'esnext',
|
|
@@ -148,7 +147,7 @@ async function applicationGeneratorInternal(tree, schema) {
|
|
|
148
147
|
? ['eslint.config.js', 'eslint.config.cjs', 'eslint.config.mjs']
|
|
149
148
|
: undefined);
|
|
150
149
|
}
|
|
151
|
-
(0,
|
|
150
|
+
(0, internal_2.sortPackageJsonFields)(tree, options.appProjectRoot);
|
|
152
151
|
if (!options.skipFormat)
|
|
153
152
|
await (0, devkit_1.formatFiles)(tree);
|
|
154
153
|
tasks.push(() => {
|
|
@@ -163,7 +162,7 @@ async function applicationGeneratorInternal(tree, schema) {
|
|
|
163
162
|
}
|
|
164
163
|
});
|
|
165
164
|
tasks.push(() => {
|
|
166
|
-
(0,
|
|
165
|
+
(0, internal_1.logShowProjectCommand)(options.projectName);
|
|
167
166
|
});
|
|
168
167
|
return (0, devkit_1.runTasksInSerial)(...tasks);
|
|
169
168
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"add-e2e.d.ts","sourceRoot":"","sources":["../../../../../../../packages/nuxt/src/generators/application/lib/add-e2e.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"add-e2e.d.ts","sourceRoot":"","sources":["../../../../../../../packages/nuxt/src/generators/application/lib/add-e2e.ts"],"names":[],"mappings":"AAIA,OAAO,EAGL,iBAAiB,EAGjB,IAAI,EAEL,MAAM,YAAY,CAAC;AAEpB,OAAO,EAAE,gBAAgB,EAAE,MAAM,WAAW,CAAC;AAG7C,wBAAsB,MAAM,CAC1B,IAAI,EAAE,IAAI,EACV,OAAO,EAAE,gBAAgB,GACxB,OAAO,CAAC,iBAAiB,CAAC,CA8G5B"}
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.addE2e = addE2e;
|
|
4
|
+
const internal_1 = require("@nx/devkit/internal");
|
|
4
5
|
const devkit_1 = require("@nx/devkit");
|
|
5
|
-
const e2e_web_server_info_utils_1 = require("@nx/devkit/src/generators/e2e-web-server-info-utils");
|
|
6
6
|
const versions_1 = require("../../../utils/versions");
|
|
7
7
|
async function addE2e(host, options) {
|
|
8
8
|
const e2eWebServerInfo = await getNuxtE2EWebServerInfo(host, options.projectName, (0, devkit_1.joinPathFragments)(options.appProjectRoot, `nuxt.config.${options.js ? 'js' : 'ts'}`));
|
|
@@ -92,11 +92,11 @@ async function addE2e(host, options) {
|
|
|
92
92
|
async function getNuxtE2EWebServerInfo(tree, projectName, configFilePath) {
|
|
93
93
|
const nxJson = (0, devkit_1.readNxJson)(tree);
|
|
94
94
|
let e2ePort = 4200;
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
e2ePort =
|
|
95
|
+
const serveTargetOptions = (0, internal_1.readTargetDefaultsForTarget)('serve', nxJson.targetDefaults)?.options;
|
|
96
|
+
if (serveTargetOptions?.port) {
|
|
97
|
+
e2ePort = serveTargetOptions.port;
|
|
98
98
|
}
|
|
99
|
-
return (0,
|
|
99
|
+
return (0, internal_1.getE2EWebServerInfo)(tree, projectName, {
|
|
100
100
|
plugin: '@nx/nuxt/plugin',
|
|
101
101
|
serveTargetName: 'serveTargetName',
|
|
102
102
|
serveStaticTargetName: 'serveStaticTargetName',
|
|
@@ -1,19 +1,19 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.normalizeOptions = normalizeOptions;
|
|
4
|
-
const
|
|
5
|
-
const
|
|
4
|
+
const internal_1 = require("@nx/devkit/internal");
|
|
5
|
+
const internal_2 = require("@nx/js/internal");
|
|
6
6
|
const version_utils_1 = require("../../../utils/version-utils");
|
|
7
7
|
async function normalizeOptions(host, options) {
|
|
8
|
-
await (0,
|
|
9
|
-
const { projectName, names: projectNames, projectRoot: appProjectRoot, importPath, } = await (0,
|
|
8
|
+
await (0, internal_1.ensureRootProjectName)(options, 'application');
|
|
9
|
+
const { projectName, names: projectNames, projectRoot: appProjectRoot, importPath, } = await (0, internal_1.determineProjectNameAndRootOptions)(host, {
|
|
10
10
|
name: options.name,
|
|
11
11
|
projectType: 'application',
|
|
12
12
|
directory: options.directory,
|
|
13
13
|
rootProject: options.rootProject,
|
|
14
14
|
});
|
|
15
15
|
options.rootProject = appProjectRoot === '.';
|
|
16
|
-
const isUsingTsSolutionConfig = (0,
|
|
16
|
+
const isUsingTsSolutionConfig = (0, internal_2.isUsingTsSolutionSetup)(host);
|
|
17
17
|
const appProjectName = !isUsingTsSolutionConfig || options.name ? projectName : importPath;
|
|
18
18
|
const e2eProjectName = options.rootProject ? 'e2e' : `${appProjectName}-e2e`;
|
|
19
19
|
const e2eProjectRoot = options.rootProject ? 'e2e' : `${appProjectRoot}-e2e`;
|
|
@@ -2,11 +2,11 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.nuxtInitGenerator = nuxtInitGenerator;
|
|
4
4
|
const devkit_1 = require("@nx/devkit");
|
|
5
|
-
const
|
|
5
|
+
const internal_1 = require("@nx/devkit/internal");
|
|
6
6
|
const plugin_1 = require("../../plugins/plugin");
|
|
7
7
|
const utils_1 = require("./lib/utils");
|
|
8
8
|
async function nuxtInitGenerator(host, schema) {
|
|
9
|
-
await (0,
|
|
9
|
+
await (0, internal_1.addPlugin)(host, await (0, devkit_1.createProjectGraphAsync)(), '@nx/nuxt/plugin', plugin_1.createNodesV2, {
|
|
10
10
|
buildTargetName: ['build', 'nuxt:build', 'nuxt-build'],
|
|
11
11
|
serveTargetName: ['serve', 'nuxt:serve', 'nuxt-serve'],
|
|
12
12
|
buildDepsTargetName: ['build-deps', 'nuxt:build-deps', 'nuxt-build-deps'],
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"plugin.d.ts","sourceRoot":"","sources":["../../../../../packages/nuxt/src/plugins/plugin.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"plugin.d.ts","sourceRoot":"","sources":["../../../../../packages/nuxt/src/plugins/plugin.ts"],"names":[],"mappings":"AAOA,OAAO,EAML,aAAa,EAMd,MAAM,YAAY,CAAC;AAapB,MAAM,WAAW,iBAAiB;IAChC,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,qBAAqB,CAAC,EAAE,MAAM,CAAC;IAC/B,qBAAqB,CAAC,EAAE,MAAM,CAAC;IAC/B,mBAAmB,CAAC,EAAE,MAAM,CAAC;IAC7B,mBAAmB,CAAC,EAAE,MAAM,CAAC;CAC9B;AAED,eAAO,MAAM,WAAW,EAAE,aAAa,CAAC,iBAAiB,CAoDxD,CAAC;AAEF,eAAO,MAAM,aAAa,kCAAc,CAAC"}
|
package/src/plugins/plugin.js
CHANGED
|
@@ -1,55 +1,62 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.createNodesV2 = exports.createNodes = void 0;
|
|
4
|
+
const internal_1 = require("@nx/devkit/internal");
|
|
4
5
|
const devkit_1 = require("@nx/devkit");
|
|
5
|
-
const config_utils_1 = require("@nx/devkit/src/utils/config-utils");
|
|
6
|
-
const get_named_inputs_1 = require("@nx/devkit/src/utils/get-named-inputs");
|
|
7
|
-
const calculate_hash_for_create_nodes_1 = require("@nx/devkit/src/utils/calculate-hash-for-create-nodes");
|
|
8
6
|
const cache_directory_1 = require("nx/src/utils/cache-directory");
|
|
9
7
|
const js_1 = require("@nx/js");
|
|
10
8
|
const path_1 = require("path");
|
|
11
9
|
const fs_1 = require("fs");
|
|
12
10
|
const executor_utils_1 = require("../utils/executor-utils");
|
|
13
|
-
const
|
|
11
|
+
const internal_2 = require("@nx/js/internal");
|
|
14
12
|
const cachePath = (0, path_1.join)(cache_directory_1.workspaceDataDirectory, 'nuxt.hash');
|
|
15
|
-
const targetsCache =
|
|
16
|
-
function readTargetsCache() {
|
|
17
|
-
return (0, fs_1.existsSync)(cachePath) ? (0, devkit_1.readJsonFile)(cachePath) : {};
|
|
18
|
-
}
|
|
19
|
-
function writeTargetsToCache() {
|
|
20
|
-
const oldCache = readTargetsCache();
|
|
21
|
-
(0, devkit_1.writeJsonFile)(cachePath, {
|
|
22
|
-
...oldCache,
|
|
23
|
-
...targetsCache,
|
|
24
|
-
});
|
|
25
|
-
}
|
|
13
|
+
const targetsCache = new internal_1.PluginCache(cachePath);
|
|
26
14
|
exports.createNodes = [
|
|
27
15
|
'**/nuxt.config.{js,ts,mjs,mts,cjs,cts}',
|
|
28
16
|
async (files, options, context) => {
|
|
29
|
-
|
|
30
|
-
const
|
|
31
|
-
|
|
32
|
-
|
|
17
|
+
const packageManager = (0, devkit_1.detectPackageManager)(context.workspaceRoot);
|
|
18
|
+
const pmc = (0, devkit_1.getPackageManagerCommand)(packageManager);
|
|
19
|
+
const lockFileName = (0, js_1.getLockFileName)(packageManager);
|
|
20
|
+
const normalizedOptions = normalizeOptions(options);
|
|
21
|
+
try {
|
|
22
|
+
const { entries, preErrors } = await filterNuxtConfigs(files, context);
|
|
23
|
+
const projectHashes = await (0, internal_1.calculateHashesForCreateNodes)(entries.map((e) => e.projectRoot), normalizedOptions, context, entries.map(() => [lockFileName]));
|
|
24
|
+
let results = [];
|
|
25
|
+
let nodeErrors = [];
|
|
26
|
+
try {
|
|
27
|
+
results = await (0, devkit_1.createNodesFromFiles)((configFile, _, ctx, idx) => createNodesInternal(configFile, normalizedOptions, ctx, pmc, projectHashes[idx]), entries.map((e) => e.configFile), options, context);
|
|
28
|
+
}
|
|
29
|
+
catch (e) {
|
|
30
|
+
if (e instanceof devkit_1.AggregateCreateNodesError) {
|
|
31
|
+
results = e.partialResults ?? [];
|
|
32
|
+
nodeErrors = e.errors;
|
|
33
|
+
}
|
|
34
|
+
else {
|
|
35
|
+
throw e;
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
const allErrors = [...preErrors, ...nodeErrors];
|
|
39
|
+
if (allErrors.length > 0) {
|
|
40
|
+
throw new devkit_1.AggregateCreateNodesError(allErrors, results);
|
|
41
|
+
}
|
|
42
|
+
return results;
|
|
43
|
+
}
|
|
44
|
+
finally {
|
|
45
|
+
targetsCache.writeToDisk();
|
|
46
|
+
}
|
|
33
47
|
},
|
|
34
48
|
];
|
|
35
49
|
exports.createNodesV2 = exports.createNodes;
|
|
36
|
-
async function createNodesInternal(configFilePath, options, context) {
|
|
50
|
+
async function createNodesInternal(configFilePath, options, context, pmc, hash) {
|
|
37
51
|
const projectRoot = (0, path_1.dirname)(configFilePath);
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
if (!siblingFiles.includes('package.json') &&
|
|
41
|
-
!siblingFiles.includes('project.json')) {
|
|
42
|
-
return {};
|
|
52
|
+
if (!targetsCache.has(hash)) {
|
|
53
|
+
targetsCache.set(hash, await buildNuxtTargets(configFilePath, projectRoot, options, context, pmc));
|
|
43
54
|
}
|
|
44
|
-
options = normalizeOptions(options);
|
|
45
|
-
const pmc = (0, devkit_1.getPackageManagerCommand)((0, devkit_1.detectPackageManager)(context.workspaceRoot));
|
|
46
|
-
const hash = await (0, calculate_hash_for_create_nodes_1.calculateHashForCreateNodes)(projectRoot, options, context, [(0, js_1.getLockFileName)((0, devkit_1.detectPackageManager)(context.workspaceRoot))]);
|
|
47
|
-
targetsCache[hash] ??= await buildNuxtTargets(configFilePath, projectRoot, options, context, pmc);
|
|
48
55
|
return {
|
|
49
56
|
projects: {
|
|
50
57
|
[projectRoot]: {
|
|
51
58
|
root: projectRoot,
|
|
52
|
-
targets: targetsCache
|
|
59
|
+
targets: targetsCache.get(hash),
|
|
53
60
|
},
|
|
54
61
|
},
|
|
55
62
|
};
|
|
@@ -57,13 +64,13 @@ async function createNodesInternal(configFilePath, options, context) {
|
|
|
57
64
|
async function buildNuxtTargets(configFilePath, projectRoot, options, context, pmc) {
|
|
58
65
|
const nuxtConfig = await getInfoFromNuxtConfig(configFilePath, context, projectRoot);
|
|
59
66
|
const { buildOutputs } = getOutputs(nuxtConfig, projectRoot);
|
|
60
|
-
const namedInputs = (0,
|
|
67
|
+
const namedInputs = (0, internal_1.getNamedInputs)(projectRoot, context);
|
|
61
68
|
const targets = {};
|
|
62
69
|
targets[options.buildTargetName] = buildTarget(options.buildTargetName, namedInputs, buildOutputs, projectRoot);
|
|
63
70
|
targets[options.serveTargetName] = serveTarget(projectRoot);
|
|
64
71
|
targets[options.serveStaticTargetName] = serveStaticTarget(options);
|
|
65
72
|
targets[options.buildStaticTargetName] = buildStaticTarget(options.buildStaticTargetName, namedInputs, buildOutputs, projectRoot);
|
|
66
|
-
(0,
|
|
73
|
+
(0, internal_2.addBuildAndWatchDepsTargets)(context.workspaceRoot, projectRoot, targets, options, pmc);
|
|
67
74
|
return targets;
|
|
68
75
|
}
|
|
69
76
|
function buildTarget(buildTargetName, namedInputs, buildOutputs, projectRoot) {
|
|
@@ -134,7 +141,7 @@ async function getInfoFromNuxtConfig(configFilePath, context, projectRoot) {
|
|
|
134
141
|
});
|
|
135
142
|
}
|
|
136
143
|
else {
|
|
137
|
-
config = await (0,
|
|
144
|
+
config = await (0, internal_1.loadConfigFile)((0, path_1.join)(context.workspaceRoot, configFilePath));
|
|
138
145
|
}
|
|
139
146
|
return {
|
|
140
147
|
buildDir: config?.buildDir ??
|
|
@@ -172,6 +179,28 @@ function normalizeOutputPath(outputPath, projectRoot) {
|
|
|
172
179
|
}
|
|
173
180
|
}
|
|
174
181
|
}
|
|
182
|
+
async function filterNuxtConfigs(configFiles, context) {
|
|
183
|
+
const preErrors = [];
|
|
184
|
+
const candidates = await Promise.all(configFiles.map(async (configFile) => {
|
|
185
|
+
try {
|
|
186
|
+
const projectRoot = (0, path_1.dirname)(configFile);
|
|
187
|
+
const siblingFiles = (0, fs_1.readdirSync)((0, path_1.join)(context.workspaceRoot, projectRoot));
|
|
188
|
+
if (!siblingFiles.includes('package.json') &&
|
|
189
|
+
!siblingFiles.includes('project.json')) {
|
|
190
|
+
return null;
|
|
191
|
+
}
|
|
192
|
+
return { configFile, projectRoot };
|
|
193
|
+
}
|
|
194
|
+
catch (e) {
|
|
195
|
+
preErrors.push([configFile, e]);
|
|
196
|
+
return null;
|
|
197
|
+
}
|
|
198
|
+
}));
|
|
199
|
+
return {
|
|
200
|
+
entries: candidates.filter((c) => c !== null),
|
|
201
|
+
preErrors,
|
|
202
|
+
};
|
|
203
|
+
}
|
|
175
204
|
function normalizeOptions(options) {
|
|
176
205
|
options ??= {};
|
|
177
206
|
options.buildTargetName ??= 'build';
|
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
|
});
|
|
@@ -3,7 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.createTsConfig = createTsConfig;
|
|
4
4
|
const tslib_1 = require("tslib");
|
|
5
5
|
const devkit_1 = require("@nx/devkit");
|
|
6
|
-
const shared = tslib_1.__importStar(require("@nx/js
|
|
6
|
+
const shared = tslib_1.__importStar(require("@nx/js"));
|
|
7
7
|
function createTsConfig(host, options, relativePathToRootTsConfig) {
|
|
8
8
|
createAppTsConfig(host, options);
|
|
9
9
|
const json = {
|
|
@@ -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)
|