mfeflow 0.0.0-stage → 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (120) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +126 -2
  3. package/assets/features/analytics-gtm/feature.json +117 -0
  4. package/assets/features/analytics-gtm/files/Analytics.tsx +13 -0
  5. package/assets/features/analytics-gtm/files/ConsentBanner.tsx +47 -0
  6. package/assets/features/analytics-gtm/files/consent-banner-test.tsx +36 -0
  7. package/assets/features/analytics-gtm/files/init.ts +26 -0
  8. package/assets/features/analytics-gtm/files/remote-analytics-test.ts.eta +18 -0
  9. package/assets/features/analytics-gtm/files/remote-analytics.ts.eta +13 -0
  10. package/assets/features/analytics-gtm/files/shell-init-test.ts +42 -0
  11. package/assets/features/ci-github/feature.json +26 -0
  12. package/assets/features/ci-github/files/ci.md +41 -0
  13. package/assets/features/ci-github/files/ci.yml +63 -0
  14. package/assets/features/env-t3/feature.json +378 -0
  15. package/assets/features/env-t3/files/check-runtime-config.ts +105 -0
  16. package/assets/features/env-t3/files/env.arktype.ts +12 -0
  17. package/assets/features/env-t3/files/env.example +1 -0
  18. package/assets/features/env-t3/files/env.valibot.ts +12 -0
  19. package/assets/features/env-t3/files/env.zod.ts +12 -0
  20. package/assets/features/env-t3/files/runtime.ts.eta +30 -0
  21. package/assets/features/env-t3/files/schema.arktype.ts +8 -0
  22. package/assets/features/env-t3/files/schema.valibot.ts +8 -0
  23. package/assets/features/env-t3/files/schema.zod.ts +8 -0
  24. package/assets/features/git-hooks/feature.json +42 -0
  25. package/assets/features/git-hooks/files/commitlint.config.js +1 -0
  26. package/assets/features/git-hooks/files/lefthook.yml +9 -0
  27. package/assets/features/lint-oxlint/feature.json +60 -0
  28. package/assets/features/lint-oxlint/files/oxlintrc.json +5 -0
  29. package/assets/features/lint-oxlint/files/prettierignore +5 -0
  30. package/assets/features/lint-oxlint/files/prettierrc.json +5 -0
  31. package/assets/features/telemetry-otel/feature.json +159 -0
  32. package/assets/features/telemetry-otel/files/init.ts.eta +53 -0
  33. package/assets/features/telemetry-otel/files/remote-telemetry-test.ts.eta +13 -0
  34. package/assets/features/telemetry-otel/files/remote-telemetry.ts.eta +11 -0
  35. package/assets/features/telemetry-otel/files/shell-init-test.ts.eta +36 -0
  36. package/assets/features/testing/feature.json +202 -0
  37. package/assets/features/testing/files/handlers.ts +5 -0
  38. package/assets/features/testing/files/playwright.config.ts +30 -0
  39. package/assets/features/testing/files/root-vitest.config.ts +6 -0
  40. package/assets/features/testing/files/sample-handlers-test.ts +16 -0
  41. package/assets/features/testing/files/sample-remote-test.tsx +12 -0
  42. package/assets/features/testing/files/sample-shell-test.tsx +11 -0
  43. package/assets/features/testing/files/sample-smoke-e2e.ts +53 -0
  44. package/assets/features/testing/files/server.ts +4 -0
  45. package/assets/features/testing/files/setup.ts +18 -0
  46. package/assets/features/testing/files/vitest.config.ts +11 -0
  47. package/assets/features/tsconfig/feature.json +78 -0
  48. package/assets/features/tsconfig/files/root.tsconfig.json +4 -0
  49. package/assets/features/tsconfig/files/tsconfig.json +5 -0
  50. package/assets/features/url-factory/feature.json +30 -0
  51. package/assets/features/url-factory/files/endpoints.ts +12 -0
  52. package/assets/templates/monorepo/feature.json +295 -0
  53. package/assets/templates/monorepo/files/remote/mfe.json.eta +7 -0
  54. package/assets/templates/monorepo/files/remote/package.json.eta +6 -0
  55. package/assets/templates/monorepo/files/remote/rsbuild.config.ts.eta +7 -0
  56. package/assets/templates/monorepo/files/remote/src/bootstrap.tsx.eta +6 -0
  57. package/assets/templates/monorepo/files/remote/src/env.d.ts.eta +1 -0
  58. package/assets/templates/monorepo/files/remote/src/exposed/App.tsx.eta +18 -0
  59. package/assets/templates/monorepo/files/remote/src/index.ts.eta +2 -0
  60. package/assets/templates/monorepo/files/root/AGENTS.md.eta +17 -0
  61. package/assets/templates/monorepo/files/root/README.md.eta +52 -0
  62. package/assets/templates/monorepo/files/root/gitignore.eta +11 -0
  63. package/assets/templates/monorepo/files/root/nvmrc.eta +1 -0
  64. package/assets/templates/monorepo/files/root/package.json.eta +9 -0
  65. package/assets/templates/monorepo/files/root/pnpm-workspace.yaml.eta +7 -0
  66. package/assets/templates/monorepo/files/root/turbo.json.eta +16 -0
  67. package/assets/templates/monorepo/files/shell/mfe.json.eta +7 -0
  68. package/assets/templates/monorepo/files/shell/package.json.eta +6 -0
  69. package/assets/templates/monorepo/files/shell/rsbuild.config.ts.eta +74 -0
  70. package/assets/templates/monorepo/files/shell/src/App.tsx.eta +46 -0
  71. package/assets/templates/monorepo/files/shell/src/bootstrap.tsx.eta +24 -0
  72. package/assets/templates/monorepo/files/shell/src/env.d.ts.eta +1 -0
  73. package/assets/templates/monorepo/files/shell/src/index.ts.eta +4 -0
  74. package/assets/templates/monorepo/files/shell/src/pages/Home.tsx.eta +12 -0
  75. package/assets/templates/monorepo/files/shell/src/remotes/route-table.tsx.eta +12 -0
  76. package/assets/templates/monorepo/glue/registry-entry.json.eta +5 -0
  77. package/assets/templates/monorepo/glue/route.tsx.eta +5 -0
  78. package/bin/mfeflow.js +4 -0
  79. package/dist/args.d.ts +68 -0
  80. package/dist/assets.d.ts +13 -0
  81. package/dist/catalog.d.ts +24 -0
  82. package/dist/checks/compat.d.ts +2 -0
  83. package/dist/checks/config.d.ts +8 -0
  84. package/dist/checks/env.d.ts +6 -0
  85. package/dist/checks/index.d.ts +6 -0
  86. package/dist/checks/plugins.d.ts +4 -0
  87. package/dist/checks/singletons.d.ts +9 -0
  88. package/dist/checks/workspace.d.ts +2 -0
  89. package/dist/cli.d.ts +11 -0
  90. package/dist/commands/add.d.ts +84 -0
  91. package/dist/commands/budget.d.ts +56 -0
  92. package/dist/commands/dev.d.ts +62 -0
  93. package/dist/commands/doctor.d.ts +141 -0
  94. package/dist/commands/env-key.d.ts +4 -0
  95. package/dist/commands/features.d.ts +53 -0
  96. package/dist/commands/init.d.ts +102 -0
  97. package/dist/devregistry.d.ts +19 -0
  98. package/dist/exit.d.ts +31 -0
  99. package/dist/findings.d.ts +35 -0
  100. package/dist/glue.d.ts +41 -0
  101. package/dist/help.d.ts +12 -0
  102. package/dist/index.d.ts +8 -0
  103. package/dist/index.js +4154 -0
  104. package/dist/inspect.d.ts +57 -0
  105. package/dist/io.d.ts +10 -0
  106. package/dist/local.d.ts +28 -0
  107. package/dist/options.d.ts +7 -0
  108. package/dist/output.d.ts +157 -0
  109. package/dist/pipeline.d.ts +7 -0
  110. package/dist/preset.d.ts +19 -0
  111. package/dist/proc.d.ts +79 -0
  112. package/dist/prompter.d.ts +28 -0
  113. package/dist/report.d.ts +26 -0
  114. package/dist/runtime.d.ts +17 -0
  115. package/dist/singletons.d.ts +11 -0
  116. package/dist/sizes.d.ts +17 -0
  117. package/dist/system.d.ts +20 -0
  118. package/dist/version.d.ts +8 -0
  119. package/dist/write.d.ts +26 -0
  120. package/package.json +66 -3
