@antelopejs/dms-frontend 0.9.2 → 0.10.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 +18 -24
- package/dist/commands/build-action.js +3 -2
- package/dist/commands/dev-action.js +2 -1
- package/dist/commands/prepare-action.js +1 -1
- package/dist/commands/start-action.js +3 -24
- package/dist/commands/workspace-task.js +2 -1
- package/dist/config.js +3 -0
- package/dist/derived-outputs.js +3 -3
- package/dist/discovery.js +17 -6
- package/dist/entry-weight.js +15 -0
- package/dist/fs-sync.js +7 -4
- package/dist/index.js +2 -1
- package/dist/internal/built-workspace.d.ts +4 -0
- package/dist/internal/built-workspace.js +43 -0
- package/dist/internal/ensure.d.ts +1 -0
- package/dist/internal/ensure.js +8 -0
- package/dist/internal/locale-catalogs.d.ts +8 -3
- package/dist/internal/locale-catalogs.js +16 -6
- package/dist/internal/manifest-decoder.d.ts +7 -0
- package/dist/internal/manifest-decoder.js +81 -0
- package/dist/internal/own-release.d.ts +5 -0
- package/dist/internal/own-release.js +20 -0
- package/dist/internal/plain-object.d.ts +1 -0
- package/dist/internal/plain-object.js +6 -0
- package/dist/internal/text-list.d.ts +1 -0
- package/dist/internal/text-list.js +10 -0
- package/dist/internal/vite-manifest.d.ts +6 -0
- package/dist/internal/vite-manifest.js +24 -0
- package/dist/layer-watch.js +25 -24
- package/dist/layers.d.ts +1 -1
- package/dist/layers.js +3 -1
- package/dist/locale-files.d.ts +2 -1
- package/dist/locale-files.js +4 -0
- package/dist/manifest.d.ts +1 -1
- package/dist/manifest.js +44 -31
- package/dist/materialize.js +6 -2
- package/dist/module-set.js +2 -1
- package/dist/node-options.d.ts +1 -0
- package/dist/node-options.js +8 -0
- package/dist/output.d.ts +15 -9
- package/dist/output.js +30 -18
- package/dist/private-values.d.ts +1 -1
- package/dist/private-values.js +5 -3
- package/dist/project-modules.js +13 -8
- package/dist/renderer-range.js +5 -3
- package/dist/server-process.js +1 -1
- package/dist/verify-source-build.js +10 -2
- package/dist/verify-source-client-bundle.d.ts +1 -0
- package/dist/verify-source-client-bundle.js +30 -0
- package/dist/verify-source-runner.js +44 -60
- package/dist/verify-source-sources.js +49 -11
- package/dist/workspace-setup.js +1 -1
- package/package.json +6 -4
- package/skills/dms-frontend/SKILL.md +1 -1
- package/templates/vue/app-runtime.ts +90 -16
- package/templates/vue/client-server-guard.mjs +16 -0
- package/templates/vue/compress-assets.mjs +9 -1
- package/templates/vue/entry-weight.mjs +22 -0
- package/templates/vue/frontend-module.ts +1 -0
- package/templates/vue/frontend-runtime/module-registration.ts +2 -2
- package/templates/vue/frontend-runtime/state.ts +2 -2
- package/templates/vue/frontend-runtime/types.ts +8 -2
- package/templates/vue/globals.d.ts +5 -0
- package/templates/vue/head-order.mjs +7 -0
- package/templates/vue/icon-bundle.ts +31 -0
- package/templates/vue/icon-scan.mjs +4 -0
- package/templates/vue/message-subset.ts +65 -0
- package/templates/vue/network-error.ts +4 -0
- package/templates/vue/server/auth/backend.mjs +41 -4
- package/templates/vue/server/auth/client-ip.mjs +52 -4
- package/templates/vue/server/auth/establish-endpoints.mjs +10 -6
- package/templates/vue/server/auth/session.mjs +81 -2
- package/templates/vue/server/auth-hooks.mjs +15 -5
- package/templates/vue/server/backend-proxies.mjs +65 -9
- package/templates/vue/server/backend-request.mjs +61 -0
- package/templates/vue/server/backend-response.mjs +23 -6
- package/templates/vue/server/built-files.mjs +7 -0
- package/templates/vue/server/cli-report.mjs +53 -11
- package/templates/vue/server/client-manifest.mjs +23 -1
- package/templates/vue/server/content.mjs +18 -1
- package/templates/vue/server/dev-private-files.mjs +17 -1
- package/templates/vue/server/dev-styles.mjs +38 -0
- package/templates/vue/server/email.mjs +9 -3
- package/templates/vue/server/favicon.mjs +3 -0
- package/templates/vue/server/generated-files.mjs +211 -0
- package/templates/vue/server/homepage.mjs +21 -9
- package/templates/vue/server/html-tag.mjs +4 -0
- package/templates/vue/server/icons.mjs +80 -7
- package/templates/vue/server/in-place-render.mjs +35 -0
- package/templates/vue/server/inertia.mjs +61 -2
- package/templates/vue/server/module-event.mjs +98 -17
- package/templates/vue/server/module-route-types.mjs +78 -0
- package/templates/vue/server/module-routes.mjs +138 -31
- package/templates/vue/server/module-server.mjs +98 -27
- package/templates/vue/server/module-testing.mjs +14 -5
- package/templates/vue/server/page-props.mjs +92 -2
- package/templates/vue/server/page-redirects.mjs +37 -0
- package/templates/vue/server/path-patterns.mjs +13 -1
- package/templates/vue/server/proxy-compression.mjs +22 -0
- package/templates/vue/server/proxy-headers.mjs +11 -1
- package/templates/vue/server/proxy-redirects.mjs +20 -5
- package/templates/vue/server/proxy-request.mjs +57 -0
- package/templates/vue/server/proxy-transfers.mjs +33 -5
- package/templates/vue/server/public-endpoints.mjs +138 -23
- package/templates/vue/server/public-options.mjs +20 -6
- package/templates/vue/server/request-failure.mjs +23 -0
- package/templates/vue/server/request-routing.mjs +26 -9
- package/templates/vue/server/request-target.mjs +9 -3
- package/templates/vue/server/route-rules.mjs +39 -15
- package/templates/vue/server/ssr-module.mjs +32 -0
- package/templates/vue/server/static-files.mjs +24 -1
- package/templates/vue/server/type-guards.mjs +71 -0
- package/templates/vue/server/vite-errors.mjs +32 -5
- package/templates/vue/server/web-body.mjs +3 -0
- package/templates/vue/server-module.ts +10 -1
- package/templates/vue/server.mjs +132 -148
- package/templates/vue/sfc-global-types.mjs +112 -13
- package/templates/vue/ssr-renderer.ts +12 -3
- package/templates/vue/ui-inertia-page.mjs +16 -1
- package/templates/vue/ui-kbd.mjs +2 -1
- package/templates/vue/ui-lazy-icons.mjs +44 -0
- package/templates/vue/ui-request-state.mjs +2 -1
- package/templates/vue/vite.config.ts +2 -0
package/dist/renderer-range.js
CHANGED
|
@@ -11,7 +11,8 @@ const semver_1 = __importDefault(require("semver"));
|
|
|
11
11
|
const layers_1 = require("./layers");
|
|
12
12
|
const output_1 = require("./output");
|
|
13
13
|
const guards_1 = require("./internal/guards");
|
|
14
|
-
const
|
|
14
|
+
const plain_object_1 = require("./internal/plain-object");
|
|
15
|
+
const own_release_1 = require("./internal/own-release");
|
|
15
16
|
const noticedModules = new Set();
|
|
16
17
|
const NOTICED_MODULES_FILE = ".renderer-range-noticed";
|
|
17
18
|
function readNoticedModules(workspaceDir) {
|
|
@@ -35,10 +36,11 @@ function shownModule(layer) {
|
|
|
35
36
|
return layer.packageName ? `${layer.packageName} (${where})` : where;
|
|
36
37
|
}
|
|
37
38
|
function declaredRange(layer, rendererName) {
|
|
39
|
+
const engines = (0, layers_1.readLayerPackage)(layer.path)?.engines;
|
|
38
40
|
return {
|
|
39
41
|
module: moduleLabel(layer),
|
|
40
42
|
shown: shownModule(layer),
|
|
41
|
-
range: (0,
|
|
43
|
+
range: (0, plain_object_1.isPlainObject)(engines) ? engines[rendererName] : undefined,
|
|
42
44
|
};
|
|
43
45
|
}
|
|
44
46
|
function releaseLine(version) {
|
|
@@ -73,7 +75,7 @@ function noticeUndeclared(declarations, renderer, warn, workspaceDir) {
|
|
|
73
75
|
});
|
|
74
76
|
}
|
|
75
77
|
function assertLayersSupportRenderer(layers, options = {}) {
|
|
76
|
-
const renderer = options.renderer ??
|
|
78
|
+
const renderer = options.renderer ?? own_release_1.ownRelease;
|
|
77
79
|
const declarations = layers.map((layer) => declaredRange(layer, renderer.name));
|
|
78
80
|
noticeUndeclared(declarations, renderer, options.warn ??
|
|
79
81
|
((text, messageOptions) => (0, cli_1.getProcessUi)().message("warn", text, messageOptions)), options.workspaceDir);
|
package/dist/server-process.js
CHANGED
|
@@ -73,7 +73,7 @@ function readListenError(message) {
|
|
|
73
73
|
function readStartError(message) {
|
|
74
74
|
if (!isServerMessage(message) || message.type !== START_ERROR_MESSAGE)
|
|
75
75
|
return undefined;
|
|
76
|
-
const
|
|
76
|
+
const location = message.location;
|
|
77
77
|
const isLocated = hasFile(location);
|
|
78
78
|
return {
|
|
79
79
|
message: String(message.message),
|
|
@@ -8,6 +8,8 @@ const cli_1 = require("@antelopejs/core/cli");
|
|
|
8
8
|
const child_output_1 = require("./child-output");
|
|
9
9
|
const build_action_1 = require("./commands/build-action");
|
|
10
10
|
const common_1 = require("./common");
|
|
11
|
+
const text_list_1 = require("./internal/text-list");
|
|
12
|
+
const plain_object_1 = require("./internal/plain-object");
|
|
11
13
|
const typecheck_output_1 = require("./typecheck-output");
|
|
12
14
|
const verify_source_result_1 = require("./verify-source-result");
|
|
13
15
|
const vite_output_1 = require("./vite-output");
|
|
@@ -39,8 +41,14 @@ const TYPECHECK_CONFIG = "tsconfig.verify.json";
|
|
|
39
41
|
function snapshotTypeDeclarations(workspace) {
|
|
40
42
|
(0, node_fs_1.cpSync)((0, node_path_1.join)(workspace, AUTO_IMPORTS_DECLARATIONS), (0, node_path_1.join)(workspace, AUTO_IMPORTS_SNAPSHOT));
|
|
41
43
|
const config = JSON.parse((0, node_fs_1.readFileSync)((0, node_path_1.join)(workspace, "tsconfig.json"), "utf8"));
|
|
42
|
-
|
|
43
|
-
if (!config.
|
|
44
|
+
const listed = (0, plain_object_1.isPlainObject)(config) ? (config.files ?? []) : undefined;
|
|
45
|
+
if (!(0, plain_object_1.isPlainObject)(config) || !(0, text_list_1.isTextList)(listed))
|
|
46
|
+
throw new cli_1.CliError({
|
|
47
|
+
title: "The workspace tsconfig.json does not list its files as paths",
|
|
48
|
+
});
|
|
49
|
+
const files = listed.map((file) => file === AUTO_IMPORTS_DECLARATIONS ? AUTO_IMPORTS_SNAPSHOT : file);
|
|
50
|
+
config.files = files;
|
|
51
|
+
if (!files.includes(AUTO_IMPORTS_SNAPSHOT))
|
|
44
52
|
throw new cli_1.CliError({
|
|
45
53
|
title: `The workspace tsconfig.json does not list ${AUTO_IMPORTS_DECLARATIONS}`,
|
|
46
54
|
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function checkClientBundle(workspace: string): void;
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.checkClientBundle = checkClientBundle;
|
|
4
|
+
const node_fs_1 = require("node:fs");
|
|
5
|
+
const node_path_1 = require("node:path");
|
|
6
|
+
const ensure_1 = require("./internal/ensure");
|
|
7
|
+
const vite_manifest_1 = require("./internal/vite-manifest");
|
|
8
|
+
const LAZY_LIBRARIES = [
|
|
9
|
+
{ name: "ApexCharts", pattern: /ApexCharts/ },
|
|
10
|
+
{ name: "Tiptap", pattern: /tiptap/i },
|
|
11
|
+
];
|
|
12
|
+
const RUNTIME_MARKERS = ["auth/login", "DefaultLayout", "error.500.title"];
|
|
13
|
+
function checkClientBundle(workspace) {
|
|
14
|
+
const clientRoot = (0, node_path_1.join)(workspace, "dist", "client");
|
|
15
|
+
const manifest = (0, vite_manifest_1.parseViteManifest)((0, node_fs_1.readFileSync)((0, node_path_1.join)(clientRoot, ".vite", "manifest.json"), "utf8"), "client build manifest");
|
|
16
|
+
const mainEntry = manifest.find((entry) => entry.isEntry);
|
|
17
|
+
(0, ensure_1.ensure)(mainEntry !== undefined, "The Vite manifest names no entry chunk");
|
|
18
|
+
const mainJavascript = (0, node_fs_1.readFileSync)((0, node_path_1.join)(clientRoot, mainEntry.file), "utf8");
|
|
19
|
+
const javascript = (0, node_fs_1.readdirSync)((0, node_path_1.join)(clientRoot, "assets"))
|
|
20
|
+
.filter((file) => file.endsWith(".js"))
|
|
21
|
+
.map((file) => (0, node_fs_1.readFileSync)((0, node_path_1.join)(clientRoot, "assets", file), "utf8"))
|
|
22
|
+
.join("\n");
|
|
23
|
+
for (const { name, pattern } of LAZY_LIBRARIES) {
|
|
24
|
+
(0, ensure_1.ensure)(!pattern.test(mainJavascript), `The main chunk bundles ${name}`, "It must stay in a lazily loaded chunk.");
|
|
25
|
+
(0, ensure_1.ensure)(pattern.test(javascript), `No client chunk bundles ${name}`);
|
|
26
|
+
}
|
|
27
|
+
(0, ensure_1.ensure)(manifest.some((chunk) => chunk.src?.endsWith("/kpi/KpiCard.vue")), "DmsKpiCard is not reachable at runtime");
|
|
28
|
+
for (const marker of RUNTIME_MARKERS)
|
|
29
|
+
(0, ensure_1.ensure)(javascript.includes(marker), `${marker} is not reachable at runtime`);
|
|
30
|
+
}
|
|
@@ -37,32 +37,27 @@ const node_fs_1 = require("node:fs");
|
|
|
37
37
|
const node_path_1 = require("node:path");
|
|
38
38
|
const cli_1 = require("@antelopejs/core/cli");
|
|
39
39
|
const child_output_1 = require("./child-output");
|
|
40
|
+
const ensure_1 = require("./internal/ensure");
|
|
41
|
+
const guards_1 = require("./internal/guards");
|
|
42
|
+
const plain_object_1 = require("./internal/plain-object");
|
|
43
|
+
const vite_manifest_1 = require("./internal/vite-manifest");
|
|
40
44
|
const common_1 = require("./common");
|
|
41
45
|
const output_1 = require("./output");
|
|
42
46
|
const temporary_workspace_1 = require("./temporary-workspace");
|
|
47
|
+
const verify_source_client_bundle_1 = require("./verify-source-client-bundle");
|
|
43
48
|
const verify_source_build_1 = require("./verify-source-build");
|
|
44
49
|
const verify_source_result_1 = require("./verify-source-result");
|
|
45
50
|
const verify_source_sources_1 = require("./verify-source-sources");
|
|
46
51
|
const vite_output_1 = require("./vite-output");
|
|
47
52
|
const EMAIL_BUILD_SIZE_CEILING_BYTES = 256 * 1024;
|
|
48
53
|
const NON_EMAIL_RUNTIME_PATTERN = /DisplayRichText|ApexCharts|Tiptap|FlowCanvas|app\/.*table|components\/table/i;
|
|
49
|
-
const LAZY_LIBRARIES = [
|
|
50
|
-
{ name: "ApexCharts", pattern: /ApexCharts/ },
|
|
51
|
-
{ name: "Tiptap", pattern: /tiptap/i },
|
|
52
|
-
];
|
|
53
|
-
const RUNTIME_MARKERS = ["auth/login", "DefaultLayout", "error.500.title"];
|
|
54
|
-
function readJson(path) {
|
|
55
|
-
return JSON.parse((0, node_fs_1.readFileSync)(path, "utf8"));
|
|
56
|
-
}
|
|
57
|
-
function ensure(condition, title, reason) {
|
|
58
|
-
if (!condition)
|
|
59
|
-
throw new cli_1.CliError({ title, reason });
|
|
60
|
-
}
|
|
61
54
|
function removeDevelopmentDependencies(root) {
|
|
62
55
|
const packagePath = (0, node_path_1.join)(root, "package.json");
|
|
63
56
|
if (!(0, node_fs_1.existsSync)(packagePath))
|
|
64
57
|
return;
|
|
65
|
-
const packageData =
|
|
58
|
+
const packageData = JSON.parse((0, node_fs_1.readFileSync)(packagePath, "utf8"));
|
|
59
|
+
if (!(0, plain_object_1.isPlainObject)(packageData))
|
|
60
|
+
return;
|
|
66
61
|
delete packageData.devDependencies;
|
|
67
62
|
(0, node_fs_1.writeFileSync)(packagePath, `${JSON.stringify(packageData, null, 2)}\n`);
|
|
68
63
|
}
|
|
@@ -82,10 +77,14 @@ function materializeWorkspace(workspace, layers, localPackages) {
|
|
|
82
77
|
(0, node_fs_1.writeFileSync)((0, node_path_1.join)(workspace, "dms-main.css"), '@import "tailwindcss";\n@import "@nuxt/ui";\n');
|
|
83
78
|
(0, common_1.writeWorkspacePackageJson)(workspace, layers);
|
|
84
79
|
const packagePath = (0, node_path_1.join)(workspace, "package.json");
|
|
85
|
-
const workspacePackage =
|
|
80
|
+
const workspacePackage = JSON.parse((0, node_fs_1.readFileSync)(packagePath, "utf8"));
|
|
81
|
+
(0, ensure_1.ensure)((0, plain_object_1.isPlainObject)(workspacePackage), "The workspace package.json is not a JSON object");
|
|
86
82
|
workspacePackage.pnpm = {
|
|
87
83
|
overrides: {
|
|
88
|
-
...Object.fromEntries(layers.map((layer) => [
|
|
84
|
+
...Object.fromEntries(layers.map((layer) => [
|
|
85
|
+
String(layer.packageName),
|
|
86
|
+
"workspace:*",
|
|
87
|
+
])),
|
|
89
88
|
...Object.fromEntries(Object.entries(localPackages).map(([name, path]) => [
|
|
90
89
|
name,
|
|
91
90
|
`link:${(0, node_path_1.resolve)(path)}`,
|
|
@@ -94,33 +93,15 @@ function materializeWorkspace(workspace, layers, localPackages) {
|
|
|
94
93
|
};
|
|
95
94
|
(0, node_fs_1.writeFileSync)(packagePath, `${JSON.stringify(workspacePackage, null, 2)}\n`);
|
|
96
95
|
}
|
|
97
|
-
function checkClientBundle(workspace) {
|
|
98
|
-
const clientRoot = (0, node_path_1.join)(workspace, "dist", "client");
|
|
99
|
-
const manifest = readJson((0, node_path_1.join)(clientRoot, ".vite", "manifest.json"));
|
|
100
|
-
const mainEntry = Object.values(manifest).find((entry) => entry.isEntry);
|
|
101
|
-
ensure(mainEntry !== undefined, "The Vite manifest names no entry chunk");
|
|
102
|
-
const mainJavascript = (0, node_fs_1.readFileSync)((0, node_path_1.join)(clientRoot, mainEntry.file), "utf8");
|
|
103
|
-
const javascript = (0, node_fs_1.readdirSync)((0, node_path_1.join)(clientRoot, "assets"))
|
|
104
|
-
.filter((file) => file.endsWith(".js"))
|
|
105
|
-
.map((file) => (0, node_fs_1.readFileSync)((0, node_path_1.join)(clientRoot, "assets", file), "utf8"))
|
|
106
|
-
.join("\n");
|
|
107
|
-
for (const { name, pattern } of LAZY_LIBRARIES) {
|
|
108
|
-
ensure(!pattern.test(mainJavascript), `The main chunk bundles ${name}`, "It must stay in a lazily loaded chunk.");
|
|
109
|
-
ensure(pattern.test(javascript), `No client chunk bundles ${name}`);
|
|
110
|
-
}
|
|
111
|
-
ensure(Object.keys(manifest).some((source) => source.endsWith("/kpi/KpiCard.vue")), "DmsKpiCard is not reachable at runtime");
|
|
112
|
-
for (const marker of RUNTIME_MARKERS)
|
|
113
|
-
ensure(javascript.includes(marker), `${marker} is not reachable at runtime`);
|
|
114
|
-
}
|
|
115
96
|
async function checkServerModulesBundle(workspace) {
|
|
116
97
|
const root = (0, node_path_1.join)(workspace, "dist", "server-modules");
|
|
117
98
|
const javascript = (0, node_fs_1.readdirSync)(root, { recursive: true, encoding: "utf8" })
|
|
118
99
|
.filter((file) => file.endsWith(".js"))
|
|
119
100
|
.map((file) => (0, node_fs_1.readFileSync)((0, node_path_1.join)(root, file), "utf8"))
|
|
120
101
|
.join("\n");
|
|
121
|
-
ensure(!/\.vue["'?]|createApp\(|DefaultLayout/.test(javascript), "The server modules bundle loads application code", "A dms.server.ts imports server code only, never .vue files or dms.frontend.ts.");
|
|
122
|
-
const
|
|
123
|
-
ensure(Array.isArray(serverModules), "The server modules bundle exports none");
|
|
102
|
+
(0, ensure_1.ensure)(!/\.vue["'?]|createApp\(|DefaultLayout/.test(javascript), "The server modules bundle loads application code", "A dms.server.ts imports server code only, never .vue files or dms.frontend.ts.");
|
|
103
|
+
const serverModules = (await Promise.resolve(`${(0, node_path_1.join)(root, "server-modules.generated.js")}`).then(s => __importStar(require(s)))).serverModules;
|
|
104
|
+
(0, ensure_1.ensure)(Array.isArray(serverModules), "The server modules bundle exports none");
|
|
124
105
|
return `${(0, cli_1.pluralize)(serverModules.length, "server module")}, ${(0, output_1.formatSize)(Buffer.byteLength(javascript))} JS`;
|
|
125
106
|
}
|
|
126
107
|
const EMAIL_CASES = [
|
|
@@ -177,19 +158,19 @@ const EMAIL_CASES = [
|
|
|
177
158
|
];
|
|
178
159
|
const BROWSER_ONLY_REASON = "It must not load rich text, charts, tables or flows.";
|
|
179
160
|
function checkLayoutEmail(html) {
|
|
180
|
-
ensure(/<meta charset="utf-8"/.test(html), "EmailLayout declares no UTF-8 charset");
|
|
181
|
-
ensure(/images\/antelope-logo\/light\.svg/.test(html), "EmailLayout renders no logo");
|
|
182
|
-
ensure(!/<h1[^>]*\bas=/.test(html), "EmailLayout leaks an `as` attribute onto its heading");
|
|
161
|
+
(0, ensure_1.ensure)(/<meta charset="utf-8"/.test(html), "EmailLayout declares no UTF-8 charset");
|
|
162
|
+
(0, ensure_1.ensure)(/images\/antelope-logo\/light\.svg/.test(html), "EmailLayout renders no logo");
|
|
163
|
+
(0, ensure_1.ensure)(!/<h1[^>]*\bas=/.test(html), "EmailLayout leaks an `as` attribute onto its heading");
|
|
183
164
|
}
|
|
184
165
|
async function checkEmailContracts(workspace) {
|
|
185
166
|
const emailRoot = (0, node_path_1.join)(workspace, "dist", "server");
|
|
186
167
|
const emailManifestContent = (0, node_fs_1.readFileSync)((0, node_path_1.join)(emailRoot, ".vite", "manifest.json"), "utf8");
|
|
187
|
-
ensure(!NON_EMAIL_RUNTIME_PATTERN.test(emailManifestContent), "The e-mail manifest references browser-only modules", BROWSER_ONLY_REASON);
|
|
188
|
-
const emailManifest =
|
|
168
|
+
(0, ensure_1.ensure)(!NON_EMAIL_RUNTIME_PATTERN.test(emailManifestContent), "The e-mail manifest references browser-only modules", BROWSER_ONLY_REASON);
|
|
169
|
+
const emailManifest = (0, vite_manifest_1.parseViteManifest)(emailManifestContent, "e-mail build manifest");
|
|
189
170
|
const emailJavascriptFiles = [
|
|
190
171
|
...new Set([
|
|
191
172
|
"email-renderer.js",
|
|
192
|
-
...
|
|
173
|
+
...emailManifest
|
|
193
174
|
.map((entry) => entry.file)
|
|
194
175
|
.filter((file) => file.endsWith(".js")),
|
|
195
176
|
]),
|
|
@@ -197,18 +178,18 @@ async function checkEmailContracts(workspace) {
|
|
|
197
178
|
const emailJavascript = emailJavascriptFiles
|
|
198
179
|
.map((file) => (0, node_fs_1.readFileSync)((0, node_path_1.join)(emailRoot, file), "utf8"))
|
|
199
180
|
.join("\n");
|
|
200
|
-
ensure(!NON_EMAIL_RUNTIME_PATTERN.test(emailJavascript), "The e-mail bundle loads browser-only modules", BROWSER_ONLY_REASON);
|
|
181
|
+
(0, ensure_1.ensure)(!NON_EMAIL_RUNTIME_PATTERN.test(emailJavascript), "The e-mail bundle loads browser-only modules", BROWSER_ONLY_REASON);
|
|
201
182
|
const emailBuildSize = Buffer.byteLength(emailJavascript);
|
|
202
183
|
const emailLocaleBytes = (0, node_fs_1.readdirSync)((0, node_path_1.join)(emailRoot, "locales")).reduce((total, file) => total + (0, node_fs_1.readFileSync)((0, node_path_1.join)(emailRoot, "locales", file)).byteLength, 0);
|
|
203
184
|
const limit = (0, output_1.formatSize)(EMAIL_BUILD_SIZE_CEILING_BYTES);
|
|
204
|
-
ensure(emailBuildSize <= EMAIL_BUILD_SIZE_CEILING_BYTES, `The e-mail bundle is ${(0, output_1.formatSize)(emailBuildSize)} of JavaScript`, `The limit is ${limit}.`);
|
|
185
|
+
(0, ensure_1.ensure)(emailBuildSize <= EMAIL_BUILD_SIZE_CEILING_BYTES, `The e-mail bundle is ${(0, output_1.formatSize)(emailBuildSize)} of JavaScript`, `The limit is ${limit}.`);
|
|
205
186
|
const { renderEmail } = await Promise.resolve(`${(0, node_path_1.join)(emailRoot, "email-renderer.js")}`).then(s => __importStar(require(s)));
|
|
206
187
|
for (const emailCase of EMAIL_CASES) {
|
|
207
188
|
const html = await renderEmail(emailCase.name, emailCase.props);
|
|
208
|
-
ensure(html.startsWith("<!doctype html>"), `${emailCase.name} does not render a full HTML document`);
|
|
189
|
+
(0, ensure_1.ensure)((0, guards_1.isText)(html) && html.startsWith("<!doctype html>"), `${emailCase.name} does not render a full HTML document`);
|
|
209
190
|
if (emailCase.name === "EmailLayout")
|
|
210
191
|
checkLayoutEmail(html);
|
|
211
|
-
ensure(html.includes(emailCase.marker), `${emailCase.name} does not render its props`, `Its HTML lacks ${emailCase.marker}.`);
|
|
192
|
+
(0, ensure_1.ensure)(html.includes(emailCase.marker), `${emailCase.name} does not render its props`, `Its HTML lacks ${emailCase.marker}.`);
|
|
212
193
|
}
|
|
213
194
|
return [
|
|
214
195
|
"E-mail contracts hold",
|
|
@@ -219,18 +200,6 @@ async function checkEmailContracts(workspace) {
|
|
|
219
200
|
}
|
|
220
201
|
const LINE_BREAK = /\r?\n/;
|
|
221
202
|
const STACK_TRACE_HINT = "Run with --verbose for the full trace.";
|
|
222
|
-
function describeError(error, isVerbose) {
|
|
223
|
-
if (error instanceof cli_1.CliError)
|
|
224
|
-
return error.problem;
|
|
225
|
-
if (!(error instanceof Error))
|
|
226
|
-
return { title: String(error) };
|
|
227
|
-
const [title] = error.message.split(LINE_BREAK);
|
|
228
|
-
const stack = (error.stack ?? "").split(LINE_BREAK);
|
|
229
|
-
return {
|
|
230
|
-
title: title || error.name,
|
|
231
|
-
details: isVerbose ? stack : [STACK_TRACE_HINT],
|
|
232
|
-
};
|
|
233
|
-
}
|
|
234
203
|
async function verifySources() {
|
|
235
204
|
const options = (0, verify_source_sources_1.readSourceOptions)();
|
|
236
205
|
const { layers, coreLayer } = (0, verify_source_sources_1.readSources)(options);
|
|
@@ -251,7 +220,7 @@ async function verifySources() {
|
|
|
251
220
|
await (0, common_1.installDeps)(workspace, context.mapLine, ["--ignore-scripts"]);
|
|
252
221
|
await (0, verify_source_build_1.prepareTypes)(context);
|
|
253
222
|
await (0, verify_source_build_1.buildAndTypeCheck)(context);
|
|
254
|
-
await tasks.run("Checking the client bundle", async () => checkClientBundle(workspace), { done: "Client bundle checked", failed: "Client bundle check failed" });
|
|
223
|
+
await tasks.run("Checking the client bundle", async () => (0, verify_source_client_bundle_1.checkClientBundle)(workspace), { done: "Client bundle checked", failed: "Client bundle check failed" });
|
|
255
224
|
await tasks.run("Checking the e-mail contracts", async () => checkEmailContracts(workspace), { done: (line) => line, failed: "E-mail contracts failed" });
|
|
256
225
|
await tasks.run("Checking the server modules", async () => checkServerModulesBundle(workspace), { done: (line) => line, failed: "Server modules check failed" });
|
|
257
226
|
return { ok: true, modules: layers.length };
|
|
@@ -276,7 +245,22 @@ async function main() {
|
|
|
276
245
|
process.exitCode = error.exitCode;
|
|
277
246
|
return;
|
|
278
247
|
}
|
|
279
|
-
|
|
248
|
+
let problem;
|
|
249
|
+
if (error instanceof cli_1.CliError) {
|
|
250
|
+
problem = error.problem;
|
|
251
|
+
}
|
|
252
|
+
else if (error instanceof Error) {
|
|
253
|
+
const [title] = error.message.split(LINE_BREAK);
|
|
254
|
+
const stack = (error.stack ?? "").split(LINE_BREAK);
|
|
255
|
+
problem = {
|
|
256
|
+
title: title || error.name,
|
|
257
|
+
details: (0, cli_1.isVerboseRun)() ? stack : [STACK_TRACE_HINT],
|
|
258
|
+
};
|
|
259
|
+
}
|
|
260
|
+
else {
|
|
261
|
+
problem = { title: String(error) };
|
|
262
|
+
}
|
|
263
|
+
result = { ok: false, problem };
|
|
280
264
|
}
|
|
281
265
|
if (process.send) {
|
|
282
266
|
process.exitCode = result.ok ? 0 : 1;
|
|
@@ -5,18 +5,20 @@ exports.readSources = readSources;
|
|
|
5
5
|
const node_fs_1 = require("node:fs");
|
|
6
6
|
const node_path_1 = require("node:path");
|
|
7
7
|
const common_1 = require("./common");
|
|
8
|
+
const guards_1 = require("./internal/guards");
|
|
9
|
+
const plain_object_1 = require("./internal/plain-object");
|
|
8
10
|
const output_1 = require("./output");
|
|
9
11
|
const verify_source_result_1 = require("./verify-source-result");
|
|
10
12
|
const DMS_PACKAGE = "@antelopejs/dms";
|
|
11
13
|
const DMS_CORE_LAYER_PACKAGE = "@antelopejs/dms-frontend-vue";
|
|
12
14
|
const CORE_LAYER_DIRECTORY = "frontend-vue";
|
|
13
15
|
const FRONTEND_ENTRY = "dms.frontend.ts";
|
|
14
|
-
const ADD_DEV_DEPENDENCY =
|
|
15
|
-
pnpm
|
|
16
|
-
npm
|
|
17
|
-
yarn
|
|
18
|
-
bun
|
|
19
|
-
|
|
16
|
+
const ADD_DEV_DEPENDENCY = new Map([
|
|
17
|
+
["pnpm", "pnpm add -D"],
|
|
18
|
+
["npm", "npm install -D"],
|
|
19
|
+
["yarn", "yarn add -D"],
|
|
20
|
+
["bun", "bun add -d"],
|
|
21
|
+
]);
|
|
20
22
|
const LOCKFILES = [
|
|
21
23
|
["pnpm-lock.yaml", "pnpm"],
|
|
22
24
|
["package-lock.json", "npm"],
|
|
@@ -24,16 +26,46 @@ const LOCKFILES = [
|
|
|
24
26
|
["bun.lock", "bun"],
|
|
25
27
|
["bun.lockb", "bun"],
|
|
26
28
|
];
|
|
29
|
+
function malformedVariable(variable, expected) {
|
|
30
|
+
return new common_1.UsageError({
|
|
31
|
+
title: `${variable} is not ${expected}`,
|
|
32
|
+
reason: `\`ajs dms verify-source\` starts the runner with ${variable} set to ${expected}.`,
|
|
33
|
+
});
|
|
34
|
+
}
|
|
35
|
+
function readTextList(variable) {
|
|
36
|
+
const value = JSON.parse(process.env[variable] ?? "[]");
|
|
37
|
+
if (Array.isArray(value) && value.every(guards_1.isText))
|
|
38
|
+
return value;
|
|
39
|
+
throw malformedVariable(variable, "a JSON list of paths");
|
|
40
|
+
}
|
|
41
|
+
function readTextRecord(variable) {
|
|
42
|
+
const value = JSON.parse(process.env[variable] ?? "{}");
|
|
43
|
+
if ((0, plain_object_1.isPlainObject)(value)) {
|
|
44
|
+
const entries = Object.entries(value);
|
|
45
|
+
if (entries.every((entry) => (0, guards_1.isText)(entry[1]))) {
|
|
46
|
+
return Object.fromEntries(entries);
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
throw malformedVariable(variable, "a JSON record of paths by package name");
|
|
50
|
+
}
|
|
27
51
|
function readSourceOptions() {
|
|
28
52
|
return {
|
|
29
53
|
projectDir: process.cwd(),
|
|
30
54
|
layerSource: process.env.DMS_LAYER_SOURCE || undefined,
|
|
31
|
-
moduleSources:
|
|
32
|
-
localPackages:
|
|
55
|
+
moduleSources: readTextList("DMS_MODULE_SOURCES"),
|
|
56
|
+
localPackages: readTextRecord("DMS_LOCAL_PACKAGES"),
|
|
33
57
|
};
|
|
34
58
|
}
|
|
35
59
|
function readManifest(packagePath) {
|
|
36
|
-
|
|
60
|
+
const parsed = JSON.parse((0, node_fs_1.readFileSync)(packagePath, "utf8"));
|
|
61
|
+
if (!(0, plain_object_1.isPlainObject)(parsed))
|
|
62
|
+
return {};
|
|
63
|
+
const { name, version, packageManager } = parsed;
|
|
64
|
+
return {
|
|
65
|
+
name: (0, guards_1.isText)(name) ? name : undefined,
|
|
66
|
+
version: (0, guards_1.isText)(version) ? version : undefined,
|
|
67
|
+
packageManager: (0, guards_1.isText)(packageManager) ? packageManager : undefined,
|
|
68
|
+
};
|
|
37
69
|
}
|
|
38
70
|
function readLayer(root) {
|
|
39
71
|
const packagePath = (0, node_path_1.join)(root, "package.json");
|
|
@@ -68,7 +100,7 @@ function packageManager(projectDir) {
|
|
|
68
100
|
const packagePath = (0, node_path_1.join)(directory, "package.json");
|
|
69
101
|
if ((0, node_fs_1.existsSync)(packagePath)) {
|
|
70
102
|
const [name] = (readManifest(packagePath).packageManager ?? "").split("@");
|
|
71
|
-
if (name && ADD_DEV_DEPENDENCY
|
|
103
|
+
if (name && ADD_DEV_DEPENDENCY.has(name))
|
|
72
104
|
return name;
|
|
73
105
|
const lockfile = LOCKFILES.find(([file]) => (0, node_fs_1.existsSync)((0, node_path_1.join)(directory, file)));
|
|
74
106
|
if (lockfile)
|
|
@@ -79,7 +111,7 @@ function packageManager(projectDir) {
|
|
|
79
111
|
}
|
|
80
112
|
}
|
|
81
113
|
function addDmsCommand(projectDir, version = "") {
|
|
82
|
-
return `${ADD_DEV_DEPENDENCY
|
|
114
|
+
return `${ADD_DEV_DEPENDENCY.get(packageManager(projectDir))} ${DMS_PACKAGE}${version}`;
|
|
83
115
|
}
|
|
84
116
|
function describeDirectory(directory) {
|
|
85
117
|
return (0, node_path_1.resolve)(directory) === process.cwd()
|
|
@@ -107,6 +139,12 @@ function installedCoreLayer(projectDir) {
|
|
|
107
139
|
],
|
|
108
140
|
});
|
|
109
141
|
const { version } = readManifest(packagePath);
|
|
142
|
+
if (version === undefined)
|
|
143
|
+
throw new common_1.UsageError({
|
|
144
|
+
title: `${DMS_PACKAGE} has no text "version" in its package.json`,
|
|
145
|
+
reason: `${(0, output_1.showPath)(packagePath)} has no "version" that is text, and the verification names the installed ${DMS_PACKAGE} by it.`,
|
|
146
|
+
fixes: [`Reinstall it: ${addDmsCommand(projectDir, "@latest")}`],
|
|
147
|
+
});
|
|
110
148
|
const root = (0, node_path_1.join)((0, node_path_1.dirname)(packagePath), CORE_LAYER_DIRECTORY);
|
|
111
149
|
const layer = (0, node_fs_1.existsSync)((0, node_path_1.join)(root, "package.json"))
|
|
112
150
|
? readLayer(root)
|
package/dist/workspace-setup.js
CHANGED
|
@@ -95,7 +95,7 @@ async function installDeps(workspaceDir, mapLine, extraArgs = []) {
|
|
|
95
95
|
task.succeed(progress.doneLabel);
|
|
96
96
|
}
|
|
97
97
|
catch (error) {
|
|
98
|
-
(0, output_1.endInterruptedTask)(task, error, "Dependency install stopped");
|
|
98
|
+
(0, output_1.endInterruptedTask)(task, error instanceof cancellation_1.CancelledError ? error : undefined, "Dependency install stopped");
|
|
99
99
|
throw error;
|
|
100
100
|
}
|
|
101
101
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@antelopejs/dms-frontend",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.10.0",
|
|
4
4
|
"description": "Frontend-agnostic loader for AntelopeJS DMS, shipping the Vue 3 renderer (Vite, Inertia, SSR)",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"antelope",
|
|
@@ -76,9 +76,7 @@
|
|
|
76
76
|
"prepare": "if git rev-parse --is-inside-work-tree >/dev/null 2>&1 && command -v lefthook >/dev/null 2>&1; then lefthook install; fi",
|
|
77
77
|
"checks": "antelopejs-checks",
|
|
78
78
|
"size": "antelopejs-check-size",
|
|
79
|
-
"ratchet": "antelopejs-ratchet",
|
|
80
79
|
"sync": "antelopejs-sync",
|
|
81
|
-
"ratchet:update": "antelopejs-ratchet --update",
|
|
82
80
|
"sync:check": "antelopejs-sync --check",
|
|
83
81
|
"format:check": "oxfmt --check .",
|
|
84
82
|
"suppressions": "antelopejs-suppressions",
|
|
@@ -92,7 +90,8 @@
|
|
|
92
90
|
"semver": "^7.7.4"
|
|
93
91
|
},
|
|
94
92
|
"devDependencies": {
|
|
95
|
-
"@antelopejs/core": "^1.
|
|
93
|
+
"@antelopejs/core": "^1.20.0",
|
|
94
|
+
"@antelopejs/interface-dms": "^0.10.0",
|
|
96
95
|
"@antelopejs/tooling-configs": "^0.2.17",
|
|
97
96
|
"@iconify/utils": "^3.1.0",
|
|
98
97
|
"@iconify/vue": "^5.0.1",
|
|
@@ -141,6 +140,9 @@
|
|
|
141
140
|
}
|
|
142
141
|
},
|
|
143
142
|
"antelopeJs": {
|
|
143
|
+
"docs": {
|
|
144
|
+
"standard": true
|
|
145
|
+
},
|
|
144
146
|
"skills": [
|
|
145
147
|
"./skills"
|
|
146
148
|
]
|
|
@@ -40,7 +40,7 @@ tags: [frontend, cli, renderer, vue, inertia, ssr, deployment, dms-module]
|
|
|
40
40
|
|
|
41
41
|
| Task | Page |
|
|
42
42
|
| --- | --- |
|
|
43
|
-
|
|
|
43
|
+
| Where to read the renderer releases a DMS release runs on | `/docs/frontend/introduction#compatibility` |
|
|
44
44
|
| One command in depth | `/docs/frontend/cli/<command>` |
|
|
45
45
|
| Variables and secrets | `/docs/frontend/configuration` |
|
|
46
46
|
| Module files and SDK | `/docs/frontend/frontend-modules/anatomy`, then `server-routes`, `sessions-and-sign-in`, `route-rules`, `emails` |
|
|
@@ -4,11 +4,11 @@ import { useToast } from "@nuxt/ui/composables/useToast";
|
|
|
4
4
|
import { useAppConfig } from "@nuxt/ui/runtime/vue/composables/useAppConfig";
|
|
5
5
|
import { en as uiEn, fr as uiFr } from "@nuxt/ui/locale";
|
|
6
6
|
import ui from "@nuxt/ui/vue-plugin";
|
|
7
|
+
import { router } from "@inertiajs/vue3";
|
|
7
8
|
import { useHead } from "@unhead/vue";
|
|
8
9
|
import type { VueHeadClient } from "@unhead/vue/types";
|
|
9
10
|
import type { IconifyIcon } from "@iconify/vue";
|
|
10
11
|
import { parse } from "devalue";
|
|
11
|
-
import type { FetchOptions } from "ofetch";
|
|
12
12
|
import {
|
|
13
13
|
type App,
|
|
14
14
|
type Component,
|
|
@@ -27,6 +27,7 @@ import {
|
|
|
27
27
|
type DmsLocaleLoader,
|
|
28
28
|
type DmsPageProps,
|
|
29
29
|
type DmsPayload,
|
|
30
|
+
type DmsServerFetch,
|
|
30
31
|
type DmsUser,
|
|
31
32
|
createDmsFrontendRuntime,
|
|
32
33
|
dmsPageSuspenseProps,
|
|
@@ -64,7 +65,13 @@ import {
|
|
|
64
65
|
supportedLocales,
|
|
65
66
|
syncLocaleMessages,
|
|
66
67
|
} from "./locales.generated";
|
|
67
|
-
import {
|
|
68
|
+
import {
|
|
69
|
+
type DmsMessageSubset,
|
|
70
|
+
SSR_MESSAGES_ID,
|
|
71
|
+
recordMessageReads,
|
|
72
|
+
} from "./message-subset";
|
|
73
|
+
import { NETWORK_ERROR_KEYS, showNetworkErrors } from "./network-error";
|
|
74
|
+
import { loadIconBundle } from "./icon-bundle";
|
|
68
75
|
import { createUiAppConfigMerger } from "./ui-app-config";
|
|
69
76
|
|
|
70
77
|
export interface DmsInertiaSetupProps {
|
|
@@ -86,17 +93,20 @@ export interface DmsAppOptions {
|
|
|
86
93
|
}
|
|
87
94
|
|
|
88
95
|
export interface DmsConfiguredApp {
|
|
89
|
-
/**
|
|
96
|
+
/**
|
|
97
|
+
* The locale of the catalog the app shows its messages from now, none when
|
|
98
|
+
* the page is served the messages it read alone.
|
|
99
|
+
*/
|
|
90
100
|
catalogLocale: () => string | undefined;
|
|
101
|
+
/**
|
|
102
|
+
* On the server, for a page of a visitor who is not signed in: the messages
|
|
103
|
+
* the render read, which the page carries instead of the whole catalog.
|
|
104
|
+
*/
|
|
105
|
+
messageSubset: () => DmsMessageSubset | undefined;
|
|
91
106
|
mounted: () => Promise<void>;
|
|
92
107
|
runtime: ReturnType<typeof createDmsFrontendRuntime>;
|
|
93
108
|
}
|
|
94
109
|
|
|
95
|
-
export type DmsServerFetch = (
|
|
96
|
-
request: string,
|
|
97
|
-
options?: FetchOptions,
|
|
98
|
-
) => Promise<unknown>;
|
|
99
|
-
|
|
100
110
|
export const DMS_PAYLOAD_ID = "dms-payload";
|
|
101
111
|
export const SSR_ASYNC_COMPONENTS_ID = "dms-ssr-async-components";
|
|
102
112
|
const DEFAULT_LOCALE = "en";
|
|
@@ -141,9 +151,7 @@ drawLoadedIconsAtOnce();
|
|
|
141
151
|
function createBrowserRuntime(
|
|
142
152
|
serverFetch?: DmsServerFetch,
|
|
143
153
|
): DmsConfiguredApp["runtime"] {
|
|
144
|
-
const runtime = createDmsFrontendRuntime(
|
|
145
|
-
serverFetch as typeof import("ofetch").ofetch,
|
|
146
|
-
);
|
|
154
|
+
const runtime = createDmsFrontendRuntime(serverFetch);
|
|
147
155
|
// Nothing when the server did not render the page: no hydration then.
|
|
148
156
|
const payload = readServerRendered<DmsPayload | undefined>(
|
|
149
157
|
DMS_PAYLOAD_ID,
|
|
@@ -238,8 +246,18 @@ const DmsPersistentInertiaPage = Object.assign(DmsInertiaPage, {
|
|
|
238
246
|
layout: DmsPersistentLayout,
|
|
239
247
|
});
|
|
240
248
|
|
|
241
|
-
|
|
242
|
-
|
|
249
|
+
/**
|
|
250
|
+
* What a visit waits for before it shows its page, set when the first one
|
|
251
|
+
* starts from a page that began without what the pages after it read: a
|
|
252
|
+
* subset of the catalog, the icon bundle. Inertia also resolves the first page
|
|
253
|
+
* once the app is set up, which must not wait for it.
|
|
254
|
+
*/
|
|
255
|
+
let completeInitialAssets: (() => Promise<void>) | undefined;
|
|
256
|
+
|
|
257
|
+
export function resolveDmsInertiaPage(): Component | Promise<Component> {
|
|
258
|
+
return completeInitialAssets
|
|
259
|
+
? completeInitialAssets().then(() => DmsPersistentInertiaPage)
|
|
260
|
+
: DmsPersistentInertiaPage;
|
|
243
261
|
}
|
|
244
262
|
|
|
245
263
|
function renderDmsPage(
|
|
@@ -310,13 +328,66 @@ export async function configureDmsApp(
|
|
|
310
328
|
userLocale(options.initialPageProps.user) ??
|
|
311
329
|
readDmsLocaleCookie(runtime, supportedLocales) ??
|
|
312
330
|
DEFAULT_LOCALE;
|
|
313
|
-
const
|
|
331
|
+
const anonymous = !options.initialPageProps.user;
|
|
332
|
+
const subset = anonymous
|
|
333
|
+
? readServerRendered<DmsMessageSubset | undefined>(
|
|
334
|
+
SSR_MESSAGES_ID,
|
|
335
|
+
undefined,
|
|
336
|
+
)
|
|
337
|
+
: undefined;
|
|
338
|
+
const started = subset?.locale === locale ? subset : undefined;
|
|
339
|
+
const messages = started?.messages ?? (await loadLocaleMessages(locale));
|
|
340
|
+
const recorder =
|
|
341
|
+
anonymous && typeof window === "undefined"
|
|
342
|
+
? recordMessageReads(messages)
|
|
343
|
+
: undefined;
|
|
344
|
+
let loading: Promise<void> | undefined;
|
|
314
345
|
const i18n = createI18n({
|
|
315
346
|
legacy: false,
|
|
316
347
|
locale,
|
|
317
348
|
fallbackLocale: DEFAULT_LOCALE,
|
|
318
|
-
messages:
|
|
349
|
+
messages: started
|
|
350
|
+
? { [locale]: messages }
|
|
351
|
+
: { ...localeMessages, [locale]: recorder?.messages ?? messages },
|
|
352
|
+
...(started && {
|
|
353
|
+
missing: (_locale: string, key: string) => {
|
|
354
|
+
if (i18n.global.locale.value !== locale) return key;
|
|
355
|
+
void loadCompleteCatalog();
|
|
356
|
+
return "";
|
|
357
|
+
},
|
|
358
|
+
}),
|
|
319
359
|
});
|
|
360
|
+
function loadCompleteCatalog(): Promise<void> {
|
|
361
|
+
if (i18n.global.locale.value !== locale) return Promise.resolve();
|
|
362
|
+
loading ??= loadLocaleMessages(locale).then(
|
|
363
|
+
(complete) => {
|
|
364
|
+
i18n.global.setLocaleMessage(locale, complete);
|
|
365
|
+
i18n.global.setMissingHandler(null);
|
|
366
|
+
},
|
|
367
|
+
() => {
|
|
368
|
+
loading = undefined;
|
|
369
|
+
},
|
|
370
|
+
);
|
|
371
|
+
return loading;
|
|
372
|
+
}
|
|
373
|
+
if (anonymous && typeof window !== "undefined") {
|
|
374
|
+
const complete = async (): Promise<void> => {
|
|
375
|
+
await Promise.all([
|
|
376
|
+
started ? loadCompleteCatalog() : undefined,
|
|
377
|
+
loadIconBundle(),
|
|
378
|
+
]);
|
|
379
|
+
};
|
|
380
|
+
router.on("before", () => {
|
|
381
|
+
completeInitialAssets = complete;
|
|
382
|
+
void complete();
|
|
383
|
+
});
|
|
384
|
+
for (const type of ["pointerdown", "keydown", "focusin", "touchstart"])
|
|
385
|
+
addEventListener(type, () => void loadIconBundle(), {
|
|
386
|
+
once: true,
|
|
387
|
+
capture: true,
|
|
388
|
+
});
|
|
389
|
+
}
|
|
390
|
+
if (recorder) for (const key of NETWORK_ERROR_KEYS) i18n.global.te(key);
|
|
320
391
|
// A translation changed in a layer during development reaches the page
|
|
321
392
|
// without a reload. A server render imports the new catalogs instead.
|
|
322
393
|
if (typeof window !== "undefined") syncLocaleMessages(i18n.global);
|
|
@@ -339,6 +410,7 @@ export async function configureDmsApp(
|
|
|
339
410
|
// rendered: the page, its layout, and the components the render reached.
|
|
340
411
|
// Hydration then adopts the server-rendered markup instead of discarding it.
|
|
341
412
|
await Promise.all([
|
|
413
|
+
anonymous ? undefined : loadIconBundle(),
|
|
342
414
|
preloadDmsPage(options.initialPageProps),
|
|
343
415
|
resolveDmsAsyncComponents(
|
|
344
416
|
readServerRendered<string[]>(SSR_ASYNC_COMPONENTS_ID, []),
|
|
@@ -352,7 +424,9 @@ export async function configureDmsApp(
|
|
|
352
424
|
supportedLocales,
|
|
353
425
|
);
|
|
354
426
|
return {
|
|
355
|
-
catalogLocale: () =>
|
|
427
|
+
catalogLocale: () =>
|
|
428
|
+
recorder ? undefined : catalogLocale(i18n.global.locale.value),
|
|
429
|
+
messageSubset: () => recorder && { locale, messages: recorder.subset() },
|
|
356
430
|
mounted,
|
|
357
431
|
runtime,
|
|
358
432
|
};
|