From 5af6ae024c939cc8fe895d82632c1825cac1c81f Mon Sep 17 00:00:00 2001 From: 2heal1 Date: Mon, 20 Jan 2025 20:23:09 +0800 Subject: [PATCH 1/2] feat: add Create a new Module Federation project cli --- .changeset/beige-otters-tease.md | 5 + .eslintignore | 2 +- .prettierignore | 2 + apps/rslib-module/rslib.config.ts | 6 +- .../create-module-federation/CHANGELOG.md | 1 + packages/create-module-federation/LICENSE | 21 + packages/create-module-federation/README.md | 1 + .../create-module-federation/jest.config.js | 27 + .../create-module-federation/package.json | 44 ++ .../create-module-federation/project.json | 65 ++ .../create-module-federation/rslib.config.ts | 7 + .../create-module-federation/src/create.ts | 400 +++++++++++ .../src/handlebars/index.ts | 66 ++ .../src/handlebars/utils.ts | 21 + .../create-module-federation/src/index.ts | 20 + .../src/materials/FsMaterial.ts | 48 ++ .../src/materials/FsResource.ts | 29 + .../src/materials/constants.ts | 12 + .../src/materials/index.ts | 2 + .../module-federation.config.ts.handlebars | 12 + .../consumer-modern-ts/src/routes/page.tsx | 34 + .../templates/consumer-rsbuild-ts/src/App.tsx | 25 + .../templates/lib-common/.gitignore | 13 + .../module-federation.config.ts.handlebars | 17 + .../templates/lib-common/rslib.config.ts | 46 ++ .../templates/lib-common/src/Description.tsx | 7 + .../templates/lib-common/src/Footer.css | 46 ++ .../templates/lib-common/src/Footer.tsx | 74 ++ .../templates/lib-common/src/Header.tsx | 9 + .../templates/lib-common/src/index.tsx | 3 + .../templates/lib-common/tsconfig.json | 14 + .../templates/modern-common/.browserslistrc | 5 + .../templates/modern-common/.gitignore | 30 + .../templates/modern-common/.npmrc | 1 + .../templates/modern-common/.nvmrc | 1 + .../modern-common/.vscode/extensions.json | 14 + .../modern-common/.vscode/settings.json | 88 +++ .../templates/modern-common/README.md | 37 + .../templates/modern-common/biome.json | 34 + .../templates/modern-common/modern.config.ts | 15 + .../templates/modern-common/package.json | 45 ++ .../modern-common/package.json.handlebars | 45 ++ .../src/components/ComponentInspector.css | 86 +++ .../src/components/ComponentInspector.tsx | 68 ++ .../modern-common/src/modern-app-env.d.ts | 3 + .../modern-common/src/modern.runtime.ts | 3 + .../modern-common/src/routes/index.css | 41 ++ .../modern-common/src/routes/layout.tsx | 9 + .../templates/modern-common/tsconfig.json | 14 + .../module-federation.config.ts.handlebars | 14 + .../src/components/Description.css | 23 + .../src/components/Description.tsx | 11 + .../src/components/Footer.css | 45 ++ .../src/components/Footer.tsx | 74 ++ .../src/components/Header.css | 11 + .../src/components/Header.tsx | 17 + .../provider-modern-ts/src/routes/page.tsx | 34 + .../module-federation.config.ts.handlebars | 14 + .../templates/provider-rsbuild-ts/src/App.tsx | 25 + .../src/components/Description.tsx | 7 + .../src/components/Footer.css | 46 ++ .../src/components/Footer.tsx | 74 ++ .../src/components/Header.tsx | 9 + .../.storybook/main.ts | 41 ++ .../provider-rslib-storybook-ts/README.md | 27 + .../package.json.handlebars | 34 + .../stories/Description.stories.tsx | 11 + .../stories/Footer.stories.tsx | 11 + .../stories/Header.stories.tsx | 11 + .../templates/provider-rslib-ts/README.md | 19 + .../provider-rslib-ts/package.json.handlebars | 48 ++ .../templates/rsbuild-common/.gitignore | 13 + .../templates/rsbuild-common/README.md | 29 + .../rsbuild-common/package.json.handlebars | 22 + .../templates/rsbuild-common/public/.gitkeep | 0 .../rsbuild-common/rsbuild.config.ts | 8 + .../templates/rsbuild-common/src/App.css | 15 + .../rsbuild-common/src/bootstrap.tsx | 13 + .../src/components/ComponentInspector.css | 86 +++ .../src/components/ComponentInspector.tsx | 68 ++ .../templates/rsbuild-common/src/env.d.ts | 1 + .../templates/rsbuild-common/src/index.tsx | 1 + .../templates/rsbuild-common/tsconfig.json | 26 + .../create-module-federation/tsconfig.json | 30 + .../tsconfig.lib.json | 10 + .../tsconfig.spec.json | 14 + packages/data-prefetch/tsconfig.json | 1 - packages/rsbuild-plugin/src/cli/index.ts | 21 +- .../tsconfig.json | 1 - pnpm-lock.yaml | 662 +++++++++++++----- 90 files changed, 3053 insertions(+), 192 deletions(-) create mode 100644 .changeset/beige-otters-tease.md create mode 100644 packages/create-module-federation/CHANGELOG.md create mode 100644 packages/create-module-federation/LICENSE create mode 100644 packages/create-module-federation/README.md create mode 100644 packages/create-module-federation/jest.config.js create mode 100644 packages/create-module-federation/package.json create mode 100644 packages/create-module-federation/project.json create mode 100644 packages/create-module-federation/rslib.config.ts create mode 100644 packages/create-module-federation/src/create.ts create mode 100644 packages/create-module-federation/src/handlebars/index.ts create mode 100644 packages/create-module-federation/src/handlebars/utils.ts create mode 100644 packages/create-module-federation/src/index.ts create mode 100644 packages/create-module-federation/src/materials/FsMaterial.ts create mode 100644 packages/create-module-federation/src/materials/FsResource.ts create mode 100644 packages/create-module-federation/src/materials/constants.ts create mode 100644 packages/create-module-federation/src/materials/index.ts create mode 100644 packages/create-module-federation/templates/consumer-modern-ts/module-federation.config.ts.handlebars create mode 100644 packages/create-module-federation/templates/consumer-modern-ts/src/routes/page.tsx create mode 100644 packages/create-module-federation/templates/consumer-rsbuild-ts/src/App.tsx create mode 100644 packages/create-module-federation/templates/lib-common/.gitignore create mode 100644 packages/create-module-federation/templates/lib-common/module-federation.config.ts.handlebars create mode 100644 packages/create-module-federation/templates/lib-common/rslib.config.ts create mode 100644 packages/create-module-federation/templates/lib-common/src/Description.tsx create mode 100644 packages/create-module-federation/templates/lib-common/src/Footer.css create mode 100644 packages/create-module-federation/templates/lib-common/src/Footer.tsx create mode 100644 packages/create-module-federation/templates/lib-common/src/Header.tsx create mode 100644 packages/create-module-federation/templates/lib-common/src/index.tsx create mode 100644 packages/create-module-federation/templates/lib-common/tsconfig.json create mode 100644 packages/create-module-federation/templates/modern-common/.browserslistrc create mode 100644 packages/create-module-federation/templates/modern-common/.gitignore create mode 100644 packages/create-module-federation/templates/modern-common/.npmrc create mode 100644 packages/create-module-federation/templates/modern-common/.nvmrc create mode 100644 packages/create-module-federation/templates/modern-common/.vscode/extensions.json create mode 100644 packages/create-module-federation/templates/modern-common/.vscode/settings.json create mode 100644 packages/create-module-federation/templates/modern-common/README.md create mode 100644 packages/create-module-federation/templates/modern-common/biome.json create mode 100644 packages/create-module-federation/templates/modern-common/modern.config.ts create mode 100644 packages/create-module-federation/templates/modern-common/package.json create mode 100644 packages/create-module-federation/templates/modern-common/package.json.handlebars create mode 100644 packages/create-module-federation/templates/modern-common/src/components/ComponentInspector.css create mode 100644 packages/create-module-federation/templates/modern-common/src/components/ComponentInspector.tsx create mode 100644 packages/create-module-federation/templates/modern-common/src/modern-app-env.d.ts create mode 100644 packages/create-module-federation/templates/modern-common/src/modern.runtime.ts create mode 100644 packages/create-module-federation/templates/modern-common/src/routes/index.css create mode 100644 packages/create-module-federation/templates/modern-common/src/routes/layout.tsx create mode 100644 packages/create-module-federation/templates/modern-common/tsconfig.json create mode 100644 packages/create-module-federation/templates/provider-modern-ts/module-federation.config.ts.handlebars create mode 100644 packages/create-module-federation/templates/provider-modern-ts/src/components/Description.css create mode 100644 packages/create-module-federation/templates/provider-modern-ts/src/components/Description.tsx create mode 100644 packages/create-module-federation/templates/provider-modern-ts/src/components/Footer.css create mode 100644 packages/create-module-federation/templates/provider-modern-ts/src/components/Footer.tsx create mode 100644 packages/create-module-federation/templates/provider-modern-ts/src/components/Header.css create mode 100644 packages/create-module-federation/templates/provider-modern-ts/src/components/Header.tsx create mode 100644 packages/create-module-federation/templates/provider-modern-ts/src/routes/page.tsx create mode 100644 packages/create-module-federation/templates/provider-rsbuild-ts/module-federation.config.ts.handlebars create mode 100644 packages/create-module-federation/templates/provider-rsbuild-ts/src/App.tsx create mode 100644 packages/create-module-federation/templates/provider-rsbuild-ts/src/components/Description.tsx create mode 100644 packages/create-module-federation/templates/provider-rsbuild-ts/src/components/Footer.css create mode 100644 packages/create-module-federation/templates/provider-rsbuild-ts/src/components/Footer.tsx create mode 100644 packages/create-module-federation/templates/provider-rsbuild-ts/src/components/Header.tsx create mode 100644 packages/create-module-federation/templates/provider-rslib-storybook-ts/.storybook/main.ts create mode 100644 packages/create-module-federation/templates/provider-rslib-storybook-ts/README.md create mode 100644 packages/create-module-federation/templates/provider-rslib-storybook-ts/package.json.handlebars create mode 100644 packages/create-module-federation/templates/provider-rslib-storybook-ts/stories/Description.stories.tsx create mode 100644 packages/create-module-federation/templates/provider-rslib-storybook-ts/stories/Footer.stories.tsx create mode 100644 packages/create-module-federation/templates/provider-rslib-storybook-ts/stories/Header.stories.tsx create mode 100644 packages/create-module-federation/templates/provider-rslib-ts/README.md create mode 100644 packages/create-module-federation/templates/provider-rslib-ts/package.json.handlebars create mode 100644 packages/create-module-federation/templates/rsbuild-common/.gitignore create mode 100644 packages/create-module-federation/templates/rsbuild-common/README.md create mode 100644 packages/create-module-federation/templates/rsbuild-common/package.json.handlebars create mode 100644 packages/create-module-federation/templates/rsbuild-common/public/.gitkeep create mode 100644 packages/create-module-federation/templates/rsbuild-common/rsbuild.config.ts create mode 100644 packages/create-module-federation/templates/rsbuild-common/src/App.css create mode 100644 packages/create-module-federation/templates/rsbuild-common/src/bootstrap.tsx create mode 100644 packages/create-module-federation/templates/rsbuild-common/src/components/ComponentInspector.css create mode 100644 packages/create-module-federation/templates/rsbuild-common/src/components/ComponentInspector.tsx create mode 100644 packages/create-module-federation/templates/rsbuild-common/src/env.d.ts create mode 100644 packages/create-module-federation/templates/rsbuild-common/src/index.tsx create mode 100644 packages/create-module-federation/templates/rsbuild-common/tsconfig.json create mode 100644 packages/create-module-federation/tsconfig.json create mode 100644 packages/create-module-federation/tsconfig.lib.json create mode 100644 packages/create-module-federation/tsconfig.spec.json diff --git a/.changeset/beige-otters-tease.md b/.changeset/beige-otters-tease.md new file mode 100644 index 00000000000..7391b167424 --- /dev/null +++ b/.changeset/beige-otters-tease.md @@ -0,0 +1,5 @@ +--- +'create-module-federation': patch +--- + +feat: add Create a new Module Federation project cli diff --git a/.eslintignore b/.eslintignore index 52908858183..91aba082d89 100644 --- a/.eslintignore +++ b/.eslintignore @@ -1,3 +1,3 @@ node_modules/ vite.config.ts -**/dist/** \ No newline at end of file +**/dist/** diff --git a/.prettierignore b/.prettierignore index 53d01379255..149a6d43d01 100644 --- a/.prettierignore +++ b/.prettierignore @@ -11,3 +11,5 @@ apps/website-new/docs /.nx/workspace-data /.nx/cache + +**/*.handlebars diff --git a/apps/rslib-module/rslib.config.ts b/apps/rslib-module/rslib.config.ts index dd3763fa44b..5e22e8f0955 100644 --- a/apps/rslib-module/rslib.config.ts +++ b/apps/rslib-module/rslib.config.ts @@ -36,11 +36,11 @@ export default defineConfig({ root: './dist/mf', }, }, - server: { - port: 3001, - }, }, ], + server: { + port: 3001, + }, plugins: [ pluginReact(), pluginModuleFederation({ diff --git a/packages/create-module-federation/CHANGELOG.md b/packages/create-module-federation/CHANGELOG.md new file mode 100644 index 00000000000..992a0c9c99a --- /dev/null +++ b/packages/create-module-federation/CHANGELOG.md @@ -0,0 +1 @@ +# create-module-federation diff --git a/packages/create-module-federation/LICENSE b/packages/create-module-federation/LICENSE new file mode 100644 index 00000000000..f428f6109cd --- /dev/null +++ b/packages/create-module-federation/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2024-present hanric(2heal1) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/packages/create-module-federation/README.md b/packages/create-module-federation/README.md new file mode 100644 index 00000000000..b1d45d330a2 --- /dev/null +++ b/packages/create-module-federation/README.md @@ -0,0 +1 @@ +# `create-module-federation` diff --git a/packages/create-module-federation/jest.config.js b/packages/create-module-federation/jest.config.js new file mode 100644 index 00000000000..e99f20c50b7 --- /dev/null +++ b/packages/create-module-federation/jest.config.js @@ -0,0 +1,27 @@ +// For a detailed explanation regarding each configuration property, visit: +// https://jestjs.io/docs/en/configuration.html + +module.exports = { + clearMocks: true, + testEnvironment: 'jsdom', + coveragePathIgnorePatterns: ['__tests__', '/node_modules/'], + coverageProvider: 'v8', + coverageReporters: ['cobertura', 'clover', 'json', 'lcov', 'text'], + globals: { + __DEV__: true, + __TEST__: true, + __BROWSER__: false, + __VERSION__: '"unknown"', + }, + preset: 'ts-jest', + transformIgnorePatterns: [ + // Change MODULE_NAME_HERE to your module that isn't being compiled + '/node_modules/(?!((@byted/garfish-)|(byted-tea-sdk))).+\\.js$', + ], + transform: { + '^.+\\.(t|j)sx?$': ['@swc/jest'], + }, + rootDir: __dirname, + testMatch: ['__tests__/**/**.spec.[jt]s?(x)'], + testPathIgnorePatterns: ['/node_modules/'], +}; diff --git a/packages/create-module-federation/package.json b/packages/create-module-federation/package.json new file mode 100644 index 00000000000..298cf225f7d --- /dev/null +++ b/packages/create-module-federation/package.json @@ -0,0 +1,44 @@ +{ + "name": "create-module-federation", + "description": "Create a new Module Federation project", + "public": true, + "sideEffects": false, + "version": "0.0.0", + "license": "MIT", + "repository": { + "type": "git", + "url": "https://github.com/module-federation/core/", + "directory": "packages/create-module-federation" + }, + "publishConfig": { + "access": "public" + }, + "type": "module", + "scripts": { + "build": "rslib build" + }, + "main": "./dist/index.js", + "bin": { + "create-module-federation": "./dist/index.js" + }, + "files": [ + "template", + "dist" + ], + "dependencies": { + "@clack/prompts": "^0.8.2", + "execa": "5.1.1", + "fs-extra": "9.1.0", + "minimist": "^1.2.8", + "rslog": "^1.2.3", + "glob": "7.2.0", + "handlebars": "4.7.7" + }, + "devDependencies": { + "@types/glob": "7.2.0", + "@types/minimist": "^1.2.5", + "@types/fs-extra": "9.0.6", + "@rslib/core": "^0.3.1", + "rsbuild-plugin-publint": "^0.2.1" + } +} diff --git a/packages/create-module-federation/project.json b/packages/create-module-federation/project.json new file mode 100644 index 00000000000..98bc4c8d17a --- /dev/null +++ b/packages/create-module-federation/project.json @@ -0,0 +1,65 @@ +{ + "name": "create-module-federation", + "$schema": "../../node_modules/nx/schemas/project-schema.json", + "sourceRoot": "packages/create-module-federation/src", + "projectType": "library", + "tags": ["type:pkg"], + "targets": { + "build": { + "executor": "nx:run-commands", + "options": { + "parallel": false, + "dependsOn": [ + { + "target": "build", + "dependencies": true + } + ], + "commands": [ + "cd packages/create-module-federation; pnpm run build", + "cp packages/create-module-federation/LICENSE packages/create-module-federation/dist" + ] + } + }, + "lint": { + "executor": "@nx/eslint:lint", + "outputs": ["{options.outputFile}"], + "options": { + "lintFilePatterns": [ + "packages/create-module-federation/**/*.ts", + "packages/create-module-federation/package.json" + ] + } + }, + "test": { + "executor": "@nx/jest:jest", + "outputs": ["{workspaceRoot}/coverage/{projectRoot}"], + "options": { + "jestConfig": "packages/create-module-federation/jest.config.js", + "passWithNoTests": true + }, + "configurations": { + "ci": { + "ci": true, + "codeCoverage": true + } + } + }, + "pre-release": { + "executor": "nx:run-commands", + "options": { + "parallel": false, + "commands": [ + { + "command": "nx run create-module-federation:test", + "forwardAllArgs": false + }, + { + "command": "nx run create-module-federation:build", + "forwardAllArgs": false + } + ] + } + } + } +} diff --git a/packages/create-module-federation/rslib.config.ts b/packages/create-module-federation/rslib.config.ts new file mode 100644 index 00000000000..bf77c216693 --- /dev/null +++ b/packages/create-module-federation/rslib.config.ts @@ -0,0 +1,7 @@ +import { pluginPublint } from 'rsbuild-plugin-publint'; +import { defineConfig } from '@rslib/core'; + +export default defineConfig({ + lib: [{ format: 'esm' }], + plugins: [pluginPublint()], +}); diff --git a/packages/create-module-federation/src/create.ts b/packages/create-module-federation/src/create.ts new file mode 100644 index 00000000000..2b798ccba0d --- /dev/null +++ b/packages/create-module-federation/src/create.ts @@ -0,0 +1,400 @@ +/** + * forked from https://github.com/rspack-contrib/create-rstack + * license at https://github.com/rspack-contrib/create-rstack/blob/main/LICENSE + */ +import path from 'path'; +import fs from 'fs'; +import { + cancel, + isCancel, + multiselect, + note, + outro, + select, + text, +} from '@clack/prompts'; +import minimist from 'minimist'; +import { logger } from 'rslog'; +import { FsMaterial } from './materials/FsMaterial'; +import { HandlebarsAPI } from './handlebars'; + +type Argv = { + help?: boolean; + dir?: string; + template?: string; + override?: boolean; +}; + +type ProjectType = 'lib' | 'app'; +type RoleType = 'consumer' | 'provider'; +type AppTemplateName = 'modern' | 'rsbuild'; +type LibTemplateName = 'rslib'; +type ProviderInfo = { + name: string; + entry: string; +}; + +function upperFirst(str: string) { + return str.charAt(0).toUpperCase() + str.slice(1); +} + +function logHelpMessage(name: string, templates: string[]) { + logger.log(` + Usage: create-${name} [options] + + Options: + + -h, --help display help for command + -d, --dir create project in specified directory + -t, --template specify the template to use + --override override files in target directory + + Templates: + + ${templates.join(', ')} +`); +} + +function pkgFromUserAgent(userAgent: string | undefined) { + if (!userAgent) return undefined; + const pkgSpec = userAgent.split(' ')[0]; + const pkgSpecArr = pkgSpec.split('/'); + return { + name: pkgSpecArr[0], + version: pkgSpecArr[1], + }; +} + +function cancelAndExit() { + cancel('Operation cancelled.'); + process.exit(0); +} + +function checkCancel(value: unknown) { + if (isCancel(value)) { + cancelAndExit(); + } + return value as T; +} + +/** + * 1. Input: 'foo' + * Output: folder `/foo`, `package.json#name` -> `foo` + * + * 2. Input: 'foo/bar' + * Output: folder -> `/foo/bar` folder, `package.json#name` -> `bar` + * + * 3. Input: '@scope/foo' + * Output: folder -> `/@scope/bar` folder, `package.json#name` -> `@scope/foo` + * + * 4. Input: './foo/bar' + * Output: folder -> `/foo/bar` folder, `package.json#name` -> `bar` + * + * 5. Input: '/root/path/to/foo' + * Output: folder -> `'/root/path/to/foo'` folder, `package.json#name` -> `foo` + */ +function formatDir(input: string) { + return { + targetDir: input.trim().replace(/\/+$/g, ''), + }; +} + +function isEmptyDir(path: string) { + const files = fs.readdirSync(path); + return files.length === 0 || (files.length === 1 && files[0] === '.git'); +} + +async function getAppTemplateName( + { + roleType, + framework, + }: { + roleType: RoleType; + framework: AppTemplateName; + }, + { template }: Argv, +) { + if (template) { + return `${template}-ts`; + } + + let providerInfo: ProviderInfo = { + name: '', + entry: '', + }; + + if (roleType === 'consumer') { + const providerName = checkCancel( + await text({ + message: + 'Please input your provider name (You can skip by press "enter"):', + defaultValue: '', + }), + ); + + if (providerName) { + providerInfo.name = providerName; + + const providerEntry = checkCancel( + await text({ + message: `Please input your provider("${providerName}") entry:`, + validate(value) { + if (value.length === 0) { + return 'Entry is required'; + } + }, + }), + ); + providerInfo.entry = providerEntry; + } + } + + return `${roleType}-${framework}-ts`; +} + +async function getLibTemplateName({ template }: Argv) { + if (template) { + return `${template}-ts`; + } + + const templateName = checkCancel( + await select({ + message: 'Select template', + options: [{ value: 'rslib', label: 'Rslib' }], + }), + ); + + type ExcludesFalse = (x: T | false) => x is T; + const tools = checkCancel( + await multiselect({ + message: + 'Select development tools (Use to select, to continue)', + required: false, + options: [ + { + value: 'storybook', + label: 'Storybook', + }, + // TODO: support Rspress Module doc in the future + ].filter(Boolean as any as ExcludesFalse), + }), + ); + + // not support consumer yet, only support consume by runtime api + const roleType: RoleType = 'provider'; + + if (!tools || !Object.keys(tools).length) { + return `${roleType}-${templateName}-ts`; + } + + return `${roleType}-${templateName}-[${Object.keys(tools)}]-ts`; +} + +function getTemplateName( + { + projectType, + roleType, + framework, + }: { + projectType: ProjectType; + roleType: RoleType; + framework: AppTemplateName; + }, + args: Argv, +) { + if (projectType === 'app') { + return getAppTemplateName( + { + roleType, + framework, + }, + args, + ); + } + return getLibTemplateName(args); +} + +function getTemplateDir(templateName: string) { + return `templates/${templateName}/`; +} + +async function forgeTemplate({ + projectType, + argv, + templateParameters, + distFolder, +}: { + projectType: ProjectType; + argv: Argv; + templateParameters: Record; + distFolder: string; +}) { + let framework: AppTemplateName = 'modern'; + let roleType: RoleType = 'provider'; + + if (projectType === 'app') { + framework = checkCancel( + await select({ + message: 'Select template', + options: [ + { value: 'modern', label: 'Modern.js Framework' }, + { value: 'rsbuild', label: 'Rsbuild' }, + ], + }), + ); + + roleType = checkCancel( + await select({ + message: 'Please select the role of project you want to create:', + initialValue: 'provider', + options: [ + { value: 'consumer', label: 'Consumer' }, + { value: 'provider', label: 'Provider' }, + ], + }), + ); + } + + const templateName = await getTemplateName( + { + projectType, + framework, + roleType, + }, + argv, + ); + const material = new FsMaterial(__dirname); + + const renderTemplate = async (templateDir: string) => { + const templatePattern = `${templateDir}**/*`; + const resourceMap = await material.find(templatePattern, { + nodir: true, + dot: true, + }); + + const parameters = { + ...templateParameters, + }; + + await Promise.all( + Object.keys(resourceMap).map(async (resourceKey) => { + const target = resourceKey + .replace(templateDir, ``) + .replace('.handlebars', ``); + + const handlebarsAPI = new HandlebarsAPI(); + await handlebarsAPI.renderTemplate( + material.get(resourceKey), + target, + distFolder, + { + ...parameters, + }, + ); + }), + ); + }; + + const templateDir = getTemplateDir(templateName); + + let commonTemplateDir = ''; + if (projectType === 'lib') { + commonTemplateDir = 'templates/lib-common/'; + } else { + commonTemplateDir = `templates/${framework}-common/`; + } + + await renderTemplate(commonTemplateDir); + await renderTemplate(templateDir); +} + +export async function create({ + name, + templates, +}: { + name: string; + templates: string[]; +}) { + const argv = minimist(process.argv.slice(2), { + alias: { h: 'help', d: 'dir', t: 'template' }, + }); + + console.log(''); + logger.greet(`◆ Create ${upperFirst(name)} Project`); + + if (argv.help) { + logHelpMessage(name, templates); + return; + } + + const cwd = process.cwd(); + const pkgInfo = pkgFromUserAgent(process.env['npm_config_user_agent']); + const pkgManager = pkgInfo ? pkgInfo.name : 'npm'; + + const mfName = + argv.dir ?? + checkCancel( + await text({ + message: 'Please input Module Federation name:', + placeholder: 'mf-project-name', + defaultValue: 'mf-project-name', + validate(value) { + if (value.length === 0) { + return 'Name is required'; + } + }, + }), + ); + + const dir = argv.dir || 'mf-project'; + + const { targetDir } = formatDir(dir); + const distFolder = path.isAbsolute(targetDir) + ? targetDir + : path.join(cwd, targetDir); + + if (!argv.override && fs.existsSync(distFolder) && !isEmptyDir(distFolder)) { + const option = checkCancel( + await select({ + message: `"${targetDir}" is not empty, please choose:`, + options: [ + { value: 'yes', label: 'Continue and override files' }, + { value: 'no', label: 'Cancel operation' }, + ], + }), + ); + + if (option === 'no') { + cancelAndExit(); + } + } + + const projectType = checkCancel( + await select({ + message: 'Please select the type of project you want to create:', + options: [ + { value: 'app', label: 'Application' }, + { value: 'lib', label: 'Lib' }, + ], + }), + ); + + await forgeTemplate({ + projectType, + argv, + templateParameters: { + mfName, + }, + distFolder, + }); + + const nextSteps = [ + `cd ${targetDir}`, + `${pkgManager} install`, + `${pkgManager} run dev`, + ]; + + note(nextSteps.join('\n'), 'Next steps'); + + outro('Done.'); +} diff --git a/packages/create-module-federation/src/handlebars/index.ts b/packages/create-module-federation/src/handlebars/index.ts new file mode 100644 index 00000000000..871d0f4892a --- /dev/null +++ b/packages/create-module-federation/src/handlebars/index.ts @@ -0,0 +1,66 @@ +import { FS_RESOURCE } from '../materials'; +import { renderString, outputFs } from './utils'; + +import type { FsMaterial, FsResource } from '../materials'; + +type TargetFunction = (globMatch: string) => string; + +type RenderTemplateDirOptions = { + nodir?: boolean; + dot?: boolean; + ignore?: string | readonly string[]; + parameters?: Record; +}; + +export { renderString }; + +export class HandlebarsAPI { + async renderTemplate( + templateResource: FsResource, + target: string, + outputFilePath: string, + parameters: Record = {}, + ) { + if (templateResource._type !== FS_RESOURCE) { + throw new Error('resource not match'); + } + const resourceValue = await templateResource.value(); + if (typeof resourceValue.content !== 'string') { + throw new Error( + `resource.value is not string, resourceValue=${ + resourceValue as unknown as string + }`, + ); + } + await outputFs( + target, + renderString(resourceValue.content, parameters), + outputFilePath, + { encoding: 'utf-8' }, + ); + } + + async renderTemplateDir( + material: FsMaterial, + findGlob: string, + target: TargetFunction, + outputFilePath: string, + options?: RenderTemplateDirOptions, + ) { + const resourceMap = await material.find(findGlob, { + nodir: true, + ...options, + }); + await Promise.all( + // resourceKey is relate path. example: in `garr-master/package.json`, package.json is resourceKey + Object.keys(resourceMap).map(async (resourceKey) => { + await this.renderTemplate( + material.get(resourceKey), + target(resourceKey), + outputFilePath, + options?.parameters, + ); + }), + ); + } +} diff --git a/packages/create-module-federation/src/handlebars/utils.ts b/packages/create-module-federation/src/handlebars/utils.ts new file mode 100644 index 00000000000..73c93ff827f --- /dev/null +++ b/packages/create-module-federation/src/handlebars/utils.ts @@ -0,0 +1,21 @@ +import fs from 'fs-extra'; +import path from 'path'; +import handlebars from 'handlebars'; + +export function renderString( + template: string, + fullData: Record, +): string { + return handlebars.compile(template)(fullData) || ''; +} + +export async function outputFs( + file: string | number, + content: any, + outputPath: string, + options?: fs.WriteFileOptions | string, +) { + const filePath = path.resolve(outputPath, file.toString()); + await fs.mkdirp(path.dirname(filePath)); + await fs.writeFile(filePath, content, options); +} diff --git a/packages/create-module-federation/src/index.ts b/packages/create-module-federation/src/index.ts new file mode 100644 index 00000000000..165aa271a85 --- /dev/null +++ b/packages/create-module-federation/src/index.ts @@ -0,0 +1,20 @@ +import { create } from './create'; + +interface Template { + template: string; + tools?: Record; +} + +export const TEMPLATES = [ + 'provider-modern', + 'consumer-modern', + 'provider-rsbuild', + 'consumer-rsbuild', + 'rslib', + 'rslib-storybook', +]; + +create({ + name: 'Module Federation', + templates: TEMPLATES, +}); diff --git a/packages/create-module-federation/src/materials/FsMaterial.ts b/packages/create-module-federation/src/materials/FsMaterial.ts new file mode 100644 index 00000000000..a91eabc506c --- /dev/null +++ b/packages/create-module-federation/src/materials/FsMaterial.ts @@ -0,0 +1,48 @@ +import path from 'path'; +import glob from 'glob'; +import { FsResource } from './FsResource'; + +const promisifyGlob = ( + pattern: string, + options: glob.IOptions, +): Promise => + new Promise((resolve, reject) => { + glob(pattern, options, (err, files) => + err === null ? resolve(files) : reject(err), + ); + }); + +export class FsMaterial { + basePath: string; + + constructor(basePath: string) { + this.basePath = basePath; + } + + get(resourceKey: string) { + return new FsResource( + path.resolve(this.basePath, resourceKey), + resourceKey, + ); + } + + async find( + globStr: string, + options?: { + nodir?: boolean; + dot?: boolean; + ignore?: string | readonly string[]; + }, + ): Promise> { + const matches = await promisifyGlob(globStr, { + cwd: path.resolve(this.basePath), + nodir: options?.nodir, + dot: options?.dot, + ignore: options?.ignore, + }); + return matches.reduce>((pre, cur) => { + pre[cur] = new FsResource(path.resolve(this.basePath, cur), cur); + return pre; + }, {}); + } +} diff --git a/packages/create-module-federation/src/materials/FsResource.ts b/packages/create-module-federation/src/materials/FsResource.ts new file mode 100644 index 00000000000..1dbbacd7d92 --- /dev/null +++ b/packages/create-module-federation/src/materials/FsResource.ts @@ -0,0 +1,29 @@ +import type { Buffer } from 'buffer'; +import path from 'path'; +import fs from 'fs-extra'; +import { IMAGE_EXT_LIST } from './constants'; + +export const FS_RESOURCE = '_mf_fs_resource'; + +export class FsResource { + _type: string = FS_RESOURCE; + + filePath: string; + + resourceKey: string; + + constructor(filePath: string, resourceKey: string) { + this.filePath = filePath; + this.resourceKey = resourceKey; + } + + async value(): Promise<{ content: string | Buffer }> { + const resourceFileExt = path.extname(this.filePath); + if (IMAGE_EXT_LIST.includes(resourceFileExt)) { + const buffer = await fs.readFile(path.resolve(this.filePath)); + return { content: buffer }; + } + const text = await fs.readFile(path.resolve(this.filePath), 'utf8'); + return { content: text }; + } +} diff --git a/packages/create-module-federation/src/materials/constants.ts b/packages/create-module-federation/src/materials/constants.ts new file mode 100644 index 00000000000..32e6a4449ec --- /dev/null +++ b/packages/create-module-federation/src/materials/constants.ts @@ -0,0 +1,12 @@ +export const IMAGE_EXT_LIST = [ + '.jpg', + '.jpeg', + '.png', + '.gif', + '.bmp', + '.ico', + '.icon', + '.mpt', + '.psd', + '.wmf', +]; diff --git a/packages/create-module-federation/src/materials/index.ts b/packages/create-module-federation/src/materials/index.ts new file mode 100644 index 00000000000..4ec1a014781 --- /dev/null +++ b/packages/create-module-federation/src/materials/index.ts @@ -0,0 +1,2 @@ +export { FsMaterial } from './FsMaterial'; +export { FsResource, FS_RESOURCE } from './FsResource'; diff --git a/packages/create-module-federation/templates/consumer-modern-ts/module-federation.config.ts.handlebars b/packages/create-module-federation/templates/consumer-modern-ts/module-federation.config.ts.handlebars new file mode 100644 index 00000000000..776aa71a4dd --- /dev/null +++ b/packages/create-module-federation/templates/consumer-modern-ts/module-federation.config.ts.handlebars @@ -0,0 +1,12 @@ +import { createModuleFederationConfig } from '@module-federation/modern-js'; + +export default createModuleFederationConfig({ + name: '{{mfName}}', + remotes: { + 'provider': 'provider@http://localhost:8080/mf-manifest.json', + }, + shared: { + react: { singleton: true }, + 'react-dom': { singleton: true }, + }, +}); diff --git a/packages/create-module-federation/templates/consumer-modern-ts/src/routes/page.tsx b/packages/create-module-federation/templates/consumer-modern-ts/src/routes/page.tsx new file mode 100644 index 00000000000..b1970527406 --- /dev/null +++ b/packages/create-module-federation/templates/consumer-modern-ts/src/routes/page.tsx @@ -0,0 +1,34 @@ +import { Helmet } from '@modern-js/runtime/head'; +import './index.css'; +import { ComponentInspector } from '../components/ComponentInspector'; +import Header from 'provider/Header'; +import Description from 'provider/Description'; +import Footer from 'provider/Footer'; + +const Index = () => ( +
+ + + + +
+ +
+ + + + + + + +
+ +
+
+); + +export default Index; diff --git a/packages/create-module-federation/templates/consumer-rsbuild-ts/src/App.tsx b/packages/create-module-federation/templates/consumer-rsbuild-ts/src/App.tsx new file mode 100644 index 00000000000..7932bce7d14 --- /dev/null +++ b/packages/create-module-federation/templates/consumer-rsbuild-ts/src/App.tsx @@ -0,0 +1,25 @@ +import './App.css'; +import { ComponentInspector } from './components/ComponentInspector'; +import Header from 'provider/Header'; +import Description from 'provider/Description'; +import Footer from 'provider/Footer'; + +const App = () => { + return ( +
+ +
+ + + + + + + +
+ +
+ ); +}; + +export default App; diff --git a/packages/create-module-federation/templates/lib-common/.gitignore b/packages/create-module-federation/templates/lib-common/.gitignore new file mode 100644 index 00000000000..38d7344c8ca --- /dev/null +++ b/packages/create-module-federation/templates/lib-common/.gitignore @@ -0,0 +1,13 @@ +# Local +.DS_Store +*.local +*.log* + +# Dist +node_modules +dist/ + +# IDE +.vscode/* +!.vscode/extensions.json +.idea diff --git a/packages/create-module-federation/templates/lib-common/module-federation.config.ts.handlebars b/packages/create-module-federation/templates/lib-common/module-federation.config.ts.handlebars new file mode 100644 index 00000000000..e22e0e062c8 --- /dev/null +++ b/packages/create-module-federation/templates/lib-common/module-federation.config.ts.handlebars @@ -0,0 +1,17 @@ +export default { + name: '{{mfName}}', + exposes: { + '.': './src/index.tsx', + './Header': './src/Header.tsx', + './Description': './src/Description.tsx', + './Footer': './src/Footer.tsx', + }, + shared: { + react: { + singleton: true, + }, + 'react-dom': { + singleton: true, + }, + }, +} diff --git a/packages/create-module-federation/templates/lib-common/rslib.config.ts b/packages/create-module-federation/templates/lib-common/rslib.config.ts new file mode 100644 index 00000000000..76460fb94f9 --- /dev/null +++ b/packages/create-module-federation/templates/lib-common/rslib.config.ts @@ -0,0 +1,46 @@ +import { pluginReact } from '@rsbuild/plugin-react'; +import { defineConfig } from '@rslib/core'; +import { pluginModuleFederation } from '@module-federation/rsbuild-plugin'; +import moduleFederationConfig from './module-federation.config'; + +const shared = { + dts: { + bundle: false, + }, +}; + +export default defineConfig({ + lib: [ + { + ...shared, + format: 'esm', + output: { + distPath: { + root: './dist/esm', + }, + }, + }, + { + ...shared, + format: 'cjs', + output: { + distPath: { + root: './dist/cjs', + }, + }, + }, + { + ...shared, + format: 'mf', + output: { + distPath: { + root: './dist/mf', + }, + }, + }, + ], + server: { + port: 3001, + }, + plugins: [pluginReact(), pluginModuleFederation(moduleFederationConfig)], +}); diff --git a/packages/create-module-federation/templates/lib-common/src/Description.tsx b/packages/create-module-federation/templates/lib-common/src/Description.tsx new file mode 100644 index 00000000000..d096ea42e4d --- /dev/null +++ b/packages/create-module-federation/templates/lib-common/src/Description.tsx @@ -0,0 +1,7 @@ +const Description = () => { + return ( +

Start building amazing things with Rsbuild.

+ ); +}; + +export default Description; diff --git a/packages/create-module-federation/templates/lib-common/src/Footer.css b/packages/create-module-federation/templates/lib-common/src/Footer.css new file mode 100644 index 00000000000..f305fc60934 --- /dev/null +++ b/packages/create-module-federation/templates/lib-common/src/Footer.css @@ -0,0 +1,46 @@ +.footer { + width: 100%; + display: flex; + align-items: center; + justify-content: center; + margin-top: 3rem; +} + +.card { + padding: 1.5rem; + display: flex; + flex-direction: column; + justify-content: center; + height: 100px; + color: inherit; + text-decoration: none; + transition: 0.15s ease; + width: 45%; +} + +.card:hover, +.card:focus { + transform: scale(1.05); +} + +.card h2 { + display: flex; + align-items: center; + font-size: 1.5rem; + margin: 0; + padding: 0; +} + +.card p { + display: flex; + opacity: 0.6; + font-size: 0.9rem; + line-height: 1.5; + margin-top: 1rem; +} + +.arrow-right { + width: 1.3rem; + margin-left: 0.5rem; + margin-top: 3px; +} diff --git a/packages/create-module-federation/templates/lib-common/src/Footer.tsx b/packages/create-module-federation/templates/lib-common/src/Footer.tsx new file mode 100644 index 00000000000..00d1cbd97b1 --- /dev/null +++ b/packages/create-module-federation/templates/lib-common/src/Footer.tsx @@ -0,0 +1,74 @@ +import './Footer.css'; + +const Footer = () => { + return ( + + ); +}; + +export default Footer; diff --git a/packages/create-module-federation/templates/lib-common/src/Header.tsx b/packages/create-module-federation/templates/lib-common/src/Header.tsx new file mode 100644 index 00000000000..53aff275ee8 --- /dev/null +++ b/packages/create-module-federation/templates/lib-common/src/Header.tsx @@ -0,0 +1,9 @@ +const Header = () => { + return ( +
+

Rsbuild with React

+
+ ); +}; + +export default Header; diff --git a/packages/create-module-federation/templates/lib-common/src/index.tsx b/packages/create-module-federation/templates/lib-common/src/index.tsx new file mode 100644 index 00000000000..3fac13945ef --- /dev/null +++ b/packages/create-module-federation/templates/lib-common/src/index.tsx @@ -0,0 +1,3 @@ +export * as Description from './Description'; +export * as Footer from './Footer'; +export * as Header from './Header'; diff --git a/packages/create-module-federation/templates/lib-common/tsconfig.json b/packages/create-module-federation/templates/lib-common/tsconfig.json new file mode 100644 index 00000000000..ae6b0a38bc7 --- /dev/null +++ b/packages/create-module-federation/templates/lib-common/tsconfig.json @@ -0,0 +1,14 @@ +{ + "compilerOptions": { + "lib": ["DOM", "ES2021"], + "module": "ESNext", + "jsx": "react-jsx", + "strict": true, + "skipLibCheck": true, + "isolatedModules": true, + "resolveJsonModule": true, + "moduleResolution": "bundler", + "useDefineForClassFields": true + }, + "include": ["src"] +} diff --git a/packages/create-module-federation/templates/modern-common/.browserslistrc b/packages/create-module-federation/templates/modern-common/.browserslistrc new file mode 100644 index 00000000000..f5ceef6bb8e --- /dev/null +++ b/packages/create-module-federation/templates/modern-common/.browserslistrc @@ -0,0 +1,5 @@ +chrome >= 51 +edge >= 15 +firefox >= 54 +safari >= 10 +ios_saf >= 10 diff --git a/packages/create-module-federation/templates/modern-common/.gitignore b/packages/create-module-federation/templates/modern-common/.gitignore new file mode 100644 index 00000000000..cd3fdf89df0 --- /dev/null +++ b/packages/create-module-federation/templates/modern-common/.gitignore @@ -0,0 +1,30 @@ +.DS_Store + +.pnp +.pnp.js +.env.local +.env.*.local +.history +*.log* + +node_modules/ +.yarn-integrity +.pnpm-store/ +*.tsbuildinfo +.changeset/pre.json + +dist/ +coverage/ +release/ +output/ +output_resource/ +log/ + +.vscode/**/* +!.vscode/settings.json +!.vscode/extensions.json +.idea/ + +**/*/typings/auto-generated + +modern.config.local.* diff --git a/packages/create-module-federation/templates/modern-common/.npmrc b/packages/create-module-federation/templates/modern-common/.npmrc new file mode 100644 index 00000000000..319e41e69dc --- /dev/null +++ b/packages/create-module-federation/templates/modern-common/.npmrc @@ -0,0 +1 @@ +strict-peer-dependencies=false diff --git a/packages/create-module-federation/templates/modern-common/.nvmrc b/packages/create-module-federation/templates/modern-common/.nvmrc new file mode 100644 index 00000000000..a77793ecc52 --- /dev/null +++ b/packages/create-module-federation/templates/modern-common/.nvmrc @@ -0,0 +1 @@ +lts/hydrogen diff --git a/packages/create-module-federation/templates/modern-common/.vscode/extensions.json b/packages/create-module-federation/templates/modern-common/.vscode/extensions.json new file mode 100644 index 00000000000..de9dced4ecd --- /dev/null +++ b/packages/create-module-federation/templates/modern-common/.vscode/extensions.json @@ -0,0 +1,14 @@ +{ + "recommendations": [ + "styled-components.vscode-styled-components", + "EditorConfig.editorconfig", + "streetsidesoftware.code-spell-checker", + "codezombiech.gitignore", + "aaron-bond.better-comments", + "jasonnutter.search-node-modules", + "jock.svg", + "mikestead.dotenv", + "vscode-icons-team.vscode-icons", + "biomejs.biome" + ] +} diff --git a/packages/create-module-federation/templates/modern-common/.vscode/settings.json b/packages/create-module-federation/templates/modern-common/.vscode/settings.json new file mode 100644 index 00000000000..7326d94d7a8 --- /dev/null +++ b/packages/create-module-federation/templates/modern-common/.vscode/settings.json @@ -0,0 +1,88 @@ +{ + "files.associations": { + ".code-workspace": "jsonc", + ".stylelintrc": "jsonc", + "stylelintrc": "jsonc", + "README": "markdown" + }, + "search.useIgnoreFiles": true, + "search.exclude": { + "**/dist": true, + "**/*.log": true, + "**/*.pid": true, + "**/.git": true, + "**/node_modules": true + }, + // + "editor.rulers": [80, 120], + "files.eol": "\n", + "files.trimTrailingWhitespace": true, + "files.insertFinalNewline": true, + // + "cSpell.diagnosticLevel": "Hint", + "javascript.validate.enable": false, + "typescript.validate.enable": true, + "css.validate": false, + "scss.validate": false, + "less.validate": false, + "[css]": { + "editor.formatOnType": true, + "editor.formatOnPaste": true, + "editor.formatOnSave": true + }, + "[scss]": { + "editor.formatOnType": true, + "editor.formatOnPaste": true, + "editor.formatOnSave": true + }, + "[less]": { + "editor.formatOnType": true, + "editor.formatOnPaste": true, + "editor.formatOnSave": true + }, + "editor.codeActionsOnSave": { + "quickfix.biome": "explicit" + }, + "editor.defaultFormatter": "biomejs.biome", + "editor.formatOnSave": true, + "[typescript]": { + "editor.defaultFormatter": "biomejs.biome", + "editor.codeActionsOnSave": { + "quickfix.biome": "explicit", + "source.organizeImports": "never", + "source.organizeImports.biome": "explicit" + } + }, + "[typescriptreact]": { + "editor.defaultFormatter": "biomejs.biome", + "editor.codeActionsOnSave": { + "quickfix.biome": "explicit", + "source.organizeImports": "never", + "source.organizeImports.biome": "explicit" + } + }, + "[json]": { + "editor.defaultFormatter": "biomejs.biome" + }, + "[jsonc]": { + "editor.defaultFormatter": "biomejs.biome" + }, + "[javascriptreact]": { + "editor.defaultFormatter": "biomejs.biome", + "editor.codeActionsOnSave": { + "quickfix.biome": "explicit", + "source.organizeImports": "never", + "source.organizeImports.biome": "explicit" + } + }, + "[javascript]": { + "editor.defaultFormatter": "biomejs.biome", + "editor.codeActionsOnSave": { + "quickfix.biome": "explicit", + "source.organizeImports": "never", + "source.organizeImports.biome": "explicit" + } + }, + "emmet.triggerExpansionOnTab": true, + "typescript.tsdk": "node_modules/typescript/lib" +} diff --git a/packages/create-module-federation/templates/modern-common/README.md b/packages/create-module-federation/templates/modern-common/README.md new file mode 100644 index 00000000000..38d1d2a1c55 --- /dev/null +++ b/packages/create-module-federation/templates/modern-common/README.md @@ -0,0 +1,37 @@ +# Modern.js App + +## Setup + +Install the dependencies: + +```bash +pnpm install +``` + +## Get Started + +Start the dev server: + +```bash +pnpm dev +``` + +Enable optional features or add a new entry: + +```bash +pnpm new +``` + +Build the app for production: + +```bash +pnpm build +``` + +Preview the production build locally: + +```bash +pnpm serve +``` + +For more information, see the [Modern.js documentation](https://modernjs.dev/en). diff --git a/packages/create-module-federation/templates/modern-common/biome.json b/packages/create-module-federation/templates/modern-common/biome.json new file mode 100644 index 00000000000..58a177b1d2e --- /dev/null +++ b/packages/create-module-federation/templates/modern-common/biome.json @@ -0,0 +1,34 @@ +{ + "$schema": "https://biomejs.dev/schemas/1.8.3/schema.json", + "vcs": { + "enabled": true, + "defaultBranch": "main", + "clientKind": "git", + "useIgnoreFile": true + }, + "formatter": { + "enabled": true, + "indentStyle": "space" + }, + "javascript": { + "formatter": { + "quoteStyle": "single", + "arrowParentheses": "asNeeded", + "jsxQuoteStyle": "double", + "lineWidth": 80 + } + }, + "linter": { + "enabled": true, + "rules": { + "recommended": true + } + }, + "organizeImports": { + "enabled": true + }, + "files": { + "ignoreUnknown": true, + "ignore": [".vscode/**/*", "node_modules/**/*", "dist/**/*"] + } +} diff --git a/packages/create-module-federation/templates/modern-common/modern.config.ts b/packages/create-module-federation/templates/modern-common/modern.config.ts new file mode 100644 index 00000000000..acaec611f69 --- /dev/null +++ b/packages/create-module-federation/templates/modern-common/modern.config.ts @@ -0,0 +1,15 @@ +import { appTools, defineConfig } from '@modern-js/app-tools'; +import { moduleFederationPlugin } from '@module-federation/modern-js'; + +// https://modernjs.dev/en/configure/app/usage +export default defineConfig({ + runtime: { + router: true, + }, + plugins: [ + appTools({ + bundler: 'rspack', // Set to 'webpack' to enable webpack + }), + moduleFederationPlugin(), + ], +}); diff --git a/packages/create-module-federation/templates/modern-common/package.json b/packages/create-module-federation/templates/modern-common/package.json new file mode 100644 index 00000000000..92467bd2e56 --- /dev/null +++ b/packages/create-module-federation/templates/modern-common/package.json @@ -0,0 +1,45 @@ +{ + "name": "new-provider", + "version": "0.1.0", + "scripts": { + "reset": "npx rimraf node_modules ./**/node_modules", + "dev": "modern dev", + "build": "modern build", + "start": "modern start", + "serve": "modern serve", + "new": "modern new", + "lint": "biome check", + "prepare": "simple-git-hooks", + "upgrade": "modern upgrade" + }, + "engines": { + "node": ">=16.18.1" + }, + "lint-staged": { + "*.{js,ts,cjs,mjs,d.cts,d.mts,jsx,tsx,json,jsonc}": [ + "biome check --files-ignore-unknown=true" + ] + }, + "simple-git-hooks": { + "pre-commit": "npx lint-staged" + }, + "dependencies": { + "@modern-js/runtime": "2.63.7", + "react": "^18.3.1", + "react-dom": "^18.3.1" + }, + "devDependencies": { + "@modern-js/app-tools": "2.63.7", + "@modern-js/tsconfig": "2.63.7", + "@biomejs/biome": "1.8.3", + "typescript": "~5.0.4", + "@types/jest": "~29.2.4", + "@types/node": "~18.11.9", + "@types/react": "^18.3.11", + "@types/react-dom": "~18.3.1", + "lint-staged": "~13.1.0", + "simple-git-hooks": "^2.11.1", + "rimraf": "^6.0.1", + "@module-federation/modern-js": "^0.8.9" + } +} diff --git a/packages/create-module-federation/templates/modern-common/package.json.handlebars b/packages/create-module-federation/templates/modern-common/package.json.handlebars new file mode 100644 index 00000000000..85e58a25a05 --- /dev/null +++ b/packages/create-module-federation/templates/modern-common/package.json.handlebars @@ -0,0 +1,45 @@ +{ + "name": "{{ mfName }}", + "version": "0.1.0", + "scripts": { + "reset": "npx rimraf node_modules ./**/node_modules", + "dev": "modern dev", + "build": "modern build", + "start": "modern start", + "serve": "modern serve", + "new": "modern new", + "lint": "biome check", + "prepare": "simple-git-hooks", + "upgrade": "modern upgrade" + }, + "engines": { + "node": ">=16.18.1" + }, + "lint-staged": { + "*.{js,ts,cjs,mjs,d.cts,d.mts,jsx,tsx,json,jsonc}": [ + "biome check --files-ignore-unknown=true" + ] + }, + "simple-git-hooks": { + "pre-commit": "npx lint-staged" + }, + "dependencies": { + "@modern-js/runtime": "2.63.7", + "react": "^18.3.1", + "react-dom": "^18.3.1" + }, + "devDependencies": { + "@modern-js/app-tools": "2.63.7", + "@modern-js/tsconfig": "2.63.7", + "@biomejs/biome": "1.8.3", + "typescript": "~5.0.4", + "@types/jest": "~29.2.4", + "@types/node": "~18.11.9", + "@types/react": "^18.3.11", + "@types/react-dom": "~18.3.1", + "lint-staged": "~13.1.0", + "simple-git-hooks": "^2.11.1", + "rimraf": "^6.0.1", + "@module-federation/modern-js":"^0.8.9" + } +} diff --git a/packages/create-module-federation/templates/modern-common/src/components/ComponentInspector.css b/packages/create-module-federation/templates/modern-common/src/components/ComponentInspector.css new file mode 100644 index 00000000000..da68e5015ea --- /dev/null +++ b/packages/create-module-federation/templates/modern-common/src/components/ComponentInspector.css @@ -0,0 +1,86 @@ +.component-inspector { + position: relative; + display: inline-block; +} + +.inspector-info { + position: fixed; + z-index: 1000; + background: #1a1a1a; + padding: 4px 8px; + border-radius: 4px; + font-size: 12px; + color: #fff; + pointer-events: none; + display: flex; + align-items: center; + gap: 8px; +} + +.inspector-overlay { + position: fixed; + border-radius: 4px; + pointer-events: none; + z-index: 999; + background: + linear-gradient(90deg, #3b82f6 0%, #ec4899 50%, #3b82f6 100%) 0 0, + linear-gradient(90deg, #3b82f6 0%, #ec4899 50%, #3b82f6 100%) 0 100%, + linear-gradient(0deg, #3b82f6 0%, #ec4899 50%, #3b82f6 100%) 0 0, + linear-gradient(0deg, #3b82f6 0%, #ec4899 50%, #3b82f6 100%) 100% 0; + background-repeat: repeat-x, repeat-x, repeat-y, repeat-y; + background-size: + 200% 3px, + 200% 3px, + 3px 200%, + 3px 200%; /* 将边框从 2px 加粗到 3px */ + animation: borderRotate 4s linear infinite; +} + +@keyframes borderRotate { + 0% { + background-position: + 0% 0, + 0% 100%, + 0 0%, + 100% 0%; + } + 100% { + background-position: + 200% 0, + -200% 100%, + 0 -200%, + 100% 200%; + } +} +.mf-tag { + color: #646cff; + font-weight: 500; + position: relative; + padding-left: 16px; +} + +.mf-tag::before { + content: ''; + position: absolute; + left: 0; + top: 50%; + transform: translateY(-50%); + width: 12px; + height: 12px; + background-image: url('https://module-federation.io/svg.svg'); + background-repeat: no-repeat; + background-position: center; + background-size: contain; +} + +.divider { + color: #4a4a4a; +} + +.gradient-text { + background: linear-gradient(90deg, #60a5fa, #ec4899); + -webkit-background-clip: text; + background-clip: text; + -webkit-text-fill-color: transparent; + font-weight: 500; +} diff --git a/packages/create-module-federation/templates/modern-common/src/components/ComponentInspector.tsx b/packages/create-module-federation/templates/modern-common/src/components/ComponentInspector.tsx new file mode 100644 index 00000000000..50128edf471 --- /dev/null +++ b/packages/create-module-federation/templates/modern-common/src/components/ComponentInspector.tsx @@ -0,0 +1,68 @@ +import React, { useState } from 'react'; +import './ComponentInspector.css'; + +interface InspectorInfo { + top: number; + left: number; + width: number; + height: number; +} + +export const ComponentInspector: React.FC<{ + children: React.ReactNode; + componentName: string; + mfName: string; +}> = ({ children, componentName, mfName }) => { + const [inspectorInfo, setInspectorInfo] = useState( + null, + ); + + const handleMouseEnter = (e: React.MouseEvent) => { + const rect = e.currentTarget.getBoundingClientRect(); + setInspectorInfo({ + top: rect.top, + left: rect.left, + width: rect.width, + height: rect.height, + }); + }; + + const handleMouseLeave = () => { + setInspectorInfo(null); + }; + + return ( +
+ {children} + {inspectorInfo && ( + <> +
+ {mfName} + | + {componentName} +
+
+ + )} +
+ ); +}; diff --git a/packages/create-module-federation/templates/modern-common/src/modern-app-env.d.ts b/packages/create-module-federation/templates/modern-common/src/modern-app-env.d.ts new file mode 100644 index 00000000000..3f453508cee --- /dev/null +++ b/packages/create-module-federation/templates/modern-common/src/modern-app-env.d.ts @@ -0,0 +1,3 @@ +/// +/// +/// diff --git a/packages/create-module-federation/templates/modern-common/src/modern.runtime.ts b/packages/create-module-federation/templates/modern-common/src/modern.runtime.ts new file mode 100644 index 00000000000..7437c8314e5 --- /dev/null +++ b/packages/create-module-federation/templates/modern-common/src/modern.runtime.ts @@ -0,0 +1,3 @@ +import { defineRuntimeConfig } from '@modern-js/runtime'; + +export default defineRuntimeConfig({}); diff --git a/packages/create-module-federation/templates/modern-common/src/routes/index.css b/packages/create-module-federation/templates/modern-common/src/routes/index.css new file mode 100644 index 00000000000..d8a5be9ffdd --- /dev/null +++ b/packages/create-module-federation/templates/modern-common/src/routes/index.css @@ -0,0 +1,41 @@ +html, +body { + padding: 0; + margin: 0; + font-family: + PingFang SC, + Hiragino Sans GB, + Microsoft YaHei, + Arial, + sans-serif; + background: linear-gradient(to bottom, transparent, #fff) #eceeef; +} + +p { + margin: 0; +} + +* { + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; + box-sizing: border-box; +} + +.container-box { + min-height: 100vh; + max-width: 100%; + display: flex; + flex-direction: column; + justify-content: center; + align-items: center; + padding: 50px; +} + +.landing-page { + padding: 20px; + flex: 1; + display: flex; + flex-direction: column; + justify-content: center; + align-items: center; +} diff --git a/packages/create-module-federation/templates/modern-common/src/routes/layout.tsx b/packages/create-module-federation/templates/modern-common/src/routes/layout.tsx new file mode 100644 index 00000000000..6433ea79e92 --- /dev/null +++ b/packages/create-module-federation/templates/modern-common/src/routes/layout.tsx @@ -0,0 +1,9 @@ +import { Outlet } from '@modern-js/runtime/router'; + +export default function Layout() { + return ( +
+ +
+ ); +} diff --git a/packages/create-module-federation/templates/modern-common/tsconfig.json b/packages/create-module-federation/templates/modern-common/tsconfig.json new file mode 100644 index 00000000000..ce7f951eefc --- /dev/null +++ b/packages/create-module-federation/templates/modern-common/tsconfig.json @@ -0,0 +1,14 @@ +{ + "extends": "@modern-js/tsconfig/base", + "compilerOptions": { + "declaration": false, + "jsx": "preserve", + "baseUrl": "./", + "paths": { + "@/*": ["./src/*"], + "@shared/*": ["./shared/*"] + } + }, + "include": ["src", "shared", "config", "modern.config.ts"], + "exclude": ["**/node_modules"] +} diff --git a/packages/create-module-federation/templates/provider-modern-ts/module-federation.config.ts.handlebars b/packages/create-module-federation/templates/provider-modern-ts/module-federation.config.ts.handlebars new file mode 100644 index 00000000000..fe617d54808 --- /dev/null +++ b/packages/create-module-federation/templates/provider-modern-ts/module-federation.config.ts.handlebars @@ -0,0 +1,14 @@ +import { createModuleFederationConfig } from '@module-federation/modern-js'; + +export default createModuleFederationConfig({ + name: '{{mfName}}', + exposes: { + './Header': './src/components/Header.tsx', + './Description': './src/components/Description.tsx', + './Footer': './src/components/Footer.tsx', + }, + shared: { + react: { singleton: true }, + 'react-dom': { singleton: true }, + }, +}); diff --git a/packages/create-module-federation/templates/provider-modern-ts/src/components/Description.css b/packages/create-module-federation/templates/provider-modern-ts/src/components/Description.css new file mode 100644 index 00000000000..ffb946c3e57 --- /dev/null +++ b/packages/create-module-federation/templates/provider-modern-ts/src/components/Description.css @@ -0,0 +1,23 @@ +.description { + text-align: center; + line-height: 1.5; + font-size: 1.3rem; + color: #1b3a42; + margin-bottom: 5rem; +} + +.code { + background: #fafafa; + border-radius: 12px; + padding: 0.6rem 0.9rem; + font-size: 1.05rem; + font-family: + Menlo, + Monaco, + Lucida Console, + Liberation Mono, + DejaVu Sans Mono, + Bitstream Vera Sans Mono, + Courier New, + monospace; +} diff --git a/packages/create-module-federation/templates/provider-modern-ts/src/components/Description.tsx b/packages/create-module-federation/templates/provider-modern-ts/src/components/Description.tsx new file mode 100644 index 00000000000..8939eb88e7b --- /dev/null +++ b/packages/create-module-federation/templates/provider-modern-ts/src/components/Description.tsx @@ -0,0 +1,11 @@ +import './Description.css'; + +const Description = () => { + return ( +

+ Get started by editing src/routes/page.tsx +

+ ); +}; + +export default Description; diff --git a/packages/create-module-federation/templates/provider-modern-ts/src/components/Footer.css b/packages/create-module-federation/templates/provider-modern-ts/src/components/Footer.css new file mode 100644 index 00000000000..da7bda94fdf --- /dev/null +++ b/packages/create-module-federation/templates/provider-modern-ts/src/components/Footer.css @@ -0,0 +1,45 @@ +.footer { + display: flex; + align-items: center; + justify-content: center; + width: 1100px; + margin-top: 3rem; +} + +.card { + padding: 1.5rem; + display: flex; + flex-direction: column; + justify-content: center; + height: 100px; + color: inherit; + text-decoration: none; + transition: 0.15s ease; + width: 45%; +} + +.card:hover, +.card:focus { + transform: scale(1.05); +} + +.card h2 { + display: flex; + align-items: center; + font-size: 1.5rem; + margin: 0; + padding: 0; +} + +.card p { + opacity: 0.6; + font-size: 0.9rem; + line-height: 1.5; + margin-top: 1rem; +} + +.arrow-right { + width: 1.3rem; + margin-left: 0.5rem; + margin-top: 3px; +} diff --git a/packages/create-module-federation/templates/provider-modern-ts/src/components/Footer.tsx b/packages/create-module-federation/templates/provider-modern-ts/src/components/Footer.tsx new file mode 100644 index 00000000000..376c31c27e6 --- /dev/null +++ b/packages/create-module-federation/templates/provider-modern-ts/src/components/Footer.tsx @@ -0,0 +1,74 @@ +import './Footer.css'; + +const Footer = () => { + return ( + + ); +}; + +export default Footer; diff --git a/packages/create-module-federation/templates/provider-modern-ts/src/components/Header.css b/packages/create-module-federation/templates/provider-modern-ts/src/components/Header.css new file mode 100644 index 00000000000..02c9c808702 --- /dev/null +++ b/packages/create-module-federation/templates/provider-modern-ts/src/components/Header.css @@ -0,0 +1,11 @@ +.header { + display: flex; + margin: 4rem 0 4rem; + align-items: center; + font-size: 4rem; + font-weight: 600; +} + +.name { + color: #4ecaff; +} diff --git a/packages/create-module-federation/templates/provider-modern-ts/src/components/Header.tsx b/packages/create-module-federation/templates/provider-modern-ts/src/components/Header.tsx new file mode 100644 index 00000000000..89da81b8a6f --- /dev/null +++ b/packages/create-module-federation/templates/provider-modern-ts/src/components/Header.tsx @@ -0,0 +1,17 @@ +import './Header.css'; + +const Header = () => { + return ( +
+ Welcome to + Modern.js Logo +

Modern.js

+
+ ); +}; + +export default Header; diff --git a/packages/create-module-federation/templates/provider-modern-ts/src/routes/page.tsx b/packages/create-module-federation/templates/provider-modern-ts/src/routes/page.tsx new file mode 100644 index 00000000000..18bbf92c261 --- /dev/null +++ b/packages/create-module-federation/templates/provider-modern-ts/src/routes/page.tsx @@ -0,0 +1,34 @@ +import { Helmet } from '@modern-js/runtime/head'; +import './index.css'; +import { ComponentInspector } from '../components/ComponentInspector'; +import Header from '../components/Header'; +import Description from '../components/Description'; +import Footer from '../components/Footer'; + +const Index = () => ( +
+ + + + +
+ +
+ + + + + + + +
+ +
+
+); + +export default Index; diff --git a/packages/create-module-federation/templates/provider-rsbuild-ts/module-federation.config.ts.handlebars b/packages/create-module-federation/templates/provider-rsbuild-ts/module-federation.config.ts.handlebars new file mode 100644 index 00000000000..7ee76fdad18 --- /dev/null +++ b/packages/create-module-federation/templates/provider-rsbuild-ts/module-federation.config.ts.handlebars @@ -0,0 +1,14 @@ +import { createModuleFederationConfig } from '@module-federation/rsbuild-plugin'; + +export default createModuleFederationConfig({ + name: '{{mfName}}', + exposes: { + './Header': './src/components/Header.tsx', + './Description': './src/components/Description.tsx', + './Footer': './src/components/Footer.tsx', + }, + shared: { + react: { singleton: true }, + 'react-dom': { singleton: true }, + }, +}); diff --git a/packages/create-module-federation/templates/provider-rsbuild-ts/src/App.tsx b/packages/create-module-federation/templates/provider-rsbuild-ts/src/App.tsx new file mode 100644 index 00000000000..fbd2f7a18e3 --- /dev/null +++ b/packages/create-module-federation/templates/provider-rsbuild-ts/src/App.tsx @@ -0,0 +1,25 @@ +import './App.css'; +import { ComponentInspector } from './components/ComponentInspector'; +import Header from './components/Header'; +import Description from './components/Description'; +import Footer from './components/Footer'; + +const App = () => { + return ( +
+ +
+ + + + + + + +
+ +
+ ); +}; + +export default App; diff --git a/packages/create-module-federation/templates/provider-rsbuild-ts/src/components/Description.tsx b/packages/create-module-federation/templates/provider-rsbuild-ts/src/components/Description.tsx new file mode 100644 index 00000000000..d096ea42e4d --- /dev/null +++ b/packages/create-module-federation/templates/provider-rsbuild-ts/src/components/Description.tsx @@ -0,0 +1,7 @@ +const Description = () => { + return ( +

Start building amazing things with Rsbuild.

+ ); +}; + +export default Description; diff --git a/packages/create-module-federation/templates/provider-rsbuild-ts/src/components/Footer.css b/packages/create-module-federation/templates/provider-rsbuild-ts/src/components/Footer.css new file mode 100644 index 00000000000..f305fc60934 --- /dev/null +++ b/packages/create-module-federation/templates/provider-rsbuild-ts/src/components/Footer.css @@ -0,0 +1,46 @@ +.footer { + width: 100%; + display: flex; + align-items: center; + justify-content: center; + margin-top: 3rem; +} + +.card { + padding: 1.5rem; + display: flex; + flex-direction: column; + justify-content: center; + height: 100px; + color: inherit; + text-decoration: none; + transition: 0.15s ease; + width: 45%; +} + +.card:hover, +.card:focus { + transform: scale(1.05); +} + +.card h2 { + display: flex; + align-items: center; + font-size: 1.5rem; + margin: 0; + padding: 0; +} + +.card p { + display: flex; + opacity: 0.6; + font-size: 0.9rem; + line-height: 1.5; + margin-top: 1rem; +} + +.arrow-right { + width: 1.3rem; + margin-left: 0.5rem; + margin-top: 3px; +} diff --git a/packages/create-module-federation/templates/provider-rsbuild-ts/src/components/Footer.tsx b/packages/create-module-federation/templates/provider-rsbuild-ts/src/components/Footer.tsx new file mode 100644 index 00000000000..00d1cbd97b1 --- /dev/null +++ b/packages/create-module-federation/templates/provider-rsbuild-ts/src/components/Footer.tsx @@ -0,0 +1,74 @@ +import './Footer.css'; + +const Footer = () => { + return ( + + ); +}; + +export default Footer; diff --git a/packages/create-module-federation/templates/provider-rsbuild-ts/src/components/Header.tsx b/packages/create-module-federation/templates/provider-rsbuild-ts/src/components/Header.tsx new file mode 100644 index 00000000000..53aff275ee8 --- /dev/null +++ b/packages/create-module-federation/templates/provider-rsbuild-ts/src/components/Header.tsx @@ -0,0 +1,9 @@ +const Header = () => { + return ( +
+

Rsbuild with React

+
+ ); +}; + +export default Header; diff --git a/packages/create-module-federation/templates/provider-rslib-storybook-ts/.storybook/main.ts b/packages/create-module-federation/templates/provider-rslib-storybook-ts/.storybook/main.ts new file mode 100644 index 00000000000..7fddb66944c --- /dev/null +++ b/packages/create-module-federation/templates/provider-rslib-storybook-ts/.storybook/main.ts @@ -0,0 +1,41 @@ +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: { + provider: 'provider@http://localhost:3001/mf-manifest.json', + }, + }, + }, + ], +}; + +export default config; diff --git a/packages/create-module-federation/templates/provider-rslib-storybook-ts/README.md b/packages/create-module-federation/templates/provider-rslib-storybook-ts/README.md new file mode 100644 index 00000000000..f8d0efb3119 --- /dev/null +++ b/packages/create-module-federation/templates/provider-rslib-storybook-ts/README.md @@ -0,0 +1,27 @@ +# Rslib MF Project + +This example demonstrates how to use Rslib to build a simple Module Federation React component. + +### Command + +Build package + +``` +pnpm build +``` + +Dev package + +1. + +``` +pnpm mf-dev +``` + +2. + +``` +pnpm storybook +``` + +visit http://localhost:6006 diff --git a/packages/create-module-federation/templates/provider-rslib-storybook-ts/package.json.handlebars b/packages/create-module-federation/templates/provider-rslib-storybook-ts/package.json.handlebars new file mode 100644 index 00000000000..4d5eced7abc --- /dev/null +++ b/packages/create-module-federation/templates/provider-rslib-storybook-ts/package.json.handlebars @@ -0,0 +1,34 @@ +{ + "name": "{{ mfName }}", + "version": "0.0.0", + "type": "module", + "exports": { + ".": { + "types": "./dist/index.d.ts", + "import": "./dist/index.js" + } + }, + "module": "./dist/index.js", + "types": "./dist/index.d.ts", + "files": [ + "dist" + ], + "scripts": { + "build": "rslib build", + "dev": "rslib build --watch", + "mf-dev": "rslib mf-dev" + }, + "devDependencies": { + "@rsbuild/core": "1.2.0-beta.0", + "@rsbuild/plugin-react": "^1.1.0", + "@rslib/core": "^0.3.1", + "@types/react": "^19.0.4", + "react": "^19.0.0", + "typescript": "^5.7.3", + "@module-federation/rsbuild-plugin":"^0.8.9" + }, + "peerDependencies": { + "react": ">=16.9.0", + "react-dom": ">=16.9.0" + } +} diff --git a/packages/create-module-federation/templates/provider-rslib-storybook-ts/stories/Description.stories.tsx b/packages/create-module-federation/templates/provider-rslib-storybook-ts/stories/Description.stories.tsx new file mode 100644 index 00000000000..7076f97ef50 --- /dev/null +++ b/packages/create-module-federation/templates/provider-rslib-storybook-ts/stories/Description.stories.tsx @@ -0,0 +1,11 @@ +import React from 'react'; +import Description from 'provider/Description'; + +const Component = () => ; + +export default { + title: 'Description Component', + component: Component, +}; + +export const Basic = {}; diff --git a/packages/create-module-federation/templates/provider-rslib-storybook-ts/stories/Footer.stories.tsx b/packages/create-module-federation/templates/provider-rslib-storybook-ts/stories/Footer.stories.tsx new file mode 100644 index 00000000000..32b189aac8b --- /dev/null +++ b/packages/create-module-federation/templates/provider-rslib-storybook-ts/stories/Footer.stories.tsx @@ -0,0 +1,11 @@ +import React from 'react'; +import Footer from 'provider/Footer'; + +const Component = () =>