@@ -0,0 +1,53 @@
1
+ import fs from 'node:fs';
2
+ import { expect, test } from '@playwright/test';
3
+
4
+ // The remotes the dev registry lists; each one's App carries data-testid="<name>".
5
+ const remotes = fs
6
+ .readdirSync('registry/envs/dev')
7
+ .filter((file) => file.endsWith('.json'))
8
+ .map((file) => file.slice(0, -'.json'.length))
9
+ .toSorted();
10
+
11
+ // With env-t3 the shell fetches /config/runtime-config.json at boot and keeps it on globalThis.
12
+ const hasRuntimeConfig = fs.existsSync('scripts/check-runtime-config.ts');
13
+
14
+ test('the shell loads cleanly, with its runtime config', async ({ page }) => {
15
+ const problems: string[] = [];
16
+ page.on('pageerror', (error) => problems.push(`page error: ${error.message}`));
17
+ page.on('console', (message) => {
18
+ if (message.type() === 'error') problems.push(`console error: ${message.text()}`);
19
+ });
20
+ page.on('requestfailed', (request) =>
21
+ problems.push(
22
+ `request failed: ${request.url()} (${request.failure()?.errorText ?? 'unknown'})`,
23
+ ),
24
+ );
25
+ page.on('response', (response) => {
26
+ if (response.status() >= 400 && !response.url().endsWith('/favicon.ico')) {
27
+ problems.push(`${String(response.status())} ${response.url()}`);
28
+ }
29
+ });
30
+ await page.goto('/');
31
+ await expect(page.locator('header')).toBeVisible();
32
+ if (hasRuntimeConfig) {
33
+ await expect
34
+ .poll(() =>
35
+ page.evaluate(() => {
36
+ const config: unknown = Reflect.get(globalThis, '__MFE_RUNTIME_CONFIG__');
37
+ return typeof config === 'object' && config !== null && 'environment' in config
38
+ ? config.environment
39
+ : undefined;
40
+ }),
41
+ )
42
+ .toBeTruthy();
43
+ }
44
+ await page.waitForLoadState('networkidle');
45
+ expect(problems).toEqual([]);
46
+ });
47
+
48
+ for (const name of remotes) {
49
+ test(`the shell renders ${name}`, async ({ page }) => {
50
+ await page.goto('/');
51
+ await expect(page.getByTestId(name)).toBeVisible();
52
+ });
53
+ }
@@ -0,0 +1,4 @@
1
+ import { setupServer } from 'msw/node';
2
+ import { handlers } from './handlers';
3
+
4
+ export const server = setupServer(...handlers);
@@ -0,0 +1,18 @@
1
+ import { cleanup } from '@testing-library/react';
2
+ import { resetPlatform } from '@mfeflow/testing';
3
+ import { afterAll, afterEach, beforeAll } from 'vitest';
4
+ import { server } from '../mocks/server';
5
+
6
+ beforeAll(() => {
7
+ server.listen({ onUnhandledFrame: 'error' });
8
+ });
9
+
10
+ afterEach(() => {
11
+ cleanup();
12
+ resetPlatform();
13
+ server.resetHandlers();
14
+ });
15
+
16
+ afterAll(() => {
17
+ server.close();
18
+ });
@@ -0,0 +1,11 @@
1
+ import { defineConfig } from 'vitest/config';
2
+
3
+ export default defineConfig({
4
+ // One copy of React and Testing Library, also when a package like @mfeflow/testing is linked from another folder.
5
+ resolve: { dedupe: ['react', 'react-dom', '@testing-library/react', '@testing-library/dom'] },
6
+ test: {
7
+ environment: 'jsdom',
8
+ include: ['src/**/*.test.{ts,tsx}'],
9
+ setupFiles: ['./src/test/setup.ts'],
10
+ },
11
+ });
@@ -0,0 +1,78 @@
1
+ {
2
+ "name": "tsconfig",
3
+ "version": "0.1.0",
4
+ "description": "Strict TypeScript 7 presets from @mfeflow/tsconfig, and a typecheck script.",
5
+ "requires": ["core"],
6
+ "files": [
7
+ {
8
+ "to": "tsconfig.json",
9
+ "target": "shell",
10
+ "from": "files/tsconfig.json"
11
+ },
12
+ {
13
+ "to": "tsconfig.json",
14
+ "target": "remotes",
15
+ "from": "files/tsconfig.json"
16
+ },
17
+ {
18
+ "to": "tsconfig.json",
19
+ "target": "root",
20
+ "from": "files/root.tsconfig.json"
21
+ }
22
+ ],
23
+ "dependencies": [
24
+ {
25
+ "name": "@mfeflow/tsconfig",
26
+ "range": "latest",
27
+ "target": "shell",
28
+ "dev": true
29
+ },
30
+ {
31
+ "name": "@mfeflow/tsconfig",
32
+ "range": "latest",
33
+ "target": "remotes",
34
+ "dev": true
35
+ },
36
+ {
37
+ "name": "@mfeflow/tsconfig",
38
+ "range": "latest",
39
+ "target": "root",
40
+ "dev": true
41
+ },
42
+ {
43
+ "name": "typescript",
44
+ "range": "7.0.2",
45
+ "target": "root",
46
+ "dev": true
47
+ },
48
+ {
49
+ "name": "typescript",
50
+ "range": "7.0.2",
51
+ "target": "shell",
52
+ "dev": true
53
+ },
54
+ {
55
+ "name": "typescript",
56
+ "range": "7.0.2",
57
+ "target": "remotes",
58
+ "dev": true
59
+ }
60
+ ],
61
+ "scripts": [
62
+ {
63
+ "name": "typecheck",
64
+ "command": "turbo run typecheck",
65
+ "target": "root"
66
+ },
67
+ {
68
+ "name": "typecheck",
69
+ "command": "tsc --noEmit",
70
+ "target": "shell"
71
+ },
72
+ {
73
+ "name": "typecheck",
74
+ "command": "tsc --noEmit",
75
+ "target": "remotes"
76
+ }
77
+ ]
78
+ }
@@ -0,0 +1,4 @@
1
+ {
2
+ "extends": "@mfeflow/tsconfig/node.json",
3
+ "include": ["*.ts", "scripts", "e2e"]
4
+ }
@@ -0,0 +1,5 @@
1
+ {
2
+ "extends": "@mfeflow/tsconfig/react-app.json",
3
+ "compilerOptions": { "types": ["node"] },
4
+ "include": ["src", "rsbuild.config.ts", "env.ts", "vitest.config.ts"]
5
+ }
@@ -0,0 +1,30 @@
1
+ {
2
+ "name": "url-factory",
3
+ "version": "0.1.0",
4
+ "description": "Typed endpoint definitions and URL building (@mfeflow/url-factory), src/api/endpoints.ts in every app.",
5
+ "requires": ["core"],
6
+ "files": [
7
+ {
8
+ "to": "src/api/endpoints.ts",
9
+ "target": "shell",
10
+ "from": "files/endpoints.ts"
11
+ },
12
+ {
13
+ "to": "src/api/endpoints.ts",
14
+ "target": "remotes",
15
+ "from": "files/endpoints.ts"
16
+ }
17
+ ],
18
+ "dependencies": [
19
+ {
20
+ "name": "@mfeflow/url-factory",
21
+ "range": "latest",
22
+ "target": "shell"
23
+ },
24
+ {
25
+ "name": "@mfeflow/url-factory",
26
+ "range": "latest",
27
+ "target": "remotes"
28
+ }
29
+ ]
30
+ }
@@ -0,0 +1,12 @@
1
+ import { createUrlFactory, defineEndpoints } from '@mfeflow/url-factory';
2
+
3
+ export const endpoints = defineEndpoints({
4
+ health: { service: 'api', path: '/health' },
5
+ });
6
+
7
+ const services: Record<string, string> = { api: '/api' };
8
+
9
+ export const apiUrl = createUrlFactory(endpoints, (service) => {
10
+ const base = services[service];
11
+ return base === undefined ? undefined : new URL(base, location.origin).toString();
12
+ });
@@ -0,0 +1,295 @@
1
+ {
2
+ "name": "core",
3
+ "version": "0.1.0",
4
+ "description": "The shell, the remotes, the registry and the workspace around them.",
5
+ "files": [
6
+ {
7
+ "to": "package.json",
8
+ "target": "root",
9
+ "from": "files/root/package.json.eta",
10
+ "template": true
11
+ },
12
+ {
13
+ "to": "pnpm-workspace.yaml",
14
+ "target": "root",
15
+ "from": "files/root/pnpm-workspace.yaml.eta",
16
+ "template": true
17
+ },
18
+ {
19
+ "to": "turbo.json",
20
+ "target": "root",
21
+ "from": "files/root/turbo.json.eta",
22
+ "template": true
23
+ },
24
+ {
25
+ "to": ".gitignore",
26
+ "target": "root",
27
+ "from": "files/root/gitignore.eta",
28
+ "template": true
29
+ },
30
+ {
31
+ "to": ".nvmrc",
32
+ "target": "root",
33
+ "from": "files/root/nvmrc.eta",
34
+ "template": true
35
+ },
36
+ {
37
+ "to": "README.md",
38
+ "target": "root",
39
+ "from": "files/root/README.md.eta",
40
+ "template": true
41
+ },
42
+ {
43
+ "to": "AGENTS.md",
44
+ "target": "root",
45
+ "from": "files/root/AGENTS.md.eta",
46
+ "template": true
47
+ },
48
+ {
49
+ "to": "registry/envs/dev/.gitkeep",
50
+ "target": "root",
51
+ "content": ""
52
+ },
53
+ {
54
+ "to": "registry/envs/staging/.gitkeep",
55
+ "target": "root",
56
+ "content": ""
57
+ },
58
+ {
59
+ "to": "registry/envs/prod/.gitkeep",
60
+ "target": "root",
61
+ "content": ""
62
+ },
63
+ {
64
+ "to": "package.json",
65
+ "target": "shell",
66
+ "from": "files/shell/package.json.eta",
67
+ "template": true
68
+ },
69
+ {
70
+ "to": "mfe.json",
71
+ "target": "shell",
72
+ "from": "files/shell/mfe.json.eta",
73
+ "template": true
74
+ },
75
+ {
76
+ "to": "rsbuild.config.ts",
77
+ "target": "shell",
78
+ "from": "files/shell/rsbuild.config.ts.eta",
79
+ "template": true
80
+ },
81
+ {
82
+ "to": "src/env.d.ts",
83
+ "target": "shell",
84
+ "from": "files/shell/src/env.d.ts.eta",
85
+ "template": true
86
+ },
87
+ {
88
+ "to": "src/index.ts",
89
+ "target": "shell",
90
+ "from": "files/shell/src/index.ts.eta",
91
+ "template": true
92
+ },
93
+ {
94
+ "to": "src/bootstrap.tsx",
95
+ "target": "shell",
96
+ "from": "files/shell/src/bootstrap.tsx.eta",
97
+ "template": true
98
+ },
99
+ {
100
+ "to": "src/App.tsx",
101
+ "target": "shell",
102
+ "from": "files/shell/src/App.tsx.eta",
103
+ "template": true
104
+ },
105
+ {
106
+ "to": "src/remotes/route-table.tsx",
107
+ "target": "shell",
108
+ "from": "files/shell/src/remotes/route-table.tsx.eta",
109
+ "template": true
110
+ },
111
+ {
112
+ "to": "package.json",
113
+ "target": "remotes",
114
+ "from": "files/remote/package.json.eta",
115
+ "template": true
116
+ },
117
+ {
118
+ "to": "mfe.json",
119
+ "target": "remotes",
120
+ "from": "files/remote/mfe.json.eta",
121
+ "template": true
122
+ },
123
+ {
124
+ "to": "rsbuild.config.ts",
125
+ "target": "remotes",
126
+ "from": "files/remote/rsbuild.config.ts.eta",
127
+ "template": true
128
+ },
129
+ {
130
+ "to": "src/env.d.ts",
131
+ "target": "remotes",
132
+ "from": "files/remote/src/env.d.ts.eta",
133
+ "template": true
134
+ },
135
+ {
136
+ "to": "src/index.ts",
137
+ "target": "remotes",
138
+ "from": "files/remote/src/index.ts.eta",
139
+ "template": true
140
+ },
141
+ {
142
+ "to": "src/bootstrap.tsx",
143
+ "target": "remotes",
144
+ "from": "files/remote/src/bootstrap.tsx.eta",
145
+ "template": true
146
+ },
147
+ {
148
+ "to": "src/exposed/App.tsx",
149
+ "target": "remotes",
150
+ "from": "files/remote/src/exposed/App.tsx.eta",
151
+ "template": true
152
+ },
153
+ {
154
+ "to": "src/pages/Home.tsx",
155
+ "target": "shell",
156
+ "from": "files/shell/src/pages/Home.tsx.eta",
157
+ "template": true
158
+ }
159
+ ],
160
+ "dependencies": [
161
+ {
162
+ "name": "turbo",
163
+ "range": "2.11.10",
164
+ "target": "root",
165
+ "dev": true
166
+ },
167
+ {
168
+ "name": "@mfeflow/runtime",
169
+ "range": "latest",
170
+ "target": "shell"
171
+ },
172
+ {
173
+ "name": "@module-federation/enhanced",
174
+ "range": "2.9.2",
175
+ "target": "shell"
176
+ },
177
+ {
178
+ "name": "react",
179
+ "range": "19.3.1",
180
+ "target": "shell"
181
+ },
182
+ {
183
+ "name": "react-dom",
184
+ "range": "19.3.1",
185
+ "target": "shell"
186
+ },
187
+ {
188
+ "name": "@mfeflow/rsbuild",
189
+ "range": "latest",
190
+ "target": "shell",
191
+ "dev": true
192
+ },
193
+ {
194
+ "name": "@rsbuild/core",
195
+ "range": "2.3.0",
196
+ "target": "shell",
197
+ "dev": true
198
+ },
199
+ {
200
+ "name": "@types/node",
201
+ "range": "26.6.6",
202
+ "target": "shell",
203
+ "dev": true
204
+ },
205
+ {
206
+ "name": "@types/react",
207
+ "range": "19.3.0",
208
+ "target": "shell",
209
+ "dev": true
210
+ },
211
+ {
212
+ "name": "@types/react-dom",
213
+ "range": "19.3.0",
214
+ "target": "shell",
215
+ "dev": true
216
+ },
217
+ {
218
+ "name": "@module-federation/enhanced",
219
+ "range": "2.9.2",
220
+ "target": "remotes"
221
+ },
222
+ {
223
+ "name": "react",
224
+ "range": "19.3.1",
225
+ "target": "remotes"
226
+ },
227
+ {
228
+ "name": "react-dom",
229
+ "range": "19.3.1",
230
+ "target": "remotes"
231
+ },
232
+ {
233
+ "name": "@mfeflow/rsbuild",
234
+ "range": "latest",
235
+ "target": "remotes",
236
+ "dev": true
237
+ },
238
+ {
239
+ "name": "@rsbuild/core",
240
+ "range": "2.3.0",
241
+ "target": "remotes",
242
+ "dev": true
243
+ },
244
+ {
245
+ "name": "@types/node",
246
+ "range": "26.6.6",
247
+ "target": "remotes",
248
+ "dev": true
249
+ },
250
+ {
251
+ "name": "@types/react",
252
+ "range": "19.3.0",
253
+ "target": "remotes",
254
+ "dev": true
255
+ },
256
+ {
257
+ "name": "@types/react-dom",
258
+ "range": "19.3.0",
259
+ "target": "remotes",
260
+ "dev": true
261
+ }
262
+ ],
263
+ "scripts": [
264
+ {
265
+ "name": "dev",
266
+ "command": "turbo run dev",
267
+ "target": "root"
268
+ },
269
+ {
270
+ "name": "build",
271
+ "command": "turbo run build",
272
+ "target": "root"
273
+ },
274
+ {
275
+ "name": "dev",
276
+ "command": "rsbuild dev",
277
+ "target": "shell"
278
+ },
279
+ {
280
+ "name": "build",
281
+ "command": "rsbuild build",
282
+ "target": "shell"
283
+ },
284
+ {
285
+ "name": "dev",
286
+ "command": "rsbuild dev",
287
+ "target": "remotes"
288
+ },
289
+ {
290
+ "name": "build",
291
+ "command": "rsbuild build",
292
+ "target": "remotes"
293
+ }
294
+ ]
295
+ }
@@ -0,0 +1,7 @@
1
+ {
2
+ "name": "<%= it.app.name %>",
3
+ "owner": "team-<%= it.app.name %>",
4
+ "shells": ["shell"],
5
+ "routes": ["/<%= it.app.name %>"],
6
+ "exposes": ["./App"]
7
+ }
@@ -0,0 +1,6 @@
1
+ {
2
+ "name": "<%= it.scope %>/<%= it.app.name %>",
3
+ "version": "0.0.0",
4
+ "private": true,
5
+ "type": "module"
6
+ }
@@ -0,0 +1,7 @@
1
+ import { createRemoteConfig } from '@mfeflow/rsbuild';
2
+
3
+ export default createRemoteConfig({
4
+ name: '<%= it.app.name %>',
5
+ port: <%= it.app.port %>,
6
+ exposes: { './App': './src/exposed/App.tsx' },
7
+ });
@@ -0,0 +1,6 @@
1
+ import { createRoot } from 'react-dom/client';
2
+ import App from './exposed/App';
3
+
4
+ // Standalone mode (`pnpm dev` in this folder). Inside the shell, only ./exposed/* is loaded.
5
+ const el = document.getElementById('root');
6
+ if (el) createRoot(el).render(<App />);
@@ -0,0 +1 @@
1
+ /// <reference types="@rsbuild/core/types" />
@@ -0,0 +1,18 @@
1
+ import { useState } from 'react';
2
+
3
+ export default function App() {
4
+ const [clicks, setClicks] = useState(0);
5
+ return (
6
+ <section data-testid="<%= it.app.name %>">
7
+ <h2><%= it.app.name %></h2>
8
+ <button
9
+ type="button"
10
+ onClick={() => {
11
+ setClicks(clicks + 1);
12
+ }}
13
+ >
14
+ Clicked {clicks} times
15
+ </button>
16
+ </section>
17
+ );
18
+ }
@@ -0,0 +1,2 @@
1
+ // The async boundary: shared singletons resolve before app code runs.
2
+ void import('./bootstrap');
@@ -0,0 +1,17 @@
1
+ # AGENTS.md
2
+
3
+ Guidance for coding agents working in this repo.
4
+
5
+ - Package manager: pnpm (via corepack). Never npm or yarn. Versions live in the `catalog` of
6
+ `pnpm-workspace.yaml`; add dependencies there and reference them as `catalog:`.
7
+ - `pnpm dev` runs the shell and every remote. `pnpm build` builds all of them. `mfeflow dev` does the same with
8
+ per-app logs and picks up new remotes without a restart.
9
+ - Before finishing: `mfeflow doctor` (and `mfeflow validate`, `mfeflow check-compat`, `mfeflow budget` after a build) must exit 0.
10
+ - TypeScript 7, strict. Relative imports have no file extension (`./plan`, not `./plan.js`).
11
+ - Each remote is deployed on its own. A remote must not import from another remote or from
12
+ the shell; talk through the registry, events or URLs.
13
+ - A remote's public surface is `src/exposed/`. Everything else is private to it.
14
+ - Add apps and features with the `mfeflow` CLI (`mfeflow add remote <name>`, `mfeflow features`).
15
+ `mfeflow.json` is the CLI's record of the project; do not edit the file hashes by hand.
16
+ - Shared singletons (react, react-dom, `@mfeflow/runtime`) are provided by the shell. Do not
17
+ bundle your own copy.
@@ -0,0 +1,52 @@
1
+ # <%= it.scope.slice(1) %>-platform
2
+
3
+ A React micro-frontend platform made with [mfeflow](https://github.com/mfeflow/mfeflow):
4
+ one shell and independently deployable remotes, loaded at runtime through Module Federation.
5
+
6
+ ```
7
+ apps/shell the host: routes, layout, registry loading
8
+ apps/<remote> one micro-frontend each, exposing ./App
9
+ registry/envs which build of each remote every environment serves
10
+ mfeflow.json what the CLI generated; `mfeflow add` reads and updates it
11
+ ```
12
+
13
+ ## Run it
14
+
15
+ ```sh
16
+ pnpm install
17
+ pnpm dev
18
+ ```
19
+
20
+ `pnpm dev` starts the shell on http://localhost:4300 and every remote on its own port
21
+ (see `registry/envs/dev`). The shell reads that dev registry and renders the remotes.
22
+
23
+ `mfeflow dev` does the same with a prefixed, coloured log per app, and it follows the project:
24
+ add a remote with `mfeflow add remote checkout` and it starts, and the shell lists it, without a restart.
25
+ Use a remote you already run elsewhere with `mfeflow dev --remote checkout=http://localhost:3001`,
26
+ or run a few apps with `--only shell,catalog`. Ctrl+C stops everything.
27
+
28
+ ## Change it
29
+
30
+ ```sh
31
+ mfeflow add remote checkout # a new remote, its registry files and its shell route
32
+ mfeflow add <feature> # see `mfeflow features`
33
+ ```
34
+
35
+ ## Check it
36
+
37
+ ```sh
38
+ mfeflow doctor # singletons, compatibility, config, drift; --fix repairs the safe ones
39
+ mfeflow validate # mfeflow.json, every mfe.json and every registry entry
40
+ mfeflow check-compat # each remote's shared ranges against its shell
41
+ mfeflow budget # after `pnpm build`: first-load JS per shell and remote
42
+ ```
43
+
44
+ All of them take `--json` and exit non-zero on errors, so they can run in CI. `mfeflow budget` checks each remote's entry
45
+ against the 10 KB a remote may add; set `budget.baselineKb` in `mfeflow.json` (your pages built as one app) to check
46
+ the shell against baseline + 32 KB too.
47
+
48
+ ## Before you deploy
49
+
50
+ `registry/envs/staging` and `registry/envs/prod` point at `example.com`. Replace the hosts
51
+ with where you serve each remote (the shell expects `/mfe/<name>/` on its own origin).
52
+ The shell loads the registry from `/registry/index.json`, so serve one per environment.
@@ -0,0 +1,11 @@
1
+ node_modules/
2
+ dist/
3
+ .turbo/
4
+ coverage/
5
+ *.tsbuildinfo
6
+ .mfeflow/
7
+ *.log
8
+ .env
9
+ .env.*
10
+ !.env.example
11
+ .DS_Store
@@ -0,0 +1,9 @@
1
+ {
2
+ "name": "<%= it.scope.slice(1) %>-platform",
3
+ "private": true,
4
+ "type": "module",
5
+ "packageManager": "pnpm@12.16.1",
6
+ "engines": {
7
+ "node": ">=22.12"
8
+ }
9
+ }