mfeflow 0.5.0 → 0.7.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.
- package/README.md +8 -3
- package/assets/features/deploy-cloudflare/feature.json +85 -0
- package/assets/features/deploy-cloudflare/files/cloudflare.json.eta +13 -0
- package/assets/features/deploy-cloudflare/files/cloudflare.md.eta +104 -0
- package/assets/features/deploy-cloudflare/files/deploy-cloudflare.ts +318 -0
- package/assets/features/deploy-cloudflare/files/deploy-cloudflare.yml +111 -0
- package/assets/features/deploy-cloudflare/files/headers.remote +14 -0
- package/assets/features/deploy-cloudflare/files/headers.shell.eta +18 -0
- package/assets/features/deploy-cloudflare/files/r2-cors.json +10 -0
- package/assets/features/deploy-cloudflare/files/registry-proxy.ts +80 -0
- package/assets/features/deploy-cloudflare/files/wrangler.remote.jsonc.eta +21 -0
- package/assets/features/deploy-cloudflare/files/wrangler.shell.jsonc.eta +40 -0
- package/assets/features/deploy-docker/feature.json +22 -0
- package/assets/features/deploy-docker/files/40-mfe-config.sh +76 -0
- package/assets/features/deploy-docker/files/Dockerfile +48 -0
- package/assets/features/deploy-docker/files/compose-registry.json.eta +19 -0
- package/assets/features/deploy-docker/files/compose.yaml.eta +50 -0
- package/assets/features/deploy-docker/files/docker.md +52 -0
- package/assets/features/deploy-docker/files/dockerignore +17 -0
- package/assets/features/deploy-docker/files/k8s.yaml +114 -0
- package/assets/features/deploy-docker/files/nginx.remote.conf +73 -0
- package/assets/features/deploy-docker/files/nginx.shell.conf +84 -0
- package/assets/features/registry-server/feature.json +128 -0
- package/assets/features/registry-server/files/deploy-server.yml +69 -0
- package/assets/features/registry-server/files/registry-server.md.eta +52 -0
- package/assets/features/registry-server/files/registry-sources.ts +12 -0
- package/assets/features/registry-static/feature.json +61 -0
- package/assets/features/registry-static/files/deploy.yml +61 -0
- package/assets/features/registry-static/files/registry.md.eta +49 -0
- package/assets/features/release/feature.json +43 -0
- package/assets/features/release/files/changeset-readme.md +7 -0
- package/assets/features/release/files/config.json +10 -0
- package/assets/features/release/files/releasing.md +21 -0
- package/assets/templates/monorepo/feature.json +7 -6
- package/assets/templates/monorepo/files/remote/{mfe.json.eta → mfeflow.app.json.eta} +1 -0
- package/assets/templates/monorepo/files/root/README.md.eta +1 -1
- package/assets/templates/monorepo/files/root/pnpm-workspace.yaml.eta +3 -2
- package/assets/templates/monorepo/files/shell/{mfe.json.eta → mfeflow.app.json.eta} +1 -0
- package/assets/templates/monorepo/files/shell/rsbuild.config.ts.eta +42 -6
- package/assets/templates/monorepo/migrations/0.4.0-app-file.mjs +58 -0
- package/assets/templates/polyrepo/feature.json +305 -0
- package/assets/templates/polyrepo/files/AGENTS.md.eta +15 -0
- package/assets/templates/polyrepo/files/changeset/README.md +5 -0
- package/assets/templates/polyrepo/files/changeset/config.json +9 -0
- package/assets/templates/polyrepo/files/packages/contracts/package.json.eta +23 -0
- package/assets/templates/polyrepo/files/packages/contracts/src/index.ts +11 -0
- package/assets/templates/polyrepo/files/packages/contracts/tsconfig.json.eta +5 -0
- package/assets/templates/polyrepo/files/packages/tokens/package.json.eta +23 -0
- package/assets/templates/polyrepo/files/packages/tokens/src/index.ts +13 -0
- package/assets/templates/polyrepo/files/packages/tokens/tsconfig.json.eta +5 -0
- package/assets/templates/polyrepo/files/platform/README.md.eta +43 -0
- package/assets/templates/polyrepo/files/platform/default.json.eta +27 -0
- package/assets/templates/polyrepo/files/remote/README.md.eta +23 -0
- package/assets/templates/polyrepo/files/renovate.json.eta +4 -0
- package/assets/templates/polyrepo/files/shell/README.md.eta +32 -0
- package/assets/templates/polyrepo/files/tsconfig.base.json.eta +13 -0
- package/assets/templates/polyrepo/files/workflows/platform-ci.yml +29 -0
- package/assets/templates/polyrepo/files/workflows/release.yml +40 -0
- package/assets/templates/polyrepo/files/workflows/reusable-ci.yml.eta +50 -0
- package/assets/templates/polyrepo/files/workflows/team-ci.yml.eta +12 -0
- package/dist/against.d.ts +16 -0
- package/dist/catalog.d.ts +4 -0
- package/dist/checks/compat.d.ts +12 -0
- package/dist/checks/index.d.ts +0 -1
- package/dist/commands/add.d.ts +0 -2
- package/dist/commands/adopt.d.ts +54 -0
- package/dist/commands/deploy.d.ts +35 -0
- package/dist/commands/doctor.d.ts +10 -0
- package/dist/commands/init.d.ts +9 -1
- package/dist/commands/server-mode.d.ts +210 -0
- package/dist/commands/status.d.ts +5 -0
- package/dist/commands/team-repo.d.ts +9 -0
- package/dist/commands/upgrade.d.ts +66 -0
- package/dist/devremotes.d.ts +15 -0
- package/dist/exit.d.ts +1 -0
- package/dist/glue.d.ts +6 -0
- package/dist/index.d.ts +1 -1
- package/dist/index.js +4600 -1875
- package/dist/inspect.d.ts +14 -2
- package/dist/output.d.ts +138 -3
- package/dist/pipeline.d.ts +5 -2
- package/dist/polyrepo.d.ts +69 -0
- package/dist/publish.d.ts +10 -1
- package/dist/runtime.d.ts +2 -0
- package/dist/server-client.d.ts +69 -0
- package/dist/system.d.ts +2 -0
- package/dist/testing.d.ts +17 -1
- package/dist/upgrade.d.ts +25 -0
- package/dist/write.d.ts +8 -0
- package/package.json +5 -5
|
@@ -2,8 +2,9 @@ packages:
|
|
|
2
2
|
- apps/*
|
|
3
3
|
- packages/*
|
|
4
4
|
|
|
5
|
-
# Dependencies allowed to run install scripts. esbuild (pulled in by Storybook)
|
|
6
|
-
# script; listing
|
|
5
|
+
# Dependencies allowed to run install scripts. esbuild (pulled in by Storybook) and workerd (pulled in
|
|
6
|
+
# by wrangler) work without their script; listing them as false keeps pnpm from stopping the install to ask.
|
|
7
7
|
allowBuilds:
|
|
8
8
|
lefthook: true
|
|
9
9
|
esbuild: false
|
|
10
|
+
workerd: false
|
|
@@ -3,7 +3,40 @@ import path from 'node:path';
|
|
|
3
3
|
import { defineConfig, mergeRsbuildConfig } from '@rsbuild/core';
|
|
4
4
|
import { createShellConfig } from '@mfeflow/rsbuild';
|
|
5
5
|
|
|
6
|
-
interface
|
|
6
|
+
<% if (it.topology === 'polyrepo') { %>interface DevRemote {
|
|
7
|
+
name: string;
|
|
8
|
+
entry: string;
|
|
9
|
+
version: string;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
function readDevRemotes(file: string): DevRemote[] {
|
|
13
|
+
let value: unknown;
|
|
14
|
+
try {
|
|
15
|
+
value = JSON.parse(fs.readFileSync(file, 'utf8'));
|
|
16
|
+
} catch (error) {
|
|
17
|
+
// A shell with no dev config yet has no remotes to list.
|
|
18
|
+
if (error instanceof Error && 'code' in error && error.code === 'ENOENT') return [];
|
|
19
|
+
throw error;
|
|
20
|
+
}
|
|
21
|
+
const remotes =
|
|
22
|
+
typeof value === 'object' && value !== null && 'devRemotes' in value ? value.devRemotes : {};
|
|
23
|
+
if (typeof remotes !== 'object' || remotes === null) {
|
|
24
|
+
throw new Error(`${file}: devRemotes must be an object of name: manifest URL`);
|
|
25
|
+
}
|
|
26
|
+
return Object.entries(remotes)
|
|
27
|
+
.toSorted(([a], [b]) => (a < b ? -1 : 1))
|
|
28
|
+
.map(([name, entry]) => {
|
|
29
|
+
if (typeof entry !== 'string') throw new Error(`${file}: devRemotes.${name} must be a URL`);
|
|
30
|
+
return { name, entry, version: 'dev' };
|
|
31
|
+
});
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
// The shell finds its remotes through the registry. The remotes live in other repos, so in dev
|
|
35
|
+
// their manifest URLs come from "devRemotes" in public/config/runtime-config.dev.json, inlined so
|
|
36
|
+
// the shell needs no registry server. `mfeflow dev` writes the merged registry (that file, plus
|
|
37
|
+
// --remote overrides) to MFEFLOW_DEV_REGISTRY; plain `pnpm dev` reads the file.
|
|
38
|
+
const devConfig = path.resolve(process.cwd(), 'public/config/runtime-config.dev.json');
|
|
39
|
+
<% } else { %>interface DevEntry {
|
|
7
40
|
manifestUrl: string;
|
|
8
41
|
version: string;
|
|
9
42
|
}
|
|
@@ -34,7 +67,7 @@ function readNames(dir: string): string[] {
|
|
|
34
67
|
// `mfeflow dev` writes the merged registry (apps from mfeflow.json, --remote overrides) to
|
|
35
68
|
// MFEFLOW_DEV_REGISTRY and rewrites it when the project changes; plain `pnpm dev` reads the folder.
|
|
36
69
|
const registryDir = path.resolve(process.cwd(), '../../registry/envs/dev');
|
|
37
|
-
const generated = process.env.MFEFLOW_DEV_REGISTRY;
|
|
70
|
+
<% } %>const generated = process.env.MFEFLOW_DEV_REGISTRY;
|
|
38
71
|
|
|
39
72
|
function readGenerated(file: string): unknown {
|
|
40
73
|
return JSON.parse(fs.readFileSync(file, 'utf8'));
|
|
@@ -44,7 +77,9 @@ function devRegistry(): string {
|
|
|
44
77
|
const remotes = generated
|
|
45
78
|
? readGenerated(generated)
|
|
46
79
|
: {
|
|
47
|
-
remotes:
|
|
80
|
+
<% if (it.topology === 'polyrepo') { %> remotes: readDevRemotes(devConfig),
|
|
81
|
+
};
|
|
82
|
+
<% } else { %> remotes: readNames(registryDir)
|
|
48
83
|
.filter((file) => file.endsWith('.json'))
|
|
49
84
|
.toSorted()
|
|
50
85
|
.map((file) => {
|
|
@@ -52,7 +87,7 @@ function devRegistry(): string {
|
|
|
52
87
|
return { name: file.slice(0, -'.json'.length), entry: manifestUrl, version };
|
|
53
88
|
}),
|
|
54
89
|
};
|
|
55
|
-
return `window.__MFE_REGISTRY__=${JSON.stringify(remotes).replaceAll('<', '\\u003c')}`;
|
|
90
|
+
<% } %> return `window.__MFE_REGISTRY__=${JSON.stringify(remotes).replaceAll('<', '\\u003c')}`;
|
|
56
91
|
}
|
|
57
92
|
|
|
58
93
|
export default defineConfig(({ command }) =>
|
|
@@ -61,8 +96,9 @@ export default defineConfig(({ command }) =>
|
|
|
61
96
|
createShellConfig({ name: '<%= it.app.name %>', port: <%= it.app.port %>, snapshot: true }),
|
|
62
97
|
defineConfig({
|
|
63
98
|
// A new or changed remote restarts the dev server, which reads the registry again.
|
|
64
|
-
dev: { watchFiles: { paths: generated ??
|
|
65
|
-
|
|
99
|
+
<% if (it.topology === 'polyrepo') { %> dev: { watchFiles: { paths: generated ?? devConfig, type: 'restart' } },
|
|
100
|
+
<% } else { %> dev: { watchFiles: { paths: generated ?? registryDir, type: 'restart' } },
|
|
101
|
+
<% } %> html: {
|
|
66
102
|
title: '<%= it.app.name %>',
|
|
67
103
|
tags:
|
|
68
104
|
command === 'dev'
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
// core 0.4.0: each app's ownership file is now mfeflow.app.json, not mfe.json.
|
|
2
|
+
// Moves the content of every app's mfe.json to mfeflow.app.json, with the schema URL updated,
|
|
3
|
+
// and deletes mfe.json. An app that already has both files is left alone with a warning (E122).
|
|
4
|
+
// Self-contained on purpose: `mfeflow upgrade` imports it from the feature folder.
|
|
5
|
+
|
|
6
|
+
const OLD = 'mfe.json';
|
|
7
|
+
const NEW = 'mfeflow.app.json';
|
|
8
|
+
const OLD_SCHEMA = 'https://mfeflow.github.io/mfeflow/schema/mfe.json';
|
|
9
|
+
const NEW_SCHEMA = 'https://mfeflow.github.io/mfeflow/schema/mfeflow-app.json';
|
|
10
|
+
|
|
11
|
+
const inApp = (dir, file) => (dir === '' ? file : `${dir}/${file}`);
|
|
12
|
+
|
|
13
|
+
/** The file text with the new schema URL: the old URL replaced, or a `$schema` line added first. */
|
|
14
|
+
export function withNewSchema(text) {
|
|
15
|
+
if (text.includes(OLD_SCHEMA)) return text.replaceAll(OLD_SCHEMA, NEW_SCHEMA);
|
|
16
|
+
if (text.includes('"$schema"')) return text;
|
|
17
|
+
const open = /^\{(\r?\n)/.exec(text);
|
|
18
|
+
if (!open) return text;
|
|
19
|
+
return `{${open[1]} "$schema": "${NEW_SCHEMA}",${open[1]}${text.slice(open[0].length)}`;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* @param {import('@mfeflow/core').PlanBuilder} builder
|
|
24
|
+
* @param {import('@mfeflow/core').MfeflowProject} project
|
|
25
|
+
*/
|
|
26
|
+
export default async function migrate(builder, project) {
|
|
27
|
+
const dirs = Object.values(project.apps)
|
|
28
|
+
.map((app) => app.path)
|
|
29
|
+
.toSorted();
|
|
30
|
+
for (const dir of dirs) {
|
|
31
|
+
const from = inApp(dir, OLD);
|
|
32
|
+
const to = inApp(dir, NEW);
|
|
33
|
+
// oxlint-disable-next-line no-await-in-loop -- apps in order keep the plan deterministic
|
|
34
|
+
const text = await builder.content(from);
|
|
35
|
+
if (text === undefined) continue;
|
|
36
|
+
// oxlint-disable-next-line no-await-in-loop -- see above
|
|
37
|
+
if (await builder.existed(to)) {
|
|
38
|
+
builder.warn(
|
|
39
|
+
`${dir || '.'} has both ${OLD} and ${NEW}; keep ${NEW} and delete ${OLD} (E122)`,
|
|
40
|
+
);
|
|
41
|
+
continue;
|
|
42
|
+
}
|
|
43
|
+
const moved = withNewSchema(text);
|
|
44
|
+
// The upgrade may already plan a fresh NEW file from the template. When the old file was
|
|
45
|
+
// never edited the two match and that tracked file stays. Otherwise your content wins, and
|
|
46
|
+
// it is not tracked, so a later upgrade shows a diff instead of replacing it.
|
|
47
|
+
// oxlint-disable-next-line no-await-in-loop -- see above
|
|
48
|
+
const planned = await builder.content(to);
|
|
49
|
+
if (planned !== moved) {
|
|
50
|
+
// oxlint-disable-next-line no-await-in-loop -- see above
|
|
51
|
+
if (planned !== undefined) await builder.delete(to);
|
|
52
|
+
// oxlint-disable-next-line no-await-in-loop -- see above
|
|
53
|
+
await builder.create(to, moved, false);
|
|
54
|
+
}
|
|
55
|
+
// oxlint-disable-next-line no-await-in-loop -- see above
|
|
56
|
+
await builder.delete(from);
|
|
57
|
+
}
|
|
58
|
+
}
|
|
@@ -0,0 +1,305 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "polyrepo",
|
|
3
|
+
"version": "0.2.0",
|
|
4
|
+
"description": "What a polyrepo repo has beyond the monorepo template: the platform packages, the reusable CI and Renovate preset, and a package.json, README and CI caller for each repo.",
|
|
5
|
+
"prompts": [
|
|
6
|
+
{
|
|
7
|
+
"name": "repo",
|
|
8
|
+
"type": "select",
|
|
9
|
+
"message": "Which repo of the polyrepo this is",
|
|
10
|
+
"choices": ["platform", "shell", "remote"],
|
|
11
|
+
"default": "platform"
|
|
12
|
+
},
|
|
13
|
+
{
|
|
14
|
+
"name": "name",
|
|
15
|
+
"type": "string",
|
|
16
|
+
"message": "The remote's name (repo remote only)",
|
|
17
|
+
"default": ""
|
|
18
|
+
},
|
|
19
|
+
{
|
|
20
|
+
"name": "org",
|
|
21
|
+
"type": "string",
|
|
22
|
+
"message": "The GitHub owner the repos will live under (default: the scope without @)",
|
|
23
|
+
"default": ""
|
|
24
|
+
}
|
|
25
|
+
],
|
|
26
|
+
"files": [
|
|
27
|
+
{
|
|
28
|
+
"to": "AGENTS.md",
|
|
29
|
+
"target": "root",
|
|
30
|
+
"from": "files/AGENTS.md.eta",
|
|
31
|
+
"template": true
|
|
32
|
+
},
|
|
33
|
+
{
|
|
34
|
+
"to": "renovate.json",
|
|
35
|
+
"target": "root",
|
|
36
|
+
"from": "files/renovate.json.eta",
|
|
37
|
+
"template": true
|
|
38
|
+
},
|
|
39
|
+
{
|
|
40
|
+
"to": "README.md",
|
|
41
|
+
"target": "root",
|
|
42
|
+
"from": "files/platform/README.md.eta",
|
|
43
|
+
"template": true,
|
|
44
|
+
"when": {
|
|
45
|
+
"repo": "platform"
|
|
46
|
+
}
|
|
47
|
+
},
|
|
48
|
+
{
|
|
49
|
+
"to": "README.md",
|
|
50
|
+
"target": "root",
|
|
51
|
+
"from": "files/shell/README.md.eta",
|
|
52
|
+
"template": true,
|
|
53
|
+
"when": {
|
|
54
|
+
"repo": "shell"
|
|
55
|
+
}
|
|
56
|
+
},
|
|
57
|
+
{
|
|
58
|
+
"to": "README.md",
|
|
59
|
+
"target": "root",
|
|
60
|
+
"from": "files/remote/README.md.eta",
|
|
61
|
+
"template": true,
|
|
62
|
+
"when": {
|
|
63
|
+
"repo": "remote"
|
|
64
|
+
}
|
|
65
|
+
},
|
|
66
|
+
{
|
|
67
|
+
"to": "default.json",
|
|
68
|
+
"target": "root",
|
|
69
|
+
"from": "files/platform/default.json.eta",
|
|
70
|
+
"template": true,
|
|
71
|
+
"when": {
|
|
72
|
+
"repo": "platform"
|
|
73
|
+
}
|
|
74
|
+
},
|
|
75
|
+
{
|
|
76
|
+
"to": "tsconfig.base.json",
|
|
77
|
+
"target": "root",
|
|
78
|
+
"from": "files/tsconfig.base.json.eta",
|
|
79
|
+
"template": true,
|
|
80
|
+
"when": {
|
|
81
|
+
"repo": "platform"
|
|
82
|
+
}
|
|
83
|
+
},
|
|
84
|
+
{
|
|
85
|
+
"to": "packages/contracts/package.json",
|
|
86
|
+
"target": "root",
|
|
87
|
+
"from": "files/packages/contracts/package.json.eta",
|
|
88
|
+
"template": true,
|
|
89
|
+
"when": {
|
|
90
|
+
"repo": "platform"
|
|
91
|
+
}
|
|
92
|
+
},
|
|
93
|
+
{
|
|
94
|
+
"to": "packages/contracts/tsconfig.json",
|
|
95
|
+
"target": "root",
|
|
96
|
+
"from": "files/packages/contracts/tsconfig.json.eta",
|
|
97
|
+
"template": true,
|
|
98
|
+
"when": {
|
|
99
|
+
"repo": "platform"
|
|
100
|
+
}
|
|
101
|
+
},
|
|
102
|
+
{
|
|
103
|
+
"to": "packages/contracts/src/index.ts",
|
|
104
|
+
"target": "root",
|
|
105
|
+
"from": "files/packages/contracts/src/index.ts",
|
|
106
|
+
"when": {
|
|
107
|
+
"repo": "platform"
|
|
108
|
+
}
|
|
109
|
+
},
|
|
110
|
+
{
|
|
111
|
+
"to": "packages/tokens/package.json",
|
|
112
|
+
"target": "root",
|
|
113
|
+
"from": "files/packages/tokens/package.json.eta",
|
|
114
|
+
"template": true,
|
|
115
|
+
"when": {
|
|
116
|
+
"repo": "platform"
|
|
117
|
+
}
|
|
118
|
+
},
|
|
119
|
+
{
|
|
120
|
+
"to": "packages/tokens/tsconfig.json",
|
|
121
|
+
"target": "root",
|
|
122
|
+
"from": "files/packages/tokens/tsconfig.json.eta",
|
|
123
|
+
"template": true,
|
|
124
|
+
"when": {
|
|
125
|
+
"repo": "platform"
|
|
126
|
+
}
|
|
127
|
+
},
|
|
128
|
+
{
|
|
129
|
+
"to": "packages/tokens/src/index.ts",
|
|
130
|
+
"target": "root",
|
|
131
|
+
"from": "files/packages/tokens/src/index.ts",
|
|
132
|
+
"when": {
|
|
133
|
+
"repo": "platform"
|
|
134
|
+
}
|
|
135
|
+
},
|
|
136
|
+
{
|
|
137
|
+
"to": ".changeset/config.json",
|
|
138
|
+
"target": "root",
|
|
139
|
+
"from": "files/changeset/config.json",
|
|
140
|
+
"when": {
|
|
141
|
+
"repo": "platform"
|
|
142
|
+
}
|
|
143
|
+
},
|
|
144
|
+
{
|
|
145
|
+
"to": ".changeset/README.md",
|
|
146
|
+
"target": "root",
|
|
147
|
+
"from": "files/changeset/README.md",
|
|
148
|
+
"when": {
|
|
149
|
+
"repo": "platform"
|
|
150
|
+
}
|
|
151
|
+
},
|
|
152
|
+
{
|
|
153
|
+
"to": ".github/workflows/ci.yml",
|
|
154
|
+
"target": "root",
|
|
155
|
+
"from": "files/workflows/platform-ci.yml",
|
|
156
|
+
"when": {
|
|
157
|
+
"repo": "platform"
|
|
158
|
+
}
|
|
159
|
+
},
|
|
160
|
+
{
|
|
161
|
+
"to": ".github/workflows/reusable-ci.yml",
|
|
162
|
+
"target": "root",
|
|
163
|
+
"from": "files/workflows/reusable-ci.yml.eta",
|
|
164
|
+
"template": true,
|
|
165
|
+
"when": {
|
|
166
|
+
"repo": "platform"
|
|
167
|
+
}
|
|
168
|
+
},
|
|
169
|
+
{
|
|
170
|
+
"to": ".github/workflows/release.yml",
|
|
171
|
+
"target": "root",
|
|
172
|
+
"from": "files/workflows/release.yml",
|
|
173
|
+
"when": {
|
|
174
|
+
"repo": "platform"
|
|
175
|
+
}
|
|
176
|
+
},
|
|
177
|
+
{
|
|
178
|
+
"to": ".github/workflows/ci.yml",
|
|
179
|
+
"target": "root",
|
|
180
|
+
"from": "files/workflows/team-ci.yml.eta",
|
|
181
|
+
"template": true,
|
|
182
|
+
"when": {
|
|
183
|
+
"repo": "shell"
|
|
184
|
+
}
|
|
185
|
+
},
|
|
186
|
+
{
|
|
187
|
+
"to": ".github/workflows/ci.yml",
|
|
188
|
+
"target": "root",
|
|
189
|
+
"from": "files/workflows/team-ci.yml.eta",
|
|
190
|
+
"template": true,
|
|
191
|
+
"when": {
|
|
192
|
+
"repo": "remote"
|
|
193
|
+
}
|
|
194
|
+
},
|
|
195
|
+
{
|
|
196
|
+
"to": "public/config/runtime-config.staging.json",
|
|
197
|
+
"target": "shell",
|
|
198
|
+
"content": "{\n \"environment\": \"staging\"\n}\n"
|
|
199
|
+
},
|
|
200
|
+
{
|
|
201
|
+
"to": "public/config/runtime-config.prod.json",
|
|
202
|
+
"target": "shell",
|
|
203
|
+
"content": "{\n \"environment\": \"prod\"\n}\n"
|
|
204
|
+
}
|
|
205
|
+
],
|
|
206
|
+
"dependencies": [
|
|
207
|
+
{
|
|
208
|
+
"name": "@changesets/cli",
|
|
209
|
+
"range": "3.0.3",
|
|
210
|
+
"target": "root",
|
|
211
|
+
"dev": true,
|
|
212
|
+
"when": {
|
|
213
|
+
"repo": "platform"
|
|
214
|
+
}
|
|
215
|
+
},
|
|
216
|
+
{
|
|
217
|
+
"name": "typescript",
|
|
218
|
+
"range": "7.0.2",
|
|
219
|
+
"target": "root",
|
|
220
|
+
"dev": true,
|
|
221
|
+
"when": {
|
|
222
|
+
"repo": "platform"
|
|
223
|
+
}
|
|
224
|
+
},
|
|
225
|
+
{
|
|
226
|
+
"name": "mfeflow",
|
|
227
|
+
"range": "latest",
|
|
228
|
+
"target": "root",
|
|
229
|
+
"dev": true,
|
|
230
|
+
"when": {
|
|
231
|
+
"repo": "platform"
|
|
232
|
+
}
|
|
233
|
+
},
|
|
234
|
+
{
|
|
235
|
+
"name": "{{scope}}/contracts",
|
|
236
|
+
"range": "latest",
|
|
237
|
+
"target": "shell"
|
|
238
|
+
},
|
|
239
|
+
{
|
|
240
|
+
"name": "{{scope}}/tokens",
|
|
241
|
+
"range": "latest",
|
|
242
|
+
"target": "shell"
|
|
243
|
+
},
|
|
244
|
+
{
|
|
245
|
+
"name": "{{scope}}/contracts",
|
|
246
|
+
"range": "latest",
|
|
247
|
+
"target": "remotes"
|
|
248
|
+
},
|
|
249
|
+
{
|
|
250
|
+
"name": "{{scope}}/tokens",
|
|
251
|
+
"range": "latest",
|
|
252
|
+
"target": "remotes"
|
|
253
|
+
}
|
|
254
|
+
],
|
|
255
|
+
"scripts": [
|
|
256
|
+
{
|
|
257
|
+
"name": "build",
|
|
258
|
+
"command": "pnpm -r --if-present run build",
|
|
259
|
+
"target": "root",
|
|
260
|
+
"when": {
|
|
261
|
+
"repo": "platform"
|
|
262
|
+
}
|
|
263
|
+
},
|
|
264
|
+
{
|
|
265
|
+
"name": "typecheck",
|
|
266
|
+
"command": "pnpm -r --if-present run typecheck",
|
|
267
|
+
"target": "root",
|
|
268
|
+
"when": {
|
|
269
|
+
"repo": "platform"
|
|
270
|
+
}
|
|
271
|
+
},
|
|
272
|
+
{
|
|
273
|
+
"name": "validate",
|
|
274
|
+
"command": "mfeflow validate",
|
|
275
|
+
"target": "root",
|
|
276
|
+
"when": {
|
|
277
|
+
"repo": "platform"
|
|
278
|
+
}
|
|
279
|
+
},
|
|
280
|
+
{
|
|
281
|
+
"name": "changeset",
|
|
282
|
+
"command": "changeset",
|
|
283
|
+
"target": "root",
|
|
284
|
+
"when": {
|
|
285
|
+
"repo": "platform"
|
|
286
|
+
}
|
|
287
|
+
},
|
|
288
|
+
{
|
|
289
|
+
"name": "version",
|
|
290
|
+
"command": "changeset version",
|
|
291
|
+
"target": "root",
|
|
292
|
+
"when": {
|
|
293
|
+
"repo": "platform"
|
|
294
|
+
}
|
|
295
|
+
},
|
|
296
|
+
{
|
|
297
|
+
"name": "release",
|
|
298
|
+
"command": "pnpm build && changeset publish",
|
|
299
|
+
"target": "root",
|
|
300
|
+
"when": {
|
|
301
|
+
"repo": "platform"
|
|
302
|
+
}
|
|
303
|
+
}
|
|
304
|
+
]
|
|
305
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
# AGENTS.md
|
|
2
|
+
|
|
3
|
+
Guidance for coding agents working in this repo.
|
|
4
|
+
|
|
5
|
+
- This is the <%= it.options.repo %> repo of a polyrepo. The other repos are its siblings: `../platform`, `../shell` and one per team.
|
|
6
|
+
- Package manager: pnpm (via corepack). Never npm or yarn. Versions live in the `catalog` of
|
|
7
|
+
`pnpm-workspace.yaml`; add dependencies there and reference them as `catalog:`.
|
|
8
|
+
<% if (it.options.repo === 'platform') { %>- Every change to a package in `packages/` needs a changeset (`pnpm changeset`).
|
|
9
|
+
- Never import from a sibling repo by path. Other repos depend on what is published from here.
|
|
10
|
+
<% } else { %>- `<%= it.scope %>/*` packages come from `../platform` (`link:` paths until they are published). Build the platform repo first.
|
|
11
|
+
- TypeScript 7, strict. Relative imports have no file extension (`./plan`, not `./plan.js`).
|
|
12
|
+
- Add features with the `mfeflow` CLI (`pnpm mfeflow add <feature>`). `mfeflow.json` is its record of this repo.
|
|
13
|
+
<% } %><% if (it.options.repo === 'remote') { %>- A remote's public surface is `src/exposed/`. It must not import from the shell or another remote.
|
|
14
|
+
<% } %><% if (it.options.repo === 'shell') { %>- A new remote gets a repo with `pnpm mfeflow add team-repo <name>`. Do not add remotes to this repo by hand.
|
|
15
|
+
<% } %>EOF
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
# Changesets
|
|
2
|
+
|
|
3
|
+
Run `pnpm changeset` in any pull request that changes a package here. Pick the bump and describe
|
|
4
|
+
the change for the teams that use it. When the pull request merges, the release workflow opens a
|
|
5
|
+
"chore: version packages" pull request. Merging that one publishes the packages.
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "<%= it.scope %>/contracts",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Event names and types the shell and every remote agree on.",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"files": ["dist"],
|
|
7
|
+
"exports": {
|
|
8
|
+
".": {
|
|
9
|
+
"types": "./dist/index.d.ts",
|
|
10
|
+
"import": "./dist/index.js"
|
|
11
|
+
}
|
|
12
|
+
},
|
|
13
|
+
"publishConfig": {
|
|
14
|
+
"access": "public"
|
|
15
|
+
},
|
|
16
|
+
"scripts": {
|
|
17
|
+
"build": "tsc",
|
|
18
|
+
"typecheck": "tsc --noEmit"
|
|
19
|
+
},
|
|
20
|
+
"devDependencies": {
|
|
21
|
+
"typescript": "catalog:"
|
|
22
|
+
}
|
|
23
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
// Names and shapes that more than one repo depends on. A change here is a change for every team,
|
|
2
|
+
// so it goes through a pull request and a changeset like any other published package.
|
|
3
|
+
export const EVENTS = {
|
|
4
|
+
navigate: 'platform:navigate',
|
|
5
|
+
} as const;
|
|
6
|
+
|
|
7
|
+
export type EventName = (typeof EVENTS)[keyof typeof EVENTS];
|
|
8
|
+
|
|
9
|
+
export interface NavigateEvent {
|
|
10
|
+
path: string;
|
|
11
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "<%= it.scope %>/tokens",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Colors and spacing every team uses.",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"files": ["dist"],
|
|
7
|
+
"exports": {
|
|
8
|
+
".": {
|
|
9
|
+
"types": "./dist/index.d.ts",
|
|
10
|
+
"import": "./dist/index.js"
|
|
11
|
+
}
|
|
12
|
+
},
|
|
13
|
+
"publishConfig": {
|
|
14
|
+
"access": "public"
|
|
15
|
+
},
|
|
16
|
+
"scripts": {
|
|
17
|
+
"build": "tsc",
|
|
18
|
+
"typecheck": "tsc --noEmit"
|
|
19
|
+
},
|
|
20
|
+
"devDependencies": {
|
|
21
|
+
"typescript": "catalog:"
|
|
22
|
+
}
|
|
23
|
+
}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
# <%= it.scope.slice(1) %>-platform
|
|
2
|
+
|
|
3
|
+
The shared repo of a polyrepo made with [mfeflow](https://github.com/mfeflow/mfeflow). The shell and
|
|
4
|
+
every team repo sit next to it and depend on what is published from here.
|
|
5
|
+
|
|
6
|
+
```
|
|
7
|
+
packages/contracts <%= it.scope %>/contracts: event names and types every repo agrees on
|
|
8
|
+
packages/tokens <%= it.scope %>/tokens: colors and spacing
|
|
9
|
+
.github/workflows reusable-ci.yml (the CI every team repo calls), ci.yml, release.yml
|
|
10
|
+
default.json the Renovate preset every repo extends
|
|
11
|
+
.changeset how a change here gets a version
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
## Run it
|
|
15
|
+
|
|
16
|
+
```sh
|
|
17
|
+
pnpm install
|
|
18
|
+
pnpm build
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
Build here first. The shell and the team repos link to `packages/*/dist` until you publish.
|
|
22
|
+
|
|
23
|
+
## Change a package
|
|
24
|
+
|
|
25
|
+
```sh
|
|
26
|
+
pnpm changeset # describe the change and pick the bump
|
|
27
|
+
pnpm build
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
Commit the changeset with your change. After the pull request merges, the release workflow opens a
|
|
31
|
+
version pull request. Merging that publishes the packages.
|
|
32
|
+
|
|
33
|
+
## Where the packages are published
|
|
34
|
+
|
|
35
|
+
The team repos install `<%= it.scope %>/*` with `link:../platform/packages/*` paths so a first run needs
|
|
36
|
+
no registry. To share the packages across machines and CI, publish them to a registry and replace the
|
|
37
|
+
`link:` ranges in each repo with a version:
|
|
38
|
+
|
|
39
|
+
- npm: set `NPM_TOKEN` as a repository secret. Packages are public.
|
|
40
|
+
- GitHub Packages: add `<%= it.scope %>:registry=https://npm.pkg.github.com` to `.npmrc` in every repo.
|
|
41
|
+
- A private registry: put its URL in `.npmrc` and set `publishConfig.registry` in each package.
|
|
42
|
+
|
|
43
|
+
Creating the GitHub repos and the registry account is up to you. mfeflow never calls GitHub.
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://docs.renovatebot.com/renovate-schema.json",
|
|
3
|
+
"description": "Renovate preset for every <%= it.options.org || it.scope.slice(1) %> repo. Each repo extends it with github><%= it.options.org || it.scope.slice(1) %>/platform.",
|
|
4
|
+
"extends": ["config:recommended"],
|
|
5
|
+
"packageRules": [
|
|
6
|
+
{
|
|
7
|
+
"description": "Singletons the shell and every remote must share move in one pull request per repo.",
|
|
8
|
+
"matchPackageNames": ["react", "react-dom", "@types/react", "@types/react-dom"],
|
|
9
|
+
"groupName": "react"
|
|
10
|
+
},
|
|
11
|
+
{
|
|
12
|
+
"description": "Module Federation and Rsbuild have to stay on versions that work together.",
|
|
13
|
+
"matchPackageNames": ["@module-federation/**", "@rsbuild/**"],
|
|
14
|
+
"groupName": "module federation and rsbuild"
|
|
15
|
+
},
|
|
16
|
+
{
|
|
17
|
+
"description": "mfeflow packages are released together.",
|
|
18
|
+
"matchPackageNames": ["mfeflow", "@mfeflow/**"],
|
|
19
|
+
"groupName": "mfeflow"
|
|
20
|
+
},
|
|
21
|
+
{
|
|
22
|
+
"description": "The packages this org publishes from its platform repo.",
|
|
23
|
+
"matchPackageNames": ["<%= it.scope %>/**"],
|
|
24
|
+
"groupName": "platform packages"
|
|
25
|
+
}
|
|
26
|
+
]
|
|
27
|
+
}
|