feat(react): add rspack module federation support (#27696)
- feat(react): add remote rspack module federation support - feat(react): add host rspack module federation support - feat(react): add federate module rspack module federation support - fix(react): migration test <!-- Please make sure you have read the submission guidelines before posting an PR --> <!-- https://github.com/nrwl/nx/blob/master/CONTRIBUTING.md#-submitting-a-pr --> <!-- Please make sure that your commit message follows our format --> <!-- Example: `fix(nx): must begin with lowercase` --> <!-- If this is a particularly complex change or feature addition, you can request a dedicated Nx release for this pull request branch. Mention someone from the Nx team or the `@nrwl/nx-pipelines-reviewers` and they will confirm if the PR warrants its own release for testing purposes, and generate it for you if appropriate. --> ## Current Behavior <!-- This is the behavior we have today --> We do not have an option to generate a react host and remote with rspack ## Expected Behavior <!-- This is the behavior we should expect with the changes in this PR --> Add rspack as an option when generating host and remote ## Related Issue(s) <!-- Please link the issue being fixed so it gets closed when this is merged. --> Fixes #
This commit is contained in:
parent
7b5c831630
commit
7351a1a25e
@ -76,6 +76,12 @@
|
|||||||
"host": {
|
"host": {
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"description": "The host / shell application for this remote."
|
"description": "The host / shell application for this remote."
|
||||||
|
},
|
||||||
|
"bundler": {
|
||||||
|
"description": "The bundler to use.",
|
||||||
|
"type": "string",
|
||||||
|
"enum": ["rspack", "webpack"],
|
||||||
|
"default": "rspack"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"required": ["name", "path", "remote"],
|
"required": ["name", "path", "remote"],
|
||||||
|
|||||||
@ -177,6 +177,14 @@
|
|||||||
"type": "boolean",
|
"type": "boolean",
|
||||||
"default": false,
|
"default": false,
|
||||||
"x-priority": "internal"
|
"x-priority": "internal"
|
||||||
|
},
|
||||||
|
"bundler": {
|
||||||
|
"description": "The bundler to use.",
|
||||||
|
"type": "string",
|
||||||
|
"enum": ["rspack", "webpack"],
|
||||||
|
"x-prompt": "Which bundler do you want to use to build the application?",
|
||||||
|
"default": "rspack",
|
||||||
|
"x-priority": "important"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"required": ["name"],
|
"required": ["name"],
|
||||||
|
|||||||
@ -176,6 +176,14 @@
|
|||||||
"type": "boolean",
|
"type": "boolean",
|
||||||
"default": false,
|
"default": false,
|
||||||
"x-priority": "internal"
|
"x-priority": "internal"
|
||||||
|
},
|
||||||
|
"bundler": {
|
||||||
|
"description": "The bundler to use.",
|
||||||
|
"type": "string",
|
||||||
|
"enum": ["rspack", "webpack"],
|
||||||
|
"x-prompt": "Which bundler do you want to use to build the application?",
|
||||||
|
"default": "rspack",
|
||||||
|
"x-priority": "important"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"required": ["name"],
|
"required": ["name"],
|
||||||
|
|||||||
@ -39,6 +39,12 @@
|
|||||||
"hidden": true,
|
"hidden": true,
|
||||||
"description": "Extra include entries in tsconfig.",
|
"description": "Extra include entries in tsconfig.",
|
||||||
"default": []
|
"default": []
|
||||||
|
},
|
||||||
|
"bundler": {
|
||||||
|
"description": "The bundler to use.",
|
||||||
|
"type": "string",
|
||||||
|
"enum": ["rspack", "webpack"],
|
||||||
|
"default": "webpack"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"required": ["project"],
|
"required": ["project"],
|
||||||
|
|||||||
1025
e2e/react/src/react-module-federation.rspack.test.ts
Normal file
1025
e2e/react/src/react-module-federation.rspack.test.ts
Normal file
File diff suppressed because it is too large
Load Diff
@ -40,7 +40,7 @@ describe('React Module Federation', () => {
|
|||||||
const remote3 = uniq('remote3');
|
const remote3 = uniq('remote3');
|
||||||
|
|
||||||
runCLI(
|
runCLI(
|
||||||
`generate @nx/react:host ${shell} --remotes=${remote1},${remote2},${remote3} --e2eTestRunner=cypress --style=css --no-interactive --skipFormat --js=${js}`
|
`generate @nx/react:host ${shell} --remotes=${remote1},${remote2},${remote3} --bundler=webpack --e2eTestRunner=cypress --style=css --no-interactive --skipFormat --js=${js}`
|
||||||
);
|
);
|
||||||
|
|
||||||
checkFilesExist(
|
checkFilesExist(
|
||||||
@ -134,7 +134,7 @@ describe('React Module Federation', () => {
|
|||||||
const remote3 = uniq('remote3');
|
const remote3 = uniq('remote3');
|
||||||
|
|
||||||
await runCLIAsync(
|
await runCLIAsync(
|
||||||
`generate @nx/react:host ${shell} --ssr --remotes=${remote1},${remote2},${remote3} --style=css --no-interactive --projectNameAndRootFormat=derived --skipFormat`
|
`generate @nx/react:host ${shell} --bundler=webpack --ssr --remotes=${remote1},${remote2},${remote3} --style=css --no-interactive --projectNameAndRootFormat=derived --skipFormat`
|
||||||
);
|
);
|
||||||
|
|
||||||
expect(readPort(shell)).toEqual(4200);
|
expect(readPort(shell)).toEqual(4200);
|
||||||
@ -165,7 +165,7 @@ describe('React Module Federation', () => {
|
|||||||
const remote3 = uniq('remote3');
|
const remote3 = uniq('remote3');
|
||||||
|
|
||||||
await runCLIAsync(
|
await runCLIAsync(
|
||||||
`generate @nx/react:host ${shell} --ssr --remotes=${remote1},${remote2},${remote3} --style=css --e2eTestRunner=cypress --no-interactive --projectNameAndRootFormat=derived --skipFormat`
|
`generate @nx/react:host ${shell} --bundler=webpack --ssr --remotes=${remote1},${remote2},${remote3} --style=css --e2eTestRunner=cypress --no-interactive --projectNameAndRootFormat=derived --skipFormat`
|
||||||
);
|
);
|
||||||
|
|
||||||
const serveResult = await runCommandUntil(`serve ${shell}`, (output) =>
|
const serveResult = await runCommandUntil(`serve ${shell}`, (output) =>
|
||||||
@ -182,7 +182,7 @@ describe('React Module Federation', () => {
|
|||||||
const remote3 = uniq('remote3');
|
const remote3 = uniq('remote3');
|
||||||
|
|
||||||
await runCLIAsync(
|
await runCLIAsync(
|
||||||
`generate @nx/react:host ${shell} --ssr --remotes=${remote1},${remote2},${remote3} --style=css --e2eTestRunner=cypress --no-interactive --projectNameAndRootFormat=derived --skipFormat`
|
`generate @nx/react:host ${shell} --bundler=webpack --ssr --remotes=${remote1},${remote2},${remote3} --style=css --e2eTestRunner=cypress --no-interactive --projectNameAndRootFormat=derived --skipFormat`
|
||||||
);
|
);
|
||||||
|
|
||||||
const capitalize = (s: string) =>
|
const capitalize = (s: string) =>
|
||||||
@ -225,10 +225,10 @@ describe('React Module Federation', () => {
|
|||||||
const remote = uniq('remote');
|
const remote = uniq('remote');
|
||||||
|
|
||||||
runCLI(
|
runCLI(
|
||||||
`generate @nx/react:host ${shell} --project-name-and-root-format=as-provided --no-interactive --skipFormat`
|
`generate @nx/react:host ${shell} --bundler=webpack --project-name-and-root-format=as-provided --no-interactive --skipFormat`
|
||||||
);
|
);
|
||||||
runCLI(
|
runCLI(
|
||||||
`generate @nx/react:remote ${remote} --host=${shell} --project-name-and-root-format=as-provided --no-interactive --skipFormat`
|
`generate @nx/react:remote ${remote} --bundler=webpack --host=${shell} --project-name-and-root-format=as-provided --no-interactive --skipFormat`
|
||||||
);
|
);
|
||||||
|
|
||||||
const shellPort = readPort(shell);
|
const shellPort = readPort(shell);
|
||||||
@ -301,7 +301,7 @@ describe('React Module Federation', () => {
|
|||||||
const host = uniq('host');
|
const host = uniq('host');
|
||||||
|
|
||||||
runCLI(
|
runCLI(
|
||||||
`generate @nx/react:host ${host} --remotes=${remote} --e2eTestRunner=cypress --no-interactive --projectNameAndRootFormat=as-provided --skipFormat`
|
`generate @nx/react:host ${host} --bundler=webpack --remotes=${remote} --e2eTestRunner=cypress --no-interactive --projectNameAndRootFormat=as-provided --skipFormat`
|
||||||
);
|
);
|
||||||
|
|
||||||
runCLI(
|
runCLI(
|
||||||
@ -310,7 +310,7 @@ describe('React Module Federation', () => {
|
|||||||
|
|
||||||
// Federate Module
|
// Federate Module
|
||||||
runCLI(
|
runCLI(
|
||||||
`generate @nx/react:federate-module ${lib}/src/index.ts --name=${module} --remote=${remote} --no-interactive --skipFormat`
|
`generate @nx/react:federate-module ${lib}/src/index.ts --bundler=webpack --name=${module} --remote=${remote} --no-interactive --skipFormat`
|
||||||
);
|
);
|
||||||
|
|
||||||
updateFile(
|
updateFile(
|
||||||
@ -402,7 +402,7 @@ describe('React Module Federation', () => {
|
|||||||
const host = uniq('host');
|
const host = uniq('host');
|
||||||
|
|
||||||
runCLI(
|
runCLI(
|
||||||
`generate @nx/react:host ${host} --remotes=${remote} --e2eTestRunner=cypress --no-interactive --projectNameAndRootFormat=as-provided --skipFormat`
|
`generate @nx/react:host ${host} --remotes=${remote} --bundler=webpack --e2eTestRunner=cypress --no-interactive --projectNameAndRootFormat=as-provided --skipFormat`
|
||||||
);
|
);
|
||||||
|
|
||||||
runCLI(
|
runCLI(
|
||||||
@ -411,7 +411,7 @@ describe('React Module Federation', () => {
|
|||||||
|
|
||||||
// Federate Module
|
// Federate Module
|
||||||
runCLI(
|
runCLI(
|
||||||
`generate @nx/react:federate-module ${lib}/src/index.ts --name=${module} --remote=${childRemote} --no-interactive --skipFormat`
|
`generate @nx/react:federate-module ${lib}/src/index.ts --bundler=webpack --name=${module} --remote=${childRemote} --no-interactive --skipFormat`
|
||||||
);
|
);
|
||||||
|
|
||||||
updateFile(
|
updateFile(
|
||||||
@ -510,7 +510,7 @@ describe('React Module Federation', () => {
|
|||||||
const host = uniq('host');
|
const host = uniq('host');
|
||||||
|
|
||||||
runCLI(
|
runCLI(
|
||||||
`generate @nx/react:host ${host} --remotes=${remote} --e2eTestRunner=cypress --no-interactive --projectNameAndRootFormat=as-provided --typescriptConfiguration=false --skipFormat`
|
`generate @nx/react:host ${host} --remotes=${remote} --bundler=webpack --e2eTestRunner=cypress --no-interactive --projectNameAndRootFormat=as-provided --typescriptConfiguration=false --skipFormat`
|
||||||
);
|
);
|
||||||
|
|
||||||
// Update remote to be loaded via script
|
// Update remote to be loaded via script
|
||||||
@ -644,7 +644,7 @@ describe('React Module Federation', () => {
|
|||||||
const lib = uniq('lib');
|
const lib = uniq('lib');
|
||||||
|
|
||||||
runCLI(
|
runCLI(
|
||||||
`generate @nx/react:host ${shell} --remotes=${remote} --e2eTestRunner=cypress --no-interactive --projectNameAndRootFormat=as-provided --skipFormat`
|
`generate @nx/react:host ${shell} --remotes=${remote} --bundler=webpack --e2eTestRunner=cypress --no-interactive --projectNameAndRootFormat=as-provided --skipFormat`
|
||||||
);
|
);
|
||||||
|
|
||||||
runCLI(
|
runCLI(
|
||||||
@ -794,7 +794,7 @@ describe('React Module Federation', () => {
|
|||||||
const remote = uniq('remote');
|
const remote = uniq('remote');
|
||||||
|
|
||||||
runCLI(
|
runCLI(
|
||||||
`generate @nx/react:host ${shell} --remotes=${remote} --e2eTestRunner=cypress --project-name-and-root-format=as-provided --no-interactive --skipFormat`
|
`generate @nx/react:host ${shell} --remotes=${remote} --bundler=webpack --e2eTestRunner=cypress --project-name-and-root-format=as-provided --no-interactive --skipFormat`
|
||||||
);
|
);
|
||||||
|
|
||||||
const shellPort = readPort(shell);
|
const shellPort = readPort(shell);
|
||||||
@ -930,7 +930,7 @@ describe('React Module Federation', () => {
|
|||||||
const remotePort = 4205;
|
const remotePort = 4205;
|
||||||
|
|
||||||
runCLI(
|
runCLI(
|
||||||
`generate @nx/react:host ${shell} --remotes=${remote} --e2eTestRunner=cypress --dynamic=true --project-name-and-root-format=as-provided --no-interactive --skipFormat`
|
`generate @nx/react:host ${shell} --remotes=${remote} --bundler=webpack --e2eTestRunner=cypress --dynamic=true --project-name-and-root-format=as-provided --no-interactive --skipFormat`
|
||||||
);
|
);
|
||||||
|
|
||||||
updateJson(`${remote}/project.json`, (project) => {
|
updateJson(`${remote}/project.json`, (project) => {
|
||||||
|
|||||||
@ -211,7 +211,13 @@ export async function applicationGeneratorInternal(
|
|||||||
project: options.projectName,
|
project: options.projectName,
|
||||||
main: joinPathFragments(
|
main: joinPathFragments(
|
||||||
options.appProjectRoot,
|
options.appProjectRoot,
|
||||||
maybeJs(options, `src/main.tsx`)
|
maybeJs(
|
||||||
|
{
|
||||||
|
js: options.js,
|
||||||
|
useJsx: true,
|
||||||
|
},
|
||||||
|
`src/main.tsx`
|
||||||
|
)
|
||||||
),
|
),
|
||||||
tsConfig: joinPathFragments(options.appProjectRoot, 'tsconfig.app.json'),
|
tsConfig: joinPathFragments(options.appProjectRoot, 'tsconfig.app.json'),
|
||||||
target: 'web',
|
target: 'web',
|
||||||
|
|||||||
@ -22,7 +22,13 @@ export function addRouting(host: Tree, options: NormalizedSchema) {
|
|||||||
}
|
}
|
||||||
const appPath = joinPathFragments(
|
const appPath = joinPathFragments(
|
||||||
options.appProjectRoot,
|
options.appProjectRoot,
|
||||||
maybeJs(options, `src/app/${options.fileName}.tsx`)
|
maybeJs(
|
||||||
|
{
|
||||||
|
js: options.js,
|
||||||
|
useJsx: options.bundler === 'vite' || options.bundler === 'rspack',
|
||||||
|
},
|
||||||
|
`src/app/${options.fileName}.tsx`
|
||||||
|
)
|
||||||
);
|
);
|
||||||
const appFileContent = host.read(appPath, 'utf-8');
|
const appFileContent = host.read(appPath, 'utf-8');
|
||||||
const appSource = tsModule.createSourceFile(
|
const appSource = tsModule.createSourceFile(
|
||||||
|
|||||||
@ -177,7 +177,7 @@ export async function createApplicationFiles(
|
|||||||
|
|
||||||
if (options.js) {
|
if (options.js) {
|
||||||
toJS(host, {
|
toJS(host, {
|
||||||
useJsx: options.bundler === 'vite',
|
useJsx: options.bundler === 'vite' || options.bundler === 'rspack',
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -204,7 +204,13 @@ function createNxWebpackPluginOptions(
|
|||||||
),
|
),
|
||||||
index: './src/index.html',
|
index: './src/index.html',
|
||||||
baseHref: '/',
|
baseHref: '/',
|
||||||
main: maybeJs(options, `./src/main.tsx`),
|
main: maybeJs(
|
||||||
|
{
|
||||||
|
js: options.js,
|
||||||
|
useJsx: options.bundler === 'vite' || options.bundler === 'rspack',
|
||||||
|
},
|
||||||
|
`./src/main.tsx`
|
||||||
|
),
|
||||||
tsConfig: './tsconfig.app.json',
|
tsConfig: './tsconfig.app.json',
|
||||||
assets: ['./src/favicon.ico', './src/assets'],
|
assets: ['./src/favicon.ico', './src/assets'],
|
||||||
styles:
|
styles:
|
||||||
|
|||||||
@ -16,6 +16,7 @@ describe('federate-module', () => {
|
|||||||
path: 'my-remote/src/my-federated-module.ts',
|
path: 'my-remote/src/my-federated-module.ts',
|
||||||
style: 'css',
|
style: 'css',
|
||||||
skipFormat: true,
|
skipFormat: true,
|
||||||
|
bundler: 'webpack',
|
||||||
};
|
};
|
||||||
// TODO(@jaysoo): Turn this back to adding the plugin
|
// TODO(@jaysoo): Turn this back to adding the plugin
|
||||||
let originalEnv: string;
|
let originalEnv: string;
|
||||||
@ -82,6 +83,7 @@ describe('federate-module', () => {
|
|||||||
linter: Linter.EsLint,
|
linter: Linter.EsLint,
|
||||||
style: 'css',
|
style: 'css',
|
||||||
unitTestRunner: 'none',
|
unitTestRunner: 'none',
|
||||||
|
bundler: 'webpack',
|
||||||
};
|
};
|
||||||
|
|
||||||
beforeEach(async () => {
|
beforeEach(async () => {
|
||||||
|
|||||||
@ -6,6 +6,8 @@ import {
|
|||||||
readJson,
|
readJson,
|
||||||
runTasksInSerial,
|
runTasksInSerial,
|
||||||
stripIndents,
|
stripIndents,
|
||||||
|
offsetFromRoot,
|
||||||
|
joinPathFragments,
|
||||||
} from '@nx/devkit';
|
} from '@nx/devkit';
|
||||||
import { Schema } from './schema';
|
import { Schema } from './schema';
|
||||||
|
|
||||||
@ -39,6 +41,7 @@ export async function federateModuleGenerator(tree: Tree, schema: Schema) {
|
|||||||
unitTestRunner: schema.unitTestRunner,
|
unitTestRunner: schema.unitTestRunner,
|
||||||
host: schema.host,
|
host: schema.host,
|
||||||
projectNameAndRootFormat: schema.projectNameAndRootFormat ?? 'derived',
|
projectNameAndRootFormat: schema.projectNameAndRootFormat ?? 'derived',
|
||||||
|
bundler: schema.bundler ?? 'rspack',
|
||||||
});
|
});
|
||||||
|
|
||||||
tasks.push(remoteGenerator);
|
tasks.push(remoteGenerator);
|
||||||
@ -60,7 +63,11 @@ export async function federateModuleGenerator(tree: Tree, schema: Schema) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// add path to exposes property
|
// add path to exposes property
|
||||||
addPathToExposes(tree, projectRoot, schema.name, schema.path);
|
const normalizedModulePath =
|
||||||
|
schema.bundler === 'rspack'
|
||||||
|
? joinPathFragments(offsetFromRoot(projectRoot), schema.path)
|
||||||
|
: schema.path;
|
||||||
|
addPathToExposes(tree, projectRoot, schema.name, normalizedModulePath);
|
||||||
|
|
||||||
// Add new path to tsconfig
|
// Add new path to tsconfig
|
||||||
const rootJSON = readJson(tree, getRootTsConfigPathInTree(tree));
|
const rootJSON = readJson(tree, getRootTsConfigPathInTree(tree));
|
||||||
|
|||||||
@ -12,4 +12,5 @@ export interface Schema {
|
|||||||
skipFormat?: boolean;
|
skipFormat?: boolean;
|
||||||
style?: SupportedStyles;
|
style?: SupportedStyles;
|
||||||
unitTestRunner?: 'jest' | 'vitest' | 'none';
|
unitTestRunner?: 'jest' | 'vitest' | 'none';
|
||||||
|
bundler?: 'rspack' | 'webpack';
|
||||||
}
|
}
|
||||||
|
|||||||
@ -76,6 +76,12 @@
|
|||||||
"host": {
|
"host": {
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"description": "The host / shell application for this remote."
|
"description": "The host / shell application for this remote."
|
||||||
|
},
|
||||||
|
"bundler": {
|
||||||
|
"description": "The bundler to use.",
|
||||||
|
"type": "string",
|
||||||
|
"enum": ["rspack", "webpack"],
|
||||||
|
"default": "rspack"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"required": ["name", "path", "remote"],
|
"required": ["name", "path", "remote"],
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||||
|
|
||||||
exports[`hostGenerator should generate host files and configs for SSR 1`] = `
|
exports[`hostGenerator bundler=webpack should generate host files and configs for SSR 1`] = `
|
||||||
"const { composePlugins, withNx } = require('@nx/webpack');
|
"const { composePlugins, withNx } = require('@nx/webpack');
|
||||||
const { withReact } = require('@nx/react');
|
const { withReact } = require('@nx/react');
|
||||||
const { withModuleFederationForSSR } = require('@nx/react/module-federation');
|
const { withModuleFederationForSSR } = require('@nx/react/module-federation');
|
||||||
@ -25,7 +25,7 @@ module.exports = composePlugins(
|
|||||||
"
|
"
|
||||||
`;
|
`;
|
||||||
|
|
||||||
exports[`hostGenerator should generate host files and configs for SSR 2`] = `
|
exports[`hostGenerator bundler=webpack should generate host files and configs for SSR 2`] = `
|
||||||
"// @ts-check
|
"// @ts-check
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -40,7 +40,7 @@ module.exports = moduleFederationConfig;
|
|||||||
"
|
"
|
||||||
`;
|
`;
|
||||||
|
|
||||||
exports[`hostGenerator should generate host files and configs for SSR when --typescriptConfiguration=true 1`] = `
|
exports[`hostGenerator bundler=webpack should generate host files and configs for SSR when --typescriptConfiguration=true 1`] = `
|
||||||
"import { composePlugins, withNx } from '@nx/webpack';
|
"import { composePlugins, withNx } from '@nx/webpack';
|
||||||
import { withReact } from '@nx/react';
|
import { withReact } from '@nx/react';
|
||||||
import { withModuleFederationForSSR } from '@nx/react/module-federation';
|
import { withModuleFederationForSSR } from '@nx/react/module-federation';
|
||||||
@ -65,7 +65,7 @@ export default composePlugins(
|
|||||||
"
|
"
|
||||||
`;
|
`;
|
||||||
|
|
||||||
exports[`hostGenerator should generate host files and configs for SSR when --typescriptConfiguration=true 2`] = `
|
exports[`hostGenerator bundler=webpack should generate host files and configs for SSR when --typescriptConfiguration=true 2`] = `
|
||||||
"import { ModuleFederationConfig } from '@nx/webpack';
|
"import { ModuleFederationConfig } from '@nx/webpack';
|
||||||
|
|
||||||
const config: ModuleFederationConfig = {
|
const config: ModuleFederationConfig = {
|
||||||
@ -77,7 +77,7 @@ export default config;
|
|||||||
"
|
"
|
||||||
`;
|
`;
|
||||||
|
|
||||||
exports[`hostGenerator should generate host files and configs when --typescriptConfiguration=false 1`] = `
|
exports[`hostGenerator bundler=webpack should generate host files and configs when --typescriptConfiguration=false 1`] = `
|
||||||
"const { composePlugins, withNx } = require('@nx/webpack');
|
"const { composePlugins, withNx } = require('@nx/webpack');
|
||||||
const { withReact } = require('@nx/react');
|
const { withReact } = require('@nx/react');
|
||||||
const { withModuleFederation } = require('@nx/react/module-federation');
|
const { withModuleFederation } = require('@nx/react/module-federation');
|
||||||
@ -102,7 +102,7 @@ module.exports = composePlugins(
|
|||||||
"
|
"
|
||||||
`;
|
`;
|
||||||
|
|
||||||
exports[`hostGenerator should generate host files and configs when --typescriptConfiguration=false 2`] = `
|
exports[`hostGenerator bundler=webpack should generate host files and configs when --typescriptConfiguration=false 2`] = `
|
||||||
"module.exports = {
|
"module.exports = {
|
||||||
name: 'test',
|
name: 'test',
|
||||||
/**
|
/**
|
||||||
@ -122,7 +122,7 @@ exports[`hostGenerator should generate host files and configs when --typescriptC
|
|||||||
"
|
"
|
||||||
`;
|
`;
|
||||||
|
|
||||||
exports[`hostGenerator should generate host files and configs when --typescriptConfiguration=true 1`] = `
|
exports[`hostGenerator bundler=webpack should generate host files and configs when --typescriptConfiguration=true 1`] = `
|
||||||
"import {composePlugins, withNx, ModuleFederationConfig} from '@nx/webpack';
|
"import {composePlugins, withNx, ModuleFederationConfig} from '@nx/webpack';
|
||||||
import {withReact} from '@nx/react';
|
import {withReact} from '@nx/react';
|
||||||
import {withModuleFederation} from '@nx/react/module-federation';
|
import {withModuleFederation} from '@nx/react/module-federation';
|
||||||
@ -143,7 +143,7 @@ export default composePlugins(withNx(), withReact(), withModuleFederation(config
|
|||||||
"
|
"
|
||||||
`;
|
`;
|
||||||
|
|
||||||
exports[`hostGenerator should generate host files and configs when --typescriptConfiguration=true 2`] = `
|
exports[`hostGenerator bundler=webpack should generate host files and configs when --typescriptConfiguration=true 2`] = `
|
||||||
"import { ModuleFederationConfig } from '@nx/webpack';
|
"import { ModuleFederationConfig } from '@nx/webpack';
|
||||||
|
|
||||||
const config: ModuleFederationConfig = {
|
const config: ModuleFederationConfig = {
|
||||||
@ -0,0 +1,33 @@
|
|||||||
|
import * as React from 'react';
|
||||||
|
<% if (!minimal) { %>
|
||||||
|
import NxWelcome from "./nx-welcome";
|
||||||
|
<% } %>
|
||||||
|
import { Link, Route, Routes } from 'react-router-dom';
|
||||||
|
|
||||||
|
<% if (remotes.length > 0) { %>
|
||||||
|
<% remotes.forEach(function(r) { %>
|
||||||
|
const <%= r.className %> = React.lazy(() => import('<%= r.fileName %>/Module'));
|
||||||
|
<% }); %>
|
||||||
|
<% } %>
|
||||||
|
export function App() {
|
||||||
|
return (
|
||||||
|
<React.Suspense fallback={null}>
|
||||||
|
<ul>
|
||||||
|
<li><Link to="/">Home</Link></li>
|
||||||
|
<% remotes.forEach(function(r) { %>
|
||||||
|
<li><Link to="/<%=r.fileName%>"><%=r.className%></Link></li>
|
||||||
|
<% }); %>
|
||||||
|
</ul>
|
||||||
|
<Routes>
|
||||||
|
<% if (!minimal) { %>
|
||||||
|
<Route path="/" element={<NxWelcome title="<%= projectName %>"/>} />
|
||||||
|
<% } %>
|
||||||
|
<% remotes.forEach(function(r) { %>
|
||||||
|
<Route path="/<%=r.fileName%>" element={<<%= r.className %>/>} />
|
||||||
|
<% }); %>
|
||||||
|
</Routes>
|
||||||
|
</React.Suspense>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default App;
|
||||||
@ -0,0 +1,10 @@
|
|||||||
|
<% if (dynamic) { %>
|
||||||
|
import { setRemoteDefinitions } from '@nx/react/mf';
|
||||||
|
|
||||||
|
fetch('/assets/module-federation.manifest.json')
|
||||||
|
.then((res) => res.json())
|
||||||
|
.then(definitions => setRemoteDefinitions(definitions))
|
||||||
|
.then(() => import('./bootstrap').catch(err => console.error(err)));
|
||||||
|
<% } else { %>
|
||||||
|
import('./bootstrap').catch(err => console.error(err));
|
||||||
|
<% } %>
|
||||||
@ -0,0 +1,13 @@
|
|||||||
|
import { ModuleFederationConfig } from '@nx/rspack/module-federation';
|
||||||
|
|
||||||
|
const config: ModuleFederationConfig = {
|
||||||
|
name: '<%= projectName %>',
|
||||||
|
remotes: [
|
||||||
|
<% if (static) {
|
||||||
|
remotes.forEach(function(r) { %> "<%= r.fileName %>", <% });
|
||||||
|
}
|
||||||
|
%>
|
||||||
|
],
|
||||||
|
};
|
||||||
|
|
||||||
|
export default config;
|
||||||
@ -0,0 +1,16 @@
|
|||||||
|
import {composePlugins, withNx, withReact} from '@nx/rspack';
|
||||||
|
import {withModuleFederationForSSR} from '@nx/rspack/module-federation';
|
||||||
|
|
||||||
|
import baseConfig from './module-federation.config';
|
||||||
|
|
||||||
|
const defaultConfig = {
|
||||||
|
...baseConfig
|
||||||
|
};
|
||||||
|
|
||||||
|
// Nx plugins for rspack to build config object from Nx options and context.
|
||||||
|
/**
|
||||||
|
* DTS Plugin is disabled in Nx Workspaces as Nx already provides Typing support for Module Federation
|
||||||
|
* The DTS Plugin can be enabled by setting dts: true
|
||||||
|
* Learn more about the DTS Plugin here: https://module-federation.io/configure/dts.html
|
||||||
|
*/
|
||||||
|
export default composePlugins(withNx(), withReact({ssr: true}), withModuleFederationForSSR(defaultConfig, { dts: false }));
|
||||||
@ -0,0 +1,16 @@
|
|||||||
|
// @ts-check
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @type {import('@nx/rspack/module-federation').ModuleFederationConfig}
|
||||||
|
**/
|
||||||
|
const moduleFederationConfig = {
|
||||||
|
name: '<%= projectName %>',
|
||||||
|
remotes: [
|
||||||
|
<% if (static) {
|
||||||
|
remotes.forEach(function(r) { %> "<%= r.fileName %>", <% });
|
||||||
|
}
|
||||||
|
%>
|
||||||
|
],
|
||||||
|
};
|
||||||
|
|
||||||
|
module.exports = moduleFederationConfig;
|
||||||
@ -0,0 +1,16 @@
|
|||||||
|
const {composePlugins, withNx, withReact} = require('@nx/rspack');
|
||||||
|
const {withModuleFederationForSSR} = require('@nx/rspack/module-federation');
|
||||||
|
|
||||||
|
const baseConfig = require('./module-federation.config');
|
||||||
|
|
||||||
|
const defaultConfig = {
|
||||||
|
...baseConfig
|
||||||
|
};
|
||||||
|
|
||||||
|
// Nx plugins for rspack to build config object from Nx options and context.
|
||||||
|
/**
|
||||||
|
* DTS Plugin is disabled in Nx Workspaces as Nx already provides Typing support for Module Federation
|
||||||
|
* The DTS Plugin can be enabled by setting dts: true
|
||||||
|
* Learn more about the DTS Plugin here: https://module-federation.io/configure/dts.html
|
||||||
|
*/
|
||||||
|
module.exports = composePlugins(withNx(), withReact({ssr: true}), withModuleFederationForSSR(defaultConfig, { dts: false }));
|
||||||
@ -0,0 +1,25 @@
|
|||||||
|
import { ModuleFederationConfig } from '@nx/rspack/module-federation';
|
||||||
|
|
||||||
|
const config: ModuleFederationConfig = {
|
||||||
|
name: '<%= projectName %>',
|
||||||
|
/**
|
||||||
|
* To use a remote that does not exist in your current Nx Workspace
|
||||||
|
* You can use the tuple-syntax to define your remote
|
||||||
|
*
|
||||||
|
* remotes: [['my-external-remote', 'https://nx-angular-remote.netlify.app']]
|
||||||
|
*
|
||||||
|
* You _may_ need to add a `remotes.d.ts` file to your `src/` folder declaring the external remote for tsc, with the
|
||||||
|
* following content:
|
||||||
|
*
|
||||||
|
* declare module 'my-external-remote';
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
remotes: [
|
||||||
|
<% if (static) {
|
||||||
|
remotes.forEach(function(r) { %> "<%= r.fileName %>", <% });
|
||||||
|
}
|
||||||
|
%>
|
||||||
|
],
|
||||||
|
};
|
||||||
|
|
||||||
|
export default config;
|
||||||
@ -0,0 +1,36 @@
|
|||||||
|
import { composePlugins, withNx, withReact } from '@nx/rspack';
|
||||||
|
import { withModuleFederation, ModuleFederationConfig } from '@nx/rspack/module-federation';
|
||||||
|
|
||||||
|
import baseConfig from './module-federation.config';
|
||||||
|
|
||||||
|
const prodConfig: ModuleFederationConfig = {
|
||||||
|
...baseConfig,
|
||||||
|
/*
|
||||||
|
* Remote overrides for production.
|
||||||
|
* Each entry is a pair of a unique name and the URL where it is deployed.
|
||||||
|
*
|
||||||
|
* e.g.
|
||||||
|
* remotes: [
|
||||||
|
* ['app1', 'http://app1.example.com'],
|
||||||
|
* ['app2', 'http://app2.example.com'],
|
||||||
|
* ]
|
||||||
|
*
|
||||||
|
* You can also use a full path to the remoteEntry.js file if desired.
|
||||||
|
*
|
||||||
|
* remotes: [
|
||||||
|
* ['app1', 'http://example.com/path/to/app1/remoteEntry.js'],
|
||||||
|
* ['app2', 'http://example.com/path/to/app2/remoteEntry.js'],
|
||||||
|
* ]
|
||||||
|
*/
|
||||||
|
remotes: [
|
||||||
|
<% remotes.forEach(function(r) {%>['<%= r.fileName %>', 'http://localhost:<%= r.port %>/'],<% }); %>
|
||||||
|
],
|
||||||
|
};
|
||||||
|
|
||||||
|
// Nx plugins for rspack to build config object from Nx options and context.
|
||||||
|
/**
|
||||||
|
* DTS Plugin is disabled in Nx Workspaces as Nx already provides Typing support for Module Federation
|
||||||
|
* The DTS Plugin can be enabled by setting dts: true
|
||||||
|
* Learn more about the DTS Plugin here: https://module-federation.io/configure/dts.html
|
||||||
|
*/
|
||||||
|
export default composePlugins(withNx(), withReact(), withModuleFederation(prodConfig, { dts: false }));
|
||||||
@ -0,0 +1,16 @@
|
|||||||
|
import {composePlugins, withNx, withReact} from '@nx/rspack';
|
||||||
|
import {withModuleFederation, ModuleFederationConfig} from '@nx/rspack/module-federation';
|
||||||
|
|
||||||
|
import baseConfig from './module-federation.config';
|
||||||
|
|
||||||
|
const config: ModuleFederationConfig = {
|
||||||
|
...baseConfig,
|
||||||
|
};
|
||||||
|
|
||||||
|
// Nx plugins for rspack to build config object from Nx options and context.
|
||||||
|
/**
|
||||||
|
* DTS Plugin is disabled in Nx Workspaces as Nx already provides Typing support for Module Federation
|
||||||
|
* The DTS Plugin can be enabled by setting dts: true
|
||||||
|
* Learn more about the DTS Plugin here: https://module-federation.io/configure/dts.html
|
||||||
|
*/
|
||||||
|
export default composePlugins(withNx(), withReact(), withModuleFederation(config, { dts: false }));
|
||||||
@ -0,0 +1,16 @@
|
|||||||
|
const { composePlugins, withNx, withReact } = require('@nx/rspack');
|
||||||
|
const { withModuleFederation } = require('@nx/rspack/module-federation');
|
||||||
|
|
||||||
|
const baseConfig = require('./module-federation.config');
|
||||||
|
|
||||||
|
const config = {
|
||||||
|
...baseConfig,
|
||||||
|
};
|
||||||
|
|
||||||
|
// Nx plugins for rspack to build config object from Nx options and context.
|
||||||
|
/**
|
||||||
|
* DTS Plugin is disabled in Nx Workspaces as Nx already provides Typing support for Module Federation
|
||||||
|
* The DTS Plugin can be enabled by setting dts: true
|
||||||
|
* Learn more about the DTS Plugin here: https://module-federation.io/configure/dts.html
|
||||||
|
*/
|
||||||
|
module.exports = composePlugins(withNx(), withReact(), withModuleFederation(config, { dts: false }));
|
||||||
@ -0,0 +1,36 @@
|
|||||||
|
const { composePlugins, withNx, withReact } = require('@nx/rspack');
|
||||||
|
const { withModuleFederation } = require('@nx/rspack/module-federation');
|
||||||
|
|
||||||
|
const baseConfig = require('./module-federation.config');
|
||||||
|
|
||||||
|
const prodConfig = {
|
||||||
|
...baseConfig,
|
||||||
|
/*
|
||||||
|
* Remote overrides for production.
|
||||||
|
* Each entry is a pair of a unique name and the URL where it is deployed.
|
||||||
|
*
|
||||||
|
* e.g.
|
||||||
|
* remotes: [
|
||||||
|
* ['app1', 'http://app1.example.com'],
|
||||||
|
* ['app2', 'http://app2.example.com'],
|
||||||
|
* ]
|
||||||
|
*
|
||||||
|
* You can also use a full path to the remoteEntry.js file if desired.
|
||||||
|
*
|
||||||
|
* remotes: [
|
||||||
|
* ['app1', 'http://example.com/path/to/app1/remoteEntry.js'],
|
||||||
|
* ['app2', 'http://example.com/path/to/app2/remoteEntry.js'],
|
||||||
|
* ]
|
||||||
|
*/
|
||||||
|
remotes: [
|
||||||
|
<% remotes.forEach(function(r) {%>['<%= r.fileName %>', 'http://localhost:<%= r.port %>/'],<% }); %>
|
||||||
|
],
|
||||||
|
};
|
||||||
|
|
||||||
|
// Nx plugins for rspack to build config object from Nx options and context.
|
||||||
|
/**
|
||||||
|
* DTS Plugin is disabled in Nx Workspaces as Nx already provides Typing support for Module Federation
|
||||||
|
* The DTS Plugin can be enabled by setting dts: true
|
||||||
|
* Learn more about the DTS Plugin here: https://module-federation.io/configure/dts.html
|
||||||
|
*/
|
||||||
|
module.exports = composePlugins(withNx(), withReact(), withModuleFederation(prodConfig, { dts: false }));
|
||||||
@ -0,0 +1,28 @@
|
|||||||
|
import * as path from 'path';
|
||||||
|
import express from 'express';
|
||||||
|
import cors from 'cors';
|
||||||
|
|
||||||
|
import { handleRequest } from './src/main.server';
|
||||||
|
|
||||||
|
const port = process.env['PORT'] || <%= port %>;
|
||||||
|
const app = express();
|
||||||
|
|
||||||
|
const browserDist = path.join(process.cwd(), '<%= browserBuildOutputPath %>');
|
||||||
|
const indexPath = path.join(browserDist, 'index.html');
|
||||||
|
|
||||||
|
app.use(cors());
|
||||||
|
|
||||||
|
app.get(
|
||||||
|
'*.*',
|
||||||
|
express.static(browserDist, {
|
||||||
|
maxAge: '1y',
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
app.use('*', handleRequest(indexPath));
|
||||||
|
|
||||||
|
const server = app.listen(port, () => {
|
||||||
|
console.log(`Express server listening on http://localhost:${port}`);
|
||||||
|
});
|
||||||
|
|
||||||
|
server.on('error', console.error);
|
||||||
@ -0,0 +1,17 @@
|
|||||||
|
{
|
||||||
|
"extends": "./tsconfig.app.json",
|
||||||
|
"compilerOptions": {
|
||||||
|
"outDir": "../../out-tsc/server",
|
||||||
|
"target": "es2019",
|
||||||
|
"types": [
|
||||||
|
"node",
|
||||||
|
"@nx/react/typings/cssmodule.d.ts",
|
||||||
|
"@nx/react/typings/image.d.ts"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"include": [
|
||||||
|
"src/remotes.d.ts",
|
||||||
|
"src/main.server.tsx",
|
||||||
|
"server.ts"
|
||||||
|
]
|
||||||
|
}
|
||||||
@ -0,0 +1,28 @@
|
|||||||
|
import * as path from 'path';
|
||||||
|
import express from 'express';
|
||||||
|
import cors from 'cors';
|
||||||
|
|
||||||
|
import { handleRequest } from './src/main.server';
|
||||||
|
|
||||||
|
const port = process.env['PORT'] || <%= port %>;
|
||||||
|
const app = express();
|
||||||
|
|
||||||
|
const browserDist = path.join(process.cwd(), '<%= browserBuildOutputPath %>');
|
||||||
|
const indexPath = path.join(browserDist, 'index.html');
|
||||||
|
|
||||||
|
app.use(cors());
|
||||||
|
|
||||||
|
app.get(
|
||||||
|
'*.*',
|
||||||
|
express.static(browserDist, {
|
||||||
|
maxAge: '1y',
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
app.use('*', handleRequest(indexPath));
|
||||||
|
|
||||||
|
const server = app.listen(port, () => {
|
||||||
|
console.log(`Express server listening on http://localhost:${port}`);
|
||||||
|
});
|
||||||
|
|
||||||
|
server.on('error', console.error);
|
||||||
@ -0,0 +1,17 @@
|
|||||||
|
{
|
||||||
|
"extends": "./tsconfig.app.json",
|
||||||
|
"compilerOptions": {
|
||||||
|
"outDir": "../../out-tsc/server",
|
||||||
|
"target": "es2019",
|
||||||
|
"types": [
|
||||||
|
"node",
|
||||||
|
"@nx/react/typings/cssmodule.d.ts",
|
||||||
|
"@nx/react/typings/image.d.ts"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"include": [
|
||||||
|
"src/remotes.d.ts",
|
||||||
|
"src/main.server.tsx",
|
||||||
|
"server.ts"
|
||||||
|
]
|
||||||
|
}
|
||||||
@ -0,0 +1,21 @@
|
|||||||
|
module.exports = {
|
||||||
|
name: '<%= projectName %>',
|
||||||
|
/**
|
||||||
|
* To use a remote that does not exist in your current Nx Workspace
|
||||||
|
* You can use the tuple-syntax to define your remote
|
||||||
|
*
|
||||||
|
* remotes: [['my-external-remote', 'https://nx-angular-remote.netlify.app']]
|
||||||
|
*
|
||||||
|
* You _may_ need to add a `remotes.d.ts` file to your `src/` folder declaring the external remote for tsc, with the
|
||||||
|
* following content:
|
||||||
|
*
|
||||||
|
* declare module 'my-external-remote';
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
remotes: [
|
||||||
|
<% if (static) {
|
||||||
|
remotes.forEach(function(r) { %> "<%= r.fileName %>", <% });
|
||||||
|
}
|
||||||
|
%>
|
||||||
|
],
|
||||||
|
};
|
||||||
379
packages/react/src/generators/host/host.rspack.spec.ts
Normal file
379
packages/react/src/generators/host/host.rspack.spec.ts
Normal file
@ -0,0 +1,379 @@
|
|||||||
|
import * as devkit from '@nx/devkit';
|
||||||
|
import type { Tree } from '@nx/devkit';
|
||||||
|
import { ProjectGraph, readJson } from '@nx/devkit';
|
||||||
|
import { createTreeWithEmptyWorkspace } from '@nx/devkit/testing';
|
||||||
|
import hostGenerator from './host';
|
||||||
|
import { Linter } from '@nx/eslint';
|
||||||
|
|
||||||
|
jest.mock('@nx/devkit', () => {
|
||||||
|
const original = jest.requireActual('@nx/devkit');
|
||||||
|
return {
|
||||||
|
...original,
|
||||||
|
readCachedProjectGraph: jest.fn().mockImplementation(
|
||||||
|
(): ProjectGraph => ({
|
||||||
|
dependencies: {},
|
||||||
|
nodes: {
|
||||||
|
test: {
|
||||||
|
name: 'test',
|
||||||
|
type: 'app',
|
||||||
|
data: {
|
||||||
|
root: 'test',
|
||||||
|
sourceRoot: 'test/src',
|
||||||
|
targets: {
|
||||||
|
build: {
|
||||||
|
executor: '@nx/rspack:rspack',
|
||||||
|
outputs: ['{options.outputPath}'],
|
||||||
|
defaultConfiguration: 'production',
|
||||||
|
options: {
|
||||||
|
compiler: 'babel',
|
||||||
|
outputPath: 'dist/test',
|
||||||
|
index: 'test/src/index.html',
|
||||||
|
baseHref: '/',
|
||||||
|
main: `test/src/main.tsx`,
|
||||||
|
tsConfig: 'test/tsconfig.app.json',
|
||||||
|
assets: ['test/src/favicon.ico', 'src/assets'],
|
||||||
|
styles: [`test/src/styles.css`],
|
||||||
|
scripts: [],
|
||||||
|
rspackConfig: 'test/rspack.config.js',
|
||||||
|
},
|
||||||
|
configurations: {
|
||||||
|
development: {
|
||||||
|
extractLicenses: false,
|
||||||
|
optimization: false,
|
||||||
|
sourceMap: true,
|
||||||
|
vendorChunk: true,
|
||||||
|
},
|
||||||
|
production: {
|
||||||
|
fileReplacements: [
|
||||||
|
{
|
||||||
|
replace: `test/src/environments/environment.ts`,
|
||||||
|
with: `test/src/environments/environment.prod.ts`,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
optimization: true,
|
||||||
|
outputHashing: 'all',
|
||||||
|
sourceMap: false,
|
||||||
|
namedChunks: false,
|
||||||
|
extractLicenses: true,
|
||||||
|
vendorChunk: false,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
serve: {
|
||||||
|
executor: '@nx/rspack:dev-server',
|
||||||
|
defaultConfiguration: 'development',
|
||||||
|
options: {
|
||||||
|
buildTarget: `test:build`,
|
||||||
|
hmr: true,
|
||||||
|
},
|
||||||
|
configurations: {
|
||||||
|
development: {
|
||||||
|
buildTarget: `test:build:development`,
|
||||||
|
},
|
||||||
|
production: {
|
||||||
|
buildTarget: `test:build:production`,
|
||||||
|
hmr: false,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
),
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
// TODO(colum): turn these on when rspack is moved into the main repo
|
||||||
|
xdescribe('hostGenerator', () => {
|
||||||
|
let tree: Tree;
|
||||||
|
|
||||||
|
// TODO(@jaysoo): Turn this back to adding the plugin
|
||||||
|
let originalEnv: string;
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
originalEnv = process.env.NX_ADD_PLUGINS;
|
||||||
|
process.env.NX_ADD_PLUGINS = 'false';
|
||||||
|
});
|
||||||
|
|
||||||
|
afterEach(() => {
|
||||||
|
process.env.NX_ADD_PLUGINS = originalEnv;
|
||||||
|
});
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
tree = createTreeWithEmptyWorkspace();
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('bundler=rspack', () => {
|
||||||
|
it('should generate host files and configs when --js=true', async () => {
|
||||||
|
await hostGenerator(tree, {
|
||||||
|
name: 'test',
|
||||||
|
style: 'css',
|
||||||
|
linter: Linter.None,
|
||||||
|
unitTestRunner: 'none',
|
||||||
|
e2eTestRunner: 'none',
|
||||||
|
projectNameAndRootFormat: 'as-provided',
|
||||||
|
typescriptConfiguration: false,
|
||||||
|
skipFormat: true,
|
||||||
|
js: true,
|
||||||
|
bundler: 'rspack',
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(tree.exists('test/tsconfig.json')).toBeTruthy();
|
||||||
|
|
||||||
|
expect(tree.exists('test/src/bootstrap.js')).toBeTruthy();
|
||||||
|
expect(tree.exists('test/src/main.js')).toBeTruthy();
|
||||||
|
expect(tree.exists('test/src/app/app.js')).toBeTruthy();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should generate host files and configs when --js=false', async () => {
|
||||||
|
await hostGenerator(tree, {
|
||||||
|
name: 'test',
|
||||||
|
style: 'css',
|
||||||
|
linter: Linter.None,
|
||||||
|
unitTestRunner: 'none',
|
||||||
|
e2eTestRunner: 'none',
|
||||||
|
projectNameAndRootFormat: 'as-provided',
|
||||||
|
typescriptConfiguration: false,
|
||||||
|
bundler: 'rspack',
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(tree.exists('test/tsconfig.json')).toBeTruthy();
|
||||||
|
|
||||||
|
expect(tree.exists('test/src/bootstrap.tsx')).toBeTruthy();
|
||||||
|
expect(tree.exists('test/src/main.ts')).toBeTruthy();
|
||||||
|
expect(tree.exists('test/src/app/app.tsx')).toBeTruthy();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should generate host files and configs when --typescriptConfiguration=true', async () => {
|
||||||
|
await hostGenerator(tree, {
|
||||||
|
name: 'test',
|
||||||
|
style: 'css',
|
||||||
|
linter: Linter.None,
|
||||||
|
unitTestRunner: 'none',
|
||||||
|
e2eTestRunner: 'none',
|
||||||
|
projectNameAndRootFormat: 'as-provided',
|
||||||
|
typescriptConfiguration: true,
|
||||||
|
skipFormat: true,
|
||||||
|
bundler: 'rspack',
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(tree.exists('test/tsconfig.json')).toBeTruthy();
|
||||||
|
|
||||||
|
expect(tree.exists('test/rspack.config.prod.ts')).toBeTruthy();
|
||||||
|
|
||||||
|
expect(tree.exists('test/rspack.config.ts')).toBeTruthy();
|
||||||
|
expect(tree.read('test/rspack.config.ts', 'utf-8')).toMatchSnapshot();
|
||||||
|
|
||||||
|
expect(tree.exists('test/module-federation.config.ts')).toBeTruthy();
|
||||||
|
expect(
|
||||||
|
tree.read('test/module-federation.config.ts', 'utf-8')
|
||||||
|
).toMatchSnapshot();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should generate host files and configs when --typescriptConfiguration=false', async () => {
|
||||||
|
await hostGenerator(tree, {
|
||||||
|
name: 'test',
|
||||||
|
style: 'css',
|
||||||
|
linter: Linter.None,
|
||||||
|
unitTestRunner: 'none',
|
||||||
|
e2eTestRunner: 'none',
|
||||||
|
projectNameAndRootFormat: 'as-provided',
|
||||||
|
typescriptConfiguration: false,
|
||||||
|
bundler: 'rspack',
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(tree.exists('test/tsconfig.json')).toBeTruthy();
|
||||||
|
|
||||||
|
expect(tree.exists('test/rspack.config.prod.js')).toBeTruthy();
|
||||||
|
|
||||||
|
expect(tree.exists('test/rspack.config.js')).toBeTruthy();
|
||||||
|
expect(tree.read('test/rspack.config.js', 'utf-8')).toMatchSnapshot();
|
||||||
|
|
||||||
|
expect(tree.exists('test/module-federation.config.js')).toBeTruthy();
|
||||||
|
expect(
|
||||||
|
tree.read('test/module-federation.config.js', 'utf-8')
|
||||||
|
).toMatchSnapshot();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should install @nx/web for the file-server executor', async () => {
|
||||||
|
const tree = createTreeWithEmptyWorkspace();
|
||||||
|
await hostGenerator(tree, {
|
||||||
|
name: 'test',
|
||||||
|
style: 'css',
|
||||||
|
linter: Linter.None,
|
||||||
|
unitTestRunner: 'none',
|
||||||
|
e2eTestRunner: 'none',
|
||||||
|
projectNameAndRootFormat: 'as-provided',
|
||||||
|
skipFormat: true,
|
||||||
|
bundler: 'rspack',
|
||||||
|
});
|
||||||
|
|
||||||
|
const packageJson = readJson(tree, 'package.json');
|
||||||
|
expect(packageJson.devDependencies['@nx/web']).toBeDefined();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should generate host files and configs for SSR', async () => {
|
||||||
|
await hostGenerator(tree, {
|
||||||
|
name: 'test',
|
||||||
|
ssr: true,
|
||||||
|
style: 'css',
|
||||||
|
linter: Linter.None,
|
||||||
|
unitTestRunner: 'none',
|
||||||
|
e2eTestRunner: 'none',
|
||||||
|
projectNameAndRootFormat: 'as-provided',
|
||||||
|
typescriptConfiguration: false,
|
||||||
|
bundler: 'rspack',
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(tree.exists('test/tsconfig.json')).toBeTruthy();
|
||||||
|
expect(tree.exists('test/rspack.config.prod.js')).toBeTruthy();
|
||||||
|
expect(tree.exists('test/rspack.server.config.js')).toBeTruthy();
|
||||||
|
expect(tree.exists('test/rspack.config.js')).toBeTruthy();
|
||||||
|
expect(tree.exists('test/module-federation.config.js')).toBeTruthy();
|
||||||
|
expect(
|
||||||
|
tree.exists('test/module-federation.server.config.js')
|
||||||
|
).toBeTruthy();
|
||||||
|
expect(tree.exists('test/src/main.server.tsx')).toBeTruthy();
|
||||||
|
expect(tree.exists('test/src/bootstrap.tsx')).toBeTruthy();
|
||||||
|
expect(tree.exists('test/src/main.ts')).toBeTruthy();
|
||||||
|
|
||||||
|
expect(readJson(tree, 'test/tsconfig.server.json')).toEqual({
|
||||||
|
compilerOptions: {
|
||||||
|
outDir: '../../out-tsc/server',
|
||||||
|
target: 'es2019',
|
||||||
|
types: [
|
||||||
|
'node',
|
||||||
|
'@nx/react/typings/cssmodule.d.ts',
|
||||||
|
'@nx/react/typings/image.d.ts',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
extends: './tsconfig.app.json',
|
||||||
|
include: ['src/remotes.d.ts', 'src/main.server.tsx', 'server.ts'],
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(
|
||||||
|
tree.read('test/rspack.server.config.js', 'utf-8')
|
||||||
|
).toMatchSnapshot();
|
||||||
|
expect(
|
||||||
|
tree.read('test/module-federation.server.config.js', 'utf-8')
|
||||||
|
).toMatchSnapshot();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should generate host files and configs for SSR when --typescriptConfiguration=true', async () => {
|
||||||
|
await hostGenerator(tree, {
|
||||||
|
name: 'test',
|
||||||
|
ssr: true,
|
||||||
|
style: 'css',
|
||||||
|
linter: Linter.None,
|
||||||
|
unitTestRunner: 'none',
|
||||||
|
e2eTestRunner: 'none',
|
||||||
|
projectNameAndRootFormat: 'as-provided',
|
||||||
|
typescriptConfiguration: true,
|
||||||
|
bundler: 'rspack',
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(tree.exists('test/tsconfig.json')).toBeTruthy();
|
||||||
|
expect(tree.exists('test/rspack.config.prod.ts')).toBeTruthy();
|
||||||
|
expect(tree.exists('test/rspack.server.config.ts')).toBeTruthy();
|
||||||
|
expect(tree.exists('test/rspack.config.ts')).toBeTruthy();
|
||||||
|
expect(tree.exists('test/module-federation.config.ts')).toBeTruthy();
|
||||||
|
expect(
|
||||||
|
tree.exists('test/module-federation.server.config.ts')
|
||||||
|
).toBeTruthy();
|
||||||
|
expect(tree.exists('test/src/main.server.tsx')).toBeTruthy();
|
||||||
|
expect(tree.exists('test/src/bootstrap.tsx')).toBeTruthy();
|
||||||
|
expect(tree.exists('test/src/main.ts')).toBeTruthy();
|
||||||
|
|
||||||
|
expect(readJson(tree, 'test/tsconfig.server.json')).toEqual({
|
||||||
|
compilerOptions: {
|
||||||
|
outDir: '../../out-tsc/server',
|
||||||
|
target: 'es2019',
|
||||||
|
types: [
|
||||||
|
'node',
|
||||||
|
'@nx/react/typings/cssmodule.d.ts',
|
||||||
|
'@nx/react/typings/image.d.ts',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
extends: './tsconfig.app.json',
|
||||||
|
include: ['src/remotes.d.ts', 'src/main.server.tsx', 'server.ts'],
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(
|
||||||
|
tree.read('test/rspack.server.config.ts', 'utf-8')
|
||||||
|
).toMatchSnapshot();
|
||||||
|
expect(
|
||||||
|
tree.read('test/module-federation.server.config.ts', 'utf-8')
|
||||||
|
).toMatchSnapshot();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should generate a host and remotes in a directory correctly when using --projectNameAndRootFormat=as-provided', async () => {
|
||||||
|
const tree = createTreeWithEmptyWorkspace();
|
||||||
|
|
||||||
|
await hostGenerator(tree, {
|
||||||
|
name: 'host-app',
|
||||||
|
directory: 'foo/host-app',
|
||||||
|
remotes: ['remote1', 'remote2', 'remote3'],
|
||||||
|
projectNameAndRootFormat: 'as-provided',
|
||||||
|
e2eTestRunner: 'none',
|
||||||
|
linter: Linter.None,
|
||||||
|
style: 'css',
|
||||||
|
unitTestRunner: 'none',
|
||||||
|
typescriptConfiguration: false,
|
||||||
|
bundler: 'rspack',
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(tree.exists('foo/remote1/project.json')).toBeTruthy();
|
||||||
|
expect(tree.exists('foo/remote2/project.json')).toBeTruthy();
|
||||||
|
expect(tree.exists('foo/remote3/project.json')).toBeTruthy();
|
||||||
|
expect(
|
||||||
|
tree.read('foo/host-app/module-federation.config.js', 'utf-8')
|
||||||
|
).toContain(`'remote1', 'remote2', 'remote3'`);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should generate a host and remotes in a directory correctly when using --projectNameAndRootFormat=as-provided and --typescriptConfiguration=true', async () => {
|
||||||
|
const tree = createTreeWithEmptyWorkspace();
|
||||||
|
|
||||||
|
await hostGenerator(tree, {
|
||||||
|
name: 'host-app',
|
||||||
|
directory: 'foo/host-app',
|
||||||
|
remotes: ['remote1', 'remote2', 'remote3'],
|
||||||
|
projectNameAndRootFormat: 'as-provided',
|
||||||
|
e2eTestRunner: 'none',
|
||||||
|
linter: Linter.None,
|
||||||
|
style: 'css',
|
||||||
|
unitTestRunner: 'none',
|
||||||
|
typescriptConfiguration: true,
|
||||||
|
bundler: 'rspack',
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(tree.exists('foo/remote1/project.json')).toBeTruthy();
|
||||||
|
expect(tree.exists('foo/remote2/project.json')).toBeTruthy();
|
||||||
|
expect(tree.exists('foo/remote3/project.json')).toBeTruthy();
|
||||||
|
expect(
|
||||||
|
tree.read('foo/host-app/module-federation.config.ts', 'utf-8')
|
||||||
|
).toContain(`'remote1', 'remote2', 'remote3'`);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should throw an error if invalid remotes names are provided and --dynamic is set to true', async () => {
|
||||||
|
const tree = createTreeWithEmptyWorkspace();
|
||||||
|
const remote = 'invalid-remote-name';
|
||||||
|
|
||||||
|
await expect(
|
||||||
|
hostGenerator(tree, {
|
||||||
|
name: 'myhostapp',
|
||||||
|
remotes: [remote],
|
||||||
|
dynamic: true,
|
||||||
|
projectNameAndRootFormat: 'as-provided',
|
||||||
|
e2eTestRunner: 'none',
|
||||||
|
linter: Linter.None,
|
||||||
|
style: 'css',
|
||||||
|
unitTestRunner: 'none',
|
||||||
|
typescriptConfiguration: false,
|
||||||
|
bundler: 'rspack',
|
||||||
|
})
|
||||||
|
).rejects.toThrowError(`Invalid remote name provided: ${remote}.`);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
@ -1,362 +0,0 @@
|
|||||||
import * as devkit from '@nx/devkit';
|
|
||||||
import type { Tree } from '@nx/devkit';
|
|
||||||
import { ProjectGraph, readJson } from '@nx/devkit';
|
|
||||||
import { createTreeWithEmptyWorkspace } from '@nx/devkit/testing';
|
|
||||||
import hostGenerator from './host';
|
|
||||||
import { Linter } from '@nx/eslint';
|
|
||||||
|
|
||||||
jest.mock('@nx/devkit', () => {
|
|
||||||
const original = jest.requireActual('@nx/devkit');
|
|
||||||
return {
|
|
||||||
...original,
|
|
||||||
readCachedProjectGraph: jest.fn().mockImplementation(
|
|
||||||
(): ProjectGraph => ({
|
|
||||||
dependencies: {},
|
|
||||||
nodes: {
|
|
||||||
test: {
|
|
||||||
name: 'test',
|
|
||||||
type: 'app',
|
|
||||||
data: {
|
|
||||||
root: 'test',
|
|
||||||
sourceRoot: 'test/src',
|
|
||||||
targets: {
|
|
||||||
build: {
|
|
||||||
executor: '@nx/webpack:webpack',
|
|
||||||
outputs: ['{options.outputPath}'],
|
|
||||||
defaultConfiguration: 'production',
|
|
||||||
options: {
|
|
||||||
compiler: 'babel',
|
|
||||||
outputPath: 'dist/test',
|
|
||||||
index: 'test/src/index.html',
|
|
||||||
baseHref: '/',
|
|
||||||
main: `test/src/main.tsx`,
|
|
||||||
tsConfig: 'test/tsconfig.app.json',
|
|
||||||
assets: ['test/src/favicon.ico', 'src/assets'],
|
|
||||||
styles: [`test/src/styles.css`],
|
|
||||||
scripts: [],
|
|
||||||
webpackConfig: 'test/webpack.config.js',
|
|
||||||
},
|
|
||||||
configurations: {
|
|
||||||
development: {
|
|
||||||
extractLicenses: false,
|
|
||||||
optimization: false,
|
|
||||||
sourceMap: true,
|
|
||||||
vendorChunk: true,
|
|
||||||
},
|
|
||||||
production: {
|
|
||||||
fileReplacements: [
|
|
||||||
{
|
|
||||||
replace: `test/src/environments/environment.ts`,
|
|
||||||
with: `test/src/environments/environment.prod.ts`,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
optimization: true,
|
|
||||||
outputHashing: 'all',
|
|
||||||
sourceMap: false,
|
|
||||||
namedChunks: false,
|
|
||||||
extractLicenses: true,
|
|
||||||
vendorChunk: false,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
serve: {
|
|
||||||
executor: '@nx/webpack:dev-server',
|
|
||||||
defaultConfiguration: 'development',
|
|
||||||
options: {
|
|
||||||
buildTarget: `test:build`,
|
|
||||||
hmr: true,
|
|
||||||
},
|
|
||||||
configurations: {
|
|
||||||
development: {
|
|
||||||
buildTarget: `test:build:development`,
|
|
||||||
},
|
|
||||||
production: {
|
|
||||||
buildTarget: `test:build:production`,
|
|
||||||
hmr: false,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
})
|
|
||||||
),
|
|
||||||
};
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('hostGenerator', () => {
|
|
||||||
let tree: Tree;
|
|
||||||
|
|
||||||
// TODO(@jaysoo): Turn this back to adding the plugin
|
|
||||||
let originalEnv: string;
|
|
||||||
|
|
||||||
beforeEach(() => {
|
|
||||||
originalEnv = process.env.NX_ADD_PLUGINS;
|
|
||||||
process.env.NX_ADD_PLUGINS = 'false';
|
|
||||||
});
|
|
||||||
|
|
||||||
afterEach(() => {
|
|
||||||
process.env.NX_ADD_PLUGINS = originalEnv;
|
|
||||||
});
|
|
||||||
|
|
||||||
beforeEach(() => {
|
|
||||||
tree = createTreeWithEmptyWorkspace();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should generate host files and configs when --js=true', async () => {
|
|
||||||
await hostGenerator(tree, {
|
|
||||||
name: 'test',
|
|
||||||
style: 'css',
|
|
||||||
linter: Linter.None,
|
|
||||||
unitTestRunner: 'none',
|
|
||||||
e2eTestRunner: 'none',
|
|
||||||
projectNameAndRootFormat: 'as-provided',
|
|
||||||
typescriptConfiguration: false,
|
|
||||||
skipFormat: true,
|
|
||||||
js: true,
|
|
||||||
});
|
|
||||||
|
|
||||||
expect(tree.exists('test/tsconfig.json')).toBeTruthy();
|
|
||||||
|
|
||||||
expect(tree.exists('test/src/bootstrap.js')).toBeTruthy();
|
|
||||||
expect(tree.exists('test/src/main.js')).toBeTruthy();
|
|
||||||
expect(tree.exists('test/src/app/app.js')).toBeTruthy();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should generate host files and configs when --js=false', async () => {
|
|
||||||
await hostGenerator(tree, {
|
|
||||||
name: 'test',
|
|
||||||
style: 'css',
|
|
||||||
linter: Linter.None,
|
|
||||||
unitTestRunner: 'none',
|
|
||||||
e2eTestRunner: 'none',
|
|
||||||
projectNameAndRootFormat: 'as-provided',
|
|
||||||
typescriptConfiguration: false,
|
|
||||||
});
|
|
||||||
|
|
||||||
expect(tree.exists('test/tsconfig.json')).toBeTruthy();
|
|
||||||
|
|
||||||
expect(tree.exists('test/src/bootstrap.tsx')).toBeTruthy();
|
|
||||||
expect(tree.exists('test/src/main.ts')).toBeTruthy();
|
|
||||||
expect(tree.exists('test/src/app/app.tsx')).toBeTruthy();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should generate host files and configs when --typescriptConfiguration=true', async () => {
|
|
||||||
await hostGenerator(tree, {
|
|
||||||
name: 'test',
|
|
||||||
style: 'css',
|
|
||||||
linter: Linter.None,
|
|
||||||
unitTestRunner: 'none',
|
|
||||||
e2eTestRunner: 'none',
|
|
||||||
projectNameAndRootFormat: 'as-provided',
|
|
||||||
typescriptConfiguration: true,
|
|
||||||
skipFormat: true,
|
|
||||||
});
|
|
||||||
|
|
||||||
expect(tree.exists('test/tsconfig.json')).toBeTruthy();
|
|
||||||
|
|
||||||
expect(tree.exists('test/webpack.config.prod.ts')).toBeTruthy();
|
|
||||||
|
|
||||||
expect(tree.exists('test/webpack.config.ts')).toBeTruthy();
|
|
||||||
expect(tree.read('test/webpack.config.ts', 'utf-8')).toMatchSnapshot();
|
|
||||||
|
|
||||||
expect(tree.exists('test/module-federation.config.ts')).toBeTruthy();
|
|
||||||
expect(
|
|
||||||
tree.read('test/module-federation.config.ts', 'utf-8')
|
|
||||||
).toMatchSnapshot();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should generate host files and configs when --typescriptConfiguration=false', async () => {
|
|
||||||
await hostGenerator(tree, {
|
|
||||||
name: 'test',
|
|
||||||
style: 'css',
|
|
||||||
linter: Linter.None,
|
|
||||||
unitTestRunner: 'none',
|
|
||||||
e2eTestRunner: 'none',
|
|
||||||
projectNameAndRootFormat: 'as-provided',
|
|
||||||
typescriptConfiguration: false,
|
|
||||||
});
|
|
||||||
|
|
||||||
expect(tree.exists('test/tsconfig.json')).toBeTruthy();
|
|
||||||
|
|
||||||
expect(tree.exists('test/webpack.config.prod.js')).toBeTruthy();
|
|
||||||
|
|
||||||
expect(tree.exists('test/webpack.config.js')).toBeTruthy();
|
|
||||||
expect(tree.read('test/webpack.config.js', 'utf-8')).toMatchSnapshot();
|
|
||||||
|
|
||||||
expect(tree.exists('test/module-federation.config.js')).toBeTruthy();
|
|
||||||
expect(
|
|
||||||
tree.read('test/module-federation.config.js', 'utf-8')
|
|
||||||
).toMatchSnapshot();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should install @nx/web for the file-server executor', async () => {
|
|
||||||
const tree = createTreeWithEmptyWorkspace();
|
|
||||||
await hostGenerator(tree, {
|
|
||||||
name: 'test',
|
|
||||||
style: 'css',
|
|
||||||
linter: Linter.None,
|
|
||||||
unitTestRunner: 'none',
|
|
||||||
e2eTestRunner: 'none',
|
|
||||||
projectNameAndRootFormat: 'as-provided',
|
|
||||||
skipFormat: true,
|
|
||||||
});
|
|
||||||
|
|
||||||
const packageJson = readJson(tree, 'package.json');
|
|
||||||
expect(packageJson.devDependencies['@nx/web']).toBeDefined();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should generate host files and configs for SSR', async () => {
|
|
||||||
await hostGenerator(tree, {
|
|
||||||
name: 'test',
|
|
||||||
ssr: true,
|
|
||||||
style: 'css',
|
|
||||||
linter: Linter.None,
|
|
||||||
unitTestRunner: 'none',
|
|
||||||
e2eTestRunner: 'none',
|
|
||||||
projectNameAndRootFormat: 'as-provided',
|
|
||||||
typescriptConfiguration: false,
|
|
||||||
});
|
|
||||||
|
|
||||||
expect(tree.exists('test/tsconfig.json')).toBeTruthy();
|
|
||||||
expect(tree.exists('test/webpack.config.prod.js')).toBeTruthy();
|
|
||||||
expect(tree.exists('test/webpack.server.config.js')).toBeTruthy();
|
|
||||||
expect(tree.exists('test/webpack.config.js')).toBeTruthy();
|
|
||||||
expect(tree.exists('test/module-federation.config.js')).toBeTruthy();
|
|
||||||
expect(tree.exists('test/module-federation.server.config.js')).toBeTruthy();
|
|
||||||
expect(tree.exists('test/src/main.server.tsx')).toBeTruthy();
|
|
||||||
expect(tree.exists('test/src/bootstrap.tsx')).toBeTruthy();
|
|
||||||
expect(tree.exists('test/src/main.ts')).toBeTruthy();
|
|
||||||
|
|
||||||
expect(readJson(tree, 'test/tsconfig.server.json')).toEqual({
|
|
||||||
compilerOptions: {
|
|
||||||
outDir: '../../out-tsc/server',
|
|
||||||
target: 'es2019',
|
|
||||||
types: [
|
|
||||||
'node',
|
|
||||||
'@nx/react/typings/cssmodule.d.ts',
|
|
||||||
'@nx/react/typings/image.d.ts',
|
|
||||||
],
|
|
||||||
},
|
|
||||||
extends: './tsconfig.app.json',
|
|
||||||
include: ['src/remotes.d.ts', 'src/main.server.tsx', 'server.ts'],
|
|
||||||
});
|
|
||||||
|
|
||||||
expect(
|
|
||||||
tree.read('test/webpack.server.config.js', 'utf-8')
|
|
||||||
).toMatchSnapshot();
|
|
||||||
expect(
|
|
||||||
tree.read('test/module-federation.server.config.js', 'utf-8')
|
|
||||||
).toMatchSnapshot();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should generate host files and configs for SSR when --typescriptConfiguration=true', async () => {
|
|
||||||
await hostGenerator(tree, {
|
|
||||||
name: 'test',
|
|
||||||
ssr: true,
|
|
||||||
style: 'css',
|
|
||||||
linter: Linter.None,
|
|
||||||
unitTestRunner: 'none',
|
|
||||||
e2eTestRunner: 'none',
|
|
||||||
projectNameAndRootFormat: 'as-provided',
|
|
||||||
typescriptConfiguration: true,
|
|
||||||
});
|
|
||||||
|
|
||||||
expect(tree.exists('test/tsconfig.json')).toBeTruthy();
|
|
||||||
expect(tree.exists('test/webpack.config.prod.ts')).toBeTruthy();
|
|
||||||
expect(tree.exists('test/webpack.server.config.ts')).toBeTruthy();
|
|
||||||
expect(tree.exists('test/webpack.config.ts')).toBeTruthy();
|
|
||||||
expect(tree.exists('test/module-federation.config.ts')).toBeTruthy();
|
|
||||||
expect(tree.exists('test/module-federation.server.config.ts')).toBeTruthy();
|
|
||||||
expect(tree.exists('test/src/main.server.tsx')).toBeTruthy();
|
|
||||||
expect(tree.exists('test/src/bootstrap.tsx')).toBeTruthy();
|
|
||||||
expect(tree.exists('test/src/main.ts')).toBeTruthy();
|
|
||||||
|
|
||||||
expect(readJson(tree, 'test/tsconfig.server.json')).toEqual({
|
|
||||||
compilerOptions: {
|
|
||||||
outDir: '../../out-tsc/server',
|
|
||||||
target: 'es2019',
|
|
||||||
types: [
|
|
||||||
'node',
|
|
||||||
'@nx/react/typings/cssmodule.d.ts',
|
|
||||||
'@nx/react/typings/image.d.ts',
|
|
||||||
],
|
|
||||||
},
|
|
||||||
extends: './tsconfig.app.json',
|
|
||||||
include: ['src/remotes.d.ts', 'src/main.server.tsx', 'server.ts'],
|
|
||||||
});
|
|
||||||
|
|
||||||
expect(
|
|
||||||
tree.read('test/webpack.server.config.ts', 'utf-8')
|
|
||||||
).toMatchSnapshot();
|
|
||||||
expect(
|
|
||||||
tree.read('test/module-federation.server.config.ts', 'utf-8')
|
|
||||||
).toMatchSnapshot();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should generate a host and remotes in a directory correctly when using --projectNameAndRootFormat=as-provided', async () => {
|
|
||||||
const tree = createTreeWithEmptyWorkspace();
|
|
||||||
|
|
||||||
await hostGenerator(tree, {
|
|
||||||
name: 'host-app',
|
|
||||||
directory: 'foo/host-app',
|
|
||||||
remotes: ['remote1', 'remote2', 'remote3'],
|
|
||||||
projectNameAndRootFormat: 'as-provided',
|
|
||||||
e2eTestRunner: 'none',
|
|
||||||
linter: Linter.None,
|
|
||||||
style: 'css',
|
|
||||||
unitTestRunner: 'none',
|
|
||||||
typescriptConfiguration: false,
|
|
||||||
});
|
|
||||||
|
|
||||||
expect(tree.exists('foo/remote1/project.json')).toBeTruthy();
|
|
||||||
expect(tree.exists('foo/remote2/project.json')).toBeTruthy();
|
|
||||||
expect(tree.exists('foo/remote3/project.json')).toBeTruthy();
|
|
||||||
expect(
|
|
||||||
tree.read('foo/host-app/module-federation.config.js', 'utf-8')
|
|
||||||
).toContain(`'remote1', 'remote2', 'remote3'`);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should generate a host and remotes in a directory correctly when using --projectNameAndRootFormat=as-provided and --typescriptConfiguration=true', async () => {
|
|
||||||
const tree = createTreeWithEmptyWorkspace();
|
|
||||||
|
|
||||||
await hostGenerator(tree, {
|
|
||||||
name: 'host-app',
|
|
||||||
directory: 'foo/host-app',
|
|
||||||
remotes: ['remote1', 'remote2', 'remote3'],
|
|
||||||
projectNameAndRootFormat: 'as-provided',
|
|
||||||
e2eTestRunner: 'none',
|
|
||||||
linter: Linter.None,
|
|
||||||
style: 'css',
|
|
||||||
unitTestRunner: 'none',
|
|
||||||
typescriptConfiguration: true,
|
|
||||||
});
|
|
||||||
|
|
||||||
expect(tree.exists('foo/remote1/project.json')).toBeTruthy();
|
|
||||||
expect(tree.exists('foo/remote2/project.json')).toBeTruthy();
|
|
||||||
expect(tree.exists('foo/remote3/project.json')).toBeTruthy();
|
|
||||||
expect(
|
|
||||||
tree.read('foo/host-app/module-federation.config.ts', 'utf-8')
|
|
||||||
).toContain(`'remote1', 'remote2', 'remote3'`);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should throw an error if invalid remotes names are provided and --dynamic is set to true', async () => {
|
|
||||||
const tree = createTreeWithEmptyWorkspace();
|
|
||||||
const remote = 'invalid-remote-name';
|
|
||||||
|
|
||||||
await expect(
|
|
||||||
hostGenerator(tree, {
|
|
||||||
name: 'myhostapp',
|
|
||||||
remotes: [remote],
|
|
||||||
dynamic: true,
|
|
||||||
projectNameAndRootFormat: 'as-provided',
|
|
||||||
e2eTestRunner: 'none',
|
|
||||||
linter: Linter.None,
|
|
||||||
style: 'css',
|
|
||||||
unitTestRunner: 'none',
|
|
||||||
typescriptConfiguration: false,
|
|
||||||
})
|
|
||||||
).rejects.toThrowError(`Invalid remote name provided: ${remote}.`);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
@ -49,6 +49,7 @@ export async function hostGeneratorInternal(
|
|||||||
dynamic: schema.dynamic ?? false,
|
dynamic: schema.dynamic ?? false,
|
||||||
// TODO(colum): remove when MF works with Crystal
|
// TODO(colum): remove when MF works with Crystal
|
||||||
addPlugin: false,
|
addPlugin: false,
|
||||||
|
bundler: schema.bundler ?? 'rspack',
|
||||||
};
|
};
|
||||||
|
|
||||||
// Check to see if remotes are provided and also check if --dynamic is provided
|
// Check to see if remotes are provided and also check if --dynamic is provided
|
||||||
@ -68,8 +69,6 @@ export async function hostGeneratorInternal(
|
|||||||
...options,
|
...options,
|
||||||
// The target use-case is loading remotes as child routes, thus always enable routing.
|
// The target use-case is loading remotes as child routes, thus always enable routing.
|
||||||
routing: true,
|
routing: true,
|
||||||
// Only webpack works with module federation for now.
|
|
||||||
bundler: 'webpack',
|
|
||||||
skipFormat: true,
|
skipFormat: true,
|
||||||
});
|
});
|
||||||
tasks.push(initTask);
|
tasks.push(initTask);
|
||||||
@ -98,6 +97,7 @@ export async function hostGeneratorInternal(
|
|||||||
dynamic: options.dynamic,
|
dynamic: options.dynamic,
|
||||||
host: options.name,
|
host: options.name,
|
||||||
skipPackageJson: options.skipPackageJson,
|
skipPackageJson: options.skipPackageJson,
|
||||||
|
bundler: options.bundler,
|
||||||
});
|
});
|
||||||
tasks.push(remoteTask);
|
tasks.push(remoteTask);
|
||||||
remotePort++;
|
remotePort++;
|
||||||
@ -125,10 +125,19 @@ export async function hostGeneratorInternal(
|
|||||||
tasks.push(setupSsrForHostTask);
|
tasks.push(setupSsrForHostTask);
|
||||||
|
|
||||||
const projectConfig = readProjectConfiguration(host, options.projectName);
|
const projectConfig = readProjectConfiguration(host, options.projectName);
|
||||||
|
if (options.bundler === 'rspack') {
|
||||||
|
projectConfig.targets.server.executor = '@nx/rspack:rspack';
|
||||||
|
projectConfig.targets.server.options.rspackConfig = joinPathFragments(
|
||||||
|
projectConfig.root,
|
||||||
|
`rspack.server.config.${options.typescriptConfiguration ? 'ts' : 'js'}`
|
||||||
|
);
|
||||||
|
delete projectConfig.targets.server.options.webpackConfig;
|
||||||
|
} else {
|
||||||
projectConfig.targets.server.options.webpackConfig = joinPathFragments(
|
projectConfig.targets.server.options.webpackConfig = joinPathFragments(
|
||||||
projectConfig.root,
|
projectConfig.root,
|
||||||
`webpack.server.config.${options.typescriptConfiguration ? 'ts' : 'js'}`
|
`webpack.server.config.${options.typescriptConfiguration ? 'ts' : 'js'}`
|
||||||
);
|
);
|
||||||
|
}
|
||||||
updateProjectConfiguration(host, options.projectName, projectConfig);
|
updateProjectConfiguration(host, options.projectName, projectConfig);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
378
packages/react/src/generators/host/host.webpack.spec.ts
Normal file
378
packages/react/src/generators/host/host.webpack.spec.ts
Normal file
@ -0,0 +1,378 @@
|
|||||||
|
import * as devkit from '@nx/devkit';
|
||||||
|
import type { Tree } from '@nx/devkit';
|
||||||
|
import { ProjectGraph, readJson } from '@nx/devkit';
|
||||||
|
import { createTreeWithEmptyWorkspace } from '@nx/devkit/testing';
|
||||||
|
import hostGenerator from './host';
|
||||||
|
import { Linter } from '@nx/eslint';
|
||||||
|
|
||||||
|
jest.mock('@nx/devkit', () => {
|
||||||
|
const original = jest.requireActual('@nx/devkit');
|
||||||
|
return {
|
||||||
|
...original,
|
||||||
|
readCachedProjectGraph: jest.fn().mockImplementation(
|
||||||
|
(): ProjectGraph => ({
|
||||||
|
dependencies: {},
|
||||||
|
nodes: {
|
||||||
|
test: {
|
||||||
|
name: 'test',
|
||||||
|
type: 'app',
|
||||||
|
data: {
|
||||||
|
root: 'test',
|
||||||
|
sourceRoot: 'test/src',
|
||||||
|
targets: {
|
||||||
|
build: {
|
||||||
|
executor: '@nx/webpack:webpack',
|
||||||
|
outputs: ['{options.outputPath}'],
|
||||||
|
defaultConfiguration: 'production',
|
||||||
|
options: {
|
||||||
|
compiler: 'babel',
|
||||||
|
outputPath: 'dist/test',
|
||||||
|
index: 'test/src/index.html',
|
||||||
|
baseHref: '/',
|
||||||
|
main: `test/src/main.tsx`,
|
||||||
|
tsConfig: 'test/tsconfig.app.json',
|
||||||
|
assets: ['test/src/favicon.ico', 'src/assets'],
|
||||||
|
styles: [`test/src/styles.css`],
|
||||||
|
scripts: [],
|
||||||
|
webpackConfig: 'test/webpack.config.js',
|
||||||
|
},
|
||||||
|
configurations: {
|
||||||
|
development: {
|
||||||
|
extractLicenses: false,
|
||||||
|
optimization: false,
|
||||||
|
sourceMap: true,
|
||||||
|
vendorChunk: true,
|
||||||
|
},
|
||||||
|
production: {
|
||||||
|
fileReplacements: [
|
||||||
|
{
|
||||||
|
replace: `test/src/environments/environment.ts`,
|
||||||
|
with: `test/src/environments/environment.prod.ts`,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
optimization: true,
|
||||||
|
outputHashing: 'all',
|
||||||
|
sourceMap: false,
|
||||||
|
namedChunks: false,
|
||||||
|
extractLicenses: true,
|
||||||
|
vendorChunk: false,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
serve: {
|
||||||
|
executor: '@nx/webpack:dev-server',
|
||||||
|
defaultConfiguration: 'development',
|
||||||
|
options: {
|
||||||
|
buildTarget: `test:build`,
|
||||||
|
hmr: true,
|
||||||
|
},
|
||||||
|
configurations: {
|
||||||
|
development: {
|
||||||
|
buildTarget: `test:build:development`,
|
||||||
|
},
|
||||||
|
production: {
|
||||||
|
buildTarget: `test:build:production`,
|
||||||
|
hmr: false,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
),
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('hostGenerator', () => {
|
||||||
|
let tree: Tree;
|
||||||
|
|
||||||
|
// TODO(@jaysoo): Turn this back to adding the plugin
|
||||||
|
let originalEnv: string;
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
originalEnv = process.env.NX_ADD_PLUGINS;
|
||||||
|
process.env.NX_ADD_PLUGINS = 'false';
|
||||||
|
});
|
||||||
|
|
||||||
|
afterEach(() => {
|
||||||
|
process.env.NX_ADD_PLUGINS = originalEnv;
|
||||||
|
});
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
tree = createTreeWithEmptyWorkspace();
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('bundler=webpack', () => {
|
||||||
|
it('should generate host files and configs when --js=true', async () => {
|
||||||
|
await hostGenerator(tree, {
|
||||||
|
name: 'test',
|
||||||
|
style: 'css',
|
||||||
|
linter: Linter.None,
|
||||||
|
unitTestRunner: 'none',
|
||||||
|
e2eTestRunner: 'none',
|
||||||
|
projectNameAndRootFormat: 'as-provided',
|
||||||
|
typescriptConfiguration: false,
|
||||||
|
skipFormat: true,
|
||||||
|
js: true,
|
||||||
|
bundler: 'webpack',
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(tree.exists('test/tsconfig.json')).toBeTruthy();
|
||||||
|
|
||||||
|
expect(tree.exists('test/src/bootstrap.js')).toBeTruthy();
|
||||||
|
expect(tree.exists('test/src/main.js')).toBeTruthy();
|
||||||
|
expect(tree.exists('test/src/app/app.js')).toBeTruthy();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should generate host files and configs when --js=false', async () => {
|
||||||
|
await hostGenerator(tree, {
|
||||||
|
name: 'test',
|
||||||
|
style: 'css',
|
||||||
|
linter: Linter.None,
|
||||||
|
unitTestRunner: 'none',
|
||||||
|
e2eTestRunner: 'none',
|
||||||
|
projectNameAndRootFormat: 'as-provided',
|
||||||
|
typescriptConfiguration: false,
|
||||||
|
bundler: 'webpack',
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(tree.exists('test/tsconfig.json')).toBeTruthy();
|
||||||
|
|
||||||
|
expect(tree.exists('test/src/bootstrap.tsx')).toBeTruthy();
|
||||||
|
expect(tree.exists('test/src/main.ts')).toBeTruthy();
|
||||||
|
expect(tree.exists('test/src/app/app.tsx')).toBeTruthy();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should generate host files and configs when --typescriptConfiguration=true', async () => {
|
||||||
|
await hostGenerator(tree, {
|
||||||
|
name: 'test',
|
||||||
|
style: 'css',
|
||||||
|
linter: Linter.None,
|
||||||
|
unitTestRunner: 'none',
|
||||||
|
e2eTestRunner: 'none',
|
||||||
|
projectNameAndRootFormat: 'as-provided',
|
||||||
|
typescriptConfiguration: true,
|
||||||
|
skipFormat: true,
|
||||||
|
bundler: 'webpack',
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(tree.exists('test/tsconfig.json')).toBeTruthy();
|
||||||
|
|
||||||
|
expect(tree.exists('test/webpack.config.prod.ts')).toBeTruthy();
|
||||||
|
|
||||||
|
expect(tree.exists('test/webpack.config.ts')).toBeTruthy();
|
||||||
|
expect(tree.read('test/webpack.config.ts', 'utf-8')).toMatchSnapshot();
|
||||||
|
|
||||||
|
expect(tree.exists('test/module-federation.config.ts')).toBeTruthy();
|
||||||
|
expect(
|
||||||
|
tree.read('test/module-federation.config.ts', 'utf-8')
|
||||||
|
).toMatchSnapshot();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should generate host files and configs when --typescriptConfiguration=false', async () => {
|
||||||
|
await hostGenerator(tree, {
|
||||||
|
name: 'test',
|
||||||
|
style: 'css',
|
||||||
|
linter: Linter.None,
|
||||||
|
unitTestRunner: 'none',
|
||||||
|
e2eTestRunner: 'none',
|
||||||
|
projectNameAndRootFormat: 'as-provided',
|
||||||
|
typescriptConfiguration: false,
|
||||||
|
bundler: 'webpack',
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(tree.exists('test/tsconfig.json')).toBeTruthy();
|
||||||
|
|
||||||
|
expect(tree.exists('test/webpack.config.prod.js')).toBeTruthy();
|
||||||
|
|
||||||
|
expect(tree.exists('test/webpack.config.js')).toBeTruthy();
|
||||||
|
expect(tree.read('test/webpack.config.js', 'utf-8')).toMatchSnapshot();
|
||||||
|
|
||||||
|
expect(tree.exists('test/module-federation.config.js')).toBeTruthy();
|
||||||
|
expect(
|
||||||
|
tree.read('test/module-federation.config.js', 'utf-8')
|
||||||
|
).toMatchSnapshot();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should install @nx/web for the file-server executor', async () => {
|
||||||
|
const tree = createTreeWithEmptyWorkspace();
|
||||||
|
await hostGenerator(tree, {
|
||||||
|
name: 'test',
|
||||||
|
style: 'css',
|
||||||
|
linter: Linter.None,
|
||||||
|
unitTestRunner: 'none',
|
||||||
|
e2eTestRunner: 'none',
|
||||||
|
projectNameAndRootFormat: 'as-provided',
|
||||||
|
skipFormat: true,
|
||||||
|
bundler: 'webpack',
|
||||||
|
});
|
||||||
|
|
||||||
|
const packageJson = readJson(tree, 'package.json');
|
||||||
|
expect(packageJson.devDependencies['@nx/web']).toBeDefined();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should generate host files and configs for SSR', async () => {
|
||||||
|
await hostGenerator(tree, {
|
||||||
|
name: 'test',
|
||||||
|
ssr: true,
|
||||||
|
style: 'css',
|
||||||
|
linter: Linter.None,
|
||||||
|
unitTestRunner: 'none',
|
||||||
|
e2eTestRunner: 'none',
|
||||||
|
projectNameAndRootFormat: 'as-provided',
|
||||||
|
typescriptConfiguration: false,
|
||||||
|
bundler: 'webpack',
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(tree.exists('test/tsconfig.json')).toBeTruthy();
|
||||||
|
expect(tree.exists('test/webpack.config.prod.js')).toBeTruthy();
|
||||||
|
expect(tree.exists('test/webpack.server.config.js')).toBeTruthy();
|
||||||
|
expect(tree.exists('test/webpack.config.js')).toBeTruthy();
|
||||||
|
expect(tree.exists('test/module-federation.config.js')).toBeTruthy();
|
||||||
|
expect(
|
||||||
|
tree.exists('test/module-federation.server.config.js')
|
||||||
|
).toBeTruthy();
|
||||||
|
expect(tree.exists('test/src/main.server.tsx')).toBeTruthy();
|
||||||
|
expect(tree.exists('test/src/bootstrap.tsx')).toBeTruthy();
|
||||||
|
expect(tree.exists('test/src/main.ts')).toBeTruthy();
|
||||||
|
|
||||||
|
expect(readJson(tree, 'test/tsconfig.server.json')).toEqual({
|
||||||
|
compilerOptions: {
|
||||||
|
outDir: '../../out-tsc/server',
|
||||||
|
target: 'es2019',
|
||||||
|
types: [
|
||||||
|
'node',
|
||||||
|
'@nx/react/typings/cssmodule.d.ts',
|
||||||
|
'@nx/react/typings/image.d.ts',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
extends: './tsconfig.app.json',
|
||||||
|
include: ['src/remotes.d.ts', 'src/main.server.tsx', 'server.ts'],
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(
|
||||||
|
tree.read('test/webpack.server.config.js', 'utf-8')
|
||||||
|
).toMatchSnapshot();
|
||||||
|
expect(
|
||||||
|
tree.read('test/module-federation.server.config.js', 'utf-8')
|
||||||
|
).toMatchSnapshot();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should generate host files and configs for SSR when --typescriptConfiguration=true', async () => {
|
||||||
|
await hostGenerator(tree, {
|
||||||
|
name: 'test',
|
||||||
|
ssr: true,
|
||||||
|
style: 'css',
|
||||||
|
linter: Linter.None,
|
||||||
|
unitTestRunner: 'none',
|
||||||
|
e2eTestRunner: 'none',
|
||||||
|
projectNameAndRootFormat: 'as-provided',
|
||||||
|
typescriptConfiguration: true,
|
||||||
|
bundler: 'webpack',
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(tree.exists('test/tsconfig.json')).toBeTruthy();
|
||||||
|
expect(tree.exists('test/webpack.config.prod.ts')).toBeTruthy();
|
||||||
|
expect(tree.exists('test/webpack.server.config.ts')).toBeTruthy();
|
||||||
|
expect(tree.exists('test/webpack.config.ts')).toBeTruthy();
|
||||||
|
expect(tree.exists('test/module-federation.config.ts')).toBeTruthy();
|
||||||
|
expect(
|
||||||
|
tree.exists('test/module-federation.server.config.ts')
|
||||||
|
).toBeTruthy();
|
||||||
|
expect(tree.exists('test/src/main.server.tsx')).toBeTruthy();
|
||||||
|
expect(tree.exists('test/src/bootstrap.tsx')).toBeTruthy();
|
||||||
|
expect(tree.exists('test/src/main.ts')).toBeTruthy();
|
||||||
|
|
||||||
|
expect(readJson(tree, 'test/tsconfig.server.json')).toEqual({
|
||||||
|
compilerOptions: {
|
||||||
|
outDir: '../../out-tsc/server',
|
||||||
|
target: 'es2019',
|
||||||
|
types: [
|
||||||
|
'node',
|
||||||
|
'@nx/react/typings/cssmodule.d.ts',
|
||||||
|
'@nx/react/typings/image.d.ts',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
extends: './tsconfig.app.json',
|
||||||
|
include: ['src/remotes.d.ts', 'src/main.server.tsx', 'server.ts'],
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(
|
||||||
|
tree.read('test/webpack.server.config.ts', 'utf-8')
|
||||||
|
).toMatchSnapshot();
|
||||||
|
expect(
|
||||||
|
tree.read('test/module-federation.server.config.ts', 'utf-8')
|
||||||
|
).toMatchSnapshot();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should generate a host and remotes in a directory correctly when using --projectNameAndRootFormat=as-provided', async () => {
|
||||||
|
const tree = createTreeWithEmptyWorkspace();
|
||||||
|
|
||||||
|
await hostGenerator(tree, {
|
||||||
|
name: 'host-app',
|
||||||
|
directory: 'foo/host-app',
|
||||||
|
remotes: ['remote1', 'remote2', 'remote3'],
|
||||||
|
projectNameAndRootFormat: 'as-provided',
|
||||||
|
e2eTestRunner: 'none',
|
||||||
|
linter: Linter.None,
|
||||||
|
style: 'css',
|
||||||
|
unitTestRunner: 'none',
|
||||||
|
typescriptConfiguration: false,
|
||||||
|
bundler: 'webpack',
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(tree.exists('foo/remote1/project.json')).toBeTruthy();
|
||||||
|
expect(tree.exists('foo/remote2/project.json')).toBeTruthy();
|
||||||
|
expect(tree.exists('foo/remote3/project.json')).toBeTruthy();
|
||||||
|
expect(
|
||||||
|
tree.read('foo/host-app/module-federation.config.js', 'utf-8')
|
||||||
|
).toContain(`'remote1', 'remote2', 'remote3'`);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should generate a host and remotes in a directory correctly when using --projectNameAndRootFormat=as-provided and --typescriptConfiguration=true', async () => {
|
||||||
|
const tree = createTreeWithEmptyWorkspace();
|
||||||
|
|
||||||
|
await hostGenerator(tree, {
|
||||||
|
name: 'host-app',
|
||||||
|
directory: 'foo/host-app',
|
||||||
|
remotes: ['remote1', 'remote2', 'remote3'],
|
||||||
|
projectNameAndRootFormat: 'as-provided',
|
||||||
|
e2eTestRunner: 'none',
|
||||||
|
linter: Linter.None,
|
||||||
|
style: 'css',
|
||||||
|
unitTestRunner: 'none',
|
||||||
|
typescriptConfiguration: true,
|
||||||
|
bundler: 'webpack',
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(tree.exists('foo/remote1/project.json')).toBeTruthy();
|
||||||
|
expect(tree.exists('foo/remote2/project.json')).toBeTruthy();
|
||||||
|
expect(tree.exists('foo/remote3/project.json')).toBeTruthy();
|
||||||
|
expect(
|
||||||
|
tree.read('foo/host-app/module-federation.config.ts', 'utf-8')
|
||||||
|
).toContain(`'remote1', 'remote2', 'remote3'`);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should throw an error if invalid remotes names are provided and --dynamic is set to true', async () => {
|
||||||
|
const tree = createTreeWithEmptyWorkspace();
|
||||||
|
const remote = 'invalid-remote-name';
|
||||||
|
|
||||||
|
await expect(
|
||||||
|
hostGenerator(tree, {
|
||||||
|
name: 'myhostapp',
|
||||||
|
remotes: [remote],
|
||||||
|
dynamic: true,
|
||||||
|
projectNameAndRootFormat: 'as-provided',
|
||||||
|
e2eTestRunner: 'none',
|
||||||
|
linter: Linter.None,
|
||||||
|
style: 'css',
|
||||||
|
unitTestRunner: 'none',
|
||||||
|
typescriptConfiguration: false,
|
||||||
|
bundler: 'webpack',
|
||||||
|
})
|
||||||
|
).rejects.toThrowError(`Invalid remote name provided: ${remote}.`);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
@ -36,10 +36,19 @@ export function addModuleFederationFiles(
|
|||||||
// Renaming original entry file so we can use `import(./bootstrap)` in
|
// Renaming original entry file so we can use `import(./bootstrap)` in
|
||||||
// new entry file.
|
// new entry file.
|
||||||
host.rename(
|
host.rename(
|
||||||
joinPathFragments(options.appProjectRoot, maybeJs(options, 'src/main.tsx')),
|
|
||||||
joinPathFragments(
|
joinPathFragments(
|
||||||
options.appProjectRoot,
|
options.appProjectRoot,
|
||||||
maybeJs(options, 'src/bootstrap.tsx')
|
maybeJs(
|
||||||
|
{ js: options.js, useJsx: options.bundler === 'rspack' },
|
||||||
|
'src/main.tsx'
|
||||||
|
)
|
||||||
|
),
|
||||||
|
joinPathFragments(
|
||||||
|
options.appProjectRoot,
|
||||||
|
maybeJs(
|
||||||
|
{ js: options.js, useJsx: options.bundler === 'rspack' },
|
||||||
|
'src/bootstrap.tsx'
|
||||||
|
)
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -47,15 +56,25 @@ export function addModuleFederationFiles(
|
|||||||
host,
|
host,
|
||||||
joinPathFragments(
|
joinPathFragments(
|
||||||
__dirname,
|
__dirname,
|
||||||
`../files/${options.js ? 'common' : 'common-ts'}`
|
`../files/${
|
||||||
|
options.js
|
||||||
|
? options.bundler === 'rspack'
|
||||||
|
? 'rspack-common'
|
||||||
|
: 'common'
|
||||||
|
: 'common-ts'
|
||||||
|
}`
|
||||||
),
|
),
|
||||||
options.appProjectRoot,
|
options.appProjectRoot,
|
||||||
templateVariables
|
templateVariables
|
||||||
);
|
);
|
||||||
|
|
||||||
const pathToModuleFederationFiles = options.typescriptConfiguration
|
const pathToModuleFederationFiles = options.typescriptConfiguration
|
||||||
? 'module-federation-ts'
|
? `${
|
||||||
: 'module-federation';
|
options.bundler === 'rspack' ? 'rspack-' : 'webpack-'
|
||||||
|
}module-federation-ts`
|
||||||
|
: `${
|
||||||
|
options.bundler === 'rspack' ? 'rspack-' : 'webpack-'
|
||||||
|
}module-federation`;
|
||||||
// New entry file is created here.
|
// New entry file is created here.
|
||||||
generateFiles(
|
generateFiles(
|
||||||
host,
|
host,
|
||||||
@ -70,22 +89,29 @@ export function addModuleFederationFiles(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function processWebpackConfig(options, host, fileName) {
|
function processBundlerConfigFile(options, host, fileName) {
|
||||||
const pathToWebpackConfig = joinPathFragments(
|
const pathToBundlerConfig = joinPathFragments(
|
||||||
options.appProjectRoot,
|
options.appProjectRoot,
|
||||||
fileName
|
fileName
|
||||||
);
|
);
|
||||||
deleteFileIfExists(host, pathToWebpackConfig);
|
deleteFileIfExists(host, pathToBundlerConfig);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (options.typescriptConfiguration) {
|
if (options.typescriptConfiguration) {
|
||||||
processWebpackConfig(options, host, 'webpack.config.js');
|
if (options.bundler === 'rspack') {
|
||||||
processWebpackConfig(options, host, 'webpack.config.prod.js');
|
processBundlerConfigFile(options, host, 'rspack.config.js');
|
||||||
|
processBundlerConfigFile(options, host, 'rspack.config.prod.js');
|
||||||
|
} else {
|
||||||
|
processBundlerConfigFile(options, host, 'webpack.config.js');
|
||||||
|
processBundlerConfigFile(options, host, 'webpack.config.prod.js');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (options.dynamic) {
|
if (options.dynamic) {
|
||||||
processWebpackConfig(options, host, 'webpack.config.prod.js');
|
processBundlerConfigFile(options, host, 'webpack.config.prod.js');
|
||||||
processWebpackConfig(options, host, 'webpack.config.prod.ts');
|
processBundlerConfigFile(options, host, 'webpack.config.prod.ts');
|
||||||
|
processBundlerConfigFile(options, host, 'rspack.config.prod.js');
|
||||||
|
processBundlerConfigFile(options, host, 'rspack.config.prod.ts');
|
||||||
if (!host.exists(pathToMFManifest)) {
|
if (!host.exists(pathToMFManifest)) {
|
||||||
host.write(
|
host.write(
|
||||||
pathToMFManifest,
|
pathToMFManifest,
|
||||||
|
|||||||
@ -20,12 +20,19 @@ export async function setupSsrForHost(
|
|||||||
) {
|
) {
|
||||||
const tasks: GeneratorCallback[] = [];
|
const tasks: GeneratorCallback[] = [];
|
||||||
let project = readProjectConfiguration(tree, appName);
|
let project = readProjectConfiguration(tree, appName);
|
||||||
project.targets.serve.executor = '@nx/react:module-federation-ssr-dev-server';
|
project.targets.serve.executor =
|
||||||
|
options.bundler === 'rspack'
|
||||||
|
? '@nx/rspack:module-federation-ssr-dev-server'
|
||||||
|
: '@nx/react:module-federation-ssr-dev-server';
|
||||||
updateProjectConfiguration(tree, appName, project);
|
updateProjectConfiguration(tree, appName, project);
|
||||||
|
|
||||||
const pathToModuleFederationSsrFiles = options.typescriptConfiguration
|
const pathToModuleFederationSsrFiles = options.typescriptConfiguration
|
||||||
? 'module-federation-ssr-ts'
|
? `${
|
||||||
: 'module-federation-ssr';
|
options.bundler === 'rspack' ? 'rspack-' : 'webpack-'
|
||||||
|
}module-federation-ssr-ts`
|
||||||
|
: `${
|
||||||
|
options.bundler === 'rspack' ? 'rspack-' : 'webpack-'
|
||||||
|
}module-federation-ssr`;
|
||||||
|
|
||||||
generateFiles(
|
generateFiles(
|
||||||
tree,
|
tree,
|
||||||
|
|||||||
@ -28,6 +28,7 @@ export interface Schema {
|
|||||||
typescriptConfiguration?: boolean;
|
typescriptConfiguration?: boolean;
|
||||||
dynamic?: boolean;
|
dynamic?: boolean;
|
||||||
addPlugin?: boolean;
|
addPlugin?: boolean;
|
||||||
|
bundler?: 'rspack' | 'webpack';
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface NormalizedSchema extends Schema {
|
export interface NormalizedSchema extends Schema {
|
||||||
|
|||||||
@ -183,6 +183,14 @@
|
|||||||
"type": "boolean",
|
"type": "boolean",
|
||||||
"default": false,
|
"default": false,
|
||||||
"x-priority": "internal"
|
"x-priority": "internal"
|
||||||
|
},
|
||||||
|
"bundler": {
|
||||||
|
"description": "The bundler to use.",
|
||||||
|
"type": "string",
|
||||||
|
"enum": ["rspack", "webpack"],
|
||||||
|
"x-prompt": "Which bundler do you want to use to build the application?",
|
||||||
|
"default": "rspack",
|
||||||
|
"x-priority": "important"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"required": ["name"],
|
"required": ["name"],
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||||
|
|
||||||
exports[`remote generator should create the remote with the correct config files 1`] = `
|
exports[`remote generator bundler=webpack should create the remote with the correct config files 1`] = `
|
||||||
"const { composePlugins, withNx } = require('@nx/webpack');
|
"const { composePlugins, withNx } = require('@nx/webpack');
|
||||||
const { withReact } = require('@nx/react');
|
const { withReact } = require('@nx/react');
|
||||||
const { withModuleFederation } = require('@nx/react/module-federation');
|
const { withModuleFederation } = require('@nx/react/module-federation');
|
||||||
@ -21,9 +21,9 @@ module.exports = composePlugins(withNx(), withReact(), withModuleFederation(conf
|
|||||||
"
|
"
|
||||||
`;
|
`;
|
||||||
|
|
||||||
exports[`remote generator should create the remote with the correct config files 2`] = `"module.exports = require('./webpack.config');"`;
|
exports[`remote generator bundler=webpack should create the remote with the correct config files 2`] = `"module.exports = require('./webpack.config');"`;
|
||||||
|
|
||||||
exports[`remote generator should create the remote with the correct config files 3`] = `
|
exports[`remote generator bundler=webpack should create the remote with the correct config files 3`] = `
|
||||||
"module.exports = {
|
"module.exports = {
|
||||||
name: 'test',
|
name: 'test',
|
||||||
|
|
||||||
@ -34,7 +34,7 @@ exports[`remote generator should create the remote with the correct config files
|
|||||||
"
|
"
|
||||||
`;
|
`;
|
||||||
|
|
||||||
exports[`remote generator should create the remote with the correct config files when --js=true 1`] = `
|
exports[`remote generator bundler=webpack should create the remote with the correct config files when --js=true 1`] = `
|
||||||
"const { composePlugins, withNx } = require('@nx/webpack');
|
"const { composePlugins, withNx } = require('@nx/webpack');
|
||||||
const { withReact } = require('@nx/react');
|
const { withReact } = require('@nx/react');
|
||||||
const { withModuleFederation } = require('@nx/react/module-federation');
|
const { withModuleFederation } = require('@nx/react/module-federation');
|
||||||
@ -55,9 +55,9 @@ module.exports = composePlugins(withNx(), withReact(), withModuleFederation(conf
|
|||||||
"
|
"
|
||||||
`;
|
`;
|
||||||
|
|
||||||
exports[`remote generator should create the remote with the correct config files when --js=true 2`] = `"module.exports = require('./webpack.config');"`;
|
exports[`remote generator bundler=webpack should create the remote with the correct config files when --js=true 2`] = `"module.exports = require('./webpack.config');"`;
|
||||||
|
|
||||||
exports[`remote generator should create the remote with the correct config files when --js=true 3`] = `
|
exports[`remote generator bundler=webpack should create the remote with the correct config files when --js=true 3`] = `
|
||||||
"module.exports = {
|
"module.exports = {
|
||||||
name: 'test',
|
name: 'test',
|
||||||
|
|
||||||
@ -68,7 +68,7 @@ exports[`remote generator should create the remote with the correct config files
|
|||||||
"
|
"
|
||||||
`;
|
`;
|
||||||
|
|
||||||
exports[`remote generator should create the remote with the correct config files when --typescriptConfiguration=true 1`] = `
|
exports[`remote generator bundler=webpack should create the remote with the correct config files when --typescriptConfiguration=true 1`] = `
|
||||||
"import { composePlugins, withNx } from '@nx/webpack';
|
"import { composePlugins, withNx } from '@nx/webpack';
|
||||||
import { withReact } from '@nx/react';
|
import { withReact } from '@nx/react';
|
||||||
import { withModuleFederation } from '@nx/react/module-federation';
|
import { withModuleFederation } from '@nx/react/module-federation';
|
||||||
@ -93,12 +93,12 @@ export default composePlugins(
|
|||||||
"
|
"
|
||||||
`;
|
`;
|
||||||
|
|
||||||
exports[`remote generator should create the remote with the correct config files when --typescriptConfiguration=true 2`] = `
|
exports[`remote generator bundler=webpack should create the remote with the correct config files when --typescriptConfiguration=true 2`] = `
|
||||||
"export default require('./webpack.config');
|
"export default require('./webpack.config');
|
||||||
"
|
"
|
||||||
`;
|
`;
|
||||||
|
|
||||||
exports[`remote generator should create the remote with the correct config files when --typescriptConfiguration=true 3`] = `
|
exports[`remote generator bundler=webpack should create the remote with the correct config files when --typescriptConfiguration=true 3`] = `
|
||||||
"import { ModuleFederationConfig } from '@nx/webpack';
|
"import { ModuleFederationConfig } from '@nx/webpack';
|
||||||
|
|
||||||
const config: ModuleFederationConfig = {
|
const config: ModuleFederationConfig = {
|
||||||
@ -113,7 +113,7 @@ export default config;
|
|||||||
"
|
"
|
||||||
`;
|
`;
|
||||||
|
|
||||||
exports[`remote generator should generate correct remote with config files when using --ssr 1`] = `
|
exports[`remote generator bundler=webpack should generate correct remote with config files when using --ssr 1`] = `
|
||||||
"const {composePlugins, withNx} = require('@nx/webpack');
|
"const {composePlugins, withNx} = require('@nx/webpack');
|
||||||
const {withReact} = require('@nx/react');
|
const {withReact} = require('@nx/react');
|
||||||
const {withModuleFederationForSSR} = require('@nx/react/module-federation');
|
const {withModuleFederationForSSR} = require('@nx/react/module-federation');
|
||||||
@ -134,7 +134,7 @@ module.exports = composePlugins(withNx(), withReact({ssr: true}), withModuleFede
|
|||||||
"
|
"
|
||||||
`;
|
`;
|
||||||
|
|
||||||
exports[`remote generator should generate correct remote with config files when using --ssr 2`] = `
|
exports[`remote generator bundler=webpack should generate correct remote with config files when using --ssr 2`] = `
|
||||||
"module.exports = {
|
"module.exports = {
|
||||||
name: 'test',
|
name: 'test',
|
||||||
exposes: {
|
exposes: {
|
||||||
@ -144,7 +144,7 @@ exports[`remote generator should generate correct remote with config files when
|
|||||||
"
|
"
|
||||||
`;
|
`;
|
||||||
|
|
||||||
exports[`remote generator should generate correct remote with config files when using --ssr and --typescriptConfiguration=true 1`] = `
|
exports[`remote generator bundler=webpack should generate correct remote with config files when using --ssr and --typescriptConfiguration=true 1`] = `
|
||||||
"import { composePlugins, withNx } from '@nx/webpack';
|
"import { composePlugins, withNx } from '@nx/webpack';
|
||||||
import { withReact } from '@nx/react';
|
import { withReact } from '@nx/react';
|
||||||
import { withModuleFederationForSSR } from '@nx/react/module-federation';
|
import { withModuleFederationForSSR } from '@nx/react/module-federation';
|
||||||
@ -169,7 +169,7 @@ export default composePlugins(
|
|||||||
"
|
"
|
||||||
`;
|
`;
|
||||||
|
|
||||||
exports[`remote generator should generate correct remote with config files when using --ssr and --typescriptConfiguration=true 2`] = `
|
exports[`remote generator bundler=webpack should generate correct remote with config files when using --ssr and --typescriptConfiguration=true 2`] = `
|
||||||
"import { ModuleFederationConfig } from '@nx/webpack';
|
"import { ModuleFederationConfig } from '@nx/webpack';
|
||||||
|
|
||||||
const config: ModuleFederationConfig = {
|
const config: ModuleFederationConfig = {
|
||||||
@ -0,0 +1 @@
|
|||||||
|
import('./bootstrap');
|
||||||
@ -0,0 +1 @@
|
|||||||
|
export { default } from './app/app';
|
||||||
@ -0,0 +1,10 @@
|
|||||||
|
import {ModuleFederationConfig} from '@nx/rspack/module-federation';
|
||||||
|
|
||||||
|
const config: ModuleFederationConfig = {
|
||||||
|
name: '<%= projectName %>',
|
||||||
|
exposes: {
|
||||||
|
'./Module': './src/remote-entry.<%= js ? 'js' : 'ts' %>',
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
export default config;
|
||||||
@ -0,0 +1,16 @@
|
|||||||
|
import {composePlugins, withNx, withReact} from '@nx/rspack';
|
||||||
|
import {withModuleFederationForSSR} from '@nx/rspack/module-federation';
|
||||||
|
|
||||||
|
import baseConfig from "./module-federation.server.config";
|
||||||
|
|
||||||
|
const defaultConfig = {
|
||||||
|
...baseConfig,
|
||||||
|
};
|
||||||
|
|
||||||
|
// Nx plugins for rspack to build config object from Nx options and context.
|
||||||
|
/**
|
||||||
|
* DTS Plugin is disabled in Nx Workspaces as Nx already provides Typing support Module Federation
|
||||||
|
* The DTS Plugin can be enabled by setting dts: true
|
||||||
|
* Learn more about the DTS Plugin here: https://module-federation.io/configure/dts.html
|
||||||
|
*/
|
||||||
|
export default composePlugins(withNx(), withReact({ssr: true}), withModuleFederationForSSR(defaultConfig, { dts: false }));
|
||||||
@ -0,0 +1,19 @@
|
|||||||
|
{
|
||||||
|
"extends": "./tsconfig.json",
|
||||||
|
"compilerOptions": {
|
||||||
|
"outDir": "../../dist/out-tsc",
|
||||||
|
"types": [
|
||||||
|
"node",
|
||||||
|
"@nx/react/typings/cssmodule.d.ts",
|
||||||
|
"@nx/react/typings/image.d.ts"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"include": [
|
||||||
|
"src/**/*.js",
|
||||||
|
"src/**/*.jsx",
|
||||||
|
"src/**/*.ts",
|
||||||
|
"src/**/*.tsx",
|
||||||
|
"rspack.config.ts",
|
||||||
|
"rspack.prod.config.ts"
|
||||||
|
]
|
||||||
|
}
|
||||||
@ -0,0 +1,6 @@
|
|||||||
|
module.exports = {
|
||||||
|
name: '<%= projectName %>',
|
||||||
|
exposes: {
|
||||||
|
'./Module': './src/remote-entry.<%= js ? 'js' : 'ts' %>',
|
||||||
|
},
|
||||||
|
};
|
||||||
@ -0,0 +1,16 @@
|
|||||||
|
const {composePlugins, withNx, withReact} = require('@nx/rspack');
|
||||||
|
const {withModuleFederationForSSR} = require('@nx/rspack/module-federation');
|
||||||
|
|
||||||
|
const baseConfig = require("./module-federation.server.config");
|
||||||
|
|
||||||
|
const defaultConfig = {
|
||||||
|
...baseConfig,
|
||||||
|
};
|
||||||
|
|
||||||
|
// Nx plugins for rspack to build config object from Nx options and context.
|
||||||
|
/**
|
||||||
|
* DTS Plugin is disabled in Nx Workspaces as Nx already provides Typing support Module Federation
|
||||||
|
* The DTS Plugin can be enabled by setting dts: true
|
||||||
|
* Learn more about the DTS Plugin here: https://module-federation.io/configure/dts.html
|
||||||
|
*/
|
||||||
|
module.exports = composePlugins(withNx(), withReact({ssr: true}), withModuleFederationForSSR(defaultConfig, { dts: false }));
|
||||||
@ -0,0 +1,13 @@
|
|||||||
|
import {ModuleFederationConfig} from '@nx/rspack/module-federation';
|
||||||
|
|
||||||
|
const config: ModuleFederationConfig = {
|
||||||
|
name: '<%= projectName %>',
|
||||||
|
<% if (dynamic) { %>
|
||||||
|
library: { type: 'var', name: '<%= projectName %>'},
|
||||||
|
<% } %>
|
||||||
|
exposes: {
|
||||||
|
'./Module': './src/remote-entry.<%= js ? 'js' : 'ts' %>',
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
export default config;
|
||||||
@ -0,0 +1 @@
|
|||||||
|
export default require('./rspack.config');
|
||||||
@ -0,0 +1,16 @@
|
|||||||
|
import {composePlugins, withNx, withReact} from '@nx/rspack';
|
||||||
|
import {withModuleFederation} from '@nx/rspack/module-federation';
|
||||||
|
|
||||||
|
import baseConfig from './module-federation.config';
|
||||||
|
|
||||||
|
const config = {
|
||||||
|
...baseConfig,
|
||||||
|
};
|
||||||
|
|
||||||
|
// Nx plugins for rspack to build config object from Nx options and context.
|
||||||
|
/**
|
||||||
|
* DTS Plugin is disabled in Nx Workspaces as Nx already provides Typing support Module Federation
|
||||||
|
* The DTS Plugin can be enabled by setting dts: true
|
||||||
|
* Learn more about the DTS Plugin here: https://module-federation.io/configure/dts.html
|
||||||
|
*/
|
||||||
|
export default composePlugins(withNx(), withReact(), withModuleFederation(config, { dts: false }));
|
||||||
@ -0,0 +1,19 @@
|
|||||||
|
{
|
||||||
|
"extends": "./tsconfig.json",
|
||||||
|
"compilerOptions": {
|
||||||
|
"outDir": "../../dist/out-tsc",
|
||||||
|
"types": [
|
||||||
|
"node",
|
||||||
|
"@nx/react/typings/cssmodule.d.ts",
|
||||||
|
"@nx/react/typings/image.d.ts"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"include": [
|
||||||
|
"src/**/*.js",
|
||||||
|
"src/**/*.jsx",
|
||||||
|
"src/**/*.ts",
|
||||||
|
"src/**/*.tsx",
|
||||||
|
"rspack.config.ts",
|
||||||
|
"rspack.prod.config.ts"
|
||||||
|
]
|
||||||
|
}
|
||||||
@ -0,0 +1,16 @@
|
|||||||
|
const { composePlugins, withNx, withReact } = require('@nx/rspack');
|
||||||
|
const { withModuleFederation } = require('@nx/rspack/module-federation');
|
||||||
|
|
||||||
|
const baseConfig = require('./module-federation.config');
|
||||||
|
|
||||||
|
const config = {
|
||||||
|
...baseConfig,
|
||||||
|
};
|
||||||
|
|
||||||
|
// Nx plugins for rspack to build config object from Nx options and context.
|
||||||
|
/**
|
||||||
|
* DTS Plugin is disabled in Nx Workspaces as Nx already provides Typing support Module Federation
|
||||||
|
* The DTS Plugin can be enabled by setting dts: true
|
||||||
|
* Learn more about the DTS Plugin here: https://module-federation.io/configure/dts.html
|
||||||
|
*/
|
||||||
|
module.exports = composePlugins(withNx(), withReact(), withModuleFederation(config, { dts: false }));
|
||||||
@ -0,0 +1 @@
|
|||||||
|
module.exports = require('./rspack.config');
|
||||||
@ -0,0 +1,45 @@
|
|||||||
|
import * as path from 'path';
|
||||||
|
import express from 'express';
|
||||||
|
import cors from 'cors';
|
||||||
|
|
||||||
|
import { handleRequest } from './src/main.server';
|
||||||
|
|
||||||
|
const port = process.env['PORT'] || <%= port %>;
|
||||||
|
const app = express();
|
||||||
|
|
||||||
|
const browserDist = path.join(process.cwd(), '<%= browserBuildOutputPath %>');
|
||||||
|
const serverDist = path.join(process.cwd(), '<%= serverBuildOutputPath %>');
|
||||||
|
const indexPath = path.join(browserDist, 'index.html');
|
||||||
|
|
||||||
|
app.use(cors());
|
||||||
|
|
||||||
|
// Client-side static bundles
|
||||||
|
app.get(
|
||||||
|
'*.*',
|
||||||
|
express.static(browserDist, {
|
||||||
|
maxAge: '1y',
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
// Static bundles for server-side module federation
|
||||||
|
app.use('/server',
|
||||||
|
express.static(serverDist, {
|
||||||
|
maxAge: '1y'
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
app.use('*', handleRequest(indexPath));
|
||||||
|
|
||||||
|
const server = app.listen(port, () => {
|
||||||
|
console.log(`Express server listening on http://localhost:${port}`);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* DO NOT REMOVE IF USING @nx/react:module-federation-dev-ssr executor
|
||||||
|
* to serve your Host application with this Remote application.
|
||||||
|
* This message allows Nx to determine when the Remote is ready to be
|
||||||
|
* consumed by the Host.
|
||||||
|
*/
|
||||||
|
process.send?.('nx.server.ready');
|
||||||
|
});
|
||||||
|
|
||||||
|
server.on('error', console.error);
|
||||||
@ -0,0 +1,45 @@
|
|||||||
|
import * as path from 'path';
|
||||||
|
import express from 'express';
|
||||||
|
import cors from 'cors';
|
||||||
|
|
||||||
|
import { handleRequest } from './src/main.server';
|
||||||
|
|
||||||
|
const port = process.env['PORT'] || <%= port %>;
|
||||||
|
const app = express();
|
||||||
|
|
||||||
|
const browserDist = path.join(process.cwd(), '<%= browserBuildOutputPath %>');
|
||||||
|
const serverDist = path.join(process.cwd(), '<%= serverBuildOutputPath %>');
|
||||||
|
const indexPath = path.join(browserDist, 'index.html');
|
||||||
|
|
||||||
|
app.use(cors());
|
||||||
|
|
||||||
|
// Client-side static bundles
|
||||||
|
app.get(
|
||||||
|
'*.*',
|
||||||
|
express.static(browserDist, {
|
||||||
|
maxAge: '1y',
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
// Static bundles for server-side module federation
|
||||||
|
app.use('/server',
|
||||||
|
express.static(serverDist, {
|
||||||
|
maxAge: '1y'
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
app.use('*', handleRequest(indexPath));
|
||||||
|
|
||||||
|
const server = app.listen(port, () => {
|
||||||
|
console.log(`Express server listening on http://localhost:${port}`);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* DO NOT REMOVE IF USING @nx/react:module-federation-dev-ssr executor
|
||||||
|
* to serve your Host application with this Remote application.
|
||||||
|
* This message allows Nx to determine when the Remote is ready to be
|
||||||
|
* consumed by the Host.
|
||||||
|
*/
|
||||||
|
process.send?.('nx.server.ready');
|
||||||
|
});
|
||||||
|
|
||||||
|
server.on('error', console.error);
|
||||||
@ -0,0 +1,19 @@
|
|||||||
|
{
|
||||||
|
"extends": "./tsconfig.json",
|
||||||
|
"compilerOptions": {
|
||||||
|
"outDir": "../../dist/out-tsc",
|
||||||
|
"types": [
|
||||||
|
"node",
|
||||||
|
"@nx/react/typings/cssmodule.d.ts",
|
||||||
|
"@nx/react/typings/image.d.ts"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"include": [
|
||||||
|
"src/**/*.js",
|
||||||
|
"src/**/*.jsx",
|
||||||
|
"src/**/*.ts",
|
||||||
|
"src/**/*.tsx",
|
||||||
|
"webpack.config.ts",
|
||||||
|
"webpack.prod.config.ts"
|
||||||
|
]
|
||||||
|
}
|
||||||
@ -0,0 +1,9 @@
|
|||||||
|
module.exports = {
|
||||||
|
name: '<%= projectName %>',
|
||||||
|
<% if (dynamic) { %>
|
||||||
|
library: { type: 'var', name: '<%= projectName %>'},
|
||||||
|
<% } %>
|
||||||
|
exposes: {
|
||||||
|
'./Module': './src/remote-entry.<%= js ? 'js' : 'ts' %>',
|
||||||
|
},
|
||||||
|
};
|
||||||
@ -21,8 +21,12 @@ export async function setupSsrForRemote(
|
|||||||
const project = readProjectConfiguration(tree, appName);
|
const project = readProjectConfiguration(tree, appName);
|
||||||
|
|
||||||
const pathToModuleFederationSsrFiles = options.typescriptConfiguration
|
const pathToModuleFederationSsrFiles = options.typescriptConfiguration
|
||||||
? 'module-federation-ssr-ts'
|
? `${
|
||||||
: 'module-federation-ssr';
|
options.bundler === 'rspack' ? 'rspack-' : 'webpack-'
|
||||||
|
}module-federation-ssr-ts`
|
||||||
|
: `${
|
||||||
|
options.bundler === 'rspack' ? 'rspack-' : 'webpack-'
|
||||||
|
}module-federation-ssr`;
|
||||||
|
|
||||||
generateFiles(
|
generateFiles(
|
||||||
tree,
|
tree,
|
||||||
|
|||||||
@ -88,11 +88,15 @@ function findAppComponentPath(host: Tree, sourceRoot: string) {
|
|||||||
'app/app.tsx',
|
'app/app.tsx',
|
||||||
'app/App.tsx',
|
'app/App.tsx',
|
||||||
'app/app.js',
|
'app/app.js',
|
||||||
|
'app/app.jsx',
|
||||||
'app/App.js',
|
'app/App.js',
|
||||||
|
'app/App.jsx',
|
||||||
'app.tsx',
|
'app.tsx',
|
||||||
'App.tsx',
|
'App.tsx',
|
||||||
'app.js',
|
'app.js',
|
||||||
'App.js',
|
'App.js',
|
||||||
|
'app.jsx',
|
||||||
|
'App.jsx',
|
||||||
];
|
];
|
||||||
for (const loc of locations) {
|
for (const loc of locations) {
|
||||||
if (host.exists(joinPathFragments(sourceRoot, loc))) {
|
if (host.exists(joinPathFragments(sourceRoot, loc))) {
|
||||||
|
|||||||
337
packages/react/src/generators/remote/remote.rspack.spec.ts
Normal file
337
packages/react/src/generators/remote/remote.rspack.spec.ts
Normal file
@ -0,0 +1,337 @@
|
|||||||
|
import 'nx/src/internal-testing-utils/mock-project-graph';
|
||||||
|
|
||||||
|
import { ProjectGraph, readJson, readNxJson } from '@nx/devkit';
|
||||||
|
import { createTreeWithEmptyWorkspace } from '@nx/devkit/testing';
|
||||||
|
import { Linter } from '@nx/eslint';
|
||||||
|
import remote from './remote';
|
||||||
|
import { getRootTsConfigPathInTree } from '@nx/js';
|
||||||
|
|
||||||
|
jest.mock('@nx/devkit', () => {
|
||||||
|
const original = jest.requireActual('@nx/devkit');
|
||||||
|
return {
|
||||||
|
...original,
|
||||||
|
readCachedProjectGraph: jest.fn().mockImplementation(
|
||||||
|
(): ProjectGraph => ({
|
||||||
|
dependencies: {},
|
||||||
|
nodes: {
|
||||||
|
test: {
|
||||||
|
name: 'test',
|
||||||
|
type: 'app',
|
||||||
|
data: {
|
||||||
|
root: 'test',
|
||||||
|
sourceRoot: 'test/src',
|
||||||
|
targets: {
|
||||||
|
build: {
|
||||||
|
executor: '@nx/rspack:rspack',
|
||||||
|
outputs: ['{options.outputPath}'],
|
||||||
|
defaultConfiguration: 'production',
|
||||||
|
options: {
|
||||||
|
compiler: 'babel',
|
||||||
|
outputPath: 'dist/test',
|
||||||
|
index: 'test/src/index.html',
|
||||||
|
baseHref: '/',
|
||||||
|
main: `test/src/main.tsx`,
|
||||||
|
tsConfig: 'test/tsconfig.app.json',
|
||||||
|
assets: ['test/src/favicon.ico', 'src/assets'],
|
||||||
|
styles: [`test/src/styles.css`],
|
||||||
|
scripts: [],
|
||||||
|
rspackConfig: 'test/rspack.config.js',
|
||||||
|
},
|
||||||
|
configurations: {
|
||||||
|
development: {
|
||||||
|
extractLicenses: false,
|
||||||
|
optimization: false,
|
||||||
|
sourceMap: true,
|
||||||
|
vendorChunk: true,
|
||||||
|
},
|
||||||
|
production: {
|
||||||
|
fileReplacements: [
|
||||||
|
{
|
||||||
|
replace: `test/src/environments/environment.ts`,
|
||||||
|
with: `test/src/environments/environment.prod.ts`,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
optimization: true,
|
||||||
|
outputHashing: 'all',
|
||||||
|
sourceMap: false,
|
||||||
|
namedChunks: false,
|
||||||
|
extractLicenses: true,
|
||||||
|
vendorChunk: false,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
serve: {
|
||||||
|
executor: '@nx/rspack:dev-server',
|
||||||
|
defaultConfiguration: 'development',
|
||||||
|
options: {
|
||||||
|
buildTarget: `test:build`,
|
||||||
|
hmr: true,
|
||||||
|
},
|
||||||
|
configurations: {
|
||||||
|
development: {
|
||||||
|
buildTarget: `test:build:development`,
|
||||||
|
},
|
||||||
|
production: {
|
||||||
|
buildTarget: `test:build:production`,
|
||||||
|
hmr: false,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
),
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
// TODO(colum): turn these on when rspack is moved into the main repo
|
||||||
|
xdescribe('remote generator', () => {
|
||||||
|
// TODO(@jaysoo): Turn this back to adding the plugin
|
||||||
|
let originalEnv: string;
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
originalEnv = process.env.NX_ADD_PLUGINS;
|
||||||
|
process.env.NX_ADD_PLUGINS = 'false';
|
||||||
|
});
|
||||||
|
|
||||||
|
afterEach(() => {
|
||||||
|
process.env.NX_ADD_PLUGINS = originalEnv;
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('bundler=rspack', () => {
|
||||||
|
it('should create the remote with the correct config files', async () => {
|
||||||
|
const tree = createTreeWithEmptyWorkspace();
|
||||||
|
await remote(tree, {
|
||||||
|
name: 'test',
|
||||||
|
devServerPort: 4201,
|
||||||
|
e2eTestRunner: 'cypress',
|
||||||
|
linter: Linter.EsLint,
|
||||||
|
skipFormat: true,
|
||||||
|
style: 'css',
|
||||||
|
unitTestRunner: 'jest',
|
||||||
|
projectNameAndRootFormat: 'as-provided',
|
||||||
|
typescriptConfiguration: false,
|
||||||
|
bundler: 'rspack',
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(tree.exists('test/rspack.config.js')).toBeTruthy();
|
||||||
|
expect(tree.exists('test/rspack.config.prod.js')).toBeTruthy();
|
||||||
|
expect(tree.exists('test/module-federation.config.js')).toBeTruthy();
|
||||||
|
|
||||||
|
expect(tree.read('test/rspack.config.js', 'utf-8')).toMatchSnapshot();
|
||||||
|
expect(
|
||||||
|
tree.read('test/rspack.config.prod.js', 'utf-8')
|
||||||
|
).toMatchSnapshot();
|
||||||
|
expect(
|
||||||
|
tree.read('test/module-federation.config.js', 'utf-8')
|
||||||
|
).toMatchSnapshot();
|
||||||
|
|
||||||
|
const tsconfigJson = readJson(tree, getRootTsConfigPathInTree(tree));
|
||||||
|
expect(tsconfigJson.compilerOptions.paths['test/Module']).toEqual([
|
||||||
|
'test/src/remote-entry.ts',
|
||||||
|
]);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should create the remote with the correct config files when --js=true', async () => {
|
||||||
|
const tree = createTreeWithEmptyWorkspace();
|
||||||
|
await remote(tree, {
|
||||||
|
name: 'test',
|
||||||
|
devServerPort: 4201,
|
||||||
|
e2eTestRunner: 'cypress',
|
||||||
|
linter: Linter.EsLint,
|
||||||
|
skipFormat: true,
|
||||||
|
style: 'css',
|
||||||
|
unitTestRunner: 'jest',
|
||||||
|
projectNameAndRootFormat: 'as-provided',
|
||||||
|
typescriptConfiguration: false,
|
||||||
|
js: true,
|
||||||
|
bundler: 'rspack',
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(tree.exists('test/rspack.config.js')).toBeTruthy();
|
||||||
|
expect(tree.exists('test/rspack.config.prod.js')).toBeTruthy();
|
||||||
|
expect(tree.exists('test/module-federation.config.js')).toBeTruthy();
|
||||||
|
|
||||||
|
expect(tree.read('test/rspack.config.js', 'utf-8')).toMatchSnapshot();
|
||||||
|
expect(
|
||||||
|
tree.read('test/rspack.config.prod.js', 'utf-8')
|
||||||
|
).toMatchSnapshot();
|
||||||
|
expect(
|
||||||
|
tree.read('test/module-federation.config.js', 'utf-8')
|
||||||
|
).toMatchSnapshot();
|
||||||
|
|
||||||
|
const tsconfigJson = readJson(tree, getRootTsConfigPathInTree(tree));
|
||||||
|
expect(tsconfigJson.compilerOptions.paths['test/Module']).toEqual([
|
||||||
|
'test/src/remote-entry.js',
|
||||||
|
]);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should create the remote with the correct config files when --typescriptConfiguration=true', async () => {
|
||||||
|
const tree = createTreeWithEmptyWorkspace();
|
||||||
|
await remote(tree, {
|
||||||
|
name: 'test',
|
||||||
|
devServerPort: 4201,
|
||||||
|
e2eTestRunner: 'cypress',
|
||||||
|
linter: Linter.EsLint,
|
||||||
|
skipFormat: false,
|
||||||
|
style: 'css',
|
||||||
|
unitTestRunner: 'jest',
|
||||||
|
projectNameAndRootFormat: 'as-provided',
|
||||||
|
typescriptConfiguration: true,
|
||||||
|
bundler: 'rspack',
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(tree.exists('test/rspack.config.ts')).toBeTruthy();
|
||||||
|
expect(tree.exists('test/rspack.config.prod.ts')).toBeTruthy();
|
||||||
|
expect(tree.exists('test/module-federation.config.ts')).toBeTruthy();
|
||||||
|
|
||||||
|
expect(tree.read('test/rspack.config.ts', 'utf-8')).toMatchSnapshot();
|
||||||
|
expect(
|
||||||
|
tree.read('test/rspack.config.prod.ts', 'utf-8')
|
||||||
|
).toMatchSnapshot();
|
||||||
|
expect(
|
||||||
|
tree.read('test/module-federation.config.ts', 'utf-8')
|
||||||
|
).toMatchSnapshot();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should install @nx/web for the file-server executor', async () => {
|
||||||
|
const tree = createTreeWithEmptyWorkspace();
|
||||||
|
await remote(tree, {
|
||||||
|
name: 'test',
|
||||||
|
devServerPort: 4201,
|
||||||
|
e2eTestRunner: 'cypress',
|
||||||
|
linter: Linter.EsLint,
|
||||||
|
skipFormat: true,
|
||||||
|
style: 'css',
|
||||||
|
unitTestRunner: 'jest',
|
||||||
|
projectNameAndRootFormat: 'as-provided',
|
||||||
|
bundler: 'rspack',
|
||||||
|
});
|
||||||
|
|
||||||
|
const packageJson = readJson(tree, 'package.json');
|
||||||
|
expect(packageJson.devDependencies['@nx/web']).toBeDefined();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should not set the remote as the default project', async () => {
|
||||||
|
const tree = createTreeWithEmptyWorkspace();
|
||||||
|
await remote(tree, {
|
||||||
|
name: 'test',
|
||||||
|
devServerPort: 4201,
|
||||||
|
e2eTestRunner: 'cypress',
|
||||||
|
linter: Linter.EsLint,
|
||||||
|
skipFormat: true,
|
||||||
|
style: 'css',
|
||||||
|
unitTestRunner: 'jest',
|
||||||
|
projectNameAndRootFormat: 'as-provided',
|
||||||
|
bundler: 'rspack',
|
||||||
|
});
|
||||||
|
|
||||||
|
const { defaultProject } = readNxJson(tree);
|
||||||
|
expect(defaultProject).toBeUndefined();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should generate a remote-specific server.ts file for --ssr', async () => {
|
||||||
|
const tree = createTreeWithEmptyWorkspace();
|
||||||
|
|
||||||
|
await remote(tree, {
|
||||||
|
name: 'test',
|
||||||
|
devServerPort: 4201,
|
||||||
|
e2eTestRunner: 'cypress',
|
||||||
|
linter: Linter.EsLint,
|
||||||
|
skipFormat: true,
|
||||||
|
style: 'css',
|
||||||
|
unitTestRunner: 'jest',
|
||||||
|
ssr: true,
|
||||||
|
projectNameAndRootFormat: 'as-provided',
|
||||||
|
bundler: 'rspack',
|
||||||
|
});
|
||||||
|
|
||||||
|
const mainFile = tree.read('test/server.ts', 'utf-8');
|
||||||
|
expect(mainFile).toContain(`join(process.cwd(), 'dist/test/browser')`);
|
||||||
|
expect(mainFile).toContain('nx.server.ready');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should generate correct remote with config files when using --ssr', async () => {
|
||||||
|
const tree = createTreeWithEmptyWorkspace();
|
||||||
|
|
||||||
|
await remote(tree, {
|
||||||
|
name: 'test',
|
||||||
|
devServerPort: 4201,
|
||||||
|
e2eTestRunner: 'cypress',
|
||||||
|
linter: Linter.EsLint,
|
||||||
|
skipFormat: true,
|
||||||
|
style: 'css',
|
||||||
|
unitTestRunner: 'jest',
|
||||||
|
ssr: true,
|
||||||
|
projectNameAndRootFormat: 'as-provided',
|
||||||
|
typescriptConfiguration: false,
|
||||||
|
bundler: 'rspack',
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(tree.exists('test/rspack.server.config.js')).toBeTruthy();
|
||||||
|
expect(
|
||||||
|
tree.exists('test/module-federation.server.config.js')
|
||||||
|
).toBeTruthy();
|
||||||
|
|
||||||
|
expect(
|
||||||
|
tree.read('test/rspack.server.config.js', 'utf-8')
|
||||||
|
).toMatchSnapshot();
|
||||||
|
expect(
|
||||||
|
tree.read('test/module-federation.server.config.js', 'utf-8')
|
||||||
|
).toMatchSnapshot();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should generate correct remote with config files when using --ssr and --typescriptConfiguration=true', async () => {
|
||||||
|
const tree = createTreeWithEmptyWorkspace();
|
||||||
|
|
||||||
|
await remote(tree, {
|
||||||
|
name: 'test',
|
||||||
|
devServerPort: 4201,
|
||||||
|
e2eTestRunner: 'cypress',
|
||||||
|
linter: Linter.EsLint,
|
||||||
|
skipFormat: false,
|
||||||
|
style: 'css',
|
||||||
|
unitTestRunner: 'jest',
|
||||||
|
ssr: true,
|
||||||
|
projectNameAndRootFormat: 'as-provided',
|
||||||
|
typescriptConfiguration: true,
|
||||||
|
bundler: 'rspack',
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(tree.exists('test/rspack.server.config.ts')).toBeTruthy();
|
||||||
|
expect(
|
||||||
|
tree.exists('test/module-federation.server.config.ts')
|
||||||
|
).toBeTruthy();
|
||||||
|
|
||||||
|
expect(
|
||||||
|
tree.read('test/rspack.server.config.ts', 'utf-8')
|
||||||
|
).toMatchSnapshot();
|
||||||
|
expect(
|
||||||
|
tree.read('test/module-federation.server.config.ts', 'utf-8')
|
||||||
|
).toMatchSnapshot();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should throw an error if invalid remotes names are provided and --dynamic is set to true', async () => {
|
||||||
|
const tree = createTreeWithEmptyWorkspace();
|
||||||
|
const name = 'invalid-dynamic-remote-name';
|
||||||
|
await expect(
|
||||||
|
remote(tree, {
|
||||||
|
name,
|
||||||
|
devServerPort: 4209,
|
||||||
|
dynamic: true,
|
||||||
|
e2eTestRunner: 'cypress',
|
||||||
|
linter: Linter.EsLint,
|
||||||
|
skipFormat: false,
|
||||||
|
style: 'css',
|
||||||
|
unitTestRunner: 'jest',
|
||||||
|
ssr: true,
|
||||||
|
projectNameAndRootFormat: 'as-provided',
|
||||||
|
typescriptConfiguration: true,
|
||||||
|
bundler: 'rspack',
|
||||||
|
})
|
||||||
|
).rejects.toThrowError(`Invalid remote name provided: ${name}.`);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
@ -1,315 +0,0 @@
|
|||||||
import 'nx/src/internal-testing-utils/mock-project-graph';
|
|
||||||
|
|
||||||
import { ProjectGraph, readJson, readNxJson } from '@nx/devkit';
|
|
||||||
import { createTreeWithEmptyWorkspace } from '@nx/devkit/testing';
|
|
||||||
import { Linter } from '@nx/eslint';
|
|
||||||
import remote from './remote';
|
|
||||||
import { getRootTsConfigPathInTree } from '@nx/js';
|
|
||||||
|
|
||||||
jest.mock('@nx/devkit', () => {
|
|
||||||
const original = jest.requireActual('@nx/devkit');
|
|
||||||
return {
|
|
||||||
...original,
|
|
||||||
readCachedProjectGraph: jest.fn().mockImplementation(
|
|
||||||
(): ProjectGraph => ({
|
|
||||||
dependencies: {},
|
|
||||||
nodes: {
|
|
||||||
test: {
|
|
||||||
name: 'test',
|
|
||||||
type: 'app',
|
|
||||||
data: {
|
|
||||||
root: 'test',
|
|
||||||
sourceRoot: 'test/src',
|
|
||||||
targets: {
|
|
||||||
build: {
|
|
||||||
executor: '@nx/webpack:webpack',
|
|
||||||
outputs: ['{options.outputPath}'],
|
|
||||||
defaultConfiguration: 'production',
|
|
||||||
options: {
|
|
||||||
compiler: 'babel',
|
|
||||||
outputPath: 'dist/test',
|
|
||||||
index: 'test/src/index.html',
|
|
||||||
baseHref: '/',
|
|
||||||
main: `test/src/main.tsx`,
|
|
||||||
tsConfig: 'test/tsconfig.app.json',
|
|
||||||
assets: ['test/src/favicon.ico', 'src/assets'],
|
|
||||||
styles: [`test/src/styles.css`],
|
|
||||||
scripts: [],
|
|
||||||
webpackConfig: 'test/webpack.config.js',
|
|
||||||
},
|
|
||||||
configurations: {
|
|
||||||
development: {
|
|
||||||
extractLicenses: false,
|
|
||||||
optimization: false,
|
|
||||||
sourceMap: true,
|
|
||||||
vendorChunk: true,
|
|
||||||
},
|
|
||||||
production: {
|
|
||||||
fileReplacements: [
|
|
||||||
{
|
|
||||||
replace: `test/src/environments/environment.ts`,
|
|
||||||
with: `test/src/environments/environment.prod.ts`,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
optimization: true,
|
|
||||||
outputHashing: 'all',
|
|
||||||
sourceMap: false,
|
|
||||||
namedChunks: false,
|
|
||||||
extractLicenses: true,
|
|
||||||
vendorChunk: false,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
serve: {
|
|
||||||
executor: '@nx/webpack:dev-server',
|
|
||||||
defaultConfiguration: 'development',
|
|
||||||
options: {
|
|
||||||
buildTarget: `test:build`,
|
|
||||||
hmr: true,
|
|
||||||
},
|
|
||||||
configurations: {
|
|
||||||
development: {
|
|
||||||
buildTarget: `test:build:development`,
|
|
||||||
},
|
|
||||||
production: {
|
|
||||||
buildTarget: `test:build:production`,
|
|
||||||
hmr: false,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
})
|
|
||||||
),
|
|
||||||
};
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('remote generator', () => {
|
|
||||||
// TODO(@jaysoo): Turn this back to adding the plugin
|
|
||||||
let originalEnv: string;
|
|
||||||
|
|
||||||
beforeEach(() => {
|
|
||||||
originalEnv = process.env.NX_ADD_PLUGINS;
|
|
||||||
process.env.NX_ADD_PLUGINS = 'false';
|
|
||||||
});
|
|
||||||
|
|
||||||
afterEach(() => {
|
|
||||||
process.env.NX_ADD_PLUGINS = originalEnv;
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should create the remote with the correct config files', async () => {
|
|
||||||
const tree = createTreeWithEmptyWorkspace();
|
|
||||||
await remote(tree, {
|
|
||||||
name: 'test',
|
|
||||||
devServerPort: 4201,
|
|
||||||
e2eTestRunner: 'cypress',
|
|
||||||
linter: Linter.EsLint,
|
|
||||||
skipFormat: true,
|
|
||||||
style: 'css',
|
|
||||||
unitTestRunner: 'jest',
|
|
||||||
projectNameAndRootFormat: 'as-provided',
|
|
||||||
typescriptConfiguration: false,
|
|
||||||
});
|
|
||||||
|
|
||||||
expect(tree.exists('test/webpack.config.js')).toBeTruthy();
|
|
||||||
expect(tree.exists('test/webpack.config.prod.js')).toBeTruthy();
|
|
||||||
expect(tree.exists('test/module-federation.config.js')).toBeTruthy();
|
|
||||||
|
|
||||||
expect(tree.read('test/webpack.config.js', 'utf-8')).toMatchSnapshot();
|
|
||||||
expect(tree.read('test/webpack.config.prod.js', 'utf-8')).toMatchSnapshot();
|
|
||||||
expect(
|
|
||||||
tree.read('test/module-federation.config.js', 'utf-8')
|
|
||||||
).toMatchSnapshot();
|
|
||||||
|
|
||||||
const tsconfigJson = readJson(tree, getRootTsConfigPathInTree(tree));
|
|
||||||
expect(tsconfigJson.compilerOptions.paths['test/Module']).toEqual([
|
|
||||||
'test/src/remote-entry.ts',
|
|
||||||
]);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should create the remote with the correct config files when --js=true', async () => {
|
|
||||||
const tree = createTreeWithEmptyWorkspace();
|
|
||||||
await remote(tree, {
|
|
||||||
name: 'test',
|
|
||||||
devServerPort: 4201,
|
|
||||||
e2eTestRunner: 'cypress',
|
|
||||||
linter: Linter.EsLint,
|
|
||||||
skipFormat: true,
|
|
||||||
style: 'css',
|
|
||||||
unitTestRunner: 'jest',
|
|
||||||
projectNameAndRootFormat: 'as-provided',
|
|
||||||
typescriptConfiguration: false,
|
|
||||||
js: true,
|
|
||||||
});
|
|
||||||
|
|
||||||
expect(tree.exists('test/webpack.config.js')).toBeTruthy();
|
|
||||||
expect(tree.exists('test/webpack.config.prod.js')).toBeTruthy();
|
|
||||||
expect(tree.exists('test/module-federation.config.js')).toBeTruthy();
|
|
||||||
|
|
||||||
expect(tree.read('test/webpack.config.js', 'utf-8')).toMatchSnapshot();
|
|
||||||
expect(tree.read('test/webpack.config.prod.js', 'utf-8')).toMatchSnapshot();
|
|
||||||
expect(
|
|
||||||
tree.read('test/module-federation.config.js', 'utf-8')
|
|
||||||
).toMatchSnapshot();
|
|
||||||
|
|
||||||
const tsconfigJson = readJson(tree, getRootTsConfigPathInTree(tree));
|
|
||||||
expect(tsconfigJson.compilerOptions.paths['test/Module']).toEqual([
|
|
||||||
'test/src/remote-entry.js',
|
|
||||||
]);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should create the remote with the correct config files when --typescriptConfiguration=true', async () => {
|
|
||||||
const tree = createTreeWithEmptyWorkspace();
|
|
||||||
await remote(tree, {
|
|
||||||
name: 'test',
|
|
||||||
devServerPort: 4201,
|
|
||||||
e2eTestRunner: 'cypress',
|
|
||||||
linter: Linter.EsLint,
|
|
||||||
skipFormat: false,
|
|
||||||
style: 'css',
|
|
||||||
unitTestRunner: 'jest',
|
|
||||||
projectNameAndRootFormat: 'as-provided',
|
|
||||||
typescriptConfiguration: true,
|
|
||||||
});
|
|
||||||
|
|
||||||
expect(tree.exists('test/webpack.config.ts')).toBeTruthy();
|
|
||||||
expect(tree.exists('test/webpack.config.prod.ts')).toBeTruthy();
|
|
||||||
expect(tree.exists('test/module-federation.config.ts')).toBeTruthy();
|
|
||||||
|
|
||||||
expect(tree.read('test/webpack.config.ts', 'utf-8')).toMatchSnapshot();
|
|
||||||
expect(tree.read('test/webpack.config.prod.ts', 'utf-8')).toMatchSnapshot();
|
|
||||||
expect(
|
|
||||||
tree.read('test/module-federation.config.ts', 'utf-8')
|
|
||||||
).toMatchSnapshot();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should install @nx/web for the file-server executor', async () => {
|
|
||||||
const tree = createTreeWithEmptyWorkspace();
|
|
||||||
await remote(tree, {
|
|
||||||
name: 'test',
|
|
||||||
devServerPort: 4201,
|
|
||||||
e2eTestRunner: 'cypress',
|
|
||||||
linter: Linter.EsLint,
|
|
||||||
skipFormat: true,
|
|
||||||
style: 'css',
|
|
||||||
unitTestRunner: 'jest',
|
|
||||||
projectNameAndRootFormat: 'as-provided',
|
|
||||||
});
|
|
||||||
|
|
||||||
const packageJson = readJson(tree, 'package.json');
|
|
||||||
expect(packageJson.devDependencies['@nx/web']).toBeDefined();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should not set the remote as the default project', async () => {
|
|
||||||
const tree = createTreeWithEmptyWorkspace();
|
|
||||||
await remote(tree, {
|
|
||||||
name: 'test',
|
|
||||||
devServerPort: 4201,
|
|
||||||
e2eTestRunner: 'cypress',
|
|
||||||
linter: Linter.EsLint,
|
|
||||||
skipFormat: true,
|
|
||||||
style: 'css',
|
|
||||||
unitTestRunner: 'jest',
|
|
||||||
projectNameAndRootFormat: 'as-provided',
|
|
||||||
});
|
|
||||||
|
|
||||||
const { defaultProject } = readNxJson(tree);
|
|
||||||
expect(defaultProject).toBeUndefined();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should generate a remote-specific server.ts file for --ssr', async () => {
|
|
||||||
const tree = createTreeWithEmptyWorkspace();
|
|
||||||
|
|
||||||
await remote(tree, {
|
|
||||||
name: 'test',
|
|
||||||
devServerPort: 4201,
|
|
||||||
e2eTestRunner: 'cypress',
|
|
||||||
linter: Linter.EsLint,
|
|
||||||
skipFormat: true,
|
|
||||||
style: 'css',
|
|
||||||
unitTestRunner: 'jest',
|
|
||||||
ssr: true,
|
|
||||||
projectNameAndRootFormat: 'as-provided',
|
|
||||||
});
|
|
||||||
|
|
||||||
const mainFile = tree.read('test/server.ts', 'utf-8');
|
|
||||||
expect(mainFile).toContain(`join(process.cwd(), 'dist/test/browser')`);
|
|
||||||
expect(mainFile).toContain('nx.server.ready');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should generate correct remote with config files when using --ssr', async () => {
|
|
||||||
const tree = createTreeWithEmptyWorkspace();
|
|
||||||
|
|
||||||
await remote(tree, {
|
|
||||||
name: 'test',
|
|
||||||
devServerPort: 4201,
|
|
||||||
e2eTestRunner: 'cypress',
|
|
||||||
linter: Linter.EsLint,
|
|
||||||
skipFormat: true,
|
|
||||||
style: 'css',
|
|
||||||
unitTestRunner: 'jest',
|
|
||||||
ssr: true,
|
|
||||||
projectNameAndRootFormat: 'as-provided',
|
|
||||||
typescriptConfiguration: false,
|
|
||||||
});
|
|
||||||
|
|
||||||
expect(tree.exists('test/webpack.server.config.js')).toBeTruthy();
|
|
||||||
expect(tree.exists('test/module-federation.server.config.js')).toBeTruthy();
|
|
||||||
|
|
||||||
expect(
|
|
||||||
tree.read('test/webpack.server.config.js', 'utf-8')
|
|
||||||
).toMatchSnapshot();
|
|
||||||
expect(
|
|
||||||
tree.read('test/module-federation.server.config.js', 'utf-8')
|
|
||||||
).toMatchSnapshot();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should generate correct remote with config files when using --ssr and --typescriptConfiguration=true', async () => {
|
|
||||||
const tree = createTreeWithEmptyWorkspace();
|
|
||||||
|
|
||||||
await remote(tree, {
|
|
||||||
name: 'test',
|
|
||||||
devServerPort: 4201,
|
|
||||||
e2eTestRunner: 'cypress',
|
|
||||||
linter: Linter.EsLint,
|
|
||||||
skipFormat: false,
|
|
||||||
style: 'css',
|
|
||||||
unitTestRunner: 'jest',
|
|
||||||
ssr: true,
|
|
||||||
projectNameAndRootFormat: 'as-provided',
|
|
||||||
typescriptConfiguration: true,
|
|
||||||
});
|
|
||||||
|
|
||||||
expect(tree.exists('test/webpack.server.config.ts')).toBeTruthy();
|
|
||||||
expect(tree.exists('test/module-federation.server.config.ts')).toBeTruthy();
|
|
||||||
|
|
||||||
expect(
|
|
||||||
tree.read('test/webpack.server.config.ts', 'utf-8')
|
|
||||||
).toMatchSnapshot();
|
|
||||||
expect(
|
|
||||||
tree.read('test/module-federation.server.config.ts', 'utf-8')
|
|
||||||
).toMatchSnapshot();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should throw an error if invalid remotes names are provided and --dynamic is set to true', async () => {
|
|
||||||
const tree = createTreeWithEmptyWorkspace();
|
|
||||||
const name = 'invalid-dynamic-remote-name';
|
|
||||||
await expect(
|
|
||||||
remote(tree, {
|
|
||||||
name,
|
|
||||||
devServerPort: 4209,
|
|
||||||
dynamic: true,
|
|
||||||
e2eTestRunner: 'cypress',
|
|
||||||
linter: Linter.EsLint,
|
|
||||||
skipFormat: false,
|
|
||||||
style: 'css',
|
|
||||||
unitTestRunner: 'jest',
|
|
||||||
ssr: true,
|
|
||||||
projectNameAndRootFormat: 'as-provided',
|
|
||||||
typescriptConfiguration: true,
|
|
||||||
})
|
|
||||||
).rejects.toThrowError(`Invalid remote name provided: ${name}.`);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
@ -39,14 +39,27 @@ export function addModuleFederationFiles(
|
|||||||
|
|
||||||
generateFiles(
|
generateFiles(
|
||||||
host,
|
host,
|
||||||
join(__dirname, `./files/${options.js ? 'common' : 'common-ts'}`),
|
join(
|
||||||
|
__dirname,
|
||||||
|
`./files/${
|
||||||
|
options.js
|
||||||
|
? options.bundler === 'rspack'
|
||||||
|
? 'rspack-common'
|
||||||
|
: 'common'
|
||||||
|
: 'common-ts'
|
||||||
|
}`
|
||||||
|
),
|
||||||
options.appProjectRoot,
|
options.appProjectRoot,
|
||||||
templateVariables
|
templateVariables
|
||||||
);
|
);
|
||||||
|
|
||||||
const pathToModuleFederationFiles = options.typescriptConfiguration
|
const pathToModuleFederationFiles = options.typescriptConfiguration
|
||||||
? 'module-federation-ts'
|
? `${
|
||||||
: 'module-federation';
|
options.bundler === 'rspack' ? 'rspack-' : 'webpack-'
|
||||||
|
}module-federation-ts`
|
||||||
|
: `${
|
||||||
|
options.bundler === 'rspack' ? 'rspack-' : 'webpack-'
|
||||||
|
}module-federation`;
|
||||||
|
|
||||||
generateFiles(
|
generateFiles(
|
||||||
host,
|
host,
|
||||||
@ -56,16 +69,18 @@ export function addModuleFederationFiles(
|
|||||||
);
|
);
|
||||||
|
|
||||||
if (options.typescriptConfiguration) {
|
if (options.typescriptConfiguration) {
|
||||||
const pathToWebpackConfig = joinPathFragments(
|
const pathToBundlerConfig = joinPathFragments(
|
||||||
options.appProjectRoot,
|
options.appProjectRoot,
|
||||||
'webpack.config.js'
|
options.bundler === 'rspack' ? 'rspack.config.js' : 'webpack.config.js'
|
||||||
);
|
);
|
||||||
const pathToWebpackProdConfig = joinPathFragments(
|
const pathToWebpackProdConfig = joinPathFragments(
|
||||||
options.appProjectRoot,
|
options.appProjectRoot,
|
||||||
'webpack.config.prod.js'
|
options.bundler === 'rspack'
|
||||||
|
? 'rspack.config.prod.js'
|
||||||
|
: 'webpack.config.prod.js'
|
||||||
);
|
);
|
||||||
if (host.exists(pathToWebpackConfig)) {
|
if (host.exists(pathToBundlerConfig)) {
|
||||||
host.delete(pathToWebpackConfig);
|
host.delete(pathToBundlerConfig);
|
||||||
}
|
}
|
||||||
if (host.exists(pathToWebpackProdConfig)) {
|
if (host.exists(pathToWebpackProdConfig)) {
|
||||||
host.delete(pathToWebpackProdConfig);
|
host.delete(pathToWebpackProdConfig);
|
||||||
@ -92,6 +107,7 @@ export async function remoteGeneratorInternal(host: Tree, schema: Schema) {
|
|||||||
dynamic: schema.dynamic ?? false,
|
dynamic: schema.dynamic ?? false,
|
||||||
// TODO(colum): remove when MF works with Crystal
|
// TODO(colum): remove when MF works with Crystal
|
||||||
addPlugin: false,
|
addPlugin: false,
|
||||||
|
bundler: schema.bundler ?? 'rspack',
|
||||||
};
|
};
|
||||||
|
|
||||||
if (options.dynamic) {
|
if (options.dynamic) {
|
||||||
@ -107,8 +123,6 @@ export async function remoteGeneratorInternal(host: Tree, schema: Schema) {
|
|||||||
|
|
||||||
const initAppTask = await applicationGenerator(host, {
|
const initAppTask = await applicationGenerator(host, {
|
||||||
...options,
|
...options,
|
||||||
// Only webpack works with module federation for now.
|
|
||||||
bundler: 'webpack',
|
|
||||||
skipFormat: true,
|
skipFormat: true,
|
||||||
});
|
});
|
||||||
tasks.push(initAppTask);
|
tasks.push(initAppTask);
|
||||||
@ -121,8 +135,20 @@ export async function remoteGeneratorInternal(host: Tree, schema: Schema) {
|
|||||||
// Renaming original entry file so we can use `import(./bootstrap)` in
|
// Renaming original entry file so we can use `import(./bootstrap)` in
|
||||||
// new entry file.
|
// new entry file.
|
||||||
host.rename(
|
host.rename(
|
||||||
join(options.appProjectRoot, maybeJs(options, 'src/main.tsx')),
|
join(
|
||||||
join(options.appProjectRoot, maybeJs(options, 'src/bootstrap.tsx'))
|
options.appProjectRoot,
|
||||||
|
maybeJs(
|
||||||
|
{ js: options.js, useJsx: options.bundler === 'rspack' },
|
||||||
|
'src/main.tsx'
|
||||||
|
)
|
||||||
|
),
|
||||||
|
join(
|
||||||
|
options.appProjectRoot,
|
||||||
|
maybeJs(
|
||||||
|
{ js: options.js, useJsx: options.bundler === 'rspack' },
|
||||||
|
'src/bootstrap.tsx'
|
||||||
|
)
|
||||||
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
addModuleFederationFiles(host, options);
|
addModuleFederationFiles(host, options);
|
||||||
@ -134,6 +160,7 @@ export async function remoteGeneratorInternal(host: Tree, schema: Schema) {
|
|||||||
project: options.projectName,
|
project: options.projectName,
|
||||||
serverPort: options.devServerPort,
|
serverPort: options.devServerPort,
|
||||||
skipFormat: true,
|
skipFormat: true,
|
||||||
|
bundler: options.bundler,
|
||||||
});
|
});
|
||||||
tasks.push(setupSsrTask);
|
tasks.push(setupSsrTask);
|
||||||
|
|
||||||
@ -145,10 +172,19 @@ export async function remoteGeneratorInternal(host: Tree, schema: Schema) {
|
|||||||
tasks.push(setupSsrForRemoteTask);
|
tasks.push(setupSsrForRemoteTask);
|
||||||
|
|
||||||
const projectConfig = readProjectConfiguration(host, options.projectName);
|
const projectConfig = readProjectConfiguration(host, options.projectName);
|
||||||
|
if (options.bundler === 'rspack') {
|
||||||
|
projectConfig.targets.server.executor = '@nx/rspack:rspack';
|
||||||
|
projectConfig.targets.server.options.rspackConfig = joinPathFragments(
|
||||||
|
projectConfig.root,
|
||||||
|
`rspack.server.config.${options.typescriptConfiguration ? 'ts' : 'js'}`
|
||||||
|
);
|
||||||
|
delete projectConfig.targets.server.options.webpackConfig;
|
||||||
|
} else {
|
||||||
projectConfig.targets.server.options.webpackConfig = joinPathFragments(
|
projectConfig.targets.server.options.webpackConfig = joinPathFragments(
|
||||||
projectConfig.root,
|
projectConfig.root,
|
||||||
`webpack.server.config.${options.typescriptConfiguration ? 'ts' : 'js'}`
|
`webpack.server.config.${options.typescriptConfiguration ? 'ts' : 'js'}`
|
||||||
);
|
);
|
||||||
|
}
|
||||||
updateProjectConfiguration(host, options.projectName, projectConfig);
|
updateProjectConfiguration(host, options.projectName, projectConfig);
|
||||||
}
|
}
|
||||||
if (!options.setParserOptionsProject) {
|
if (!options.setParserOptionsProject) {
|
||||||
|
|||||||
336
packages/react/src/generators/remote/remote.webpack.spec.ts
Normal file
336
packages/react/src/generators/remote/remote.webpack.spec.ts
Normal file
@ -0,0 +1,336 @@
|
|||||||
|
import 'nx/src/internal-testing-utils/mock-project-graph';
|
||||||
|
|
||||||
|
import { ProjectGraph, readJson, readNxJson } from '@nx/devkit';
|
||||||
|
import { createTreeWithEmptyWorkspace } from '@nx/devkit/testing';
|
||||||
|
import { Linter } from '@nx/eslint';
|
||||||
|
import remote from './remote';
|
||||||
|
import { getRootTsConfigPathInTree } from '@nx/js';
|
||||||
|
|
||||||
|
jest.mock('@nx/devkit', () => {
|
||||||
|
const original = jest.requireActual('@nx/devkit');
|
||||||
|
return {
|
||||||
|
...original,
|
||||||
|
readCachedProjectGraph: jest.fn().mockImplementation(
|
||||||
|
(): ProjectGraph => ({
|
||||||
|
dependencies: {},
|
||||||
|
nodes: {
|
||||||
|
test: {
|
||||||
|
name: 'test',
|
||||||
|
type: 'app',
|
||||||
|
data: {
|
||||||
|
root: 'test',
|
||||||
|
sourceRoot: 'test/src',
|
||||||
|
targets: {
|
||||||
|
build: {
|
||||||
|
executor: '@nx/webpack:webpack',
|
||||||
|
outputs: ['{options.outputPath}'],
|
||||||
|
defaultConfiguration: 'production',
|
||||||
|
options: {
|
||||||
|
compiler: 'babel',
|
||||||
|
outputPath: 'dist/test',
|
||||||
|
index: 'test/src/index.html',
|
||||||
|
baseHref: '/',
|
||||||
|
main: `test/src/main.tsx`,
|
||||||
|
tsConfig: 'test/tsconfig.app.json',
|
||||||
|
assets: ['test/src/favicon.ico', 'src/assets'],
|
||||||
|
styles: [`test/src/styles.css`],
|
||||||
|
scripts: [],
|
||||||
|
webpackConfig: 'test/webpack.config.js',
|
||||||
|
},
|
||||||
|
configurations: {
|
||||||
|
development: {
|
||||||
|
extractLicenses: false,
|
||||||
|
optimization: false,
|
||||||
|
sourceMap: true,
|
||||||
|
vendorChunk: true,
|
||||||
|
},
|
||||||
|
production: {
|
||||||
|
fileReplacements: [
|
||||||
|
{
|
||||||
|
replace: `test/src/environments/environment.ts`,
|
||||||
|
with: `test/src/environments/environment.prod.ts`,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
optimization: true,
|
||||||
|
outputHashing: 'all',
|
||||||
|
sourceMap: false,
|
||||||
|
namedChunks: false,
|
||||||
|
extractLicenses: true,
|
||||||
|
vendorChunk: false,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
serve: {
|
||||||
|
executor: '@nx/webpack:dev-server',
|
||||||
|
defaultConfiguration: 'development',
|
||||||
|
options: {
|
||||||
|
buildTarget: `test:build`,
|
||||||
|
hmr: true,
|
||||||
|
},
|
||||||
|
configurations: {
|
||||||
|
development: {
|
||||||
|
buildTarget: `test:build:development`,
|
||||||
|
},
|
||||||
|
production: {
|
||||||
|
buildTarget: `test:build:production`,
|
||||||
|
hmr: false,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
),
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('remote generator', () => {
|
||||||
|
// TODO(@jaysoo): Turn this back to adding the plugin
|
||||||
|
let originalEnv: string;
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
originalEnv = process.env.NX_ADD_PLUGINS;
|
||||||
|
process.env.NX_ADD_PLUGINS = 'false';
|
||||||
|
});
|
||||||
|
|
||||||
|
afterEach(() => {
|
||||||
|
process.env.NX_ADD_PLUGINS = originalEnv;
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('bundler=webpack', () => {
|
||||||
|
it('should create the remote with the correct config files', async () => {
|
||||||
|
const tree = createTreeWithEmptyWorkspace();
|
||||||
|
await remote(tree, {
|
||||||
|
name: 'test',
|
||||||
|
devServerPort: 4201,
|
||||||
|
e2eTestRunner: 'cypress',
|
||||||
|
linter: Linter.EsLint,
|
||||||
|
skipFormat: true,
|
||||||
|
style: 'css',
|
||||||
|
unitTestRunner: 'jest',
|
||||||
|
projectNameAndRootFormat: 'as-provided',
|
||||||
|
typescriptConfiguration: false,
|
||||||
|
bundler: 'webpack',
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(tree.exists('test/webpack.config.js')).toBeTruthy();
|
||||||
|
expect(tree.exists('test/webpack.config.prod.js')).toBeTruthy();
|
||||||
|
expect(tree.exists('test/module-federation.config.js')).toBeTruthy();
|
||||||
|
|
||||||
|
expect(tree.read('test/webpack.config.js', 'utf-8')).toMatchSnapshot();
|
||||||
|
expect(
|
||||||
|
tree.read('test/webpack.config.prod.js', 'utf-8')
|
||||||
|
).toMatchSnapshot();
|
||||||
|
expect(
|
||||||
|
tree.read('test/module-federation.config.js', 'utf-8')
|
||||||
|
).toMatchSnapshot();
|
||||||
|
|
||||||
|
const tsconfigJson = readJson(tree, getRootTsConfigPathInTree(tree));
|
||||||
|
expect(tsconfigJson.compilerOptions.paths['test/Module']).toEqual([
|
||||||
|
'test/src/remote-entry.ts',
|
||||||
|
]);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should create the remote with the correct config files when --js=true', async () => {
|
||||||
|
const tree = createTreeWithEmptyWorkspace();
|
||||||
|
await remote(tree, {
|
||||||
|
name: 'test',
|
||||||
|
devServerPort: 4201,
|
||||||
|
e2eTestRunner: 'cypress',
|
||||||
|
linter: Linter.EsLint,
|
||||||
|
skipFormat: true,
|
||||||
|
style: 'css',
|
||||||
|
unitTestRunner: 'jest',
|
||||||
|
projectNameAndRootFormat: 'as-provided',
|
||||||
|
typescriptConfiguration: false,
|
||||||
|
js: true,
|
||||||
|
bundler: 'webpack',
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(tree.exists('test/webpack.config.js')).toBeTruthy();
|
||||||
|
expect(tree.exists('test/webpack.config.prod.js')).toBeTruthy();
|
||||||
|
expect(tree.exists('test/module-federation.config.js')).toBeTruthy();
|
||||||
|
|
||||||
|
expect(tree.read('test/webpack.config.js', 'utf-8')).toMatchSnapshot();
|
||||||
|
expect(
|
||||||
|
tree.read('test/webpack.config.prod.js', 'utf-8')
|
||||||
|
).toMatchSnapshot();
|
||||||
|
expect(
|
||||||
|
tree.read('test/module-federation.config.js', 'utf-8')
|
||||||
|
).toMatchSnapshot();
|
||||||
|
|
||||||
|
const tsconfigJson = readJson(tree, getRootTsConfigPathInTree(tree));
|
||||||
|
expect(tsconfigJson.compilerOptions.paths['test/Module']).toEqual([
|
||||||
|
'test/src/remote-entry.js',
|
||||||
|
]);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should create the remote with the correct config files when --typescriptConfiguration=true', async () => {
|
||||||
|
const tree = createTreeWithEmptyWorkspace();
|
||||||
|
await remote(tree, {
|
||||||
|
name: 'test',
|
||||||
|
devServerPort: 4201,
|
||||||
|
e2eTestRunner: 'cypress',
|
||||||
|
linter: Linter.EsLint,
|
||||||
|
skipFormat: false,
|
||||||
|
style: 'css',
|
||||||
|
unitTestRunner: 'jest',
|
||||||
|
projectNameAndRootFormat: 'as-provided',
|
||||||
|
typescriptConfiguration: true,
|
||||||
|
bundler: 'webpack',
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(tree.exists('test/webpack.config.ts')).toBeTruthy();
|
||||||
|
expect(tree.exists('test/webpack.config.prod.ts')).toBeTruthy();
|
||||||
|
expect(tree.exists('test/module-federation.config.ts')).toBeTruthy();
|
||||||
|
|
||||||
|
expect(tree.read('test/webpack.config.ts', 'utf-8')).toMatchSnapshot();
|
||||||
|
expect(
|
||||||
|
tree.read('test/webpack.config.prod.ts', 'utf-8')
|
||||||
|
).toMatchSnapshot();
|
||||||
|
expect(
|
||||||
|
tree.read('test/module-federation.config.ts', 'utf-8')
|
||||||
|
).toMatchSnapshot();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should install @nx/web for the file-server executor', async () => {
|
||||||
|
const tree = createTreeWithEmptyWorkspace();
|
||||||
|
await remote(tree, {
|
||||||
|
name: 'test',
|
||||||
|
devServerPort: 4201,
|
||||||
|
e2eTestRunner: 'cypress',
|
||||||
|
linter: Linter.EsLint,
|
||||||
|
skipFormat: true,
|
||||||
|
style: 'css',
|
||||||
|
unitTestRunner: 'jest',
|
||||||
|
projectNameAndRootFormat: 'as-provided',
|
||||||
|
bundler: 'webpack',
|
||||||
|
});
|
||||||
|
|
||||||
|
const packageJson = readJson(tree, 'package.json');
|
||||||
|
expect(packageJson.devDependencies['@nx/web']).toBeDefined();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should not set the remote as the default project', async () => {
|
||||||
|
const tree = createTreeWithEmptyWorkspace();
|
||||||
|
await remote(tree, {
|
||||||
|
name: 'test',
|
||||||
|
devServerPort: 4201,
|
||||||
|
e2eTestRunner: 'cypress',
|
||||||
|
linter: Linter.EsLint,
|
||||||
|
skipFormat: true,
|
||||||
|
style: 'css',
|
||||||
|
unitTestRunner: 'jest',
|
||||||
|
projectNameAndRootFormat: 'as-provided',
|
||||||
|
bundler: 'webpack',
|
||||||
|
});
|
||||||
|
|
||||||
|
const { defaultProject } = readNxJson(tree);
|
||||||
|
expect(defaultProject).toBeUndefined();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should generate a remote-specific server.ts file for --ssr', async () => {
|
||||||
|
const tree = createTreeWithEmptyWorkspace();
|
||||||
|
|
||||||
|
await remote(tree, {
|
||||||
|
name: 'test',
|
||||||
|
devServerPort: 4201,
|
||||||
|
e2eTestRunner: 'cypress',
|
||||||
|
linter: Linter.EsLint,
|
||||||
|
skipFormat: true,
|
||||||
|
style: 'css',
|
||||||
|
unitTestRunner: 'jest',
|
||||||
|
ssr: true,
|
||||||
|
projectNameAndRootFormat: 'as-provided',
|
||||||
|
bundler: 'webpack',
|
||||||
|
});
|
||||||
|
|
||||||
|
const mainFile = tree.read('test/server.ts', 'utf-8');
|
||||||
|
expect(mainFile).toContain(`join(process.cwd(), 'dist/test/browser')`);
|
||||||
|
expect(mainFile).toContain('nx.server.ready');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should generate correct remote with config files when using --ssr', async () => {
|
||||||
|
const tree = createTreeWithEmptyWorkspace();
|
||||||
|
|
||||||
|
await remote(tree, {
|
||||||
|
name: 'test',
|
||||||
|
devServerPort: 4201,
|
||||||
|
e2eTestRunner: 'cypress',
|
||||||
|
linter: Linter.EsLint,
|
||||||
|
skipFormat: true,
|
||||||
|
style: 'css',
|
||||||
|
unitTestRunner: 'jest',
|
||||||
|
ssr: true,
|
||||||
|
projectNameAndRootFormat: 'as-provided',
|
||||||
|
typescriptConfiguration: false,
|
||||||
|
bundler: 'webpack',
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(tree.exists('test/webpack.server.config.js')).toBeTruthy();
|
||||||
|
expect(
|
||||||
|
tree.exists('test/module-federation.server.config.js')
|
||||||
|
).toBeTruthy();
|
||||||
|
|
||||||
|
expect(
|
||||||
|
tree.read('test/webpack.server.config.js', 'utf-8')
|
||||||
|
).toMatchSnapshot();
|
||||||
|
expect(
|
||||||
|
tree.read('test/module-federation.server.config.js', 'utf-8')
|
||||||
|
).toMatchSnapshot();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should generate correct remote with config files when using --ssr and --typescriptConfiguration=true', async () => {
|
||||||
|
const tree = createTreeWithEmptyWorkspace();
|
||||||
|
|
||||||
|
await remote(tree, {
|
||||||
|
name: 'test',
|
||||||
|
devServerPort: 4201,
|
||||||
|
e2eTestRunner: 'cypress',
|
||||||
|
linter: Linter.EsLint,
|
||||||
|
skipFormat: false,
|
||||||
|
style: 'css',
|
||||||
|
unitTestRunner: 'jest',
|
||||||
|
ssr: true,
|
||||||
|
projectNameAndRootFormat: 'as-provided',
|
||||||
|
typescriptConfiguration: true,
|
||||||
|
bundler: 'webpack',
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(tree.exists('test/webpack.server.config.ts')).toBeTruthy();
|
||||||
|
expect(
|
||||||
|
tree.exists('test/module-federation.server.config.ts')
|
||||||
|
).toBeTruthy();
|
||||||
|
|
||||||
|
expect(
|
||||||
|
tree.read('test/webpack.server.config.ts', 'utf-8')
|
||||||
|
).toMatchSnapshot();
|
||||||
|
expect(
|
||||||
|
tree.read('test/module-federation.server.config.ts', 'utf-8')
|
||||||
|
).toMatchSnapshot();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should throw an error if invalid remotes names are provided and --dynamic is set to true', async () => {
|
||||||
|
const tree = createTreeWithEmptyWorkspace();
|
||||||
|
const name = 'invalid-dynamic-remote-name';
|
||||||
|
await expect(
|
||||||
|
remote(tree, {
|
||||||
|
name,
|
||||||
|
devServerPort: 4209,
|
||||||
|
dynamic: true,
|
||||||
|
e2eTestRunner: 'cypress',
|
||||||
|
linter: Linter.EsLint,
|
||||||
|
skipFormat: false,
|
||||||
|
style: 'css',
|
||||||
|
unitTestRunner: 'jest',
|
||||||
|
ssr: true,
|
||||||
|
projectNameAndRootFormat: 'as-provided',
|
||||||
|
typescriptConfiguration: true,
|
||||||
|
bundler: 'webpack',
|
||||||
|
})
|
||||||
|
).rejects.toThrowError(`Invalid remote name provided: ${name}.`);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
@ -28,6 +28,7 @@ export interface Schema {
|
|||||||
unitTestRunner: 'jest' | 'vitest' | 'none';
|
unitTestRunner: 'jest' | 'vitest' | 'none';
|
||||||
typescriptConfiguration?: boolean;
|
typescriptConfiguration?: boolean;
|
||||||
dynamic?: boolean;
|
dynamic?: boolean;
|
||||||
|
bundler?: 'rspack' | 'webpack';
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface NormalizedSchema extends ApplicationNormalizedSchema {
|
export interface NormalizedSchema extends ApplicationNormalizedSchema {
|
||||||
|
|||||||
@ -182,6 +182,14 @@
|
|||||||
"type": "boolean",
|
"type": "boolean",
|
||||||
"default": false,
|
"default": false,
|
||||||
"x-priority": "internal"
|
"x-priority": "internal"
|
||||||
|
},
|
||||||
|
"bundler": {
|
||||||
|
"description": "The bundler to use.",
|
||||||
|
"type": "string",
|
||||||
|
"enum": ["rspack", "webpack"],
|
||||||
|
"x-prompt": "Which bundler do you want to use to build the application?",
|
||||||
|
"default": "rspack",
|
||||||
|
"x-priority": "important"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"required": ["name"],
|
"required": ["name"],
|
||||||
|
|||||||
@ -4,4 +4,5 @@ export interface Schema {
|
|||||||
serverPort?: number;
|
serverPort?: number;
|
||||||
skipFormat?: boolean;
|
skipFormat?: boolean;
|
||||||
extraInclude?: string[];
|
extraInclude?: string[];
|
||||||
|
bundler?: 'rspack' | 'webpack';
|
||||||
}
|
}
|
||||||
|
|||||||
@ -41,6 +41,12 @@
|
|||||||
"hidden": true,
|
"hidden": true,
|
||||||
"description": "Extra include entries in tsconfig.",
|
"description": "Extra include entries in tsconfig.",
|
||||||
"default": []
|
"default": []
|
||||||
|
},
|
||||||
|
"bundler": {
|
||||||
|
"description": "The bundler to use.",
|
||||||
|
"type": "string",
|
||||||
|
"enum": ["rspack", "webpack"],
|
||||||
|
"default": "webpack"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"required": ["project"],
|
"required": ["project"],
|
||||||
|
|||||||
@ -128,7 +128,10 @@ export async function setupSsrGenerator(tree: Tree, options: Schema) {
|
|||||||
...projectConfig.targets,
|
...projectConfig.targets,
|
||||||
server: {
|
server: {
|
||||||
dependsOn: ['build'],
|
dependsOn: ['build'],
|
||||||
executor: '@nx/webpack:webpack',
|
executor:
|
||||||
|
options.bundler === 'rspack'
|
||||||
|
? '@nx/rspack:rspack'
|
||||||
|
: '@nx/webpack:webpack',
|
||||||
outputs: ['{options.outputPath}'],
|
outputs: ['{options.outputPath}'],
|
||||||
defaultConfiguration: 'production',
|
defaultConfiguration: 'production',
|
||||||
options: {
|
options: {
|
||||||
@ -140,7 +143,14 @@ export async function setupSsrGenerator(tree: Tree, options: Schema) {
|
|||||||
compiler: 'babel',
|
compiler: 'babel',
|
||||||
externalDependencies: 'all',
|
externalDependencies: 'all',
|
||||||
outputHashing: 'none',
|
outputHashing: 'none',
|
||||||
webpackConfig: joinPathFragments(projectRoot, 'webpack.config.js'),
|
...(options.bundler === 'rspack'
|
||||||
|
? { rspackConfig: joinPathFragments(projectRoot, 'rspack.config.js') }
|
||||||
|
: {
|
||||||
|
webpackConfig: joinPathFragments(
|
||||||
|
projectRoot,
|
||||||
|
'webpack.config.js'
|
||||||
|
),
|
||||||
|
}),
|
||||||
},
|
},
|
||||||
configurations: {
|
configurations: {
|
||||||
development: {
|
development: {
|
||||||
@ -176,7 +186,10 @@ export async function setupSsrGenerator(tree: Tree, options: Schema) {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
serve: {
|
serve: {
|
||||||
executor: '@nx/webpack:ssr-dev-server',
|
executor:
|
||||||
|
options.bundler === 'rspack'
|
||||||
|
? '@nx/rspack:ssr-dev-server'
|
||||||
|
: '@nx/webpack:ssr-dev-server',
|
||||||
defaultConfiguration: 'development',
|
defaultConfiguration: 'development',
|
||||||
options: {
|
options: {
|
||||||
browserTarget: `${options.project}:build:development`,
|
browserTarget: `${options.project}:build:development`,
|
||||||
|
|||||||
@ -20,6 +20,7 @@ describe('update-19-6-0 update-ssr-server-port migration', () => {
|
|||||||
projectNameAndRootFormat: 'as-provided',
|
projectNameAndRootFormat: 'as-provided',
|
||||||
style: 'css',
|
style: 'css',
|
||||||
remotes: ['product'],
|
remotes: ['product'],
|
||||||
|
bundler: 'webpack',
|
||||||
});
|
});
|
||||||
const remotePort = readProjectConfiguration(tree, 'product').targets.serve
|
const remotePort = readProjectConfiguration(tree, 'product').targets.serve
|
||||||
.options.port;
|
.options.port;
|
||||||
@ -32,10 +33,7 @@ describe('update-19-6-0 update-ssr-server-port migration', () => {
|
|||||||
'product/server.ts',
|
'product/server.ts',
|
||||||
tree
|
tree
|
||||||
.read('product/server.ts', 'utf-8')
|
.read('product/server.ts', 'utf-8')
|
||||||
.replace(
|
.replace('const port = 4201;', `const port = process.env.PORT || 4200;`)
|
||||||
'const port = 4201;',
|
|
||||||
`const port = process.env['PORT'] || 4200;`
|
|
||||||
)
|
|
||||||
);
|
);
|
||||||
|
|
||||||
updateSsrServerPort(tree);
|
updateSsrServerPort(tree);
|
||||||
@ -137,6 +135,7 @@ describe('update-19-6-0 update-ssr-server-port migration', () => {
|
|||||||
linter: Linter.EsLint,
|
linter: Linter.EsLint,
|
||||||
projectNameAndRootFormat: 'as-provided',
|
projectNameAndRootFormat: 'as-provided',
|
||||||
style: 'css',
|
style: 'css',
|
||||||
|
bundler: 'webpack',
|
||||||
});
|
});
|
||||||
|
|
||||||
const hostPort = readProjectConfiguration(tree, 'host').targets.serve
|
const hostPort = readProjectConfiguration(tree, 'host').targets.serve
|
||||||
@ -188,6 +187,7 @@ describe('update-19-6-0 update-ssr-server-port migration', () => {
|
|||||||
linter: Linter.EsLint,
|
linter: Linter.EsLint,
|
||||||
projectNameAndRootFormat: 'as-provided',
|
projectNameAndRootFormat: 'as-provided',
|
||||||
style: 'css',
|
style: 'css',
|
||||||
|
bundler: 'webpack',
|
||||||
});
|
});
|
||||||
|
|
||||||
tree.write('shell-not-ssr/server.ts', 'const port = 9999;');
|
tree.write('shell-not-ssr/server.ts', 'const port = 9999;');
|
||||||
|
|||||||
@ -18,10 +18,32 @@ export function updateModuleFederationProject(
|
|||||||
devServerPort?: number;
|
devServerPort?: number;
|
||||||
typescriptConfiguration?: boolean;
|
typescriptConfiguration?: boolean;
|
||||||
dynamic?: boolean;
|
dynamic?: boolean;
|
||||||
|
bundler?: 'rspack' | 'webpack';
|
||||||
}
|
}
|
||||||
): GeneratorCallback {
|
): GeneratorCallback {
|
||||||
const projectConfig = readProjectConfiguration(host, options.projectName);
|
const projectConfig = readProjectConfiguration(host, options.projectName);
|
||||||
|
|
||||||
|
if (options.bundler === 'rspack') {
|
||||||
|
projectConfig.targets.build.executor = '@nx/rspack:rspack';
|
||||||
|
projectConfig.targets.build.options = {
|
||||||
|
...projectConfig.targets.build.options,
|
||||||
|
main: maybeJs(
|
||||||
|
{ js: options.js, useJsx: true },
|
||||||
|
`${options.appProjectRoot}/src/main.ts`
|
||||||
|
),
|
||||||
|
rspackConfig: `${options.appProjectRoot}/rspack.config.${
|
||||||
|
options.typescriptConfiguration && !options.js ? 'ts' : 'js'
|
||||||
|
}`,
|
||||||
|
target: 'web',
|
||||||
|
};
|
||||||
|
|
||||||
|
projectConfig.targets.build.configurations.production = {
|
||||||
|
...projectConfig.targets.build.configurations.production,
|
||||||
|
rspackConfig: `${options.appProjectRoot}/rspack.config.prod.${
|
||||||
|
options.typescriptConfiguration && !options.js ? 'ts' : 'js'
|
||||||
|
}`,
|
||||||
|
};
|
||||||
|
} else {
|
||||||
projectConfig.targets.build.options = {
|
projectConfig.targets.build.options = {
|
||||||
...projectConfig.targets.build.options,
|
...projectConfig.targets.build.options,
|
||||||
main: maybeJs(options, `${options.appProjectRoot}/src/main.ts`),
|
main: maybeJs(options, `${options.appProjectRoot}/src/main.ts`),
|
||||||
@ -36,9 +58,24 @@ export function updateModuleFederationProject(
|
|||||||
options.typescriptConfiguration && !options.js ? 'ts' : 'js'
|
options.typescriptConfiguration && !options.js ? 'ts' : 'js'
|
||||||
}`,
|
}`,
|
||||||
};
|
};
|
||||||
|
}
|
||||||
|
|
||||||
// If host should be configured to use dynamic federation
|
// If host should be configured to use dynamic federation
|
||||||
if (options.dynamic) {
|
if (options.dynamic) {
|
||||||
|
if (options.bundler === 'rspack') {
|
||||||
|
const pathToProdRspackConfig = joinPathFragments(
|
||||||
|
projectConfig.root,
|
||||||
|
`rspack.prod.config.${
|
||||||
|
options.typescriptConfiguration && !options.js ? 'ts' : 'js'
|
||||||
|
}`
|
||||||
|
);
|
||||||
|
if (host.exists(pathToProdRspackConfig)) {
|
||||||
|
host.delete(pathToProdRspackConfig);
|
||||||
|
}
|
||||||
|
|
||||||
|
delete projectConfig.targets.build.configurations.production
|
||||||
|
?.rspackConfig;
|
||||||
|
} else {
|
||||||
const pathToProdWebpackConfig = joinPathFragments(
|
const pathToProdWebpackConfig = joinPathFragments(
|
||||||
projectConfig.root,
|
projectConfig.root,
|
||||||
`webpack.prod.config.${
|
`webpack.prod.config.${
|
||||||
@ -49,11 +86,18 @@ export function updateModuleFederationProject(
|
|||||||
host.delete(pathToProdWebpackConfig);
|
host.delete(pathToProdWebpackConfig);
|
||||||
}
|
}
|
||||||
|
|
||||||
delete projectConfig.targets.build.configurations.production?.webpackConfig;
|
delete projectConfig.targets.build.configurations.production
|
||||||
|
?.webpackConfig;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (options.bundler === 'rspack') {
|
||||||
|
projectConfig.targets.serve.executor =
|
||||||
|
'@nx/rspack:module-federation-dev-server';
|
||||||
|
} else {
|
||||||
projectConfig.targets.serve.executor =
|
projectConfig.targets.serve.executor =
|
||||||
'@nx/react:module-federation-dev-server';
|
'@nx/react:module-federation-dev-server';
|
||||||
|
}
|
||||||
projectConfig.targets.serve.options.port = options.devServerPort;
|
projectConfig.targets.serve.options.port = options.devServerPort;
|
||||||
|
|
||||||
// `serve-static` for remotes that don't need to be in development mode
|
// `serve-static` for remotes that don't need to be in development mode
|
||||||
|
|||||||
@ -1,5 +1,8 @@
|
|||||||
export function maybeJs(options: { js?: boolean }, path: string): string {
|
export function maybeJs(
|
||||||
|
options: { js?: boolean; useJsx?: boolean },
|
||||||
|
path: string
|
||||||
|
): string {
|
||||||
return options.js && (path.endsWith('.ts') || path.endsWith('.tsx'))
|
return options.js && (path.endsWith('.ts') || path.endsWith('.tsx'))
|
||||||
? path.replace(/\.tsx?$/, '.js')
|
? path.replace(/\.tsx?$/, options.useJsx ? '.jsx' : '.js')
|
||||||
: path;
|
: path;
|
||||||
}
|
}
|
||||||
|
|||||||
26
pnpm-lock.yaml
generated
26
pnpm-lock.yaml
generated
@ -24955,7 +24955,7 @@ snapshots:
|
|||||||
|
|
||||||
'@npmcli/fs@3.1.0':
|
'@npmcli/fs@3.1.0':
|
||||||
dependencies:
|
dependencies:
|
||||||
semver: 7.6.2
|
semver: 7.6.3
|
||||||
|
|
||||||
'@npmcli/git@4.1.0':
|
'@npmcli/git@4.1.0':
|
||||||
dependencies:
|
dependencies:
|
||||||
@ -24965,7 +24965,7 @@ snapshots:
|
|||||||
proc-log: 3.0.0
|
proc-log: 3.0.0
|
||||||
promise-inflight: 1.0.1
|
promise-inflight: 1.0.1
|
||||||
promise-retry: 2.0.1
|
promise-retry: 2.0.1
|
||||||
semver: 7.6.2
|
semver: 7.6.3
|
||||||
which: 3.0.1
|
which: 3.0.1
|
||||||
transitivePeerDependencies:
|
transitivePeerDependencies:
|
||||||
- bluebird
|
- bluebird
|
||||||
@ -24978,7 +24978,7 @@ snapshots:
|
|||||||
proc-log: 3.0.0
|
proc-log: 3.0.0
|
||||||
promise-inflight: 1.0.1
|
promise-inflight: 1.0.1
|
||||||
promise-retry: 2.0.1
|
promise-retry: 2.0.1
|
||||||
semver: 7.6.2
|
semver: 7.6.3
|
||||||
which: 4.0.0
|
which: 4.0.0
|
||||||
transitivePeerDependencies:
|
transitivePeerDependencies:
|
||||||
- bluebird
|
- bluebird
|
||||||
@ -25424,7 +25424,7 @@ snapshots:
|
|||||||
pkg-types: 1.0.3
|
pkg-types: 1.0.3
|
||||||
rc9: 2.1.1
|
rc9: 2.1.1
|
||||||
scule: 1.2.0
|
scule: 1.2.0
|
||||||
semver: 7.6.2
|
semver: 7.6.3
|
||||||
simple-git: 3.22.0
|
simple-git: 3.22.0
|
||||||
sirv: 2.0.4
|
sirv: 2.0.4
|
||||||
unimport: 3.7.1(rollup@4.14.3)
|
unimport: 3.7.1(rollup@4.14.3)
|
||||||
@ -28587,7 +28587,7 @@ snapshots:
|
|||||||
globby: 11.1.0
|
globby: 11.1.0
|
||||||
is-glob: 4.0.3
|
is-glob: 4.0.3
|
||||||
minimatch: 9.0.3
|
minimatch: 9.0.3
|
||||||
semver: 7.6.2
|
semver: 7.6.3
|
||||||
ts-api-utils: 1.3.0(typescript@5.5.3)
|
ts-api-utils: 1.3.0(typescript@5.5.3)
|
||||||
optionalDependencies:
|
optionalDependencies:
|
||||||
typescript: 5.5.3
|
typescript: 5.5.3
|
||||||
@ -30121,7 +30121,7 @@ snapshots:
|
|||||||
bin-version-check@5.0.0:
|
bin-version-check@5.0.0:
|
||||||
dependencies:
|
dependencies:
|
||||||
bin-version: 6.0.0
|
bin-version: 6.0.0
|
||||||
semver: 7.6.2
|
semver: 7.6.3
|
||||||
semver-truncate: 2.0.0
|
semver-truncate: 2.0.0
|
||||||
|
|
||||||
bin-version@6.0.0:
|
bin-version@6.0.0:
|
||||||
@ -33087,7 +33087,7 @@ snapshots:
|
|||||||
minimatch: 3.1.2
|
minimatch: 3.1.2
|
||||||
node-abort-controller: 3.1.1
|
node-abort-controller: 3.1.1
|
||||||
schema-utils: 3.2.0
|
schema-utils: 3.2.0
|
||||||
semver: 7.6.2
|
semver: 7.6.3
|
||||||
tapable: 2.2.1
|
tapable: 2.2.1
|
||||||
typescript: 5.5.3
|
typescript: 5.5.3
|
||||||
webpack: 5.88.0(@swc/core@1.5.7(@swc/helpers@0.5.11))(esbuild@0.19.5)(webpack-cli@5.1.4)
|
webpack: 5.88.0(@swc/core@1.5.7(@swc/helpers@0.5.11))(esbuild@0.19.5)(webpack-cli@5.1.4)
|
||||||
@ -34897,7 +34897,7 @@ snapshots:
|
|||||||
jest-util: 29.7.0
|
jest-util: 29.7.0
|
||||||
natural-compare: 1.4.0
|
natural-compare: 1.4.0
|
||||||
pretty-format: 29.7.0
|
pretty-format: 29.7.0
|
||||||
semver: 7.6.2
|
semver: 7.6.3
|
||||||
transitivePeerDependencies:
|
transitivePeerDependencies:
|
||||||
- supports-color
|
- supports-color
|
||||||
|
|
||||||
@ -35149,7 +35149,7 @@ snapshots:
|
|||||||
lodash.isstring: 4.0.1
|
lodash.isstring: 4.0.1
|
||||||
lodash.once: 4.1.1
|
lodash.once: 4.1.1
|
||||||
ms: 2.1.3
|
ms: 2.1.3
|
||||||
semver: 7.6.2
|
semver: 7.6.3
|
||||||
|
|
||||||
jsprim@2.0.2:
|
jsprim@2.0.2:
|
||||||
dependencies:
|
dependencies:
|
||||||
@ -36695,7 +36695,7 @@ snapshots:
|
|||||||
rollup: 4.18.0
|
rollup: 4.18.0
|
||||||
rollup-plugin-visualizer: 5.12.0(rollup@4.18.0)
|
rollup-plugin-visualizer: 5.12.0(rollup@4.18.0)
|
||||||
scule: 1.2.0
|
scule: 1.2.0
|
||||||
semver: 7.6.2
|
semver: 7.6.3
|
||||||
serve-placeholder: 2.0.1
|
serve-placeholder: 2.0.1
|
||||||
serve-static: 1.15.0
|
serve-static: 1.15.0
|
||||||
std-env: 3.7.0
|
std-env: 3.7.0
|
||||||
@ -36848,14 +36848,14 @@ snapshots:
|
|||||||
dependencies:
|
dependencies:
|
||||||
hosted-git-info: 4.1.0
|
hosted-git-info: 4.1.0
|
||||||
is-core-module: 2.13.1
|
is-core-module: 2.13.1
|
||||||
semver: 7.6.2
|
semver: 7.6.3
|
||||||
validate-npm-package-license: 3.0.4
|
validate-npm-package-license: 3.0.4
|
||||||
|
|
||||||
normalize-package-data@5.0.0:
|
normalize-package-data@5.0.0:
|
||||||
dependencies:
|
dependencies:
|
||||||
hosted-git-info: 6.1.1
|
hosted-git-info: 6.1.1
|
||||||
is-core-module: 2.13.1
|
is-core-module: 2.13.1
|
||||||
semver: 7.6.2
|
semver: 7.6.3
|
||||||
validate-npm-package-license: 3.0.4
|
validate-npm-package-license: 3.0.4
|
||||||
|
|
||||||
normalize-package-data@6.0.0:
|
normalize-package-data@6.0.0:
|
||||||
@ -36877,7 +36877,7 @@ snapshots:
|
|||||||
|
|
||||||
npm-install-checks@6.0.0:
|
npm-install-checks@6.0.0:
|
||||||
dependencies:
|
dependencies:
|
||||||
semver: 7.6.2
|
semver: 7.6.3
|
||||||
|
|
||||||
npm-normalize-package-bin@1.0.1: {}
|
npm-normalize-package-bin@1.0.1: {}
|
||||||
|
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user