@antelopejs/dms-frontend 0.8.0 → 0.9.1
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/commands/build-action.js +3 -0
- package/dist/commands/dev-action.d.ts +2 -0
- package/dist/commands/dev-action.js +12 -5
- package/dist/commands/dev.js +3 -1
- package/dist/commands/start-action.js +36 -5
- package/dist/common.d.ts +1 -0
- package/dist/common.js +1 -0
- package/dist/config.js +3 -0
- package/dist/derived-outputs.d.ts +1 -5
- package/dist/derived-outputs.js +46 -33
- package/dist/dev-modules.d.ts +5 -0
- package/dist/dev-modules.js +88 -0
- package/dist/entry-weight.d.ts +24 -0
- package/dist/entry-weight.js +86 -0
- package/dist/help.js +5 -1
- package/dist/layer-watch.js +3 -2
- package/dist/layers.d.ts +1 -0
- package/dist/locale-files.d.ts +6 -0
- package/dist/locale-files.js +75 -0
- package/dist/manifest.js +2 -0
- package/dist/private-values.d.ts +1 -0
- package/dist/private-values.js +16 -0
- package/dist/project-modules-child.d.ts +13 -0
- package/dist/project-modules-child.js +26 -0
- package/dist/project-modules.d.ts +1 -0
- package/dist/project-modules.js +73 -38
- package/dist/workspace-setup.js +41 -3
- package/dist/workspace.d.ts +6 -0
- package/package.json +24 -7
- package/skills/dms-frontend/SKILL.md +1 -0
- package/templates/vue/app-runtime.ts +8 -1
- package/templates/vue/entry-weight.mjs +132 -0
- package/templates/vue/frontend-build-loader.ts +227 -9
- package/templates/vue/frontend-build.ts +34 -1
- package/templates/vue/frontend-module.ts +144 -1
- package/templates/vue/gitignore +1 -0
- package/templates/vue/main.ts +21 -0
- package/templates/vue/network-error.ts +3 -1
- package/templates/vue/package.json +1 -1
- package/templates/vue/patches/@vueuse__core@14.4.0.patch +24 -0
- package/templates/vue/pnpm-workspace.yaml +5 -0
- package/templates/vue/server/client-manifest.mjs +12 -0
- package/templates/vue/server/in-place-render.mjs +110 -0
- package/templates/vue/server/page-props.mjs +10 -0
- package/templates/vue/server/proxy-transfers.mjs +35 -9
- package/templates/vue/server.mjs +39 -29
- package/templates/vue/sfc-global-types.mjs +119 -0
- package/templates/vue/ssr-renderer.ts +8 -0
- package/templates/vue/ui-kbd.mjs +45 -0
- package/templates/vue/vite.config.ts +34 -2
|
@@ -10,6 +10,7 @@ const child_output_1 = require("../child-output");
|
|
|
10
10
|
const common_1 = require("../common");
|
|
11
11
|
const output_1 = require("../output");
|
|
12
12
|
const vite_output_1 = require("../vite-output");
|
|
13
|
+
const entry_weight_1 = require("../entry-weight");
|
|
13
14
|
const workspace_task_1 = require("./workspace-task");
|
|
14
15
|
exports.DEFAULT_WORKSPACE_DIR = ".dms-frontend";
|
|
15
16
|
function productionWorkspace(options) {
|
|
@@ -107,6 +108,7 @@ async function runBuild(options) {
|
|
|
107
108
|
}
|
|
108
109
|
}
|
|
109
110
|
async function buildProduction(options, startedAt) {
|
|
111
|
+
(0, entry_weight_1.entryWeightThreshold)(process.env);
|
|
110
112
|
const ui = (0, cli_1.getProcessUi)();
|
|
111
113
|
const workspace = productionWorkspace(options);
|
|
112
114
|
(0, output_1.writeHeader)("build", [options.project, options.env]);
|
|
@@ -136,6 +138,7 @@ async function buildProduction(options, startedAt) {
|
|
|
136
138
|
for (const step of exports.BUILD_STEPS)
|
|
137
139
|
await runBuildStep(step, context);
|
|
138
140
|
(0, vite_output_1.reportViteWarnings)(context.warnings, isVerbose, ui);
|
|
141
|
+
(0, entry_weight_1.reportEntryWeight)(ui, workspaceDir, process.env);
|
|
139
142
|
(0, output_1.writeBlankLine)();
|
|
140
143
|
ui.summary({
|
|
141
144
|
headline: "Built the production frontend",
|
|
@@ -14,6 +14,7 @@ const manifest_1 = require("../manifest");
|
|
|
14
14
|
const module_set_1 = require("../module-set");
|
|
15
15
|
const project_modules_1 = require("../project-modules");
|
|
16
16
|
const workspace_task_1 = require("./workspace-task");
|
|
17
|
+
const BASE_ENVIRONMENT = "default";
|
|
17
18
|
function clientHost() {
|
|
18
19
|
const host = process.env.HOST?.trim();
|
|
19
20
|
if (!host || host === "0.0.0.0" || host === "::" || host === "[::]") {
|
|
@@ -41,6 +42,10 @@ async function runDev(options) {
|
|
|
41
42
|
const offline = (0, common_1.flagOrEnv)(options.offline, "DMS_OFFLINE");
|
|
42
43
|
const sessionSecret = (0, common_1.resolveSessionSecret)("dev");
|
|
43
44
|
const { backendUrl, workspaceKey, projectDir } = resolveBackend(options);
|
|
45
|
+
const project = {
|
|
46
|
+
projectDir: options.project ?? projectDir ?? process.cwd(),
|
|
47
|
+
environment: options.env ?? BASE_ENVIRONMENT,
|
|
48
|
+
};
|
|
44
49
|
const bootstrapSecret = (0, common_1.resolveBootstrapSecret)(options.bootstrapSecret, backendUrl);
|
|
45
50
|
const ui = (0, cli_1.getProcessUi)();
|
|
46
51
|
(0, output_1.writeHeader)("dev", [backendUrl, "development"]);
|
|
@@ -58,6 +63,7 @@ async function runDev(options) {
|
|
|
58
63
|
clientUrl,
|
|
59
64
|
workspaceKey,
|
|
60
65
|
bootstrapSecret,
|
|
66
|
+
project,
|
|
61
67
|
});
|
|
62
68
|
if (port !== requestedPort) {
|
|
63
69
|
ui.message("warn", `Port ${requestedPort} is busy, using ${port}`);
|
|
@@ -103,16 +109,17 @@ async function runDev(options) {
|
|
|
103
109
|
clientUrl,
|
|
104
110
|
workspaceKey,
|
|
105
111
|
bootstrapSecret: session.bootstrapSecret,
|
|
112
|
+
project,
|
|
106
113
|
});
|
|
107
114
|
}
|
|
108
115
|
}
|
|
109
|
-
function
|
|
116
|
+
function backendModuleSet(setup) {
|
|
110
117
|
return (0, manifest_1.readCachedManifest)(setup.workspaceDir)?.manifest.modules ?? [];
|
|
111
118
|
}
|
|
112
119
|
function reportServed(session, setup) {
|
|
113
|
-
return (0, module_set_1.reportFrontendReady)(session.backendUrl,
|
|
114
|
-
name:
|
|
115
|
-
version:
|
|
120
|
+
return (0, module_set_1.reportFrontendReady)(session.backendUrl, setup.layers.map((layer) => ({
|
|
121
|
+
name: layer.name ?? layer.path,
|
|
122
|
+
version: (0, project_modules_1.readShippingVersion)(layer.path),
|
|
116
123
|
})), session.bootstrapSecret);
|
|
117
124
|
}
|
|
118
125
|
async function serveWorkspace(session, setup) {
|
|
@@ -126,7 +133,7 @@ async function serveWorkspace(session, setup) {
|
|
|
126
133
|
session.bootstrapSecret = session.refreshBootstrapSecret();
|
|
127
134
|
return session.bootstrapSecret;
|
|
128
135
|
},
|
|
129
|
-
fingerprint: (0, module_set_1.moduleSetFingerprint)(
|
|
136
|
+
fingerprint: (0, module_set_1.moduleSetFingerprint)(backendModuleSet(setup)),
|
|
130
137
|
onChange: () => restart.abort(),
|
|
131
138
|
onUnchanged: () => void reportServed(session, setup),
|
|
132
139
|
});
|
package/dist/commands/dev.js
CHANGED
|
@@ -50,8 +50,10 @@ const DEV_EXAMPLES = [
|
|
|
50
50
|
function cmdDev() {
|
|
51
51
|
const command = new commander_1.Command("dev")
|
|
52
52
|
.summary("Run the dev server against a local backend, with hot reload")
|
|
53
|
-
.description("Run the dev server with hot reload. It reads the frontend modules in place
|
|
53
|
+
.description("Run the dev server with hot reload. It reads the frontend modules from the AntelopeJS project as build does, each module's frontendModule called with dev: true, and serves them in place from their sources. The backend, on this machine, gives the secrets it holds and says when the module store changes its modules.")
|
|
54
54
|
.addOption(config_1.Options.backendUrl("Backend URL; when omitted, discovered from the enclosing antelope project's .antelope/dev.json"))
|
|
55
|
+
.addOption(new commander_1.Option("--project <dir>", "The AntelopeJS project the modules are read from (default: the discovered backend's, else the current directory)"))
|
|
56
|
+
.addOption(new commander_1.Option("-e, --env <environment>", "The AntelopeJS environment the backend runs, as ajs project dev -e (default: the base configuration)").env("ANTELOPEJS_LAUNCH_ENV"))
|
|
55
57
|
.addOption(config_1.Options.port)
|
|
56
58
|
.addOption(config_1.Options.force("Reinstall the workspace dependencies"))
|
|
57
59
|
.addOption(config_1.Options.offline("Reuse the last cached manifest instead of fetching it"))
|
|
@@ -61,11 +61,40 @@ function publicOptionsById(modules) {
|
|
|
61
61
|
];
|
|
62
62
|
}));
|
|
63
63
|
}
|
|
64
|
+
function builtModules(workspaceDir) {
|
|
65
|
+
try {
|
|
66
|
+
const { modules } = JSON.parse((0, node_fs_1.readFileSync)((0, node_path_1.join)(workspaceDir, "generated-frontend-modules.json"), "utf8"));
|
|
67
|
+
return (modules ?? []).map((module) => ({
|
|
68
|
+
name: module.name ?? module.id,
|
|
69
|
+
version: module.version,
|
|
70
|
+
}));
|
|
71
|
+
}
|
|
72
|
+
catch {
|
|
73
|
+
return [];
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
function reportModuleSetDrift(built, modules) {
|
|
77
|
+
const builtNames = new Set(built.map(({ name }) => name));
|
|
78
|
+
const currentNames = new Set(modules.map(({ layer }) => layer.name ?? layer.path));
|
|
79
|
+
const added = [...currentNames].filter((name) => !builtNames.has(name));
|
|
80
|
+
const removed = [...builtNames].filter((name) => !currentNames.has(name));
|
|
81
|
+
if (added.length === 0 && removed.length === 0)
|
|
82
|
+
return;
|
|
83
|
+
const ui = (0, cli_1.getProcessUi)();
|
|
84
|
+
ui.message("warn", "The project's modules differ from the ones the build serves: rebuild to apply them", {
|
|
85
|
+
details: [
|
|
86
|
+
...(added.length > 0
|
|
87
|
+
? [`Not in the build: ${added.sort().join(", ")}`]
|
|
88
|
+
: []),
|
|
89
|
+
...(removed.length > 0
|
|
90
|
+
? [`Built, no longer in the project: ${removed.sort().join(", ")}`]
|
|
91
|
+
: []),
|
|
92
|
+
`${ui.symbols.levels.hint} Build again with the deployment's variables: ajs dms build`,
|
|
93
|
+
],
|
|
94
|
+
});
|
|
95
|
+
}
|
|
64
96
|
async function reportServedModules(backendUrl, modules) {
|
|
65
|
-
const isTaken = await (0, module_set_1.reportFrontendReadyOnceAnswered)(backendUrl, modules
|
|
66
|
-
name: layer.name ?? layer.path,
|
|
67
|
-
version,
|
|
68
|
-
})), process.env.DMS_BOOTSTRAP_SECRET);
|
|
97
|
+
const isTaken = await (0, module_set_1.reportFrontendReadyOnceAnswered)(backendUrl, modules, process.env.DMS_BOOTSTRAP_SECRET);
|
|
69
98
|
const ui = (0, cli_1.getProcessUi)();
|
|
70
99
|
if (isTaken) {
|
|
71
100
|
ui.message("info", `[DMS] Reported ${modules.length} modules to the backend (dms-frontend ${module_set_1.OWN_RELEASE.version})`);
|
|
@@ -116,6 +145,8 @@ async function runStart(options) {
|
|
|
116
145
|
projectDir: options.project,
|
|
117
146
|
environment: options.env,
|
|
118
147
|
});
|
|
148
|
+
const built = builtModules(workspaceDir);
|
|
149
|
+
reportModuleSetDrift(built, modules);
|
|
119
150
|
const secrets = (0, common_1.resolveManifestSecrets)({ published: {}, conflicts: [] });
|
|
120
151
|
(0, common_1.reportManifestSecrets)(secrets, (count) => `Set ${count === 1 ? "it" : "them"} in the server's environment, with the value${count === 1 ? "" : "s"} the backend uses`);
|
|
121
152
|
reportUnfilledPrivateValues(workspaceDir);
|
|
@@ -148,7 +179,7 @@ async function runStart(options) {
|
|
|
148
179
|
]),
|
|
149
180
|
footer: "Ctrl+C to stop",
|
|
150
181
|
});
|
|
151
|
-
void reportServedModules(backendUrl,
|
|
182
|
+
void reportServedModules(backendUrl, built);
|
|
152
183
|
void (0, update_check_1.reportAvailableUpdate)({ isFollowed: true });
|
|
153
184
|
},
|
|
154
185
|
});
|
package/dist/common.d.ts
CHANGED
|
@@ -8,6 +8,7 @@ export * from "./manifest-secrets";
|
|
|
8
8
|
export * from "./layer-watch";
|
|
9
9
|
export * from "./materialize";
|
|
10
10
|
export * from "./derived-outputs";
|
|
11
|
+
export * from "./locale-files";
|
|
11
12
|
export * from "./app-configs";
|
|
12
13
|
export * from "./renderer-range";
|
|
13
14
|
export * from "./workspace";
|
package/dist/common.js
CHANGED
|
@@ -24,6 +24,7 @@ __exportStar(require("./manifest-secrets"), exports);
|
|
|
24
24
|
__exportStar(require("./layer-watch"), exports);
|
|
25
25
|
__exportStar(require("./materialize"), exports);
|
|
26
26
|
__exportStar(require("./derived-outputs"), exports);
|
|
27
|
+
__exportStar(require("./locale-files"), exports);
|
|
27
28
|
__exportStar(require("./app-configs"), exports);
|
|
28
29
|
__exportStar(require("./renderer-range"), exports);
|
|
29
30
|
__exportStar(require("./workspace"), exports);
|
package/dist/config.js
CHANGED
|
@@ -63,11 +63,14 @@ exports.TEMPLATE_FILES = [
|
|
|
63
63
|
["compress-assets.mjs", "compress-assets.mjs"],
|
|
64
64
|
["head-order.mjs", "head-order.mjs"],
|
|
65
65
|
["client-chunking.mjs", "client-chunking.mjs"],
|
|
66
|
+
["entry-weight.mjs", "entry-weight.mjs"],
|
|
66
67
|
["client-server-guard.mjs", "client-server-guard.mjs"],
|
|
67
68
|
["ui-inertia-page.mjs", "ui-inertia-page.mjs"],
|
|
68
69
|
["ui-request-state.mjs", "ui-request-state.mjs"],
|
|
70
|
+
["sfc-global-types.mjs", "sfc-global-types.mjs"],
|
|
69
71
|
["icon-api.mjs", "icon-api.mjs"],
|
|
70
72
|
["icon-scan.mjs", "icon-scan.mjs"],
|
|
73
|
+
["ui-kbd.mjs", "ui-kbd.mjs"],
|
|
71
74
|
["email-renderer.ts", "email-renderer.ts"],
|
|
72
75
|
["email-runtime.ts", "email-runtime.ts"],
|
|
73
76
|
["email-locales.ts", "email-locales.ts"],
|
|
@@ -3,6 +3,7 @@ export interface FrontendModuleRegistryEntry {
|
|
|
3
3
|
id: string;
|
|
4
4
|
name?: string;
|
|
5
5
|
packageName?: string;
|
|
6
|
+
version?: string;
|
|
6
7
|
root: string;
|
|
7
8
|
priority: number;
|
|
8
9
|
entry?: string;
|
|
@@ -14,11 +15,6 @@ export interface FrontendModuleRegistry {
|
|
|
14
15
|
modules: FrontendModuleRegistryEntry[];
|
|
15
16
|
}
|
|
16
17
|
export declare function createFrontendModuleRegistry(workspaceDir: string, layers: ResolvedLayer[]): FrontendModuleRegistry;
|
|
17
|
-
export declare class InvalidLocaleFileError extends Error {
|
|
18
|
-
readonly path: string;
|
|
19
|
-
readonly reason: string;
|
|
20
|
-
constructor(path: string, reason: string);
|
|
21
|
-
}
|
|
22
18
|
export declare function affectsLocaleMessages(relativePath: string): boolean;
|
|
23
19
|
export declare function writeLocaleMessages(workspaceDir: string, registry: FrontendModuleRegistry): boolean;
|
|
24
20
|
export interface DerivedOutput {
|
package/dist/derived-outputs.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.DERIVED_OUTPUTS =
|
|
3
|
+
exports.DERIVED_OUTPUTS = void 0;
|
|
4
4
|
exports.createFrontendModuleRegistry = createFrontendModuleRegistry;
|
|
5
5
|
exports.affectsLocaleMessages = affectsLocaleMessages;
|
|
6
6
|
exports.writeLocaleMessages = writeLocaleMessages;
|
|
@@ -8,9 +8,11 @@ const node_fs_1 = require("node:fs");
|
|
|
8
8
|
const node_path_1 = require("node:path");
|
|
9
9
|
const config_1 = require("./config");
|
|
10
10
|
const app_configs_1 = require("./app-configs");
|
|
11
|
+
const locale_files_1 = require("./locale-files");
|
|
11
12
|
const asset_roots_1 = require("./asset-roots");
|
|
12
13
|
const layer_aliases_1 = require("./layer-aliases");
|
|
13
14
|
const layers_1 = require("./layers");
|
|
15
|
+
const project_modules_1 = require("./project-modules");
|
|
14
16
|
const fs_sync_1 = require("./fs-sync");
|
|
15
17
|
const GENERATED_MODULE_ID = /^[A-Za-z0-9_-]+$/;
|
|
16
18
|
function validateFrontendModuleRegistry(registry, workspaceDir) {
|
|
@@ -42,6 +44,7 @@ function createFrontendModuleRegistry(workspaceDir, layers) {
|
|
|
42
44
|
id: (0, layers_1.getLayerSafeName)(layer),
|
|
43
45
|
name: layer.name ?? layer.packageName,
|
|
44
46
|
packageName: layer.packageName,
|
|
47
|
+
version: (0, project_modules_1.readShippingVersion)(layer.sourcePath ?? layer.path),
|
|
45
48
|
root: (0, layers_1.getLayerWorkspacePath)(workspaceDir, layer),
|
|
46
49
|
priority: layer.priority ?? 0,
|
|
47
50
|
entry: (0, node_fs_1.existsSync)(sourceEntry) ? config_1.FRONTEND_MODULE_ENTRY : undefined,
|
|
@@ -148,24 +151,6 @@ function mergeLocaleMessages(source, existing) {
|
|
|
148
151
|
return merged;
|
|
149
152
|
}
|
|
150
153
|
const LOCALE_DIRECTORY = "i18n/locales";
|
|
151
|
-
class InvalidLocaleFileError extends Error {
|
|
152
|
-
constructor(path, reason) {
|
|
153
|
-
super(`Invalid locale file ${path}: ${reason}`);
|
|
154
|
-
this.path = path;
|
|
155
|
-
this.reason = reason;
|
|
156
|
-
this.name = "InvalidLocaleFileError";
|
|
157
|
-
}
|
|
158
|
-
}
|
|
159
|
-
exports.InvalidLocaleFileError = InvalidLocaleFileError;
|
|
160
|
-
function readLocaleFile(path) {
|
|
161
|
-
try {
|
|
162
|
-
return JSON.parse((0, node_fs_1.readFileSync)(path, "utf8"));
|
|
163
|
-
}
|
|
164
|
-
catch (error) {
|
|
165
|
-
const reason = error instanceof Error ? error.message : String(error);
|
|
166
|
-
throw new InvalidLocaleFileError(path, reason);
|
|
167
|
-
}
|
|
168
|
-
}
|
|
169
154
|
function readLocaleMessages(assets, registry) {
|
|
170
155
|
const roots = new Map(registry.modules.map((module) => [module.id, module.root]));
|
|
171
156
|
const messages = {};
|
|
@@ -174,7 +159,7 @@ function readLocaleMessages(assets, registry) {
|
|
|
174
159
|
const root = roots.get(asset.moduleId);
|
|
175
160
|
if (!locale || !root)
|
|
176
161
|
continue;
|
|
177
|
-
const value = readLocaleFile((0, node_path_1.join)(root, asset.relativePath));
|
|
162
|
+
const value = (0, locale_files_1.readLocaleFile)((0, node_path_1.join)(root, asset.relativePath));
|
|
178
163
|
messages[locale] = mergeLocaleMessages(value, messages[locale] ?? {});
|
|
179
164
|
}
|
|
180
165
|
return messages;
|
|
@@ -182,13 +167,28 @@ function readLocaleMessages(assets, registry) {
|
|
|
182
167
|
function affectsLocaleMessages(relativePath) {
|
|
183
168
|
return (0, asset_roots_1.affectsAssetDirectory)(LOCALE_DIRECTORY, relativePath);
|
|
184
169
|
}
|
|
170
|
+
const FALLBACK_LOCALE = "en";
|
|
171
|
+
const RENDERER_TRANSLATED_KEYS = ["error.network"];
|
|
172
|
+
function withoutMessage(messages, key) {
|
|
173
|
+
const [head, ...rest] = key.split(".");
|
|
174
|
+
if (!(head in messages))
|
|
175
|
+
return messages;
|
|
176
|
+
const { [head]: value, ...others } = messages;
|
|
177
|
+
if (!rest.length)
|
|
178
|
+
return others;
|
|
179
|
+
if (!isRecord(value))
|
|
180
|
+
return messages;
|
|
181
|
+
const remaining = withoutMessage(value, rest.join("."));
|
|
182
|
+
return Object.keys(remaining).length
|
|
183
|
+
? { ...others, [head]: remaining }
|
|
184
|
+
: others;
|
|
185
|
+
}
|
|
185
186
|
function localeModuleSource(supportedLocales) {
|
|
186
|
-
const loaders = supportedLocales
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
type LocaleMessages = Record<string, unknown>;
|
|
187
|
+
const loaders = supportedLocales.map((locale) => ` ${JSON.stringify(locale)}: () => import(${JSON.stringify(`./locales.generated/${locale}`)}),`);
|
|
188
|
+
const fallback = supportedLocales.includes(FALLBACK_LOCALE)
|
|
189
|
+
? FALLBACK_LOCALE
|
|
190
|
+
: supportedLocales[0];
|
|
191
|
+
return `type LocaleMessages = Record<string, unknown>;
|
|
192
192
|
type LocaleLoader = () => Promise<{ default: LocaleMessages }>;
|
|
193
193
|
interface LocaleMessagesTarget {
|
|
194
194
|
setLocaleMessage(locale: string, messages: LocaleMessages): void;
|
|
@@ -200,13 +200,13 @@ interface LocaleState {
|
|
|
200
200
|
}
|
|
201
201
|
|
|
202
202
|
export const supportedLocales = ${JSON.stringify(supportedLocales)};
|
|
203
|
+
const fallbackLocale: string | undefined = ${JSON.stringify(fallback)};
|
|
203
204
|
const localeLoaders: Record<string, LocaleLoader> = {
|
|
204
|
-
"en": async () => ({ default: defaultMessages }),
|
|
205
205
|
${loaders.join("\n")}
|
|
206
206
|
};
|
|
207
207
|
const shared: LocaleState | undefined = import.meta.hot?.data.state;
|
|
208
208
|
const state: LocaleState = shared ?? {
|
|
209
|
-
messages: {
|
|
209
|
+
messages: {},
|
|
210
210
|
targets: new Set(),
|
|
211
211
|
version: 0,
|
|
212
212
|
};
|
|
@@ -216,11 +216,20 @@ async function readLocaleMessages(locale: string): Promise<LocaleMessages> {
|
|
|
216
216
|
return (await localeLoaders[locale]()).default;
|
|
217
217
|
}
|
|
218
218
|
|
|
219
|
-
|
|
219
|
+
/**
|
|
220
|
+
* The catalog that serves \`locale\`: its own when the application has one,
|
|
221
|
+
* else the fallback locale's.
|
|
222
|
+
*/
|
|
223
|
+
export function catalogLocale(locale: string): string | undefined {
|
|
220
224
|
const normalized = locale.slice(0, 2);
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
225
|
+
return normalized in localeLoaders ? normalized : fallbackLocale;
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
export async function loadLocaleMessages(locale: string): Promise<LocaleMessages> {
|
|
229
|
+
const catalog = catalogLocale(locale);
|
|
230
|
+
if (!catalog) return {};
|
|
231
|
+
localeMessages[catalog] ??= await readLocaleMessages(catalog);
|
|
232
|
+
return localeMessages[catalog];
|
|
224
233
|
}
|
|
225
234
|
|
|
226
235
|
/** Development only: applies every regenerated catalog to \`target\`. */
|
|
@@ -262,10 +271,14 @@ function writeLocaleMessages(workspaceDir, registry) {
|
|
|
262
271
|
changed = (0, fs_sync_1.applyContent)(content, path) || changed;
|
|
263
272
|
};
|
|
264
273
|
(0, node_fs_1.mkdirSync)(outputDir, { recursive: true });
|
|
274
|
+
const fallback = RENDERER_TRANSLATED_KEYS.reduce(withoutMessage, messages[FALLBACK_LOCALE] ?? {});
|
|
265
275
|
for (const [locale, value] of Object.entries(messages)) {
|
|
266
276
|
const catalog = JSON.stringify(value);
|
|
277
|
+
const complete = locale === FALLBACK_LOCALE
|
|
278
|
+
? catalog
|
|
279
|
+
: JSON.stringify(mergeLocaleMessages(fallback, value));
|
|
267
280
|
apply(catalog, (0, node_path_1.join)(outputDir, `${locale}.json`));
|
|
268
|
-
apply(`const messages = ${
|
|
281
|
+
apply(`const messages = ${complete};\nexport default messages;\n`, (0, node_path_1.join)(outputDir, `${locale}.ts`));
|
|
269
282
|
outputs.add(`${locale}.json`).add(`${locale}.ts`);
|
|
270
283
|
}
|
|
271
284
|
const supportedLocales = Object.keys(messages);
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import type { ProjectFrontendModule, ProjectModulesOptions } from "./project-modules";
|
|
2
|
+
import type { Manifest, ResolvedLayer } from "./workspace";
|
|
3
|
+
export declare function readProjectModulesFresh(options: ProjectModulesOptions): Promise<ProjectFrontendModule[]>;
|
|
4
|
+
export declare function warnModuleDrift(layers: readonly ResolvedLayer[], manifest: Manifest): void;
|
|
5
|
+
export declare function withBackendValues(modules: readonly ProjectFrontendModule[], manifest: Manifest): ResolvedLayer[];
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.readProjectModulesFresh = readProjectModulesFresh;
|
|
4
|
+
exports.warnModuleDrift = warnModuleDrift;
|
|
5
|
+
exports.withBackendValues = withBackendValues;
|
|
6
|
+
const node_child_process_1 = require("node:child_process");
|
|
7
|
+
const cli_1 = require("@antelopejs/core/cli");
|
|
8
|
+
const private_values_1 = require("./private-values");
|
|
9
|
+
function readProjectModulesFresh(options) {
|
|
10
|
+
return new Promise((resolve, reject) => {
|
|
11
|
+
const child = (0, node_child_process_1.fork)(require.resolve("./project-modules-child"), [], {
|
|
12
|
+
stdio: ["ignore", "inherit", "inherit", "ipc"],
|
|
13
|
+
});
|
|
14
|
+
let reply;
|
|
15
|
+
child.once("message", (message) => {
|
|
16
|
+
reply = message;
|
|
17
|
+
});
|
|
18
|
+
child.once("error", reject);
|
|
19
|
+
child.once("exit", (code, signal) => {
|
|
20
|
+
if (!reply) {
|
|
21
|
+
reject(new Error(`Reading the project's modules stopped (${signal ?? `exit code ${code}`})`));
|
|
22
|
+
return;
|
|
23
|
+
}
|
|
24
|
+
const ui = (0, cli_1.getProcessUi)();
|
|
25
|
+
for (const message of reply.messages)
|
|
26
|
+
ui.message(...message);
|
|
27
|
+
if ("modules" in reply)
|
|
28
|
+
resolve(reply.modules);
|
|
29
|
+
else if ("problem" in reply)
|
|
30
|
+
reject(new cli_1.CliError(reply.problem));
|
|
31
|
+
else
|
|
32
|
+
reject(new Error(reply.failure));
|
|
33
|
+
});
|
|
34
|
+
child.send(options);
|
|
35
|
+
});
|
|
36
|
+
}
|
|
37
|
+
const URL_BOUNDARY = "(?=[/?#]|$)";
|
|
38
|
+
function escapeRegExp(value) {
|
|
39
|
+
return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
40
|
+
}
|
|
41
|
+
function rewriteOrigins(options, rewrites) {
|
|
42
|
+
if (rewrites.length === 0)
|
|
43
|
+
return options;
|
|
44
|
+
const replacements = new Map(rewrites.map(({ from, to }) => [from, to]));
|
|
45
|
+
const alternation = [...replacements.keys()]
|
|
46
|
+
.sort((left, right) => right.length - left.length)
|
|
47
|
+
.map(escapeRegExp)
|
|
48
|
+
.join("|");
|
|
49
|
+
const pattern = new RegExp(`(?:${alternation})${URL_BOUNDARY}`, "g");
|
|
50
|
+
const rewrite = (value) => {
|
|
51
|
+
if (typeof value === "string")
|
|
52
|
+
return value.replace(pattern, (match) => replacements.get(match) ?? match);
|
|
53
|
+
if (Array.isArray(value))
|
|
54
|
+
return value.map(rewrite);
|
|
55
|
+
if (value !== null && typeof value === "object")
|
|
56
|
+
return rewriteObject(value);
|
|
57
|
+
return value;
|
|
58
|
+
};
|
|
59
|
+
const rewriteObject = (value) => Object.fromEntries(Object.entries(value).map(([key, item]) => [key, rewrite(item)]));
|
|
60
|
+
return rewriteObject(options);
|
|
61
|
+
}
|
|
62
|
+
function warnModuleDrift(layers, manifest) {
|
|
63
|
+
const served = new Set(layers.map((layer) => layer.name ?? ""));
|
|
64
|
+
const registered = new Set(manifest.modules.map((module) => module.name));
|
|
65
|
+
const ui = (0, cli_1.getProcessUi)();
|
|
66
|
+
for (const name of served) {
|
|
67
|
+
if (!registered.has(name))
|
|
68
|
+
ui.message("warn", `${name} is served from the project, but the backend does not register it`, {
|
|
69
|
+
detail: "Its frontendModule returns a registration the running backend did not add: check the module's configuration and startup",
|
|
70
|
+
});
|
|
71
|
+
}
|
|
72
|
+
for (const name of registered) {
|
|
73
|
+
if (!served.has(name))
|
|
74
|
+
ui.message("warn", `${name} is registered by the backend, but not in the project: its pages are left out`, {
|
|
75
|
+
detail: `A build leaves it out too. Declare its frontendModule (dmsFrontend in its package.json), or add the module to antelope.config.ts`,
|
|
76
|
+
});
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
function withBackendValues(modules, manifest) {
|
|
80
|
+
const registered = new Map(manifest.modules.map((module) => [module.name, module]));
|
|
81
|
+
return modules.map(({ layer }) => ({
|
|
82
|
+
...layer,
|
|
83
|
+
options: layer.options &&
|
|
84
|
+
rewriteOrigins(layer.options, manifest.originRewrites ?? []),
|
|
85
|
+
privateOptions: layer.privateOptions &&
|
|
86
|
+
(0, private_values_1.filledOptions)(layer.privateOptions, registered.get(layer.name ?? "")?.privateOptions),
|
|
87
|
+
}));
|
|
88
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
export declare const ENTRY_WEIGHT_VARIABLE = "DMS_ENTRY_WEIGHT_WARNING";
|
|
2
|
+
export declare const DEFAULT_ENTRY_WEIGHT_WARNING_KB = 50;
|
|
3
|
+
export interface EntryShare {
|
|
4
|
+
owner: string;
|
|
5
|
+
bytes: number;
|
|
6
|
+
brotli: number;
|
|
7
|
+
}
|
|
8
|
+
export interface EntryWeight {
|
|
9
|
+
bytes: number;
|
|
10
|
+
brotli: number;
|
|
11
|
+
shares: EntryShare[];
|
|
12
|
+
}
|
|
13
|
+
export declare function readEntryWeight(workspaceDir: string): EntryWeight | undefined;
|
|
14
|
+
export declare function entryWeightThreshold(env: NodeJS.ProcessEnv): number;
|
|
15
|
+
export declare function heavyEntryModules(weight: EntryWeight, threshold: number): EntryShare[];
|
|
16
|
+
export declare function describeEntryShares(weight: EntryWeight): string[];
|
|
17
|
+
interface ReportUi {
|
|
18
|
+
message(level: "info" | "warn", text: string, options?: {
|
|
19
|
+
detail?: string;
|
|
20
|
+
details?: string[];
|
|
21
|
+
}): void;
|
|
22
|
+
}
|
|
23
|
+
export declare function reportEntryWeight(ui: ReportUi, workspaceDir: string, env: NodeJS.ProcessEnv): void;
|
|
24
|
+
export {};
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.DEFAULT_ENTRY_WEIGHT_WARNING_KB = exports.ENTRY_WEIGHT_VARIABLE = void 0;
|
|
4
|
+
exports.readEntryWeight = readEntryWeight;
|
|
5
|
+
exports.entryWeightThreshold = entryWeightThreshold;
|
|
6
|
+
exports.heavyEntryModules = heavyEntryModules;
|
|
7
|
+
exports.describeEntryShares = describeEntryShares;
|
|
8
|
+
exports.reportEntryWeight = reportEntryWeight;
|
|
9
|
+
const node_fs_1 = require("node:fs");
|
|
10
|
+
const node_path_1 = require("node:path");
|
|
11
|
+
const node_zlib_1 = require("node:zlib");
|
|
12
|
+
const cli_1 = require("@antelopejs/core/cli");
|
|
13
|
+
const output_1 = require("./output");
|
|
14
|
+
exports.ENTRY_WEIGHT_VARIABLE = "DMS_ENTRY_WEIGHT_WARNING";
|
|
15
|
+
exports.DEFAULT_ENTRY_WEIGHT_WARNING_KB = 50;
|
|
16
|
+
const ENTRY_WEIGHT_FILE = (0, node_path_1.join)("dist", "client", ".vite", "entry-weight.json");
|
|
17
|
+
const SHARED_OWNER = "shared";
|
|
18
|
+
const TRANSLATIONS_OWNER = "translations";
|
|
19
|
+
const NON_MODULE_OWNERS = new Set([SHARED_OWNER, TRANSLATIONS_OWNER]);
|
|
20
|
+
const BYTES_PER_KB = 1024;
|
|
21
|
+
function brotliSize(file) {
|
|
22
|
+
const compressed = `${file}.br`;
|
|
23
|
+
if ((0, node_fs_1.existsSync)(compressed))
|
|
24
|
+
return (0, node_fs_1.statSync)(compressed).size;
|
|
25
|
+
return (0, node_zlib_1.brotliCompressSync)((0, node_fs_1.readFileSync)(file), {
|
|
26
|
+
params: { [node_zlib_1.constants.BROTLI_PARAM_QUALITY]: node_zlib_1.constants.BROTLI_MAX_QUALITY },
|
|
27
|
+
}).length;
|
|
28
|
+
}
|
|
29
|
+
function readEntryWeight(workspaceDir) {
|
|
30
|
+
const path = (0, node_path_1.join)(workspaceDir, ENTRY_WEIGHT_FILE);
|
|
31
|
+
if (!(0, node_fs_1.existsSync)(path))
|
|
32
|
+
return undefined;
|
|
33
|
+
const recorded = JSON.parse((0, node_fs_1.readFileSync)(path, "utf8"));
|
|
34
|
+
const brotli = recorded.files.reduce((sum, file) => sum + brotliSize((0, node_path_1.join)(workspaceDir, "dist", "client", file)), 0);
|
|
35
|
+
const ratio = recorded.bytes ? brotli / recorded.bytes : 0;
|
|
36
|
+
const shares = Object.entries(recorded.owners)
|
|
37
|
+
.map(([owner, bytes]) => ({
|
|
38
|
+
owner,
|
|
39
|
+
bytes,
|
|
40
|
+
brotli: Math.round(bytes * ratio),
|
|
41
|
+
}))
|
|
42
|
+
.sort((left, right) => right.bytes - left.bytes);
|
|
43
|
+
return { bytes: recorded.bytes, brotli, shares };
|
|
44
|
+
}
|
|
45
|
+
function entryWeightThreshold(env) {
|
|
46
|
+
const value = env[exports.ENTRY_WEIGHT_VARIABLE]?.trim();
|
|
47
|
+
if (!value)
|
|
48
|
+
return exports.DEFAULT_ENTRY_WEIGHT_WARNING_KB * BYTES_PER_KB;
|
|
49
|
+
const kilobytes = Number(value);
|
|
50
|
+
if (!Number.isFinite(kilobytes) || kilobytes < 0)
|
|
51
|
+
throw new cli_1.CliError({
|
|
52
|
+
title: `${exports.ENTRY_WEIGHT_VARIABLE} is not a size`,
|
|
53
|
+
reason: `It is ${JSON.stringify(value)}; it takes a number of KB of Brotli.`,
|
|
54
|
+
fixes: [
|
|
55
|
+
`Set ${exports.ENTRY_WEIGHT_VARIABLE}=${exports.DEFAULT_ENTRY_WEIGHT_WARNING_KB}, or 0 to turn the warning off`,
|
|
56
|
+
],
|
|
57
|
+
});
|
|
58
|
+
return kilobytes * BYTES_PER_KB;
|
|
59
|
+
}
|
|
60
|
+
function heavyEntryModules(weight, threshold) {
|
|
61
|
+
if (threshold <= 0)
|
|
62
|
+
return [];
|
|
63
|
+
return weight.shares.filter((share) => !NON_MODULE_OWNERS.has(share.owner) && share.brotli > threshold);
|
|
64
|
+
}
|
|
65
|
+
function describeEntryShares(weight) {
|
|
66
|
+
return weight.shares.map((share) => `${share.owner} ${(0, output_1.formatSize)(share.brotli)} (${(0, output_1.formatSize)(share.bytes)})`);
|
|
67
|
+
}
|
|
68
|
+
function reportEntryWeight(ui, workspaceDir, env) {
|
|
69
|
+
const threshold = entryWeightThreshold(env);
|
|
70
|
+
const weight = readEntryWeight(workspaceDir);
|
|
71
|
+
if (!weight)
|
|
72
|
+
return;
|
|
73
|
+
ui.message("info", `Client entry ${(0, output_1.formatSize)(weight.brotli)} Brotli (${(0, output_1.formatSize)(weight.bytes)}), loaded first by every page`, { details: describeEntryShares(weight) });
|
|
74
|
+
const heavy = heavyEntryModules(weight, threshold);
|
|
75
|
+
heavy.forEach((share, index) => {
|
|
76
|
+
const isLast = index === heavy.length - 1;
|
|
77
|
+
ui.message("warn", `${share.owner} adds ${(0, output_1.formatSize)(share.brotli)} Brotli to the client entry`, {
|
|
78
|
+
details: isLast
|
|
79
|
+
? [
|
|
80
|
+
"What a module's dms.frontend.ts and its plugins import is loaded by every page: render a component they only show on some pages by its registered name or with defineAsyncComponent",
|
|
81
|
+
`The threshold is ${(0, output_1.formatSize)(threshold)} (${exports.ENTRY_WEIGHT_VARIABLE}, in KB; 0 turns the warning off)`,
|
|
82
|
+
]
|
|
83
|
+
: [],
|
|
84
|
+
});
|
|
85
|
+
});
|
|
86
|
+
}
|
package/dist/help.js
CHANGED
|
@@ -17,9 +17,13 @@ exports.ENVIRONMENT_VARIABLES = [
|
|
|
17
17
|
["DMS_WORKSPACE_DIR", "Same as build and start --workspace"],
|
|
18
18
|
[
|
|
19
19
|
"ANTELOPEJS_LAUNCH_ENV",
|
|
20
|
-
"AntelopeJS environment build and start evaluate the project in, same as -e",
|
|
20
|
+
"AntelopeJS environment dev, build and start evaluate the project in, same as -e",
|
|
21
21
|
],
|
|
22
22
|
["DMS_PREPARE_STRICT", "Same as prepare --strict, read like DMS_OFFLINE"],
|
|
23
|
+
[
|
|
24
|
+
"DMS_ENTRY_WEIGHT_WARNING",
|
|
25
|
+
"KB of Brotli a module may add to the client entry before build warns (default: 50; 0: never)",
|
|
26
|
+
],
|
|
23
27
|
["PORT", "Same as -p"],
|
|
24
28
|
[
|
|
25
29
|
"HOST",
|
package/dist/layer-watch.js
CHANGED
|
@@ -12,6 +12,7 @@ const child_output_1 = require("./child-output");
|
|
|
12
12
|
const config_1 = require("./config");
|
|
13
13
|
const layers_1 = require("./layers");
|
|
14
14
|
const derived_outputs_1 = require("./derived-outputs");
|
|
15
|
+
const locale_files_1 = require("./locale-files");
|
|
15
16
|
const output_1 = require("./output");
|
|
16
17
|
const fs_sync_1 = require("./fs-sync");
|
|
17
18
|
const WATCH_DEBOUNCE_MS = 80;
|
|
@@ -128,11 +129,11 @@ function createLayerSync(src, dest, reporter, onStaged) {
|
|
|
128
129
|
}
|
|
129
130
|
function describeOutputFailure(output, err, reporter) {
|
|
130
131
|
const kept = `The dev server keeps the previous ${output.name} until the file is fixed.`;
|
|
131
|
-
if (err instanceof
|
|
132
|
+
if (err instanceof locale_files_1.InvalidLocaleFileError) {
|
|
132
133
|
const file = reporter.show(err.path);
|
|
133
134
|
return {
|
|
134
135
|
text: `${file} is not a valid locale file`,
|
|
135
|
-
options: { details: [err.reason, kept] },
|
|
136
|
+
options: { details: [...err.reason.split("\n"), kept] },
|
|
136
137
|
fixed: [`${file} fixed`, `${output.name} regenerated`].join((0, cli_1.getProcessUi)().symbols.separator),
|
|
137
138
|
};
|
|
138
139
|
}
|
package/dist/layers.d.ts
CHANGED
|
@@ -11,6 +11,7 @@ export interface LayerPackage {
|
|
|
11
11
|
name?: unknown;
|
|
12
12
|
engines?: Record<string, unknown>;
|
|
13
13
|
dmsFrontend?: unknown;
|
|
14
|
+
antelopeJs?: unknown;
|
|
14
15
|
}
|
|
15
16
|
export declare function readLayerPackage(layerPath: string): LayerPackage | undefined;
|
|
16
17
|
export declare function buildLayersFromPaths(modules: ServedWithPath[]): ResolvedLayer[];
|