@mfeflow/core 0.5.0 → 0.6.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/dist/adopt/detect.d.ts +11 -0
- package/dist/adopt/disk.d.ts +20 -0
- package/dist/adopt/evaluate.d.ts +70 -0
- package/dist/adopt/federation.d.ts +20 -0
- package/dist/adopt/index.d.ts +4 -0
- package/dist/adopt/plan.d.ts +39 -0
- package/dist/adopt/types.d.ts +73 -0
- package/dist/adopt/workspace.d.ts +16 -0
- package/dist/app-file.d.ts +20 -0
- package/dist/apply.d.ts +2 -0
- package/dist/errors.d.ts +42 -9
- package/dist/features/load.d.ts +2 -0
- package/dist/fs.d.ts +5 -0
- package/dist/index.d.ts +4 -1
- package/dist/index.js +2248 -426
- package/dist/plan.d.ts +3 -0
- package/dist/transforms/add-env-key.d.ts +2 -0
- package/dist/transforms/ast.d.ts +1 -0
- package/dist/upgrade.d.ts +109 -0
- package/package.json +2 -2
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { DetectFederationOptions, FederationReport } from './types.js';
|
|
2
|
+
/**
|
|
3
|
+
* Works out the Module Federation setup of an existing project by reading
|
|
4
|
+
* its config files as source. Config files are parsed, never imported or run:
|
|
5
|
+
* only literal values (and `const` names in the same file) are trusted, and
|
|
6
|
+
* everything else is listed in `notAdopted` with the reason.
|
|
7
|
+
*
|
|
8
|
+
* Apps are the folder itself plus each workspace package from
|
|
9
|
+
* `pnpm-workspace.yaml` or package.json `workspaces`.
|
|
10
|
+
*/
|
|
11
|
+
export declare function detectFederation(dir: string, options?: DetectFederationOptions): Promise<FederationReport>;
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import type { ReadFile } from '../plan.js';
|
|
2
|
+
/** Largest file the detector will read. A config this big is not a config. */
|
|
3
|
+
export declare const MAX_FILE_BYTES = 1000000;
|
|
4
|
+
/** A path the detector refused to read, and why. `file` is relative to the scanned folder. */
|
|
5
|
+
export interface Refusal {
|
|
6
|
+
file: string;
|
|
7
|
+
reason: string;
|
|
8
|
+
}
|
|
9
|
+
/** Whether `target` is `root` or something under it. Compares whole segments, so `..x` is inside. */
|
|
10
|
+
export declare function isInside(root: string, target: string): boolean;
|
|
11
|
+
/**
|
|
12
|
+
* Reads from the disk without leaving `root`: every path is resolved through
|
|
13
|
+
* symlinks first and must still be inside the (resolved) root and outside
|
|
14
|
+
* node_modules. Anything refused or unreadable is recorded in `refused`
|
|
15
|
+
* instead of throwing.
|
|
16
|
+
*/
|
|
17
|
+
export declare function guardedDisk(root: string, refused: Refusal[]): {
|
|
18
|
+
read: ReadFile;
|
|
19
|
+
listDirs: (dir: string) => Promise<string[]>;
|
|
20
|
+
};
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import { type File, type Node, type ObjectExpression } from '@babel/types';
|
|
2
|
+
/** A value written out in full in the source: what the detector is allowed to trust. */
|
|
3
|
+
export type Value = string | number | boolean | null | Value[] | {
|
|
4
|
+
[key: string]: Value;
|
|
5
|
+
};
|
|
6
|
+
export type Result<T> = {
|
|
7
|
+
ok: true;
|
|
8
|
+
value: T;
|
|
9
|
+
} | {
|
|
10
|
+
ok: false;
|
|
11
|
+
reason: string;
|
|
12
|
+
};
|
|
13
|
+
export declare const ok: <T>(value: T) => Result<T>;
|
|
14
|
+
export declare const fail: <T = never>(reason: string) => Result<T>;
|
|
15
|
+
/** What a top-level name in a file is bound to. */
|
|
16
|
+
export type Binding = {
|
|
17
|
+
kind: 'const';
|
|
18
|
+
init: Node;
|
|
19
|
+
} | {
|
|
20
|
+
kind: 'import';
|
|
21
|
+
source: string;
|
|
22
|
+
imported: string;
|
|
23
|
+
depth?: number;
|
|
24
|
+
} | {
|
|
25
|
+
kind: 'other';
|
|
26
|
+
what: string;
|
|
27
|
+
};
|
|
28
|
+
/** The top-level names and exports of one parsed file. */
|
|
29
|
+
export interface Scope {
|
|
30
|
+
/** Absolute path of the file. */
|
|
31
|
+
file: string;
|
|
32
|
+
bindings: Map<string, Binding>;
|
|
33
|
+
/** Names whose value is changed after it is declared (`x.y = …`, `Object.assign(x, …)`). */
|
|
34
|
+
mutated: Set<string>;
|
|
35
|
+
/** Exported name (`default` for the default export) to the exported expression. */
|
|
36
|
+
exports: Map<string, Node>;
|
|
37
|
+
/** Exported name to the local const it was exported from, when there is one. */
|
|
38
|
+
exportedFrom: Map<string, string>;
|
|
39
|
+
}
|
|
40
|
+
/** Strips `as`, `satisfies`, `!` and `<T>x` so only the value is left. */
|
|
41
|
+
export declare function unwrap(node: Node | null | undefined): Node | undefined;
|
|
42
|
+
/** Reads the top-level bindings and exports of a parsed file. */
|
|
43
|
+
export declare function scopeOf(ast: File, file: string): Scope;
|
|
44
|
+
/** `process.env.X`, `process.env['X']` or `import.meta.env.X` under `node`, as text. */
|
|
45
|
+
export declare function envAccess(node: Node): string | undefined;
|
|
46
|
+
/** Why `node` is not a literal value, in plain words. */
|
|
47
|
+
export declare function reasonFor(node: Node): string;
|
|
48
|
+
/**
|
|
49
|
+
* Follows `const` names in the same file to the expression they hold, so a
|
|
50
|
+
* caller can look at its shape. Stops at anything that is not a plain const.
|
|
51
|
+
*/
|
|
52
|
+
export declare function resolveNode(node: Node | null | undefined, scope: Scope, seen?: ReadonlySet<string>): Result<Node>;
|
|
53
|
+
export interface Entry {
|
|
54
|
+
key: string;
|
|
55
|
+
value: Node;
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* The properties of an object literal, with spreads of same-file const
|
|
59
|
+
* objects folded in. Anything that cannot be read is a problem, keyed by the
|
|
60
|
+
* property it belongs to when there is one.
|
|
61
|
+
*/
|
|
62
|
+
export declare function entriesOf(object: ObjectExpression, scope: Scope, seen?: ReadonlySet<string>): {
|
|
63
|
+
entries: Entry[];
|
|
64
|
+
problems: {
|
|
65
|
+
key?: string;
|
|
66
|
+
reason: string;
|
|
67
|
+
}[];
|
|
68
|
+
};
|
|
69
|
+
/** The literal value of `node`, or why it has none. Only same-file `const` names are followed. */
|
|
70
|
+
export declare function evaluate(node: Node | null | undefined, scope: Scope, seen?: ReadonlySet<string>): Result<Value>;
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import type { ObjectExpression } from '@babel/types';
|
|
2
|
+
import { type Result, type Scope } from './evaluate.js';
|
|
3
|
+
import type { DetectedRemote, DetectedShared } from './types.js';
|
|
4
|
+
/** What the federation options say, minus the parts that could not be read. */
|
|
5
|
+
export interface FederationOptions {
|
|
6
|
+
name?: string;
|
|
7
|
+
remotes: Record<string, DetectedRemote>;
|
|
8
|
+
exposes: Record<string, string>;
|
|
9
|
+
shared: Record<string, DetectedShared>;
|
|
10
|
+
/** Whether the options expose anything, counting entries that could not be read. */
|
|
11
|
+
exposing: boolean;
|
|
12
|
+
}
|
|
13
|
+
/** Records a part that was not read: `field` is a dotted path, `reason` plain words. */
|
|
14
|
+
export type Report = (field: string, reason: string) => void;
|
|
15
|
+
/** `shared.react`, or `shared['react-dom']` when the key is not a plain name. */
|
|
16
|
+
export declare const fieldOf: (base: string, key: string) => string;
|
|
17
|
+
/** `name@https://…/mf-manifest.json` or a bare URL, split into its parts. */
|
|
18
|
+
export declare function parseRemote(value: string): Result<DetectedRemote>;
|
|
19
|
+
/** Reads the fields adopt needs out of a federation options object literal. */
|
|
20
|
+
export declare function readFederationOptions(options: ObjectExpression, scope: Scope, report: Report): FederationOptions;
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
export { detectFederation } from './detect.js';
|
|
2
|
+
export { planAdoption } from './plan.js';
|
|
3
|
+
export type { AdoptAppFile, AdoptInput, AdoptPlan, AdoptRemote } from './plan.js';
|
|
4
|
+
export type { BuildTool, DetectedApp, DetectedRemote, DetectedShared, DetectFederationOptions, FederationReport, FederationRole, NotAdopted, } from './types.js';
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { type MfeflowProject } from '@mfeflow/contracts';
|
|
2
|
+
import type { FederationReport, NotAdopted } from './types.js';
|
|
3
|
+
export interface AdoptInput {
|
|
4
|
+
report: FederationReport;
|
|
5
|
+
scope: string;
|
|
6
|
+
packageManager: MfeflowProject['packageManager'];
|
|
7
|
+
}
|
|
8
|
+
/** One `mfeflow.app.json` adopt will write, as plain data. */
|
|
9
|
+
export interface AdoptAppFile {
|
|
10
|
+
name: string;
|
|
11
|
+
/** Folder of the app, relative to the project, `/` separated. `.` for the project itself. */
|
|
12
|
+
dir: string;
|
|
13
|
+
role: 'shell' | 'remote';
|
|
14
|
+
content: Record<string, unknown>;
|
|
15
|
+
}
|
|
16
|
+
/** A remote that gets `registry/envs/<env>/<name>.json`. */
|
|
17
|
+
export interface AdoptRemote {
|
|
18
|
+
name: string;
|
|
19
|
+
port: number;
|
|
20
|
+
}
|
|
21
|
+
export interface AdoptPlan {
|
|
22
|
+
/** The `mfeflow.json` content, before the CLI stamps its version. */
|
|
23
|
+
project: MfeflowProject;
|
|
24
|
+
appFiles: AdoptAppFile[];
|
|
25
|
+
remotes: AdoptRemote[];
|
|
26
|
+
/** Folders of the shells; `@mfeflow/runtime` goes into each package.json. */
|
|
27
|
+
shellDirs: string[];
|
|
28
|
+
/** What the detector skipped plus what adopt itself could not carry, each with a reason. */
|
|
29
|
+
notAdopted: NotAdopted[];
|
|
30
|
+
/** Things the person should look at, in plain words. */
|
|
31
|
+
warnings: string[];
|
|
32
|
+
}
|
|
33
|
+
export declare const printable: (text: string) => string;
|
|
34
|
+
/**
|
|
35
|
+
* Turns what {@link detectFederation} found into the files adopt writes, as data. Needs a
|
|
36
|
+
* shell: without one the project file would not validate, and a lone remote belongs to a
|
|
37
|
+
* multi-repo setup this release does not write.
|
|
38
|
+
*/
|
|
39
|
+
export declare function planAdoption(input: AdoptInput): AdoptPlan;
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import type { ReadFile } from '../plan.js';
|
|
2
|
+
/** The bundler an adopted app builds with, judged from its config file name. */
|
|
3
|
+
export type BuildTool = 'rsbuild' | 'rspack' | 'webpack';
|
|
4
|
+
/** `remote` when the app exposes modules, `shell` (a host) when it only consumes them. */
|
|
5
|
+
export type FederationRole = 'shell' | 'remote';
|
|
6
|
+
/** A remote an app loads, as written in its `remotes`. */
|
|
7
|
+
export interface DetectedRemote {
|
|
8
|
+
/** The remote's federation name: the part before `@` in `name@url`. Absent for a bare URL. */
|
|
9
|
+
name?: string;
|
|
10
|
+
/** The URL after `@`, or the whole string for a bare URL. */
|
|
11
|
+
entry: string;
|
|
12
|
+
/** `manifest` for an `mf-manifest.json` style entry, `script` for a `remoteEntry.js`. */
|
|
13
|
+
type: 'manifest' | 'script';
|
|
14
|
+
}
|
|
15
|
+
/** One shared package, with the two settings adopt carries over. */
|
|
16
|
+
export interface DetectedShared {
|
|
17
|
+
singleton?: boolean;
|
|
18
|
+
/** A semver range, or `false` to turn the version check off. */
|
|
19
|
+
requiredVersion?: string | false;
|
|
20
|
+
}
|
|
21
|
+
/** One app with a Module Federation config, as far as it could be read without running it. */
|
|
22
|
+
export interface DetectedApp {
|
|
23
|
+
/** Folder of the app relative to the scanned folder, with `/` separators. `.` for the folder itself. */
|
|
24
|
+
dir: string;
|
|
25
|
+
/** The federation `name`, when it is a literal. */
|
|
26
|
+
name?: string;
|
|
27
|
+
/** The `name` in the app's package.json, when there is one. */
|
|
28
|
+
packageName?: string;
|
|
29
|
+
role: FederationRole;
|
|
30
|
+
buildTool: BuildTool;
|
|
31
|
+
/** The build config, relative to the scanned folder. */
|
|
32
|
+
configFile: string;
|
|
33
|
+
/** The file the federation options were read from, when it is not `configFile`. */
|
|
34
|
+
federationFile?: string;
|
|
35
|
+
/** Remote alias (the key in `remotes`) to where it loads from. */
|
|
36
|
+
remotes: Record<string, DetectedRemote>;
|
|
37
|
+
/** Expose key (`./Button`) to the module path it points at. */
|
|
38
|
+
exposes: Record<string, string>;
|
|
39
|
+
shared: Record<string, DetectedShared>;
|
|
40
|
+
/** The dev server port, when it is a literal number. */
|
|
41
|
+
port?: number;
|
|
42
|
+
}
|
|
43
|
+
/** A part of a config the detector would not read, and why. Nothing here is guessed. */
|
|
44
|
+
export interface NotAdopted {
|
|
45
|
+
/** The app folder, as in {@link DetectedApp.dir}. */
|
|
46
|
+
dir: string;
|
|
47
|
+
/** The file the part is in, relative to the scanned folder. */
|
|
48
|
+
file: string;
|
|
49
|
+
/** Where in the config: `remotes`, `shared.react.requiredVersion`, `port`, `options`, … */
|
|
50
|
+
field: string;
|
|
51
|
+
/** Plain words, e.g. "comes from a function call (getRemotes())". */
|
|
52
|
+
reason: string;
|
|
53
|
+
}
|
|
54
|
+
export interface FederationReport {
|
|
55
|
+
apps: DetectedApp[];
|
|
56
|
+
notAdopted: NotAdopted[];
|
|
57
|
+
}
|
|
58
|
+
export interface DetectFederationOptions {
|
|
59
|
+
/**
|
|
60
|
+
* Reads a file by absolute path; undefined when it does not exist. Defaults
|
|
61
|
+
* to a reader that stays inside the folder. A custom reader replaces that
|
|
62
|
+
* guard and gets only the lexical checks (imports that leave the folder or
|
|
63
|
+
* enter node_modules are refused by path), so custom readers must enforce
|
|
64
|
+
* containment, symlinks and size themselves.
|
|
65
|
+
*/
|
|
66
|
+
read?: ReadFile;
|
|
67
|
+
/**
|
|
68
|
+
* Names of the sub-folders of an absolute path, used to expand workspace
|
|
69
|
+
* globs; an empty list when the folder does not exist. Defaults to the disk.
|
|
70
|
+
* Like `read`, a custom one must enforce containment and symlinks itself.
|
|
71
|
+
*/
|
|
72
|
+
listDirs?: (dir: string) => Promise<string[]>;
|
|
73
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import type { ReadFile } from '../plan.js';
|
|
2
|
+
/** Folders never searched for apps: dependencies, build output, caches, VCS. */
|
|
3
|
+
export declare const SKIPPED_DIRS: Set<string>;
|
|
4
|
+
export interface WorkspaceProblem {
|
|
5
|
+
file: string;
|
|
6
|
+
reason: string;
|
|
7
|
+
}
|
|
8
|
+
/** A whole-path glob (`apps/**`, `packages/*-legacy`) as a regular expression. */
|
|
9
|
+
export declare function globToRegExp(pattern: string): RegExp;
|
|
10
|
+
/** Runs `fn` on each item one after the other, so a big folder tree never opens thousands of files at once. */
|
|
11
|
+
export declare function eachInSequence<T>(items: readonly T[], fn: (item: T) => Promise<void>): Promise<void>;
|
|
12
|
+
/**
|
|
13
|
+
* The folders that may hold an app, relative to `root` with `/` separators,
|
|
14
|
+
* sorted: the root itself plus every workspace package with a package.json.
|
|
15
|
+
*/
|
|
16
|
+
export declare function findAppDirs(root: string, read: ReadFile, listDirs: (dir: string) => Promise<string[]>, problems?: WorkspaceProblem[]): Promise<string[]>;
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { type FsOps } from './fs.js';
|
|
2
|
+
/** The ownership file in each app's folder. */
|
|
3
|
+
export declare const APP_FILE = "mfeflow.app.json";
|
|
4
|
+
/** The name the app file had before 0.6. Still read, with a notice. */
|
|
5
|
+
export declare const LEGACY_APP_FILE = "mfe.json";
|
|
6
|
+
export type AppFileName = typeof APP_FILE | typeof LEGACY_APP_FILE;
|
|
7
|
+
export interface AppFileText {
|
|
8
|
+
/** Which of the two names was found. */
|
|
9
|
+
name: AppFileName;
|
|
10
|
+
text: string;
|
|
11
|
+
/** True when only the old name exists. */
|
|
12
|
+
legacy: boolean;
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* The text of the app file in `dir`: `mfeflow.app.json`, else the old `mfe.json`, else undefined.
|
|
16
|
+
* Throws E122 when both exist, because mfeflow cannot tell which one the team meant.
|
|
17
|
+
*/
|
|
18
|
+
export declare function readAppFile(dir: string, ops?: FsOps): Promise<AppFileText | undefined>;
|
|
19
|
+
/** The one-line deprecation notice for apps that still use the old name, given their folders. */
|
|
20
|
+
export declare function legacyAppFileNotice(folders: readonly string[]): string;
|
package/dist/apply.d.ts
CHANGED
|
@@ -15,6 +15,8 @@ export interface ApplyOptions {
|
|
|
15
15
|
fs?: FsOps;
|
|
16
16
|
/** Check the plan against the files and stop before writing anything. */
|
|
17
17
|
dryRun?: boolean;
|
|
18
|
+
/** Refuse any write through a symlink, first-party included. Third-party changes always are. */
|
|
19
|
+
refuseSymlinks?: boolean;
|
|
18
20
|
}
|
|
19
21
|
export interface ApplyResult {
|
|
20
22
|
written: string[];
|
package/dist/errors.d.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Every error the engine throws. Ranges: E1xx project and config, E2xx
|
|
3
|
-
* features, E3xx build and runtime, E4xx feature registries, E5xx deploy and the static registry
|
|
3
|
+
* features, E3xx build and runtime, E4xx feature registries, E5xx deploy and the static registry,
|
|
4
|
+
* E6xx upgrade.
|
|
4
5
|
*/
|
|
5
6
|
export declare const ERROR_CATALOGUE: {
|
|
6
7
|
readonly E101: {
|
|
@@ -52,8 +53,8 @@ export declare const ERROR_CATALOGUE: {
|
|
|
52
53
|
readonly fix: 'Restore the folder, or remove the app from "apps" in mfeflow.json.';
|
|
53
54
|
};
|
|
54
55
|
readonly E113: {
|
|
55
|
-
readonly title: '
|
|
56
|
-
readonly fix: "Give the app a valid
|
|
56
|
+
readonly title: 'mfeflow.app.json is missing or invalid';
|
|
57
|
+
readonly fix: "Give the app a valid mfeflow.app.json (name, owner, shells, routes, exposes). The schema is at https://mfeflow.github.io/mfeflow/schema/mfeflow-app.json.";
|
|
57
58
|
};
|
|
58
59
|
readonly E114: {
|
|
59
60
|
readonly title: 'A registry entry is missing or invalid';
|
|
@@ -64,8 +65,8 @@ export declare const ERROR_CATALOGUE: {
|
|
|
64
65
|
readonly fix: 'Give one of them another port in its rsbuild config and in "apps" in mfeflow.json.';
|
|
65
66
|
};
|
|
66
67
|
readonly E116: {
|
|
67
|
-
readonly title: 'Two remotes claim routes that overlap, or an
|
|
68
|
-
readonly fix: 'Give each route to one remote (a wildcard such as /checkout/* also covers /checkout/pay), and keep the name in
|
|
68
|
+
readonly title: 'Two remotes claim routes that overlap, or an app file disagrees with mfeflow.json';
|
|
69
|
+
readonly fix: 'Give each route to one remote (a wildcard such as /checkout/* also covers /checkout/pay), and keep the name in mfeflow.app.json equal to the app name.';
|
|
69
70
|
};
|
|
70
71
|
readonly E117: {
|
|
71
72
|
readonly title: 'A copied file changed since mfeflow wrote it';
|
|
@@ -87,6 +88,18 @@ export declare const ERROR_CATALOGUE: {
|
|
|
87
88
|
readonly title: 'A server env key is not listed in turbo.json';
|
|
88
89
|
readonly fix: 'Add the key to "globalEnv" in turbo.json. Turborepo hides unlisted variables from tasks and replays a cached build without running env.ts, so a missing value would not fail the build.';
|
|
89
90
|
};
|
|
91
|
+
readonly E122: {
|
|
92
|
+
readonly title: 'An app has both mfeflow.app.json and the old mfe.json';
|
|
93
|
+
readonly fix: "Keep mfeflow.app.json and delete mfe.json. Move any field you still need into mfeflow.app.json first. See https://mfeflow.github.io/mfeflow/reference/mfeflow-app-json.";
|
|
94
|
+
};
|
|
95
|
+
readonly E123: {
|
|
96
|
+
readonly title: 'This folder already has an mfeflow.json';
|
|
97
|
+
readonly fix: 'adopt only starts a project file; it never changes one. Use `mfeflow add` or `mfeflow upgrade` on this project, or run adopt in a folder that has no mfeflow.json.';
|
|
98
|
+
};
|
|
99
|
+
readonly E124: {
|
|
100
|
+
readonly title: 'adopt found nothing it can set up';
|
|
101
|
+
readonly fix: 'adopt needs a Module Federation host (shell) it can read, and a literal `name` for each app. Run it in the folder that holds the shell, or a workspace root that contains it, and read `mfeflow adopt --dry-run` for what was skipped.';
|
|
102
|
+
};
|
|
90
103
|
readonly E201: {
|
|
91
104
|
readonly title: 'Feature not found';
|
|
92
105
|
readonly fix: 'List what is available with `mfeflow features`, or check the spelling.';
|
|
@@ -153,7 +166,7 @@ export declare const ERROR_CATALOGUE: {
|
|
|
153
166
|
};
|
|
154
167
|
readonly E302: {
|
|
155
168
|
readonly title: 'A remote is not compatible with its shell';
|
|
156
|
-
readonly fix: 'Align the remote and the shell on the same major of the package, or update the "platform" range in the remote\'s
|
|
169
|
+
readonly fix: 'Align the remote and the shell on the same major of the package, or update the "platform" range in the remote\'s mfeflow.app.json.';
|
|
157
170
|
};
|
|
158
171
|
readonly E303: {
|
|
159
172
|
readonly title: 'A runtime plugin imports a shared module';
|
|
@@ -161,7 +174,7 @@ export declare const ERROR_CATALOGUE: {
|
|
|
161
174
|
};
|
|
162
175
|
readonly E304: {
|
|
163
176
|
readonly title: 'A bundle is over its budget';
|
|
164
|
-
readonly fix: 'Cut what the app ships on first load, or raise the budget in mfeflow.json ("budget") or
|
|
177
|
+
readonly fix: 'Cut what the app ships on first load, or raise the budget in mfeflow.json ("budget") or mfeflow.app.json ("budgets") on purpose.';
|
|
165
178
|
};
|
|
166
179
|
readonly E305: {
|
|
167
180
|
readonly title: 'There is no build output to measure';
|
|
@@ -171,6 +184,10 @@ export declare const ERROR_CATALOGUE: {
|
|
|
171
184
|
readonly title: 'A dev server could not start';
|
|
172
185
|
readonly fix: "Free the port or change it in mfeflow.json and the app's rsbuild config, then run `mfeflow dev` again.";
|
|
173
186
|
};
|
|
187
|
+
readonly E307: {
|
|
188
|
+
readonly title: 'The shell to check against could not be read';
|
|
189
|
+
readonly fix: 'Pass --against a registry index, a deployed shell mf-manifest.json or a local file of either. A URL must be https (http only for localhost) and under 1 MB.';
|
|
190
|
+
};
|
|
174
191
|
readonly E401: {
|
|
175
192
|
readonly title: 'Could not fetch from the registry';
|
|
176
193
|
readonly fix: 'Check the URL in "registries" and your network, then try again.';
|
|
@@ -189,7 +206,7 @@ export declare const ERROR_CATALOGUE: {
|
|
|
189
206
|
};
|
|
190
207
|
readonly E501: {
|
|
191
208
|
readonly title: 'A deploy failed the schema gate';
|
|
192
|
-
readonly fix: "Fix what the message names: the remote's
|
|
209
|
+
readonly fix: "Fix what the message names: the remote's mfeflow.app.json, its mf-manifest.json or the version. Nothing was uploaded. See https://mfeflow.github.io/mfeflow/guide/deploy-and-rollback.";
|
|
193
210
|
};
|
|
194
211
|
readonly E502: {
|
|
195
212
|
readonly title: 'A route of this remote overlaps a route of another remote';
|
|
@@ -201,7 +218,7 @@ export declare const ERROR_CATALOGUE: {
|
|
|
201
218
|
};
|
|
202
219
|
readonly E504: {
|
|
203
220
|
readonly title: 'The remote is over its first-load JS budget';
|
|
204
|
-
readonly fix: "Cut what the remote loads first, or raise budgets.initialJsKb in its
|
|
221
|
+
readonly fix: "Cut what the remote loads first, or raise budgets.initialJsKb in its mfeflow.app.json on purpose. Nothing was uploaded. See https://mfeflow.github.io/mfeflow/guide/deploy-and-rollback.";
|
|
205
222
|
};
|
|
206
223
|
readonly E505: {
|
|
207
224
|
readonly title: 'A rollout was asked for, and a static host cannot split traffic';
|
|
@@ -243,6 +260,22 @@ export declare const ERROR_CATALOGUE: {
|
|
|
243
260
|
readonly title: 'The registry changed a feature since you added it';
|
|
244
261
|
readonly fix: 'Nothing was written. Look at what changed; if you accept the new content, run the command again with --accept-changed-content.';
|
|
245
262
|
};
|
|
263
|
+
readonly E601: {
|
|
264
|
+
readonly title: 'Cannot tell which version of mfeflow made this project';
|
|
265
|
+
readonly fix: 'Add "cli": "<version>" to mfeflow.json (the version you created the project with), or give the shell a plain @mfeflow/runtime range in its package.json, then run the command again.';
|
|
266
|
+
};
|
|
267
|
+
readonly E602: {
|
|
268
|
+
readonly title: 'The project is newer than the version you are upgrading to';
|
|
269
|
+
readonly fix: 'Upgrade only moves forward. Install a newer mfeflow, or leave the project as it is.';
|
|
270
|
+
};
|
|
271
|
+
readonly E603: {
|
|
272
|
+
readonly title: 'A feature migration could not be loaded or failed';
|
|
273
|
+
readonly fix: 'Nothing was written. Update mfeflow to a build that ships the migration, or report the feature and versions named above.';
|
|
274
|
+
};
|
|
275
|
+
readonly E604: {
|
|
276
|
+
readonly title: 'Files you changed need a decision';
|
|
277
|
+
readonly fix: 'Nothing was written. Run `mfeflow upgrade --dry-run` to see each diff, then pass --accept <path> to take the new version of a file, --keep <path> to keep yours, or `all` for every file.';
|
|
278
|
+
};
|
|
246
279
|
};
|
|
247
280
|
export type ErrorCode = keyof typeof ERROR_CATALOGUE;
|
|
248
281
|
export interface MfeflowErrorOptions {
|
package/dist/features/load.d.ts
CHANGED
|
@@ -7,6 +7,8 @@ export interface LoadedFeature {
|
|
|
7
7
|
trust: Trust;
|
|
8
8
|
/** Reads a `from` file of a local feature. Absent for remote features. */
|
|
9
9
|
readAsset?: (relative: string) => Promise<string>;
|
|
10
|
+
/** Folder of a local feature; migration modules are loaded from here. Absent for remote features. */
|
|
11
|
+
dir?: string;
|
|
10
12
|
/** `sha256-<hex>` of the feature.json a registry served. Absent for local features. */
|
|
11
13
|
integrity?: string;
|
|
12
14
|
}
|
package/dist/fs.d.ts
CHANGED
|
@@ -22,3 +22,8 @@ export declare function readIfExists(ops: FsOps, file: string): Promise<string |
|
|
|
22
22
|
export declare function tempSibling(file: string): string;
|
|
23
23
|
/** Writes through a temp file and a rename, so readers never see half a file. */
|
|
24
24
|
export declare function writeFileAtomic(ops: FsOps, file: string, content: string): Promise<void>;
|
|
25
|
+
/**
|
|
26
|
+
* The first part of a project-relative path, from the root down, that is itself a symlink:
|
|
27
|
+
* a folder on the way or the file. Undefined when none is. The root itself is not looked at.
|
|
28
|
+
*/
|
|
29
|
+
export declare function symlinkedPrefix(ops: FsOps, root: string, relative: string): Promise<string | undefined>;
|
package/dist/index.d.ts
CHANGED
|
@@ -4,13 +4,16 @@ export declare function packageInfo(): {
|
|
|
4
4
|
version: string;
|
|
5
5
|
};
|
|
6
6
|
export { ERROR_CATALOGUE, hasErrno, isMfeflowError, messageOf, MfeflowError, type ErrorCode, type MfeflowErrorOptions, } from './errors.js';
|
|
7
|
+
export { APP_FILE, LEGACY_APP_FILE, legacyAppFileNotice, readAppFile, type AppFileName, type AppFileText, } from './app-file.js';
|
|
7
8
|
export { hashContent } from './hash.js';
|
|
8
9
|
export { inOrder } from './sequence.js';
|
|
9
|
-
export { realFs, writeFileAtomic, type FsOps } from './fs.js';
|
|
10
|
+
export { realFs, symlinkedPrefix, writeFileAtomic, type FsOps } from './fs.js';
|
|
10
11
|
export { findDiverged, findProjectRoot, loadProject, parseProject, PROJECT_FILE, recordPlan, saveProject, serializeProject, type DivergedFile, type MfeflowProject, } from './project.js';
|
|
11
12
|
export { emptyPlan, PlanBuilder, renderDiff, summarize, type ChangeAction, type DependencyAdd, type EnvKeyAdd, type FileChange, type Plan, type PlanBuilderOptions, type PlanFeature, type ReadFile, type ScriptAdd, } from './plan.js';
|
|
12
13
|
export { applyPlan, type ApplyOptions, type ApplyResult, type InstallContext, type InstallStep, } from './apply.js';
|
|
14
|
+
export { detectBaseline, planUpgrade, type DivergedUpgrade, type DivergeReason, type Migration, type UpgradeDecisions, type UpgradeInput, type UpgradePlan, } from './upgrade.js';
|
|
13
15
|
export { renderTemplate, substitute, type TemplateData } from './templates.js';
|
|
14
16
|
export { RUNTIME_CONFIG_FILE, runtimeConfigFiles } from './runtime-config.js';
|
|
15
17
|
export { applyTransform, dotenvKeys, schemaKeys, type SchemaKey } from './transforms/index.js';
|
|
18
|
+
export { detectFederation, planAdoption, type AdoptAppFile, type AdoptInput, type AdoptPlan, type AdoptRemote, type BuildTool, type DetectedApp, type DetectedRemote, type DetectedShared, type DetectFederationOptions, type FederationReport, type FederationRole, type NotAdopted, } from './adopt/index.js';
|
|
16
19
|
export { buildRegistry, fetchFeatureIndex, fetchRemoteText, isProtectedPath, listLocalFeatures, loadFeature, parseFeature, addTrusted, configDirOf, parseTrustEnv, readTrusted, removeTrusted, TRUST_ENV, trustedNamespaces, trustFileIn, type EnvTrust, type Trusted, type TrustedRegistry, getHttps, checkIntegrity, indexedVersion, integrityOf, readCappedText, registryUrl, resolveFeature, resolveOptions, type FetchLike, type FetchResponse, type LoadedFeature, type LoadOptions, type RegistryBuild, type RegistryFile, type ResolveInput, type Trust, } from './features/index.js';
|