Skip to content

Commit

Permalink
feat: support use storybook to dev rslib module federation format ass…
Browse files Browse the repository at this point in the history
…ets and also support hmr (#349)

Co-authored-by: nyqykk <[email protected]>
Co-authored-by: fi3ework <[email protected]>
Co-authored-by: Timeless0911 <[email protected]>
  • Loading branch information
4 people authored Nov 11, 2024
1 parent 7dd9a48 commit d52e410
Show file tree
Hide file tree
Showing 17 changed files with 1,515 additions and 63 deletions.
1 change: 1 addition & 0 deletions .npmrc
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
registry = 'https://registry.npmjs.org/'
strict-peer-dependencies=false
auto-install-peers=false
hoist-patterns[]=[]
2 changes: 1 addition & 1 deletion examples/module-federation/mf-host/package.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{
"name": "@example/mf-host",
"name": "@examples/mf-host",
"version": "1.0.0",
"private": true,
"scripts": {
Expand Down
2 changes: 2 additions & 0 deletions examples/module-federation/mf-host/rsbuild.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ export default defineConfig({
singleton: true,
},
},
// Enable this when the output of Rslib is build under 'production' mode, while the host app is 'development'.
// Reference: https://lib.rsbuild.dev/guide/advanced/module-federation#faqs
shareStrategy: 'loaded-first',
}),
],
Expand Down
42 changes: 42 additions & 0 deletions examples/module-federation/mf-react-component/.storybook/main.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import { dirname, join } from 'node:path';
import type { StorybookConfig } from 'storybook-react-rsbuild';

/**
* This function is used to resolve the absolute path of a package.
* It is needed in projects that use Yarn PnP or are set up within a monorepo.
*/
function getAbsolutePath(value: string): any {
return dirname(require.resolve(join(value, 'package.json')));
}

const config: StorybookConfig = {
stories: [
'../stories/**/*.mdx',
'../stories/**/*.stories.@(js|jsx|mjs|ts|tsx)',
],
framework: {
name: getAbsolutePath('storybook-react-rsbuild'),
options: {},
},
addons: [
{
name: getAbsolutePath('storybook-addon-rslib'),
options: {
rslib: {
include: ['**/stories/**'],
},
},
},
{
name: '@module-federation/storybook-addon/preset',
options: {
remotes: {
'rslib-module':
'rslib-module@http://localhost:3001/mf/mf-manifest.json',
},
},
},
],
};

export default config;
34 changes: 34 additions & 0 deletions examples/module-federation/mf-react-component/README.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,37 @@
# @examples/mf-react-component

This example demonstrates how to use Rslib to build a simple Module Federation React component.

### Usage

Dev MF module

1. Start remote module which is loaded by Rslib module.

```
nx run @examples/mf-remote:dev
```

2. Start MF dev mode.

```
nx run @examples/mf-react-component:dev
```

3. Use Storybook to development component.

```
nx run @examples/mf-react-component:storybook
```

Build

```
nx run @examples/mf-react-component:build
```

Build and Serve dist

