@nx/react 18.2.2 → 18.2.3
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/package.json +7 -6
- package/plugins/nx-react-webpack-plugin/lib/apply-react-config.js +26 -0
- package/src/executors/module-federation-dev-server/module-federation-dev-server.impl.d.ts +1 -0
- package/src/executors/module-federation-dev-server/module-federation-dev-server.impl.js +37 -20
- package/src/executors/module-federation-dev-server/schema.json +4 -0
- package/src/generators/application/lib/add-e2e.js +5 -5
- package/src/generators/application/lib/normalize-options.js +30 -0
- package/src/generators/application/schema.d.ts +3 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@nx/react",
|
|
3
|
-
"version": "18.2.
|
|
3
|
+
"version": "18.2.3",
|
|
4
4
|
"private": false,
|
|
5
5
|
"description": "The React plugin for Nx contains executors and generators for managing React applications and libraries within an Nx workspace. It provides:\n\n\n- Integration with libraries such as Jest, Cypress, and Storybook.\n\n- Generators for applications, libraries, components, hooks, 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": {
|
|
@@ -34,13 +34,14 @@
|
|
|
34
34
|
"@phenomnomnominal/tsquery": "~5.0.1",
|
|
35
35
|
"@svgr/webpack": "^8.0.1",
|
|
36
36
|
"chalk": "^4.1.0",
|
|
37
|
+
"file-loader": "^6.2.0",
|
|
37
38
|
"minimatch": "9.0.3",
|
|
38
39
|
"tslib": "^2.3.0",
|
|
39
|
-
"@nx/devkit": "18.2.
|
|
40
|
-
"@nx/js": "18.2.
|
|
41
|
-
"@nx/eslint": "18.2.
|
|
42
|
-
"@nx/web": "18.2.
|
|
43
|
-
"@nrwl/react": "18.2.
|
|
40
|
+
"@nx/devkit": "18.2.3",
|
|
41
|
+
"@nx/js": "18.2.3",
|
|
42
|
+
"@nx/eslint": "18.2.3",
|
|
43
|
+
"@nx/web": "18.2.3",
|
|
44
|
+
"@nrwl/react": "18.2.3"
|
|
44
45
|
},
|
|
45
46
|
"publishConfig": {
|
|
46
47
|
"access": "public"
|
|
@@ -7,6 +7,26 @@ function applyReactConfig(options, config = {}) {
|
|
|
7
7
|
addHotReload(config);
|
|
8
8
|
if (options.svgr !== false) {
|
|
9
9
|
removeSvgLoaderIfPresent(config);
|
|
10
|
+
// TODO(v20): Remove file-loader and use `?react` querystring to differentiate between asset and SVGR.
|
|
11
|
+
// It should be:
|
|
12
|
+
// use: [{
|
|
13
|
+
// test: /\.svg$/i,
|
|
14
|
+
// type: 'asset',
|
|
15
|
+
// resourceQuery: /react/, // *.svg?react
|
|
16
|
+
// },
|
|
17
|
+
// {
|
|
18
|
+
// test: /\.svg$/i,
|
|
19
|
+
// issuer: /\.[jt]sx?$/,
|
|
20
|
+
// resourceQuery: { not: [/react/] }, // exclude react component if *.svg?react
|
|
21
|
+
// use: ['@svgr/webpack'],
|
|
22
|
+
// }],
|
|
23
|
+
// See:
|
|
24
|
+
// - SVGR: https://react-svgr.com/docs/webpack/#use-svgr-and-asset-svg-in-the-same-project
|
|
25
|
+
// - Vite: https://www.npmjs.com/package/vite-plugin-svgr
|
|
26
|
+
// - Rsbuild: https://github.com/web-infra-dev/rsbuild/pull/1783
|
|
27
|
+
// Note: We also need a migration for any projects that are using SVGR to convert
|
|
28
|
+
// `import { ReactComponent as X } from './x.svg` to
|
|
29
|
+
// `import X from './x.svg?react';
|
|
10
30
|
config.module.rules.push({
|
|
11
31
|
test: /\.svg$/,
|
|
12
32
|
issuer: /\.(js|ts|md)x?$/,
|
|
@@ -19,6 +39,12 @@ function applyReactConfig(options, config = {}) {
|
|
|
19
39
|
ref: true,
|
|
20
40
|
},
|
|
21
41
|
},
|
|
42
|
+
{
|
|
43
|
+
loader: require.resolve('file-loader'),
|
|
44
|
+
options: {
|
|
45
|
+
name: '[name].[hash].[ext]',
|
|
46
|
+
},
|
|
47
|
+
},
|
|
22
48
|
],
|
|
23
49
|
});
|
|
24
50
|
}
|
|
@@ -11,6 +11,8 @@ const cache_directory_1 = require("nx/src/utils/cache-directory");
|
|
|
11
11
|
const node_child_process_1 = require("node:child_process");
|
|
12
12
|
const node_path_1 = require("node:path");
|
|
13
13
|
const node_fs_1 = require("node:fs");
|
|
14
|
+
const fs_1 = require("fs");
|
|
15
|
+
const path_1 = require("path");
|
|
14
16
|
function getBuildOptions(buildTarget, context) {
|
|
15
17
|
const target = (0, devkit_1.parseTargetString)(buildTarget, context);
|
|
16
18
|
const buildOptions = (0, devkit_1.readTargetOptions)(target, context);
|
|
@@ -56,27 +58,30 @@ function startStaticRemotesFileServer(staticRemotesConfig, context, options) {
|
|
|
56
58
|
}, context);
|
|
57
59
|
return staticRemotesIter;
|
|
58
60
|
}
|
|
59
|
-
async function
|
|
60
|
-
const
|
|
61
|
-
for (const app of remotes
|
|
62
|
-
const remoteProjectServeTarget = context.projectGraph.nodes[app].data.targets[
|
|
61
|
+
async function startRemotes(remotes, context, options, target = 'serve') {
|
|
62
|
+
const remoteIters = [];
|
|
63
|
+
for (const app of remotes) {
|
|
64
|
+
const remoteProjectServeTarget = context.projectGraph.nodes[app].data.targets[target];
|
|
63
65
|
const isUsingModuleFederationDevServerExecutor = remoteProjectServeTarget.executor.includes('module-federation-dev-server');
|
|
64
|
-
|
|
66
|
+
const overrides = target === 'serve'
|
|
67
|
+
? {
|
|
68
|
+
watch: true,
|
|
69
|
+
...(options.host ? { host: options.host } : {}),
|
|
70
|
+
...(options.ssl ? { ssl: options.ssl } : {}),
|
|
71
|
+
...(options.sslCert ? { sslCert: options.sslCert } : {}),
|
|
72
|
+
...(options.sslKey ? { sslKey: options.sslKey } : {}),
|
|
73
|
+
...(isUsingModuleFederationDevServerExecutor
|
|
74
|
+
? { isInitialHost: false }
|
|
75
|
+
: {}),
|
|
76
|
+
}
|
|
77
|
+
: {};
|
|
78
|
+
remoteIters.push(await (0, devkit_1.runExecutor)({
|
|
65
79
|
project: app,
|
|
66
|
-
target
|
|
80
|
+
target,
|
|
67
81
|
configuration: context.configurationName,
|
|
68
|
-
},
|
|
69
|
-
watch: true,
|
|
70
|
-
...(options.host ? { host: options.host } : {}),
|
|
71
|
-
...(options.ssl ? { ssl: options.ssl } : {}),
|
|
72
|
-
...(options.sslCert ? { sslCert: options.sslCert } : {}),
|
|
73
|
-
...(options.sslKey ? { sslKey: options.sslKey } : {}),
|
|
74
|
-
...(isUsingModuleFederationDevServerExecutor
|
|
75
|
-
? { isInitialHost: false }
|
|
76
|
-
: {}),
|
|
77
|
-
}, context));
|
|
82
|
+
}, overrides, context));
|
|
78
83
|
}
|
|
79
|
-
return
|
|
84
|
+
return remoteIters;
|
|
80
85
|
}
|
|
81
86
|
async function buildStaticRemotes(staticRemotesConfig, nxBin, context, options) {
|
|
82
87
|
if (!staticRemotesConfig.remotes.length) {
|
|
@@ -155,6 +160,17 @@ async function* moduleFederationDevServer(options, context) {
|
|
|
155
160
|
: (0, dev_server_impl_1.default)(options, context);
|
|
156
161
|
const p = context.projectsConfigurations.projects[context.projectName];
|
|
157
162
|
const buildOptions = getBuildOptions(options.buildTarget, context);
|
|
163
|
+
let pathToManifestFile = (0, node_path_1.join)(context.root, p.sourceRoot, 'assets/module-federation.manifest.json');
|
|
164
|
+
if (options.pathToManifestFile) {
|
|
165
|
+
const userPathToManifestFile = (0, node_path_1.join)(context.root, options.pathToManifestFile);
|
|
166
|
+
if (!(0, fs_1.existsSync)(userPathToManifestFile)) {
|
|
167
|
+
throw new Error(`The provided Module Federation manifest file path does not exist. Please check the file exists at "${userPathToManifestFile}".`);
|
|
168
|
+
}
|
|
169
|
+
else if ((0, path_1.extname)(options.pathToManifestFile) !== '.json') {
|
|
170
|
+
throw new Error(`The Module Federation manifest file must be a JSON. Please ensure the file at ${userPathToManifestFile} is a JSON.`);
|
|
171
|
+
}
|
|
172
|
+
pathToManifestFile = userPathToManifestFile;
|
|
173
|
+
}
|
|
158
174
|
if (!options.isInitialHost) {
|
|
159
175
|
return yield* currIter;
|
|
160
176
|
}
|
|
@@ -163,7 +179,7 @@ async function* moduleFederationDevServer(options, context) {
|
|
|
163
179
|
projectName: context.projectName,
|
|
164
180
|
projectGraph: context.projectGraph,
|
|
165
181
|
root: context.root,
|
|
166
|
-
});
|
|
182
|
+
}, pathToManifestFile);
|
|
167
183
|
if (remotes.devRemotes.length > 0 && !initialStaticRemotesPorts) {
|
|
168
184
|
options.staticRemotesPort = options.devRemotes.reduce((portToUse, r) => {
|
|
169
185
|
const remotePort = context.projectGraph.nodes[r].data.targets['serve'].options.port;
|
|
@@ -177,11 +193,12 @@ async function* moduleFederationDevServer(options, context) {
|
|
|
177
193
|
}
|
|
178
194
|
const staticRemotesConfig = parseStaticRemotesConfig(remotes.staticRemotes, context);
|
|
179
195
|
await buildStaticRemotes(staticRemotesConfig, nxBin, context, options);
|
|
180
|
-
const devRemoteIters = await
|
|
196
|
+
const devRemoteIters = await startRemotes(remotes.devRemotes, context, options, 'serve');
|
|
197
|
+
const dynamicRemotesIters = await startRemotes(remotes.dynamicRemotes, context, options, 'serve-static');
|
|
181
198
|
const staticRemotesIter = remotes.staticRemotes.length > 0
|
|
182
199
|
? startStaticRemotesFileServer(staticRemotesConfig, context, options)
|
|
183
200
|
: undefined;
|
|
184
|
-
return yield* (0, async_iterable_1.combineAsyncIterables)(currIter, ...devRemoteIters, ...(staticRemotesIter ? [staticRemotesIter] : []), (0, async_iterable_1.createAsyncIterable)(async ({ next, done }) => {
|
|
201
|
+
return yield* (0, async_iterable_1.combineAsyncIterables)(currIter, ...devRemoteIters, ...dynamicRemotesIters, ...(staticRemotesIter ? [staticRemotesIter] : []), (0, async_iterable_1.createAsyncIterable)(async ({ next, done }) => {
|
|
185
202
|
if (!options.isInitialHost) {
|
|
186
203
|
done();
|
|
187
204
|
return;
|
|
@@ -109,6 +109,10 @@
|
|
|
109
109
|
"staticRemotesPort": {
|
|
110
110
|
"type": "number",
|
|
111
111
|
"description": "The port at which to serve the file-server for the static remotes."
|
|
112
|
+
},
|
|
113
|
+
"pathToManifestFile": {
|
|
114
|
+
"type": "string",
|
|
115
|
+
"description": "Path to a Module Federation manifest file (e.g. `my/path/to/module-federation.manifest.json`) containing the dynamic remote applications relative to the workspace root."
|
|
112
116
|
}
|
|
113
117
|
}
|
|
114
118
|
}
|
|
@@ -34,13 +34,13 @@ async function addE2e(tree, options) {
|
|
|
34
34
|
// the name and root are already normalized, instruct the generator to use them as is
|
|
35
35
|
bundler: options.bundler === 'rspack' ? 'webpack' : options.bundler,
|
|
36
36
|
skipFormat: true,
|
|
37
|
-
devServerTarget: `${options.projectName}
|
|
38
|
-
baseUrl:
|
|
37
|
+
devServerTarget: `${options.projectName}:${options.e2eWebServerTarget}`,
|
|
38
|
+
baseUrl: options.e2eWebServerAddress,
|
|
39
39
|
jsx: true,
|
|
40
40
|
rootProject: options.rootProject,
|
|
41
41
|
webServerCommands: hasNxBuildPlugin
|
|
42
42
|
? {
|
|
43
|
-
default: `nx run ${options.projectName}
|
|
43
|
+
default: `nx run ${options.projectName}:${options.e2eWebServerTarget}`,
|
|
44
44
|
production: `nx run ${options.projectName}:preview`,
|
|
45
45
|
}
|
|
46
46
|
: undefined,
|
|
@@ -66,8 +66,8 @@ async function addE2e(tree, options) {
|
|
|
66
66
|
js: false,
|
|
67
67
|
linter: options.linter,
|
|
68
68
|
setParserOptionsProject: options.setParserOptionsProject,
|
|
69
|
-
webServerCommand: `${(0, devkit_1.getPackageManagerCommand)().exec} nx
|
|
70
|
-
webServerAddress:
|
|
69
|
+
webServerCommand: `${(0, devkit_1.getPackageManagerCommand)().exec} nx ${options.e2eWebServerTarget} ${options.name}`,
|
|
70
|
+
webServerAddress: options.e2eWebServerAddress,
|
|
71
71
|
rootProject: options.rootProject,
|
|
72
72
|
addPlugin: options.addPlugin,
|
|
73
73
|
});
|
|
@@ -32,8 +32,35 @@ async function normalizeOptions(host, options, callingGenerator = '@nx/react:app
|
|
|
32
32
|
options.addPlugin ??= addPlugin;
|
|
33
33
|
options.rootProject = appProjectRoot === '.';
|
|
34
34
|
options.projectNameAndRootFormat = projectNameAndRootFormat;
|
|
35
|
+
let e2eWebServerTarget = 'serve';
|
|
36
|
+
if (options.addPlugin) {
|
|
37
|
+
if (nxJson.plugins) {
|
|
38
|
+
for (const plugin of nxJson.plugins) {
|
|
39
|
+
if (options.bundler === 'vite' &&
|
|
40
|
+
typeof plugin === 'object' &&
|
|
41
|
+
plugin.plugin === '@nx/vite/plugin' &&
|
|
42
|
+
plugin.options.serveTargetName) {
|
|
43
|
+
e2eWebServerTarget = plugin.options
|
|
44
|
+
.serveTargetName;
|
|
45
|
+
}
|
|
46
|
+
else if (options.bundler === 'webpack' &&
|
|
47
|
+
typeof plugin === 'object' &&
|
|
48
|
+
plugin.plugin === '@nx/webpack/plugin' &&
|
|
49
|
+
plugin.options.serveTargetName) {
|
|
50
|
+
e2eWebServerTarget = plugin.options
|
|
51
|
+
.serveTargetName;
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
let e2ePort = options.devServerPort ?? 4200;
|
|
57
|
+
if (nxJson.targetDefaults?.[e2eWebServerTarget] &&
|
|
58
|
+
nxJson.targetDefaults?.[e2eWebServerTarget].options?.port) {
|
|
59
|
+
e2ePort = nxJson.targetDefaults?.[e2eWebServerTarget].options?.port;
|
|
60
|
+
}
|
|
35
61
|
const e2eProjectName = options.rootProject ? 'e2e' : `${appProjectName}-e2e`;
|
|
36
62
|
const e2eProjectRoot = options.rootProject ? 'e2e' : `${appProjectRoot}-e2e`;
|
|
63
|
+
const e2eWebServerAddress = `http://localhost:${e2ePort}`;
|
|
37
64
|
const parsedTags = options.tags
|
|
38
65
|
? options.tags.split(',').map((s) => s.trim())
|
|
39
66
|
: [];
|
|
@@ -52,6 +79,9 @@ async function normalizeOptions(host, options, callingGenerator = '@nx/react:app
|
|
|
52
79
|
appProjectRoot,
|
|
53
80
|
e2eProjectName,
|
|
54
81
|
e2eProjectRoot,
|
|
82
|
+
e2eWebServerAddress,
|
|
83
|
+
e2eWebServerTarget,
|
|
84
|
+
e2ePort,
|
|
55
85
|
parsedTags,
|
|
56
86
|
fileName,
|
|
57
87
|
styledModule,
|
|
@@ -36,6 +36,9 @@ export interface NormalizedSchema<T extends Schema = Schema> extends T {
|
|
|
36
36
|
appProjectRoot: string;
|
|
37
37
|
e2eProjectName: string;
|
|
38
38
|
e2eProjectRoot: string;
|
|
39
|
+
e2eWebServerAddress: string;
|
|
40
|
+
e2eWebServerTarget: string;
|
|
41
|
+
e2ePort: number;
|
|
39
42
|
parsedTags: string[];
|
|
40
43
|
fileName: string;
|
|
41
44
|
styledModule: null | SupportedStyles;
|