Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(react): Add federate-module generator #19286

Merged
merged 1 commit into from
Oct 6, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions docs/generated/manifests/menus.json
Original file line number Diff line number Diff line change
Expand Up @@ -8058,6 +8058,14 @@
"children": [],
"isExternal": false,
"disableCollapsible": false
},
{
"id": "federate-module",
"path": "/nx-api/react/generators/federate-module",
"name": "federate-module",
"children": [],
"isExternal": false,
"disableCollapsible": false
}
],
"isExternal": false,
Expand Down
9 changes: 9 additions & 0 deletions docs/generated/manifests/nx-api.json
Original file line number Diff line number Diff line change
Expand Up @@ -2164,6 +2164,15 @@
"originalFilePath": "/packages/react/src/generators/setup-ssr/schema.json",
"path": "/nx-api/react/generators/setup-ssr",
"type": "generator"
},
"/nx-api/react/generators/federate-module": {
"description": "Federate a module.",
"file": "generated/packages/react/generators/federate-module.json",
"hidden": false,
"name": "federate-module",
"originalFilePath": "/packages/react/src/generators/federate-module/schema.json",
"path": "/nx-api/react/generators/federate-module",
"type": "generator"
}
},
"path": "/nx-api/react"
Expand Down
9 changes: 9 additions & 0 deletions docs/generated/packages-metadata.json
Original file line number Diff line number Diff line change
Expand Up @@ -2140,6 +2140,15 @@
"originalFilePath": "/packages/react/src/generators/setup-ssr/schema.json",
"path": "react/generators/setup-ssr",
"type": "generator"
},
{
"description": "Federate a module.",
"file": "generated/packages/react/generators/federate-module.json",
"hidden": false,
"name": "federate-module",
"originalFilePath": "/packages/react/src/generators/federate-module/schema.json",
"path": "react/generators/federate-module",
"type": "generator"
}
],
"githubRoot": "https://github.com/nrwl/nx/blob/master",
Expand Down
118 changes: 118 additions & 0 deletions docs/generated/packages/react/generators/federate-module.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
{
"name": "federate-module",
"factory": "./src/generators/federate-module/federate-module#federateModuleGenerator",
"schema": {
"$schema": "http://json-schema.org/schema",
"cli": "nx",
"$id": "NxReactFederateModule",
"title": "Federate Module",
"description": "Create a federated module, which can be loaded by a remote host.",
"examples": [
{
"command": "nx g federate-module MyModule --path=./src/component/my-cmp.ts --remote=my-remote-app",
"description": "Create a federated module from my-remote-app, that exposes my-cmp from ./src/component/my-cmp.ts as MyModule."
}
],
"type": "object",
"properties": {
"name": {
"description": "The name of the module.",
"type": "string",
"$default": { "$source": "argv", "index": 0 },
"x-prompt": "What name would you like to use for the module?",
"pattern": "^[a-zA-Z][^:]*$",
"x-priority": "important"
},
"path": {
"type": "string",
"description": "The path to locate the federated module.",
"x-prompt": "What is the path to the module to be federated?"
},
"remote": {
"type": "string",
"description": "The name of the remote.",
"x-prompt": "What is/should the remote be named?"
},
"projectNameAndRootFormat": {
"description": "Whether to generate the project name and root directory as provided (`as-provided`) or generate them composing their values and taking the configured layout into account (`derived`).",
"type": "string",
"enum": ["as-provided", "derived"]
},
"style": {
"description": "The file extension to be used for style files.",
"type": "string",
"default": "css",
"alias": "s",
"x-prompt": {
"message": "Which stylesheet format would you like to use?",
"type": "list",
"items": [
{ "value": "css", "label": "CSS" },
{
"value": "scss",
"label": "SASS(.scss) [ http://sass-lang.com ]"
},
{
"value": "less",
"label": "LESS [ http://lesscss.org ]"
},
{
"value": "styled-components",
"label": "styled-components [ https://styled-components.com ]"
},
{
"value": "@emotion/styled",
"label": "emotion [ https://emotion.sh ]"
},
{
"value": "styled-jsx",
"label": "styled-jsx [ https://www.npmjs.com/package/styled-jsx ]"
},
{
"value": "styl",
"label": "DEPRECATD: Stylus(.styl) [ http://stylus-lang.com ]"
},
{ "value": "none", "label": "None" }
]
}
},
"linter": {
"description": "The tool to use for running lint checks.",
"type": "string",
"enum": ["eslint"],
"default": "eslint"
},
"skipFormat": {
"description": "Skip formatting files.",
"type": "boolean",
"default": false,
"x-priority": "internal"
},
"unitTestRunner": {
"type": "string",
"enum": ["jest", "none"],
"description": "Test runner to use for unit tests.",
"default": "jest"
},
"e2eTestRunner": {
"type": "string",
"enum": ["cypress", "none"],
"description": "Test runner to use for end to end (e2e) tests.",
"default": "cypress"
},
"host": {
"type": "string",
"description": "The host / shell application for this remote."
}
},
"required": ["name", "path", "remote"],
"additionalProperties": false,
"presets": []
},
"description": "Federate a module.",
"hidden": false,
"implementation": "/packages/react/src/generators/federate-module/federate-module#federateModuleGenerator.ts",
"aliases": [],
"path": "/packages/react/src/generators/federate-module/schema.json",
"type": "generator"
}
1 change: 1 addition & 0 deletions docs/shared/reference/sitemap.md
Original file line number Diff line number Diff line change
Expand Up @@ -557,6 +557,7 @@
- [component-test](/nx-api/react/generators/component-test)
- [setup-tailwind](/nx-api/react/generators/setup-tailwind)
- [setup-ssr](/nx-api/react/generators/setup-ssr)
- [federate-module](/nx-api/react/generators/federate-module)
- [react-native](/nx-api/react-native)
- [documents](/nx-api/react-native/documents)
- [Overview](/nx-api/react-native/documents/overview)
Expand Down
102 changes: 100 additions & 2 deletions e2e/react-core/src/react-module-federation.test.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { stripIndents } from '@nx/devkit';
import { Tree, stripIndents } from '@nx/devkit';
import {
checkFilesExist,
cleanupProject,
Expand All @@ -15,11 +15,16 @@ import {
updateJson,
} from '@nx/e2e/utils';
import { join } from 'path';
import { createTreeWithEmptyWorkspace } from 'nx/src/devkit-testing-exports';

describe('React Module Federation', () => {
let proj: string;
let tree: Tree;

beforeAll(() => (proj = newProject()));
beforeAll(() => {
tree = createTreeWithEmptyWorkspace();
proj = newProject();
});

afterAll(() => cleanupProject());

Expand Down Expand Up @@ -382,6 +387,99 @@ describe('React Module Federation', () => {
}
}, 500_000);

// Federate Module
describe('Federate Module', () => {
it('should federate a module from a library and update an existing remote', async () => {
const lib = uniq('lib');
const remote = uniq('remote');
const module = uniq('module');
const host = uniq('host');

runCLI(
`generate @nx/react:host ${host} --remotes=${remote} --no-interactive --projectNameAndRootFormat=as-provided`
);

runCLI(
`generate @nx/js:lib ${lib} --no-interactive --projectNameAndRootFormat=as-provided`
);

// Federate Module
runCLI(
`generate @nx/react:federate-module ${module} --remote=${remote} --path=${lib}/src/index.ts --no-interactive`
);

updateFile(
`${lib}/src/index.ts`,
`export { default } from './lib/${lib}';`
);
updateFile(
`${lib}/src/lib/${lib}.ts`,
`export default function lib() { return 'Hello from ${lib}'; };`
);

// Update Host to use the module
updateFile(
`${host}/src/app/app.tsx`,
`
import * as React from 'react';
import NxWelcome from './nx-welcome';
import { Link, Route, Routes } from 'react-router-dom';

import myLib from '${remote}/${module}';

export function App() {
return (
<React.Suspense fallback={null}>
<div className='remote'>
My Remote Library: { myLib() }
</div>
<ul>
<li>
<Link to="/">Home</Link>
</li>
</ul>
<Routes>
<Route path="/" element={<NxWelcome title="Host" />} />
</Routes>
</React.Suspense>
);
}

export default App;
`
);

// Update e2e test to check the module
updateFile(
`${host}-e2e/src/e2e/app.cy.ts`,
`
describe('${host}', () => {
beforeEach(() => cy.visit('/'));

it('should display contain the remote library', () => {
expect(cy.get('div.remote')).to.exist;
expect(cy.get('div.remote').contains('My Remote Library: Hello from ${lib}'));
});
});

`
);

// Build host and remote
const buildOutput = runCLI(`build ${host}`);
const remoteOutput = runCLI(`build ${remote}`);

expect(buildOutput).toContain('Successfully ran target build');
expect(remoteOutput).toContain('Successfully ran target build');

if (runE2ETests()) {
const hostE2eResults = runCLI(`e2e ${host}-e2e --no-watch --verbose`);

expect(hostE2eResults).toContain('All specs passed!');
}
}, 500_000);
});

function readPort(appName: string): number {
const config = readJson(join('apps', appName, 'project.json'));
return config.targets.serve.options.port;
Expand Down
14 changes: 14 additions & 0 deletions packages/react/generators.json
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,13 @@
"schema": "./src/generators/setup-ssr/schema.json",
"description": "Set up SSR configuration for a project.",
"hidden": false
},

"federate-module": {
"factory": "./src/generators/federate-module/federate-module#federateModuleSchematic",
"schema": "./src/generators/federate-module/schema.json",
"description": "Federate a module.",
"hidden": false
}
},
"generators": {
Expand Down Expand Up @@ -218,6 +225,13 @@
"schema": "./src/generators/setup-ssr/schema.json",
"description": "Set up SSR configuration for a project.",
"hidden": false
},

"federate-module": {
"factory": "./src/generators/federate-module/federate-module#federateModuleGenerator",
"schema": "./src/generators/federate-module/schema.json",
"description": "Federate a module.",
"hidden": false
}
}
}
Loading