```
nx run @examples/mf-react-component:serve
```
14 changes: 10 additions & 4 deletions examples/module-federation/mf-react-component/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,27 +3,33 @@
"private": true,
"exports": {
".": {
"types": "./dist/cjs/index.d.ts",
"import": "./dist/esm/index.mjs",
"require": "./dist/cjs/index.js",
"types": "./dist/cjs/index.d.ts"
"require": "./dist/cjs/index.js"
}
},
"main": "./dist/cjs/index.js",
"module": "./dist/esm/index.mjs",
"types": "./dist/cjs/index.d.ts",
"scripts": {
"build": "rslib build",
"serve": "pnpm build && http-server -p 3001 ./dist/ --cors"
"dev": "rslib mf dev",
"serve": "pnpm build & http-server -p 3001 ./dist/ --cors",
"storybook": "storybook dev -p 6006"
},
"devDependencies": {
"@module-federation/enhanced": "^0.7.1",
"@module-federation/rsbuild-plugin": "^0.7.1",
"@module-federation/storybook-addon": "0.0.0-next-20241106024856",
"@rsbuild/plugin-react": "^1.0.7",
"@rslib/core": "workspace:*",
"@types/react": "^18.3.12",
"http-server": "^14.1.1",
"react": "^18.3.1",
"react-dom": "^18.3.1"
"react-dom": "^18.3.1",
"storybook": "^8.3.6",
"storybook-addon-rslib": "^0.1.3",
"storybook-react-rsbuild": "^0.1.3"
},
"peerDependencies": {
"react": "*"
Expand Down
7 changes: 7 additions & 0 deletions examples/module-federation/mf-react-component/rslib.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,13 @@ export default defineConfig({
},
assetPrefix: 'http://localhost:3001/mf',
},
dev: {
assetPrefix: 'http://localhost:3001/mf',
},
// just for dev
server: {
port: 3001,
},
plugins: [
pluginModuleFederation({
name: 'rslib_provider',
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import React from 'react';
// @ts-ignore ignore remote module type check for passing ci run build because we set @mf-types folder in .gitignore
import { Counter } from 'rslib-module';

const Component = () => <Counter />;

export default {
title: 'App Component',
component: Component,
};

export const Primary = {};
8 changes: 6 additions & 2 deletions examples/module-federation/mf-react-component/tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,11 @@
"compilerOptions": {
"jsx": "react-jsx",
"strict": true,
"skipLibCheck": true
"skipLibCheck": true,
"esModuleInterop": true,
"paths": {
"*": ["./@mf-types/*"]
}
},
"include": ["src/**/*"]
"include": ["src/**/*", "stories"]
}
2 changes: 1 addition & 1 deletion examples/module-federation/mf-remote/package.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{
"name": "@example/mf-remote",
"name": "@examples/mf-remote",
"version": "1.0.0",
"private": true,
"scripts": {
Expand Down
8 changes: 4 additions & 4 deletions examples/module-federation/package.json
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
{
"name": "@example/mf",
"name": "@examples/mf",
"private": true,
"scripts": {
"dev:all": "pnpm dev:host & pnpm dev:lib & pnpm dev:remote",
"dev:all": "pnpm dev:host & pnpm serve:lib & pnpm dev:remote",
"dev:host": "pnpm --filter mf-host dev",
"dev:lib": "pnpm --filter mf-react-component run serve",
"dev:remote": "pnpm --filter mf-remote dev"
"dev:remote": "pnpm --filter mf-remote dev",
"serve:lib": "pnpm --filter mf-react-component run serve"
}
}
19 changes: 18 additions & 1 deletion packages/core/src/cli/commands.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import {
loadConfig,
pruneEnvironments,
} from '../config';
import { startMFDevServer } from '../mf';
import { logger } from '../utils/logger';

export type CommonOptions = {
Expand Down Expand Up @@ -45,8 +46,9 @@ export function runCli(): void {

const buildCommand = program.command('build');
const inspectCommand = program.command('inspect');
const mfDevCommand = program.command('mf dev');

[buildCommand, inspectCommand].forEach(applyCommonOptions);
[buildCommand, inspectCommand, mfDevCommand].forEach(applyCommonOptions);

buildCommand
.option(
Expand Down Expand Up @@ -109,5 +111,20 @@ export function runCli(): void {
}
});

mfDevCommand
.description('start Rsbuild dev server of Module Federation format')
.action(async (options: CommonOptions) => {
try {
const rslibConfig = await loadConfig({
path: options.config,
envMode: options.envMode,
});
await startMFDevServer(rslibConfig);
} catch (err) {
logger.error('Failed to start mf dev.');
logger.error(err);
process.exit(1);
}
});
program.parse();
}
49 changes: 49 additions & 0 deletions packages/core/src/mf.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
import { createRsbuild, mergeRsbuildConfig } from '@rsbuild/core';
import { composeCreateRsbuildConfig } from './config';

import type { RsbuildConfig, RsbuildInstance } from '@rsbuild/core';
import type { RslibConfig } from './types';

export async function startMFDevServer(
config: RslibConfig,
): Promise<RsbuildInstance | undefined> {
const rsbuildInstance = await initMFRsbuild(config);
return rsbuildInstance;
}

async function initMFRsbuild(
rslibConfig: RslibConfig,
): Promise<RsbuildInstance | undefined> {
const rsbuildConfigObject = await composeCreateRsbuildConfig(rslibConfig);
const mfRsbuildConfig = rsbuildConfigObject.find(
(config) => config.format === 'mf',
);

if (!mfRsbuildConfig) {
// no mf format, return.
return;
}

mfRsbuildConfig.config = changeEnvToDev(mfRsbuildConfig.config);
const rsbuildInstance = await createRsbuild({
rsbuildConfig: mfRsbuildConfig.config,
});
await rsbuildInstance.startDevServer();
return rsbuildInstance;
}

function changeEnvToDev(rsbuildConfig: RsbuildConfig) {
return mergeRsbuildConfig(rsbuildConfig, {
mode: 'development',
dev: {
writeToDisk: true,
},
tools: {
rspack: {
optimization: {
nodeEnv: 'development',
},
},
},
});
}
Loading

0 comments on commit d52e410

Please sign in to comment.