feat(react): switch default to typescript configuration for module federation (#19031)
This commit is contained in:
parent
b2a9d4d79a
commit
11fcb8f2d4
@ -160,6 +160,11 @@
|
|||||||
"description": "Generate a React app with a minimal setup. No nx starter template.",
|
"description": "Generate a React app with a minimal setup. No nx starter template.",
|
||||||
"type": "boolean",
|
"type": "boolean",
|
||||||
"default": false
|
"default": false
|
||||||
|
},
|
||||||
|
"typescriptConfiguration": {
|
||||||
|
"type": "boolean",
|
||||||
|
"description": "Whether the module federation configuration and webpack configuration files should use TS.",
|
||||||
|
"default": true
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"required": ["name"],
|
"required": ["name"],
|
||||||
|
|||||||
@ -158,6 +158,11 @@
|
|||||||
"description": "Whether to configure SSR for the host application",
|
"description": "Whether to configure SSR for the host application",
|
||||||
"type": "boolean",
|
"type": "boolean",
|
||||||
"default": false
|
"default": false
|
||||||
|
},
|
||||||
|
"typescriptConfiguration": {
|
||||||
|
"type": "boolean",
|
||||||
|
"description": "Whether the module federation configuration and webpack configuration files should use TS.",
|
||||||
|
"default": true
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"required": ["name"],
|
"required": ["name"],
|
||||||
|
|||||||
@ -39,10 +39,10 @@ describe('React Module Federation', () => {
|
|||||||
`generate @nx/react:remote ${remote3} --style=css --host=${shell} --no-interactive`
|
`generate @nx/react:remote ${remote3} --style=css --host=${shell} --no-interactive`
|
||||||
);
|
);
|
||||||
|
|
||||||
checkFilesExist(`apps/${shell}/module-federation.config.js`);
|
checkFilesExist(`apps/${shell}/module-federation.config.ts`);
|
||||||
checkFilesExist(`apps/${remote1}/module-federation.config.js`);
|
checkFilesExist(`apps/${remote1}/module-federation.config.ts`);
|
||||||
checkFilesExist(`apps/${remote2}/module-federation.config.js`);
|
checkFilesExist(`apps/${remote2}/module-federation.config.ts`);
|
||||||
checkFilesExist(`apps/${remote3}/module-federation.config.js`);
|
checkFilesExist(`apps/${remote3}/module-federation.config.ts`);
|
||||||
|
|
||||||
await expect(runCLIAsync(`test ${shell}`)).resolves.toMatchObject({
|
await expect(runCLIAsync(`test ${shell}`)).resolves.toMatchObject({
|
||||||
combinedOutput: expect.stringContaining('Test Suites: 1 passed, 1 total'),
|
combinedOutput: expect.stringContaining('Test Suites: 1 passed, 1 total'),
|
||||||
@ -54,7 +54,7 @@ describe('React Module Federation', () => {
|
|||||||
expect(readPort(remote3)).toEqual(4203);
|
expect(readPort(remote3)).toEqual(4203);
|
||||||
|
|
||||||
updateFile(
|
updateFile(
|
||||||
`apps/${shell}/webpack.config.js`,
|
`apps/${shell}/webpack.config.ts`,
|
||||||
stripIndents`
|
stripIndents`
|
||||||
import { composePlugins, withNx, ModuleFederationConfig } from '@nx/webpack';
|
import { composePlugins, withNx, ModuleFederationConfig } from '@nx/webpack';
|
||||||
import { withReact } from '@nx/react';
|
import { withReact } from '@nx/react';
|
||||||
@ -135,8 +135,8 @@ describe('React Module Federation', () => {
|
|||||||
|
|
||||||
// check files are generated without the layout directory ("apps/") and
|
// check files are generated without the layout directory ("apps/") and
|
||||||
// using the project name as the directory when no directory is provided
|
// using the project name as the directory when no directory is provided
|
||||||
checkFilesExist(`${shell}/module-federation.config.js`);
|
checkFilesExist(`${shell}/module-federation.config.ts`);
|
||||||
checkFilesExist(`${remote}/module-federation.config.js`);
|
checkFilesExist(`${remote}/module-federation.config.ts`);
|
||||||
|
|
||||||
// check default generated host is built successfully
|
// check default generated host is built successfully
|
||||||
const buildOutput = runCLI(`run ${shell}:build:development`);
|
const buildOutput = runCLI(`run ${shell}:build:development`);
|
||||||
|
|||||||
@ -0,0 +1,128 @@
|
|||||||
|
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||||
|
|
||||||
|
exports[`hostGenerator should generate host files and configs 1`] = `
|
||||||
|
"const { composePlugins, withNx } = require('@nx/webpack');
|
||||||
|
const { withReact } = require('@nx/react');
|
||||||
|
const { withModuleFederation } = require('@nx/react/module-federation');
|
||||||
|
|
||||||
|
const baseConfig = require('./module-federation.config');
|
||||||
|
|
||||||
|
const config = {
|
||||||
|
...baseConfig,
|
||||||
|
};
|
||||||
|
|
||||||
|
// Nx plugins for webpack to build config object from Nx options and context.
|
||||||
|
module.exports = composePlugins(
|
||||||
|
withNx(),
|
||||||
|
withReact(),
|
||||||
|
withModuleFederation(config)
|
||||||
|
);
|
||||||
|
"
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`hostGenerator should generate host files and configs 2`] = `
|
||||||
|
"module.exports = {
|
||||||
|
name: 'test',
|
||||||
|
remotes: [],
|
||||||
|
};
|
||||||
|
"
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`hostGenerator should generate host files and configs for SSR 1`] = `
|
||||||
|
"const { composePlugins, withNx } = require('@nx/webpack');
|
||||||
|
const { withReact } = require('@nx/react');
|
||||||
|
const { withModuleFederationForSSR } = require('@nx/react/module-federation');
|
||||||
|
|
||||||
|
const baseConfig = require('./module-federation.config');
|
||||||
|
|
||||||
|
const defaultConfig = {
|
||||||
|
...baseConfig,
|
||||||
|
};
|
||||||
|
|
||||||
|
// Nx plugins for webpack to build config object from Nx options and context.
|
||||||
|
module.exports = composePlugins(
|
||||||
|
withNx(),
|
||||||
|
withReact({ ssr: true }),
|
||||||
|
withModuleFederationForSSR(defaultConfig)
|
||||||
|
);
|
||||||
|
"
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`hostGenerator should generate host files and configs for SSR 2`] = `
|
||||||
|
"// @ts-check
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @type {import('@nx/webpack').ModuleFederationConfig}
|
||||||
|
**/
|
||||||
|
const moduleFederationConfig = {
|
||||||
|
name: 'test',
|
||||||
|
remotes: [],
|
||||||
|
};
|
||||||
|
|
||||||
|
module.exports = moduleFederationConfig;
|
||||||
|
"
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`hostGenerator should generate host files and configs for SSR when --typescriptConfiguration=true 1`] = `
|
||||||
|
"import { composePlugins, withNx } from '@nx/webpack';
|
||||||
|
import { withReact } from '@nx/react';
|
||||||
|
import { withModuleFederationForSSR } from '@nx/react/module-federation';
|
||||||
|
|
||||||
|
import baseConfig from './module-federation.config';
|
||||||
|
|
||||||
|
const defaultConfig = {
|
||||||
|
...baseConfig,
|
||||||
|
};
|
||||||
|
|
||||||
|
// Nx plugins for webpack to build config object from Nx options and context.
|
||||||
|
export default composePlugins(
|
||||||
|
withNx(),
|
||||||
|
withReact({ ssr: true }),
|
||||||
|
withModuleFederationForSSR(defaultConfig)
|
||||||
|
);
|
||||||
|
"
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`hostGenerator should generate host files and configs for SSR when --typescriptConfiguration=true 2`] = `
|
||||||
|
"import { ModuleFederationConfig } from '@nx/webpack';
|
||||||
|
|
||||||
|
const config: ModuleFederationConfig = {
|
||||||
|
name: 'test',
|
||||||
|
remotes: [],
|
||||||
|
};
|
||||||
|
|
||||||
|
export default config;
|
||||||
|
"
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`hostGenerator should generate host files and configs when --typescriptConfiguration=true 1`] = `
|
||||||
|
"import { composePlugins, withNx } from '@nx/webpack';
|
||||||
|
import { withReact } from '@nx/react';
|
||||||
|
import { withModuleFederation } from '@nx/react/module-federation';
|
||||||
|
|
||||||
|
import baseConfig from './module-federation.config';
|
||||||
|
|
||||||
|
const config = {
|
||||||
|
...baseConfig,
|
||||||
|
};
|
||||||
|
|
||||||
|
// Nx plugins for webpack to build config object from Nx options and context.
|
||||||
|
export default composePlugins(
|
||||||
|
withNx(),
|
||||||
|
withReact(),
|
||||||
|
withModuleFederation(config)
|
||||||
|
);
|
||||||
|
"
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`hostGenerator should generate host files and configs when --typescriptConfiguration=true 2`] = `
|
||||||
|
"import { ModuleFederationConfig } from '@nx/webpack';
|
||||||
|
|
||||||
|
const config: ModuleFederationConfig = {
|
||||||
|
name: 'test',
|
||||||
|
remotes: [],
|
||||||
|
};
|
||||||
|
|
||||||
|
export default config;
|
||||||
|
"
|
||||||
|
`;
|
||||||
@ -0,0 +1,10 @@
|
|||||||
|
import { ModuleFederationConfig } from '@nx/webpack';
|
||||||
|
|
||||||
|
const config: ModuleFederationConfig = {
|
||||||
|
name: '<%= projectName %>',
|
||||||
|
remotes: [
|
||||||
|
<% remotes.forEach(function(r) {%> "<%= r.fileName %>", <% }); %>
|
||||||
|
],
|
||||||
|
};
|
||||||
|
|
||||||
|
export default config;
|
||||||
@ -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'] || 4200;
|
||||||
|
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,15 @@
|
|||||||
|
{
|
||||||
|
"extends": "./tsconfig.app.json",
|
||||||
|
"compilerOptions": {
|
||||||
|
"outDir": "../../out-tsc/server",
|
||||||
|
"target": "es2019",
|
||||||
|
"types": [
|
||||||
|
"node"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"include": [
|
||||||
|
"src/remotes.d.ts",
|
||||||
|
"src/main.server.tsx",
|
||||||
|
"server.ts"
|
||||||
|
]
|
||||||
|
}
|
||||||
@ -0,0 +1,12 @@
|
|||||||
|
import {composePlugins, withNx} from '@nx/webpack';
|
||||||
|
import {withReact} from '@nx/react';
|
||||||
|
import {withModuleFederationForSSR} from '@nx/react/module-federation';
|
||||||
|
|
||||||
|
import baseConfig from './module-federation.config';
|
||||||
|
|
||||||
|
const defaultConfig = {
|
||||||
|
...baseConfig
|
||||||
|
};
|
||||||
|
|
||||||
|
// Nx plugins for webpack to build config object from Nx options and context.
|
||||||
|
export default composePlugins(withNx(), withReact({ssr: true}), withModuleFederationForSSR(defaultConfig));
|
||||||
@ -0,0 +1,10 @@
|
|||||||
|
import { ModuleFederationConfig } from '@nx/webpack';
|
||||||
|
|
||||||
|
const config: ModuleFederationConfig = {
|
||||||
|
name: '<%= projectName %>',
|
||||||
|
remotes: [
|
||||||
|
<% remotes.forEach(function(r) {%> "<%= r.fileName %>", <% }); %>
|
||||||
|
],
|
||||||
|
};
|
||||||
|
|
||||||
|
export default config;
|
||||||
@ -0,0 +1 @@
|
|||||||
|
import('./bootstrap');
|
||||||
@ -0,0 +1,4 @@
|
|||||||
|
// Declare your remote Modules here
|
||||||
|
// Example declare module 'about/Module';
|
||||||
|
|
||||||
|
<% remotes.forEach(function(r) { %>declare module '<%= r.fileName %>/Module';<% }); %>
|
||||||
@ -0,0 +1,32 @@
|
|||||||
|
import { composePlugins, withNx } from '@nx/webpack';
|
||||||
|
import { withReact } from '@nx/react';
|
||||||
|
import { withModuleFederation } from '@nx/react/module-federation';
|
||||||
|
|
||||||
|
import baseConfig from './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 webpack to build config object from Nx options and context.
|
||||||
|
export default composePlugins(withNx(), withReact(), withModuleFederation(prodConfig));
|
||||||
@ -0,0 +1,12 @@
|
|||||||
|
import {composePlugins, withNx} from '@nx/webpack';
|
||||||
|
import {withReact} from '@nx/react';
|
||||||
|
import {withModuleFederation} from '@nx/react/module-federation';
|
||||||
|
|
||||||
|
import baseConfig from './module-federation.config';
|
||||||
|
|
||||||
|
const config = {
|
||||||
|
...baseConfig,
|
||||||
|
};
|
||||||
|
|
||||||
|
// Nx plugins for webpack to build config object from Nx options and context.
|
||||||
|
export default composePlugins(withNx(), withReact(), withModuleFederation(config));
|
||||||
@ -19,14 +19,44 @@ describe('hostGenerator', () => {
|
|||||||
unitTestRunner: 'none',
|
unitTestRunner: 'none',
|
||||||
e2eTestRunner: 'none',
|
e2eTestRunner: 'none',
|
||||||
projectNameAndRootFormat: 'as-provided',
|
projectNameAndRootFormat: 'as-provided',
|
||||||
|
typescriptConfiguration: false,
|
||||||
});
|
});
|
||||||
|
|
||||||
expect(tree.exists('test/tsconfig.json'));
|
expect(tree.exists('test/tsconfig.json')).toBeTruthy();
|
||||||
expect(tree.exists('test/webpack.config.prod.js'));
|
expect(tree.exists('test/webpack.config.prod.js')).toBeTruthy();
|
||||||
expect(tree.exists('test/webpack.config.js'));
|
expect(tree.exists('test/webpack.config.js')).toBeTruthy();
|
||||||
expect(tree.exists('test/src/bootstrap.tsx'));
|
expect(tree.exists('test/module-federation.config.js')).toBeTruthy();
|
||||||
expect(tree.exists('test/src/main.ts'));
|
expect(tree.exists('test/src/bootstrap.tsx')).toBeTruthy();
|
||||||
expect(tree.exists('test/src/remotes.d.ts'));
|
expect(tree.exists('test/src/main.ts')).toBeTruthy();
|
||||||
|
expect(tree.exists('test/src/remotes.d.ts')).toBeTruthy();
|
||||||
|
expect(tree.read('test/webpack.config.js', 'utf-8')).toMatchSnapshot();
|
||||||
|
expect(
|
||||||
|
tree.read('test/module-federation.config.js', 'utf-8')
|
||||||
|
).toMatchSnapshot();
|
||||||
|
});
|
||||||
|
|
||||||
|
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,
|
||||||
|
});
|
||||||
|
|
||||||
|
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.exists('test/module-federation.config.ts')).toBeTruthy();
|
||||||
|
expect(tree.exists('test/src/bootstrap.tsx')).toBeTruthy();
|
||||||
|
expect(tree.exists('test/src/main.ts')).toBeTruthy();
|
||||||
|
expect(tree.exists('test/src/remotes.d.ts')).toBeTruthy();
|
||||||
|
expect(tree.read('test/webpack.config.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 () => {
|
it('should install @nx/web for the file-server executor', async () => {
|
||||||
@ -53,16 +83,19 @@ describe('hostGenerator', () => {
|
|||||||
unitTestRunner: 'none',
|
unitTestRunner: 'none',
|
||||||
e2eTestRunner: 'none',
|
e2eTestRunner: 'none',
|
||||||
projectNameAndRootFormat: 'as-provided',
|
projectNameAndRootFormat: 'as-provided',
|
||||||
|
typescriptConfiguration: false,
|
||||||
});
|
});
|
||||||
|
|
||||||
expect(tree.exists('test/tsconfig.json'));
|
expect(tree.exists('test/tsconfig.json')).toBeTruthy();
|
||||||
expect(tree.exists('test/webpack.config.prod.js'));
|
expect(tree.exists('test/webpack.config.prod.js')).toBeTruthy();
|
||||||
expect(tree.exists('test/webpack.config.server.js'));
|
expect(tree.exists('test/webpack.server.config.js')).toBeTruthy();
|
||||||
expect(tree.exists('test/webpack.config.js'));
|
expect(tree.exists('test/webpack.config.js')).toBeTruthy();
|
||||||
expect(tree.exists('test/src/main.server.tsx'));
|
expect(tree.exists('test/module-federation.config.js')).toBeTruthy();
|
||||||
expect(tree.exists('test/src/bootstrap.tsx'));
|
expect(tree.exists('test/module-federation.server.config.js')).toBeTruthy();
|
||||||
expect(tree.exists('test/src/main.ts'));
|
expect(tree.exists('test/src/main.server.tsx')).toBeTruthy();
|
||||||
expect(tree.exists('test/src/remotes.d.ts'));
|
expect(tree.exists('test/src/bootstrap.tsx')).toBeTruthy();
|
||||||
|
expect(tree.exists('test/src/main.ts')).toBeTruthy();
|
||||||
|
expect(tree.exists('test/src/remotes.d.ts')).toBeTruthy();
|
||||||
|
|
||||||
expect(readJson(tree, 'test/tsconfig.server.json')).toEqual({
|
expect(readJson(tree, 'test/tsconfig.server.json')).toEqual({
|
||||||
compilerOptions: {
|
compilerOptions: {
|
||||||
@ -73,6 +106,54 @@ describe('hostGenerator', () => {
|
|||||||
extends: './tsconfig.app.json',
|
extends: './tsconfig.app.json',
|
||||||
include: ['src/remotes.d.ts', 'src/main.server.tsx', 'server.ts'],
|
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(tree.exists('test/src/remotes.d.ts')).toBeTruthy();
|
||||||
|
|
||||||
|
expect(readJson(tree, 'test/tsconfig.server.json')).toEqual({
|
||||||
|
compilerOptions: {
|
||||||
|
outDir: '../../out-tsc/server',
|
||||||
|
target: 'es2019',
|
||||||
|
types: ['node'],
|
||||||
|
},
|
||||||
|
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 () => {
|
it('should generate a host and remotes in a directory correctly when using --projectNameAndRootFormat=as-provided', async () => {
|
||||||
@ -87,6 +168,7 @@ describe('hostGenerator', () => {
|
|||||||
linter: Linter.None,
|
linter: Linter.None,
|
||||||
style: 'css',
|
style: 'css',
|
||||||
unitTestRunner: 'none',
|
unitTestRunner: 'none',
|
||||||
|
typescriptConfiguration: false,
|
||||||
});
|
});
|
||||||
|
|
||||||
expect(tree.exists('foo/remote1/project.json')).toBeTruthy();
|
expect(tree.exists('foo/remote1/project.json')).toBeTruthy();
|
||||||
@ -96,4 +178,27 @@ describe('hostGenerator', () => {
|
|||||||
tree.read('foo/host-app/module-federation.config.js', 'utf-8')
|
tree.read('foo/host-app/module-federation.config.js', 'utf-8')
|
||||||
).toContain(`'remote1', 'remote2', 'remote3'`);
|
).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: 'hostApp',
|
||||||
|
directory: 'foo/hostApp',
|
||||||
|
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'`);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@ -19,7 +19,7 @@ import {
|
|||||||
} from './lib/normalize-remote';
|
} from './lib/normalize-remote';
|
||||||
import { setupSsrForHost } from './lib/setup-ssr-for-host';
|
import { setupSsrForHost } from './lib/setup-ssr-for-host';
|
||||||
import { updateModuleFederationE2eProject } from './lib/update-module-federation-e2e-project';
|
import { updateModuleFederationE2eProject } from './lib/update-module-federation-e2e-project';
|
||||||
import { Schema } from './schema';
|
import { NormalizedSchema, Schema } from './schema';
|
||||||
|
|
||||||
export async function hostGenerator(host: Tree, schema: Schema) {
|
export async function hostGenerator(host: Tree, schema: Schema) {
|
||||||
return hostGeneratorInternal(host, {
|
return hostGeneratorInternal(host, {
|
||||||
@ -30,11 +30,10 @@ export async function hostGenerator(host: Tree, schema: Schema) {
|
|||||||
|
|
||||||
export async function hostGeneratorInternal(host: Tree, schema: Schema) {
|
export async function hostGeneratorInternal(host: Tree, schema: Schema) {
|
||||||
const tasks: GeneratorCallback[] = [];
|
const tasks: GeneratorCallback[] = [];
|
||||||
const options = await normalizeOptions<Schema>(
|
const options: NormalizedSchema = {
|
||||||
host,
|
...(await normalizeOptions<Schema>(host, schema, '@nx/react:host')),
|
||||||
schema,
|
typescriptConfiguration: schema.typescriptConfiguration ?? true,
|
||||||
'@nx/react:host'
|
};
|
||||||
);
|
|
||||||
|
|
||||||
const initTask = await applicationGenerator(host, {
|
const initTask = await applicationGenerator(host, {
|
||||||
...options,
|
...options,
|
||||||
@ -65,6 +64,7 @@ export async function hostGeneratorInternal(host: Tree, schema: Schema) {
|
|||||||
ssr: options.ssr,
|
ssr: options.ssr,
|
||||||
skipFormat: true,
|
skipFormat: true,
|
||||||
projectNameAndRootFormat: options.projectNameAndRootFormat,
|
projectNameAndRootFormat: options.projectNameAndRootFormat,
|
||||||
|
typescriptConfiguration: options.typescriptConfiguration,
|
||||||
});
|
});
|
||||||
remotePort++;
|
remotePort++;
|
||||||
}
|
}
|
||||||
@ -93,7 +93,7 @@ export async function hostGeneratorInternal(host: Tree, schema: Schema) {
|
|||||||
const projectConfig = readProjectConfiguration(host, options.projectName);
|
const projectConfig = readProjectConfiguration(host, options.projectName);
|
||||||
projectConfig.targets.server.options.webpackConfig = joinPathFragments(
|
projectConfig.targets.server.options.webpackConfig = joinPathFragments(
|
||||||
projectConfig.root,
|
projectConfig.root,
|
||||||
'webpack.server.config.js'
|
`webpack.server.config.${options.typescriptConfiguration ? 'ts' : 'js'}`
|
||||||
);
|
);
|
||||||
updateProjectConfiguration(host, options.projectName, projectConfig);
|
updateProjectConfiguration(host, options.projectName, projectConfig);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
import { NormalizedSchema } from '../schema';
|
import { NormalizedSchema } from '../schema';
|
||||||
import { generateFiles, names } from '@nx/devkit';
|
import { generateFiles, joinPathFragments, names } from '@nx/devkit';
|
||||||
import { join } from 'path';
|
import { join } from 'path';
|
||||||
|
|
||||||
export function addModuleFederationFiles(
|
export function addModuleFederationFiles(
|
||||||
@ -34,11 +34,31 @@ export function addModuleFederationFiles(
|
|||||||
templateVariables
|
templateVariables
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const pathToModuleFederationFiles = options.typescriptConfiguration
|
||||||
|
? 'module-federation-ts'
|
||||||
|
: 'module-federation';
|
||||||
// New entry file is created here.
|
// New entry file is created here.
|
||||||
generateFiles(
|
generateFiles(
|
||||||
host,
|
host,
|
||||||
join(__dirname, `../files/module-federation`),
|
join(__dirname, `../files/${pathToModuleFederationFiles}`),
|
||||||
options.appProjectRoot,
|
options.appProjectRoot,
|
||||||
templateVariables
|
templateVariables
|
||||||
);
|
);
|
||||||
|
|
||||||
|
if (options.typescriptConfiguration) {
|
||||||
|
const pathToWebpackConfig = joinPathFragments(
|
||||||
|
options.appProjectRoot,
|
||||||
|
'webpack.config.js'
|
||||||
|
);
|
||||||
|
const pathToWebpackProdConfig = joinPathFragments(
|
||||||
|
options.appProjectRoot,
|
||||||
|
'webpack.config.prod.js'
|
||||||
|
);
|
||||||
|
if (host.exists(pathToWebpackConfig)) {
|
||||||
|
host.delete(pathToWebpackConfig);
|
||||||
|
}
|
||||||
|
if (host.exists(pathToWebpackProdConfig)) {
|
||||||
|
host.delete(pathToWebpackProdConfig);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -23,9 +23,13 @@ export async function setupSsrForHost(
|
|||||||
project.targets.serve.executor = '@nx/react:module-federation-ssr-dev-server';
|
project.targets.serve.executor = '@nx/react:module-federation-ssr-dev-server';
|
||||||
updateProjectConfiguration(tree, appName, project);
|
updateProjectConfiguration(tree, appName, project);
|
||||||
|
|
||||||
|
const pathToModuleFederationSsrFiles = options.typescriptConfiguration
|
||||||
|
? 'module-federation-ssr-ts'
|
||||||
|
: 'module-federation-ssr';
|
||||||
|
|
||||||
generateFiles(
|
generateFiles(
|
||||||
tree,
|
tree,
|
||||||
joinPathFragments(__dirname, '../files/module-federation-ssr'),
|
joinPathFragments(__dirname, `../files/${pathToModuleFederationSsrFiles}`),
|
||||||
project.root,
|
project.root,
|
||||||
{
|
{
|
||||||
...options,
|
...options,
|
||||||
|
|||||||
@ -24,6 +24,7 @@ export interface Schema {
|
|||||||
tags?: string;
|
tags?: string;
|
||||||
unitTestRunner: 'jest' | 'vitest' | 'none';
|
unitTestRunner: 'jest' | 'vitest' | 'none';
|
||||||
minimal?: boolean;
|
minimal?: boolean;
|
||||||
|
typescriptConfiguration?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface NormalizedSchema extends Schema {
|
export interface NormalizedSchema extends Schema {
|
||||||
|
|||||||
@ -166,6 +166,11 @@
|
|||||||
"description": "Generate a React app with a minimal setup. No nx starter template.",
|
"description": "Generate a React app with a minimal setup. No nx starter template.",
|
||||||
"type": "boolean",
|
"type": "boolean",
|
||||||
"default": false
|
"default": false
|
||||||
|
},
|
||||||
|
"typescriptConfiguration": {
|
||||||
|
"type": "boolean",
|
||||||
|
"description": "Whether the module federation configuration and webpack configuration files should use TS.",
|
||||||
|
"default": true
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"required": ["name"],
|
"required": ["name"],
|
||||||
|
|||||||
@ -0,0 +1,139 @@
|
|||||||
|
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||||
|
|
||||||
|
exports[`remote generator should create the remote with the correct config files 1`] = `
|
||||||
|
"const { composePlugins, withNx } = require('@nx/webpack');
|
||||||
|
const { withReact } = require('@nx/react');
|
||||||
|
const { withModuleFederation } = require('@nx/react/module-federation');
|
||||||
|
|
||||||
|
const baseConfig = require('./module-federation.config');
|
||||||
|
|
||||||
|
const config = {
|
||||||
|
...baseConfig,
|
||||||
|
};
|
||||||
|
|
||||||
|
// Nx plugins for webpack to build config object from Nx options and context.
|
||||||
|
module.exports = composePlugins(
|
||||||
|
withNx(),
|
||||||
|
withReact(),
|
||||||
|
withModuleFederation(config)
|
||||||
|
);
|
||||||
|
"
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`remote generator 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`] = `
|
||||||
|
"module.exports = {
|
||||||
|
name: 'test',
|
||||||
|
exposes: {
|
||||||
|
'./Module': './src/remote-entry.ts',
|
||||||
|
},
|
||||||
|
};
|
||||||
|
"
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`remote generator should create the remote with the correct config files when --typescriptConfiguration=true 1`] = `
|
||||||
|
"import { composePlugins, withNx } from '@nx/webpack';
|
||||||
|
import { withReact } from '@nx/react';
|
||||||
|
import { withModuleFederation } from '@nx/react/module-federation';
|
||||||
|
|
||||||
|
import baseConfig from './module-federation.config';
|
||||||
|
|
||||||
|
const config = {
|
||||||
|
...baseConfig,
|
||||||
|
};
|
||||||
|
|
||||||
|
// Nx plugins for webpack to build config object from Nx options and context.
|
||||||
|
export default composePlugins(
|
||||||
|
withNx(),
|
||||||
|
withReact(),
|
||||||
|
withModuleFederation(config)
|
||||||
|
);
|
||||||
|
"
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`remote generator should create the remote with the correct config files when --typescriptConfiguration=true 2`] = `
|
||||||
|
"export default require('./webpack.config');
|
||||||
|
"
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`remote generator should create the remote with the correct config files when --typescriptConfiguration=true 3`] = `
|
||||||
|
"import { ModuleFederationConfig } from '@nx/webpack';
|
||||||
|
|
||||||
|
const config: ModuleFederationConfig = {
|
||||||
|
name: 'test',
|
||||||
|
exposes: {
|
||||||
|
'./Module': './src/remote-entry.ts',
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
export default config;
|
||||||
|
"
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`remote generator should generate correct remote with config files when using --ssr 1`] = `
|
||||||
|
"const { composePlugins, withNx } = require('@nx/webpack');
|
||||||
|
const { withReact } = require('@nx/react');
|
||||||
|
const { withModuleFederationForSSR } = require('@nx/react/module-federation');
|
||||||
|
|
||||||
|
const baseConfig = require('./module-federation.server.config');
|
||||||
|
|
||||||
|
const defaultConfig = {
|
||||||
|
...baseConfig,
|
||||||
|
};
|
||||||
|
|
||||||
|
// Nx plugins for webpack to build config object from Nx options and context.
|
||||||
|
module.exports = composePlugins(
|
||||||
|
withNx(),
|
||||||
|
withReact({ ssr: true }),
|
||||||
|
withModuleFederationForSSR(defaultConfig)
|
||||||
|
);
|
||||||
|
"
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`remote generator should generate correct remote with config files when using --ssr 2`] = `
|
||||||
|
"module.exports = {
|
||||||
|
name: 'test',
|
||||||
|
exposes: {
|
||||||
|
'./Module': 'test/src/remote-entry.ts',
|
||||||
|
},
|
||||||
|
};
|
||||||
|
"
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`remote generator should generate correct remote with config files when using --ssr and --typescriptConfiguration=true 1`] = `
|
||||||
|
"import { composePlugins, withNx } from '@nx/webpack';
|
||||||
|
import { withReact } from '@nx/react';
|
||||||
|
import { withModuleFederationForSSR } from '@nx/react/module-federation';
|
||||||
|
|
||||||
|
import baseConfig from './module-federation.server.config';
|
||||||
|
|
||||||
|
const defaultConfig = {
|
||||||
|
...baseConfig,
|
||||||
|
};
|
||||||
|
|
||||||
|
// Nx plugins for webpack to build config object from Nx options and context.
|
||||||
|
export default composePlugins(
|
||||||
|
withNx(),
|
||||||
|
withReact({ ssr: true }),
|
||||||
|
withModuleFederationForSSR(defaultConfig)
|
||||||
|
);
|
||||||
|
"
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`remote generator should generate correct remote with config files when using --ssr and --typescriptConfiguration=true 2`] = `
|
||||||
|
"import { ModuleFederationConfig } from '@nx/webpack';
|
||||||
|
|
||||||
|
const config: ModuleFederationConfig = {
|
||||||
|
name: 'test',
|
||||||
|
exposes: {
|
||||||
|
'./Module': 'test/src/remote-entry.ts',
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
export default config;
|
||||||
|
"
|
||||||
|
`;
|
||||||
@ -0,0 +1,10 @@
|
|||||||
|
import {ModuleFederationConfig} from '@nx/webpack';
|
||||||
|
|
||||||
|
const config: ModuleFederationConfig = {
|
||||||
|
name: '<%= projectName %>',
|
||||||
|
exposes: {
|
||||||
|
'./Module': '<%= appProjectRoot %>/src/remote-entry.ts',
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
export default 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'] || 4200;
|
||||||
|
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,12 @@
|
|||||||
|
import {composePlugins, withNx} from '@nx/webpack';
|
||||||
|
import {withReact} from '@nx/react';
|
||||||
|
import {withModuleFederationForSSR} from '@nx/react/module-federation';
|
||||||
|
|
||||||
|
import baseConfig from "./module-federation.server.config";
|
||||||
|
|
||||||
|
const defaultConfig = {
|
||||||
|
...baseConfig,
|
||||||
|
};
|
||||||
|
|
||||||
|
// Nx plugins for webpack to build config object from Nx options and context.
|
||||||
|
export default composePlugins(withNx(), withReact({ssr: true}), withModuleFederationForSSR(defaultConfig));
|
||||||
@ -0,0 +1,10 @@
|
|||||||
|
import {ModuleFederationConfig} from '@nx/webpack';
|
||||||
|
|
||||||
|
const config: ModuleFederationConfig = {
|
||||||
|
name: '<%= projectName %>',
|
||||||
|
exposes: {
|
||||||
|
'./Module': './src/remote-entry.ts',
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
export default config;
|
||||||
@ -0,0 +1 @@
|
|||||||
|
import('./bootstrap');
|
||||||
@ -0,0 +1 @@
|
|||||||
|
export { default } from './app/app';
|
||||||
@ -0,0 +1 @@
|
|||||||
|
export default require('./webpack.config');
|
||||||
@ -0,0 +1,12 @@
|
|||||||
|
import {composePlugins, withNx} from '@nx/webpack';
|
||||||
|
import {withReact} from '@nx/react';
|
||||||
|
import {withModuleFederation} from '@nx/react/module-federation';
|
||||||
|
|
||||||
|
import baseConfig from './module-federation.config';
|
||||||
|
|
||||||
|
const config = {
|
||||||
|
...baseConfig,
|
||||||
|
};
|
||||||
|
|
||||||
|
// Nx plugins for webpack to build config object from Nx options and context.
|
||||||
|
export default composePlugins(withNx(), withReact(), withModuleFederation(config));
|
||||||
@ -20,9 +20,13 @@ export async function setupSsrForRemote(
|
|||||||
const tasks: GeneratorCallback[] = [];
|
const tasks: GeneratorCallback[] = [];
|
||||||
const project = readProjectConfiguration(tree, appName);
|
const project = readProjectConfiguration(tree, appName);
|
||||||
|
|
||||||
|
const pathToModuleFederationSsrFiles = options.typescriptConfiguration
|
||||||
|
? 'module-federation-ssr-ts'
|
||||||
|
: 'module-federation-ssr';
|
||||||
|
|
||||||
generateFiles(
|
generateFiles(
|
||||||
tree,
|
tree,
|
||||||
joinPathFragments(__dirname, '../files/module-federation-ssr'),
|
joinPathFragments(__dirname, `../files/${pathToModuleFederationSsrFiles}`),
|
||||||
project.root,
|
project.root,
|
||||||
{
|
{
|
||||||
...options,
|
...options,
|
||||||
|
|||||||
@ -25,10 +25,18 @@ export function updateHostWithRemote(
|
|||||||
}
|
}
|
||||||
|
|
||||||
const hostConfig = readProjectConfiguration(host, hostName);
|
const hostConfig = readProjectConfiguration(host, hostName);
|
||||||
const moduleFederationConfigPath = joinPathFragments(
|
let moduleFederationConfigPath = joinPathFragments(
|
||||||
hostConfig.root,
|
hostConfig.root,
|
||||||
'module-federation.config.js'
|
'module-federation.config.js'
|
||||||
);
|
);
|
||||||
|
|
||||||
|
if (!host.exists(moduleFederationConfigPath)) {
|
||||||
|
moduleFederationConfigPath = joinPathFragments(
|
||||||
|
hostConfig.root,
|
||||||
|
'module-federation.config.ts'
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
const remoteDefsPath = joinPathFragments(
|
const remoteDefsPath = joinPathFragments(
|
||||||
hostConfig.sourceRoot,
|
hostConfig.sourceRoot,
|
||||||
'remotes.d.ts'
|
'remotes.d.ts'
|
||||||
|
|||||||
@ -4,6 +4,56 @@ import { Linter } from '@nx/linter';
|
|||||||
import remote from './remote';
|
import remote from './remote';
|
||||||
|
|
||||||
describe('remote generator', () => {
|
describe('remote generator', () => {
|
||||||
|
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: false,
|
||||||
|
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();
|
||||||
|
});
|
||||||
|
|
||||||
|
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 () => {
|
it('should install @nx/web for the file-server executor', async () => {
|
||||||
const tree = createTreeWithEmptyWorkspace();
|
const tree = createTreeWithEmptyWorkspace();
|
||||||
await remote(tree, {
|
await remote(tree, {
|
||||||
@ -57,4 +107,58 @@ describe('remote generator', () => {
|
|||||||
expect(mainFile).toContain(`join(process.cwd(), 'dist/test/browser')`);
|
expect(mainFile).toContain(`join(process.cwd(), 'dist/test/browser')`);
|
||||||
expect(mainFile).toContain('nx.server.ready');
|
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: false,
|
||||||
|
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();
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@ -22,7 +22,7 @@ import { setupSsrForRemote } from './lib/setup-ssr-for-remote';
|
|||||||
|
|
||||||
export function addModuleFederationFiles(
|
export function addModuleFederationFiles(
|
||||||
host: Tree,
|
host: Tree,
|
||||||
options: NormalizedSchema
|
options: NormalizedSchema<Schema>
|
||||||
) {
|
) {
|
||||||
const templateVariables = {
|
const templateVariables = {
|
||||||
...names(options.name),
|
...names(options.name),
|
||||||
@ -30,12 +30,33 @@ export function addModuleFederationFiles(
|
|||||||
tmpl: '',
|
tmpl: '',
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const pathToModuleFederationFiles = options.typescriptConfiguration
|
||||||
|
? 'module-federation-ts'
|
||||||
|
: 'module-federation';
|
||||||
|
|
||||||
generateFiles(
|
generateFiles(
|
||||||
host,
|
host,
|
||||||
join(__dirname, `./files/module-federation`),
|
join(__dirname, `./files/${pathToModuleFederationFiles}`),
|
||||||
options.appProjectRoot,
|
options.appProjectRoot,
|
||||||
templateVariables
|
templateVariables
|
||||||
);
|
);
|
||||||
|
|
||||||
|
if (options.typescriptConfiguration) {
|
||||||
|
const pathToWebpackConfig = joinPathFragments(
|
||||||
|
options.appProjectRoot,
|
||||||
|
'webpack.config.js'
|
||||||
|
);
|
||||||
|
const pathToWebpackProdConfig = joinPathFragments(
|
||||||
|
options.appProjectRoot,
|
||||||
|
'webpack.config.prod.js'
|
||||||
|
);
|
||||||
|
if (host.exists(pathToWebpackConfig)) {
|
||||||
|
host.delete(pathToWebpackConfig);
|
||||||
|
}
|
||||||
|
if (host.exists(pathToWebpackProdConfig)) {
|
||||||
|
host.delete(pathToWebpackProdConfig);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function remoteGenerator(host: Tree, schema: Schema) {
|
export async function remoteGenerator(host: Tree, schema: Schema) {
|
||||||
@ -47,11 +68,10 @@ export async function remoteGenerator(host: Tree, schema: Schema) {
|
|||||||
|
|
||||||
export async function remoteGeneratorInternal(host: Tree, schema: Schema) {
|
export async function remoteGeneratorInternal(host: Tree, schema: Schema) {
|
||||||
const tasks: GeneratorCallback[] = [];
|
const tasks: GeneratorCallback[] = [];
|
||||||
const options = await normalizeOptions<Schema>(
|
const options: NormalizedSchema<Schema> = {
|
||||||
host,
|
...(await normalizeOptions<Schema>(host, schema, '@nx/react:remote')),
|
||||||
schema,
|
typescriptConfiguration: schema.typescriptConfiguration ?? true,
|
||||||
'@nx/react:remote'
|
};
|
||||||
);
|
|
||||||
const initAppTask = await applicationGenerator(host, {
|
const initAppTask = await applicationGenerator(host, {
|
||||||
...options,
|
...options,
|
||||||
// Only webpack works with module federation for now.
|
// Only webpack works with module federation for now.
|
||||||
@ -93,7 +113,7 @@ export async function remoteGeneratorInternal(host: Tree, schema: Schema) {
|
|||||||
const projectConfig = readProjectConfiguration(host, options.projectName);
|
const projectConfig = readProjectConfiguration(host, options.projectName);
|
||||||
projectConfig.targets.server.options.webpackConfig = joinPathFragments(
|
projectConfig.targets.server.options.webpackConfig = joinPathFragments(
|
||||||
projectConfig.root,
|
projectConfig.root,
|
||||||
'webpack.server.config.js'
|
`webpack.server.config.${options.typescriptConfiguration ? 'ts' : 'js'}`
|
||||||
);
|
);
|
||||||
updateProjectConfiguration(host, options.projectName, projectConfig);
|
updateProjectConfiguration(host, options.projectName, projectConfig);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,6 +1,7 @@
|
|||||||
import type { ProjectNameAndRootFormat } from '@nx/devkit/src/generators/project-name-and-root-utils';
|
import type { ProjectNameAndRootFormat } from '@nx/devkit/src/generators/project-name-and-root-utils';
|
||||||
import type { Linter } from '@nx/linter';
|
import type { Linter } from '@nx/linter';
|
||||||
import type { SupportedStyles } from '../../../typings';
|
import type { SupportedStyles } from '../../../typings';
|
||||||
|
import type { NormalizedSchema as ApplicationNormalizedSchema } from '../application/schema';
|
||||||
|
|
||||||
export interface Schema {
|
export interface Schema {
|
||||||
classComponent?: boolean;
|
classComponent?: boolean;
|
||||||
@ -24,4 +25,9 @@ export interface Schema {
|
|||||||
style: SupportedStyles;
|
style: SupportedStyles;
|
||||||
tags?: string;
|
tags?: string;
|
||||||
unitTestRunner: 'jest' | 'vitest' | 'none';
|
unitTestRunner: 'jest' | 'vitest' | 'none';
|
||||||
|
typescriptConfiguration?: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface NormalizedSchema extends ApplicationNormalizedSchema {
|
||||||
|
typescriptConfiguration: boolean;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -164,6 +164,11 @@
|
|||||||
"description": "Whether to configure SSR for the host application",
|
"description": "Whether to configure SSR for the host application",
|
||||||
"type": "boolean",
|
"type": "boolean",
|
||||||
"default": false
|
"default": false
|
||||||
|
},
|
||||||
|
"typescriptConfiguration": {
|
||||||
|
"type": "boolean",
|
||||||
|
"description": "Whether the module federation configuration and webpack configuration files should use TS.",
|
||||||
|
"default": true
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"required": ["name"],
|
"required": ["name"],
|
||||||
|
|||||||
@ -13,6 +13,7 @@ export function updateModuleFederationProject(
|
|||||||
projectName: string;
|
projectName: string;
|
||||||
appProjectRoot: string;
|
appProjectRoot: string;
|
||||||
devServerPort?: number;
|
devServerPort?: number;
|
||||||
|
typescriptConfiguration?: boolean;
|
||||||
}
|
}
|
||||||
): GeneratorCallback {
|
): GeneratorCallback {
|
||||||
const projectConfig = readProjectConfiguration(host, options.projectName);
|
const projectConfig = readProjectConfiguration(host, options.projectName);
|
||||||
@ -20,12 +21,16 @@ export function updateModuleFederationProject(
|
|||||||
projectConfig.targets.build.options = {
|
projectConfig.targets.build.options = {
|
||||||
...projectConfig.targets.build.options,
|
...projectConfig.targets.build.options,
|
||||||
main: `${options.appProjectRoot}/src/main.ts`,
|
main: `${options.appProjectRoot}/src/main.ts`,
|
||||||
webpackConfig: `${options.appProjectRoot}/webpack.config.js`,
|
webpackConfig: `${options.appProjectRoot}/webpack.config.${
|
||||||
|
options.typescriptConfiguration ? 'ts' : 'js'
|
||||||
|
}`,
|
||||||
};
|
};
|
||||||
|
|
||||||
projectConfig.targets.build.configurations.production = {
|
projectConfig.targets.build.configurations.production = {
|
||||||
...projectConfig.targets.build.configurations.production,
|
...projectConfig.targets.build.configurations.production,
|
||||||
webpackConfig: `${options.appProjectRoot}/webpack.config.prod.js`,
|
webpackConfig: `${options.appProjectRoot}/webpack.config.prod.${
|
||||||
|
options.typescriptConfiguration ? 'ts' : 'js'
|
||||||
|
}`,
|
||||||
};
|
};
|
||||||
|
|
||||||
projectConfig.targets.serve.executor =
|
projectConfig.targets.serve.executor =
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user