@antelopejs/dms-frontend 0.8.0 → 0.9.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/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 +9 -2
- package/templates/vue/app-runtime.ts +8 -1
- package/templates/vue/entry-weight.mjs +132 -0
- 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 +32 -1
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.InvalidLocaleFileError = void 0;
|
|
4
|
+
exports.readLocaleFile = readLocaleFile;
|
|
5
|
+
const node_fs_1 = require("node:fs");
|
|
6
|
+
const message_compiler_1 = require("@intlify/message-compiler");
|
|
7
|
+
class InvalidLocaleFileError extends Error {
|
|
8
|
+
constructor(path, reason) {
|
|
9
|
+
super(`Invalid locale file ${path}: ${reason}`);
|
|
10
|
+
this.path = path;
|
|
11
|
+
this.reason = reason;
|
|
12
|
+
this.name = "InvalidLocaleFileError";
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
exports.InvalidLocaleFileError = InvalidLocaleFileError;
|
|
16
|
+
const LITERAL_ESCAPES = {
|
|
17
|
+
"@": "{'@'}",
|
|
18
|
+
"{": "{'{'}",
|
|
19
|
+
"}": "{'}'}",
|
|
20
|
+
"|": "{'|'}",
|
|
21
|
+
};
|
|
22
|
+
function reservedCharacterAt(message, offset) {
|
|
23
|
+
for (let index = Math.min(offset, message.length - 1); index >= 0; index--) {
|
|
24
|
+
if (message[index] in LITERAL_ESCAPES)
|
|
25
|
+
return message[index];
|
|
26
|
+
}
|
|
27
|
+
return null;
|
|
28
|
+
}
|
|
29
|
+
function describeProblem({ key, message, error }) {
|
|
30
|
+
const where = `${key}: ${error.message} in ${JSON.stringify(message)}`;
|
|
31
|
+
const character = reservedCharacterAt(message, error.location?.start.offset ?? message.length);
|
|
32
|
+
if (!character) {
|
|
33
|
+
return `${where}. Write ${Object.values(LITERAL_ESCAPES).join(", ")} for a literal ${Object.keys(LITERAL_ESCAPES).join(", ")}.`;
|
|
34
|
+
}
|
|
35
|
+
const fixed = message.replaceAll(character, LITERAL_ESCAPES[character]);
|
|
36
|
+
return `${where}. Write a literal ${character} as ${LITERAL_ESCAPES[character]}: ${JSON.stringify(fixed)}.`;
|
|
37
|
+
}
|
|
38
|
+
function firstCompileError(message) {
|
|
39
|
+
let first;
|
|
40
|
+
(0, message_compiler_1.baseCompile)(message, {
|
|
41
|
+
location: true,
|
|
42
|
+
onError: (error) => {
|
|
43
|
+
first ??= error;
|
|
44
|
+
},
|
|
45
|
+
});
|
|
46
|
+
return first;
|
|
47
|
+
}
|
|
48
|
+
function messageProblems(value, key) {
|
|
49
|
+
if (typeof value === "string") {
|
|
50
|
+
const error = firstCompileError(value);
|
|
51
|
+
return error ? [{ key, message: value, error }] : [];
|
|
52
|
+
}
|
|
53
|
+
if (typeof value !== "object" || value === null)
|
|
54
|
+
return [];
|
|
55
|
+
return Object.entries(value).flatMap(([child, nested]) => messageProblems(nested, key ? `${key}.${child}` : child));
|
|
56
|
+
}
|
|
57
|
+
function readLocaleFile(path) {
|
|
58
|
+
let value;
|
|
59
|
+
try {
|
|
60
|
+
value = JSON.parse((0, node_fs_1.readFileSync)(path, "utf8"));
|
|
61
|
+
}
|
|
62
|
+
catch (error) {
|
|
63
|
+
const reason = error instanceof Error ? error.message : String(error);
|
|
64
|
+
throw new InvalidLocaleFileError(path, reason);
|
|
65
|
+
}
|
|
66
|
+
const problems = messageProblems(value, "");
|
|
67
|
+
if (problems.length) {
|
|
68
|
+
const reason = [
|
|
69
|
+
`${problems.length} message(s) vue-i18n cannot compile:`,
|
|
70
|
+
...problems.map(describeProblem),
|
|
71
|
+
].join("\n");
|
|
72
|
+
throw new InvalidLocaleFileError(path, reason);
|
|
73
|
+
}
|
|
74
|
+
return value;
|
|
75
|
+
}
|
package/dist/manifest.js
CHANGED
|
@@ -197,6 +197,8 @@ async function fetchManifest(backendUrl, clientUrl, bootstrapSecret) {
|
|
|
197
197
|
const resolved = { modules: manifest.modules };
|
|
198
198
|
if (manifest.settling)
|
|
199
199
|
resolved.settling = true;
|
|
200
|
+
if (manifest.originRewrites)
|
|
201
|
+
resolved.originRewrites = manifest.originRewrites;
|
|
200
202
|
return resolved;
|
|
201
203
|
}
|
|
202
204
|
const MANIFEST_CACHE_FILE = ".manifest-cache.json";
|
package/dist/private-values.d.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { FrontendModuleOptions } from "./workspace";
|
|
2
2
|
export declare function emptiedOptions(options: FrontendModuleOptions): FrontendModuleOptions;
|
|
3
|
+
export declare function filledOptions(shape: FrontendModuleOptions, values: FrontendModuleOptions | undefined): FrontendModuleOptions;
|
|
3
4
|
export declare const PRIVATE_OPTIONS_VARIABLE = "DMS_PRIVATE_OPTIONS";
|
|
4
5
|
export interface UnfilledPrivateValue {
|
|
5
6
|
module: string;
|
package/dist/private-values.js
CHANGED
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.PRIVATE_OPTIONS_VARIABLE = void 0;
|
|
4
4
|
exports.emptiedOptions = emptiedOptions;
|
|
5
|
+
exports.filledOptions = filledOptions;
|
|
5
6
|
exports.parsePrivateOptions = parsePrivateOptions;
|
|
6
7
|
exports.unfilledPrivateValues = unfilledPrivateValues;
|
|
7
8
|
const config_1 = require("./config");
|
|
@@ -17,6 +18,21 @@ function emptied(value) {
|
|
|
17
18
|
function emptiedOptions(options) {
|
|
18
19
|
return Object.fromEntries(Object.entries(options).map(([key, value]) => [key, emptied(value)]));
|
|
19
20
|
}
|
|
21
|
+
function filled(shape, value) {
|
|
22
|
+
if (typeof shape === "string")
|
|
23
|
+
return typeof value === "string" ? value : "";
|
|
24
|
+
if (Array.isArray(shape))
|
|
25
|
+
return shape.map((item, index) => filled(item, Array.isArray(value) ? value[index] : undefined));
|
|
26
|
+
if (shape !== null && typeof shape === "object")
|
|
27
|
+
return filledOptions(shape, isOptions(value) ? value : undefined);
|
|
28
|
+
return shape;
|
|
29
|
+
}
|
|
30
|
+
function filledOptions(shape, values) {
|
|
31
|
+
return Object.fromEntries(Object.entries(shape).map(([key, value]) => [
|
|
32
|
+
key,
|
|
33
|
+
filled(value, values?.[key]),
|
|
34
|
+
]));
|
|
35
|
+
}
|
|
20
36
|
exports.PRIVATE_OPTIONS_VARIABLE = "DMS_PRIVATE_OPTIONS";
|
|
21
37
|
function isOptions(value) {
|
|
22
38
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { CliError, type Ui } from "@antelopejs/core/cli";
|
|
2
|
+
import { type ProjectFrontendModule } from "./project-modules";
|
|
3
|
+
type MessageArgs = Parameters<Ui["message"]>;
|
|
4
|
+
export type ProjectModulesReply = {
|
|
5
|
+
messages: MessageArgs[];
|
|
6
|
+
} & ({
|
|
7
|
+
modules: ProjectFrontendModule[];
|
|
8
|
+
} | {
|
|
9
|
+
problem: CliError["problem"];
|
|
10
|
+
} | {
|
|
11
|
+
failure: string;
|
|
12
|
+
});
|
|
13
|
+
export {};
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const cli_1 = require("@antelopejs/core/cli");
|
|
4
|
+
const project_modules_1 = require("./project-modules");
|
|
5
|
+
process.once("message", async (options) => {
|
|
6
|
+
const messages = [];
|
|
7
|
+
(0, cli_1.getProcessUi)().message = (...args) => {
|
|
8
|
+
messages.push(args);
|
|
9
|
+
};
|
|
10
|
+
let reply;
|
|
11
|
+
try {
|
|
12
|
+
reply = { messages, modules: await (0, project_modules_1.readProjectFrontendModules)(options) };
|
|
13
|
+
}
|
|
14
|
+
catch (error) {
|
|
15
|
+
reply =
|
|
16
|
+
error instanceof cli_1.CliError
|
|
17
|
+
? { messages, problem: error.problem }
|
|
18
|
+
: {
|
|
19
|
+
messages,
|
|
20
|
+
failure: error instanceof Error
|
|
21
|
+
? (error.stack ?? error.message)
|
|
22
|
+
: String(error),
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
process.send?.(reply, () => process.disconnect());
|
|
26
|
+
});
|
package/dist/project-modules.js
CHANGED
|
@@ -48,10 +48,7 @@ const FRONTEND_SOURCE_DIR = "frontend-vue";
|
|
|
48
48
|
const RENDERER = { name: "vue", version: "3" };
|
|
49
49
|
const CONFIG_VAR_PATTERN = /\$\{@([^.}]+)\.([^}]+)\}/g;
|
|
50
50
|
const PURE_CONFIG_VAR = /^\$\{@([^.}]+)\.([^}]+)\}$/;
|
|
51
|
-
const
|
|
52
|
-
const DEFAULT_API_PORT = 80;
|
|
53
|
-
const LOOPBACK_HOST = "127.0.0.1";
|
|
54
|
-
const WILDCARD_HOSTS = new Set(["0.0.0.0", "::", "[::]"]);
|
|
51
|
+
const MODULE_MANIFEST_FILE = "antelope.module.json";
|
|
55
52
|
const NODE_FILE_SYSTEM = {
|
|
56
53
|
readFile: (path) => fsPromises.readFile(path),
|
|
57
54
|
readFileString: (path, encoding = "utf-8") => fsPromises.readFile(path, encoding),
|
|
@@ -67,48 +64,79 @@ const NODE_FILE_SYSTEM = {
|
|
|
67
64
|
copyFile: (src, dest) => fsPromises.copyFile(src, dest),
|
|
68
65
|
rename: (oldPath, newPath) => fsPromises.rename(oldPath, newPath),
|
|
69
66
|
};
|
|
70
|
-
function
|
|
71
|
-
const
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
return
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
.
|
|
67
|
+
function readAntelopeManifest(folder) {
|
|
68
|
+
const dedicated = (0, node_path_1.join)(folder, MODULE_MANIFEST_FILE);
|
|
69
|
+
if ((0, node_fs_1.existsSync)(dedicated))
|
|
70
|
+
return JSON.parse((0, node_fs_1.readFileSync)(dedicated, "utf-8"));
|
|
71
|
+
return (0, layers_1.readLayerPackage)(folder)?.antelopeJs;
|
|
72
|
+
}
|
|
73
|
+
function warnUndeclaredConfigVars(id) {
|
|
74
|
+
(0, cli_1.getProcessUi)().message("warn", `${id} declares no antelopeJs.configVarsFile in its package.json: the \${@${id}.<VAR>} references are left unresolved`, { detail: `Upgrade ${id} to a version that declares it` });
|
|
75
|
+
}
|
|
76
|
+
function configVarsProvider(id, folder) {
|
|
77
|
+
const manifest = readAntelopeManifest(folder);
|
|
78
|
+
const file = manifest?.configVarsFile;
|
|
79
|
+
if (typeof file !== "string") {
|
|
80
|
+
warnUndeclaredConfigVars(id);
|
|
81
|
+
return undefined;
|
|
82
|
+
}
|
|
83
|
+
const { configVars } = require((0, node_path_1.join)(folder, file));
|
|
84
|
+
if (typeof configVars !== "function") {
|
|
85
|
+
throw new cli_1.CliError({
|
|
86
|
+
title: `${id}: ${file} exports no configVars function`,
|
|
87
|
+
fixes: [`Export configVars(config) from ${file}`],
|
|
88
|
+
});
|
|
89
|
+
}
|
|
90
|
+
const declared = Array.isArray(manifest?.configVars)
|
|
91
|
+
? manifest.configVars.filter((name) => typeof name === "string")
|
|
92
|
+
: [];
|
|
93
|
+
return { declared, configVars };
|
|
94
|
+
}
|
|
95
|
+
function computeConfigVars(id, provider, config) {
|
|
96
|
+
let computed;
|
|
97
|
+
try {
|
|
98
|
+
computed = provider.configVars(config) ?? {};
|
|
99
|
+
}
|
|
100
|
+
catch (error) {
|
|
101
|
+
throw new cli_1.CliError({
|
|
102
|
+
title: `${id}: could not compute its config variables`,
|
|
103
|
+
reason: error instanceof Error ? error.message : String(error),
|
|
104
|
+
}, { cause: error });
|
|
105
|
+
}
|
|
106
|
+
return Object.fromEntries(provider.declared
|
|
107
|
+
.filter((name) => name in computed)
|
|
108
|
+
.map((name) => [name, computed[name]]));
|
|
109
|
+
}
|
|
110
|
+
function configVarLookup(modules) {
|
|
111
|
+
const published = new Map();
|
|
112
|
+
const lookup = (id, name) => {
|
|
113
|
+
if (!published.has(id)) {
|
|
114
|
+
published.set(id, undefined);
|
|
115
|
+
const module = modules[id];
|
|
116
|
+
const provider = module?.folder && configVarsProvider(id, module.folder);
|
|
117
|
+
if (provider)
|
|
118
|
+
published.set(id, computeConfigVars(id, provider, substituteConfigVars(module.config, lookup)));
|
|
119
|
+
}
|
|
120
|
+
return published.get(id)?.[name];
|
|
121
|
+
};
|
|
122
|
+
return lookup;
|
|
95
123
|
}
|
|
96
|
-
function substituteConfigVars(value,
|
|
124
|
+
function substituteConfigVars(value, lookup) {
|
|
97
125
|
if (typeof value === "string") {
|
|
98
126
|
const pure = value.match(PURE_CONFIG_VAR);
|
|
99
127
|
if (pure)
|
|
100
|
-
return
|
|
128
|
+
return lookup(pure[1], pure[2]);
|
|
101
129
|
return value.replace(CONFIG_VAR_PATTERN, (token, module, name) => {
|
|
102
|
-
const resolved =
|
|
130
|
+
const resolved = lookup(module, name);
|
|
103
131
|
return resolved === undefined ? token : String(resolved);
|
|
104
132
|
});
|
|
105
133
|
}
|
|
106
134
|
if (Array.isArray(value))
|
|
107
|
-
return value.map((item) => substituteConfigVars(item,
|
|
135
|
+
return value.map((item) => substituteConfigVars(item, lookup));
|
|
108
136
|
if (value && typeof value === "object") {
|
|
109
137
|
return Object.fromEntries(Object.entries(value).map(([key, item]) => [
|
|
110
138
|
key,
|
|
111
|
-
substituteConfigVars(item,
|
|
139
|
+
substituteConfigVars(item, lookup),
|
|
112
140
|
]));
|
|
113
141
|
}
|
|
114
142
|
return value;
|
|
@@ -127,12 +155,19 @@ async function loadProjectConfig(projectDir, environment) {
|
|
|
127
155
|
fixes: ["Run from the project root, or pass --project <dir>"],
|
|
128
156
|
}, { cause: error });
|
|
129
157
|
}
|
|
130
|
-
const
|
|
158
|
+
const located = Object.fromEntries(Object.entries(loaded.modules).map(([id, module]) => [
|
|
159
|
+
id,
|
|
160
|
+
{
|
|
161
|
+
...module,
|
|
162
|
+
folder: moduleFolder(projectDir, loaded.cacheFolder, module.source),
|
|
163
|
+
},
|
|
164
|
+
]));
|
|
165
|
+
const lookup = configVarLookup(located);
|
|
131
166
|
return {
|
|
132
167
|
cacheFolder: loaded.cacheFolder,
|
|
133
|
-
modules: Object.fromEntries(Object.entries(
|
|
168
|
+
modules: Object.fromEntries(Object.entries(located).map(([id, module]) => [
|
|
134
169
|
id,
|
|
135
|
-
{ ...module, config: substituteConfigVars(module.config,
|
|
170
|
+
{ ...module, config: substituteConfigVars(module.config, lookup) },
|
|
136
171
|
])),
|
|
137
172
|
};
|
|
138
173
|
}
|
|
@@ -214,7 +249,7 @@ async function readProjectFrontendModules(options) {
|
|
|
214
249
|
const config = await loadProjectConfig(projectDir, options.environment);
|
|
215
250
|
const context = { dev: !!options.dev };
|
|
216
251
|
const registrations = Object.entries(config.modules).flatMap(([id, module]) => {
|
|
217
|
-
const folder =
|
|
252
|
+
const folder = module.folder;
|
|
218
253
|
if (!folder) {
|
|
219
254
|
warnMissingModule(id);
|
|
220
255
|
return [];
|
package/dist/workspace-setup.js
CHANGED
|
@@ -15,6 +15,7 @@ const cli_1 = require("@antelopejs/core/cli");
|
|
|
15
15
|
const child_output_1 = require("./child-output");
|
|
16
16
|
const layers_1 = require("./layers");
|
|
17
17
|
const manifest_1 = require("./manifest");
|
|
18
|
+
const dev_modules_1 = require("./dev-modules");
|
|
18
19
|
const project_modules_1 = require("./project-modules");
|
|
19
20
|
const manifest_secrets_1 = require("./manifest-secrets");
|
|
20
21
|
const renderer_range_1 = require("./renderer-range");
|
|
@@ -23,6 +24,7 @@ const workspace_1 = require("./workspace");
|
|
|
23
24
|
const cancellation_1 = require("./cancellation");
|
|
24
25
|
const output_1 = require("./output");
|
|
25
26
|
const config_1 = require("./config");
|
|
27
|
+
const locale_files_1 = require("./locale-files");
|
|
26
28
|
const WORKSPACE_PNPM_CONFIG = "pnpm-workspace.yaml";
|
|
27
29
|
const WORKSPACE_PATCHES_DIR = "patches";
|
|
28
30
|
function workspaceInstallInputs(workspaceDir) {
|
|
@@ -223,10 +225,33 @@ async function projectLayers(project) {
|
|
|
223
225
|
secrets: { published: {}, conflicts: [] },
|
|
224
226
|
};
|
|
225
227
|
}
|
|
226
|
-
|
|
228
|
+
function servedManifest(opts, backendUrl, workspaceDir) {
|
|
227
229
|
const { offline, clientUrl, bootstrapSecret } = opts;
|
|
228
230
|
const cacheUse = offline ? "offline" : "fallback";
|
|
229
|
-
|
|
231
|
+
return (0, manifest_1.resolveManifest)(workspaceDir, backendUrl, cacheUse, {
|
|
232
|
+
clientUrl,
|
|
233
|
+
bootstrapSecret,
|
|
234
|
+
});
|
|
235
|
+
}
|
|
236
|
+
async function projectLayersInDev(opts, project, backendUrl, workspaceDir) {
|
|
237
|
+
const { manifest, fromCache, fetchedAt } = await servedManifest(opts, backendUrl, workspaceDir);
|
|
238
|
+
const modules = await (0, dev_modules_1.readProjectModulesFresh)({ ...project, dev: true });
|
|
239
|
+
const layers = (0, dev_modules_1.withBackendValues)(modules, manifest);
|
|
240
|
+
(0, dev_modules_1.warnModuleDrift)(layers, manifest);
|
|
241
|
+
return {
|
|
242
|
+
fromCache,
|
|
243
|
+
fetchedAt,
|
|
244
|
+
layers,
|
|
245
|
+
secrets: (0, manifest_secrets_1.collectManifestSecrets)(layers.map((layer) => ({
|
|
246
|
+
name: layer.name ?? "",
|
|
247
|
+
priority: layer.priority ?? 0,
|
|
248
|
+
privateOptions: layer.privateOptions,
|
|
249
|
+
}))),
|
|
250
|
+
};
|
|
251
|
+
}
|
|
252
|
+
async function servedLayers(opts, backendUrl, workspaceDir) {
|
|
253
|
+
const { bootstrapSecret } = opts;
|
|
254
|
+
const { manifest, fromCache, fetchedAt } = await servedManifest(opts, backendUrl, workspaceDir);
|
|
230
255
|
(0, layers_1.assertLayerPathsServed)(manifest.modules, backendUrl, {
|
|
231
256
|
bootstrapSecret,
|
|
232
257
|
cache: fromCache ? { fetchedAt } : undefined,
|
|
@@ -261,6 +286,8 @@ function workspaceSource(opts, workspaceDir) {
|
|
|
261
286
|
throw new Error("A build reads a project");
|
|
262
287
|
return projectLayers(opts.project);
|
|
263
288
|
}
|
|
289
|
+
if (opts.project)
|
|
290
|
+
return projectLayersInDev(opts, opts.project, requireDevBackend(opts), workspaceDir);
|
|
264
291
|
return servedLayers(opts, requireDevBackend(opts), workspaceDir);
|
|
265
292
|
}
|
|
266
293
|
async function setupWorkspace(opts) {
|
|
@@ -270,7 +297,7 @@ async function setupWorkspace(opts) {
|
|
|
270
297
|
(0, materialize_1.copyStaticTemplates)(workspaceDir);
|
|
271
298
|
(0, materialize_1.writeWorkspacePackageJson)(workspaceDir, layers);
|
|
272
299
|
(0, materialize_1.materializeLayers)(workspaceDir, layers);
|
|
273
|
-
(
|
|
300
|
+
writeDerivedOutputs(workspaceDir, layers);
|
|
274
301
|
(0, materialize_1.writeDmsMainCss)(workspaceDir, layers);
|
|
275
302
|
const layerPaths = layers.map((layer) => layer.path);
|
|
276
303
|
const shouldInstall = needsInstall(workspaceDir, layerPaths, opts.force);
|
|
@@ -287,3 +314,14 @@ async function setupWorkspace(opts) {
|
|
|
287
314
|
manifestSecrets: secrets,
|
|
288
315
|
};
|
|
289
316
|
}
|
|
317
|
+
function writeDerivedOutputs(workspaceDir, layers) {
|
|
318
|
+
try {
|
|
319
|
+
(0, materialize_1.writeFrontendModuleRegistry)(workspaceDir, layers);
|
|
320
|
+
}
|
|
321
|
+
catch (error) {
|
|
322
|
+
if (!(error instanceof locale_files_1.InvalidLocaleFileError))
|
|
323
|
+
throw error;
|
|
324
|
+
const showSource = (0, child_output_1.createPathMapper)(workspaceDir, layers);
|
|
325
|
+
throw new locale_files_1.InvalidLocaleFileError(showSource(error.path), error.reason);
|
|
326
|
+
}
|
|
327
|
+
}
|
package/dist/workspace.d.ts
CHANGED
|
@@ -27,14 +27,20 @@ export interface PublicEndpointDeclaration {
|
|
|
27
27
|
methods?: string[];
|
|
28
28
|
headers?: string[];
|
|
29
29
|
}
|
|
30
|
+
export interface OriginRewrite {
|
|
31
|
+
from: string;
|
|
32
|
+
to: string;
|
|
33
|
+
}
|
|
30
34
|
export interface Manifest {
|
|
31
35
|
modules: ManifestModule[];
|
|
32
36
|
settling?: boolean;
|
|
37
|
+
originRewrites?: OriginRewrite[];
|
|
33
38
|
}
|
|
34
39
|
export interface FrontendManifest {
|
|
35
40
|
version: number;
|
|
36
41
|
modules: ManifestModule[];
|
|
37
42
|
settling?: boolean;
|
|
43
|
+
originRewrites?: OriginRewrite[];
|
|
38
44
|
}
|
|
39
45
|
export interface ResolvedLayer {
|
|
40
46
|
path: string;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@antelopejs/dms-frontend",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.9.0",
|
|
4
4
|
"description": "Frontend-agnostic loader for AntelopeJS DMS, shipping the Vue 3 renderer (Vite, Inertia, SSR)",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"antelope",
|
|
@@ -73,6 +73,7 @@
|
|
|
73
73
|
"docs:reference:check": "tsx scripts/docs-reference.ts --check"
|
|
74
74
|
},
|
|
75
75
|
"dependencies": {
|
|
76
|
+
"@intlify/message-compiler": "^11.1.12",
|
|
76
77
|
"chokidar": "^4.0.3",
|
|
77
78
|
"commander": "^13.1.0",
|
|
78
79
|
"ignore": "^7.0.5",
|
|
@@ -89,6 +90,7 @@
|
|
|
89
90
|
"@unhead/vue": "^2.1.4",
|
|
90
91
|
"@vitejs/plugin-vue": "^6.0.6",
|
|
91
92
|
"@vue/server-renderer": "^3.5.25",
|
|
93
|
+
"@vueuse/core": "14.4.0",
|
|
92
94
|
"defu": "^6.1.4",
|
|
93
95
|
"devalue": "^5.9.4",
|
|
94
96
|
"knip": "^6.34.0",
|
|
@@ -99,7 +101,7 @@
|
|
|
99
101
|
"release-it": "^19.2.4",
|
|
100
102
|
"release-it-changelogen": "^0.1.0",
|
|
101
103
|
"rimraf": "^6.1.2",
|
|
102
|
-
"tsx": "^4.
|
|
104
|
+
"tsx": "^4.23.15",
|
|
103
105
|
"typescript": "^6.0.3",
|
|
104
106
|
"unplugin-auto-import": "^20.2.0",
|
|
105
107
|
"vite": "^8.3.4",
|
|
@@ -120,6 +122,11 @@
|
|
|
120
122
|
"node": ">=20.19.0"
|
|
121
123
|
},
|
|
122
124
|
"packageManager": "pnpm@10.6.5",
|
|
125
|
+
"pnpm": {
|
|
126
|
+
"patchedDependencies": {
|
|
127
|
+
"@vueuse/core@14.4.0": "templates/vue/patches/@vueuse__core@14.4.0.patch"
|
|
128
|
+
}
|
|
129
|
+
},
|
|
123
130
|
"antelopeJs": {
|
|
124
131
|
"skills": [
|
|
125
132
|
"./skills"
|
|
@@ -58,6 +58,7 @@ import {
|
|
|
58
58
|
drawLoadedIconsAtOnce,
|
|
59
59
|
} from "./icon-hydration";
|
|
60
60
|
import {
|
|
61
|
+
catalogLocale,
|
|
61
62
|
loadLocaleMessages,
|
|
62
63
|
localeMessages,
|
|
63
64
|
supportedLocales,
|
|
@@ -85,6 +86,8 @@ export interface DmsAppOptions {
|
|
|
85
86
|
}
|
|
86
87
|
|
|
87
88
|
export interface DmsConfiguredApp {
|
|
89
|
+
/** The locale of the catalog the app shows its messages from now. */
|
|
90
|
+
catalogLocale: () => string | undefined;
|
|
88
91
|
mounted: () => Promise<void>;
|
|
89
92
|
runtime: ReturnType<typeof createDmsFrontendRuntime>;
|
|
90
93
|
}
|
|
@@ -348,7 +351,11 @@ export async function configureDmsApp(
|
|
|
348
351
|
loadLocaleMessages as DmsLocaleLoader,
|
|
349
352
|
supportedLocales,
|
|
350
353
|
);
|
|
351
|
-
return {
|
|
354
|
+
return {
|
|
355
|
+
catalogLocale: () => catalogLocale(i18n.global.locale.value),
|
|
356
|
+
mounted,
|
|
357
|
+
runtime,
|
|
358
|
+
};
|
|
352
359
|
}
|
|
353
360
|
|
|
354
361
|
export { trackDmsAsyncComponents };
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
// What each frontend module adds to the client entry.
|
|
2
|
+
//
|
|
3
|
+
// Every page loads the entry, and everything it imports statically, before it
|
|
4
|
+
// runs anything: a module's `dms.frontend.ts`, its plugins and whatever they
|
|
5
|
+
// import land there, on every page of every application that loads the
|
|
6
|
+
// module. The client build records, from the bundle's own metadata, how much
|
|
7
|
+
// of the entry each module's sources make up; `ajs dms build` reports it and
|
|
8
|
+
// warns about a module above its threshold.
|
|
9
|
+
//
|
|
10
|
+
// Kept beside vite.config.ts rather than inside it so the plugin can be
|
|
11
|
+
// exercised on its own, the way client-chunking.mjs is.
|
|
12
|
+
|
|
13
|
+
import { mkdirSync, writeFileSync } from "node:fs";
|
|
14
|
+
import { dirname, join, relative, sep } from "node:path";
|
|
15
|
+
|
|
16
|
+
/** Where the client build writes the attribution, under `dist/client`. */
|
|
17
|
+
export const ENTRY_WEIGHT_FILE = ".vite/entry-weight.json";
|
|
18
|
+
|
|
19
|
+
/** Owners of the entry's bytes that are no frontend module. */
|
|
20
|
+
export const SHARED_OWNER = "shared";
|
|
21
|
+
export const TRANSLATIONS_OWNER = "translations";
|
|
22
|
+
|
|
23
|
+
const LOCALE_CATALOG = /[\\/]locales\.generated[\\/]/;
|
|
24
|
+
|
|
25
|
+
function stripQuery(id) {
|
|
26
|
+
const query = id.indexOf("?");
|
|
27
|
+
return query === -1 ? id : id.slice(0, query);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
function isInside(root, file) {
|
|
31
|
+
const path = relative(root, file);
|
|
32
|
+
return Boolean(path) && !path.startsWith("..") && !path.startsWith(sep);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* The owner of a module id of the bundle: the frontend module whose sources
|
|
37
|
+
* hold the file, by its manifest name; the merged translation catalogs; else
|
|
38
|
+
* what every page shares (the renderer's runtime, the UI and icon libraries,
|
|
39
|
+
* an installed package).
|
|
40
|
+
*
|
|
41
|
+
* @param {{ root: string, name?: string, packageName?: string, id: string }[]} modules
|
|
42
|
+
*/
|
|
43
|
+
export function entryOwner(id, modules) {
|
|
44
|
+
if (id.startsWith("\0")) return SHARED_OWNER;
|
|
45
|
+
const file = stripQuery(id);
|
|
46
|
+
if (/[\\/]node_modules[\\/]/.test(file)) return SHARED_OWNER;
|
|
47
|
+
if (LOCALE_CATALOG.test(file)) return TRANSLATIONS_OWNER;
|
|
48
|
+
const module = modules.find(({ root }) => isInside(root, file));
|
|
49
|
+
return module
|
|
50
|
+
? (module.name ?? module.packageName ?? module.id)
|
|
51
|
+
: SHARED_OWNER;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* The files a page loads before its first line runs: the entry chunks and
|
|
56
|
+
* their static imports, transitively, by file name.
|
|
57
|
+
*/
|
|
58
|
+
function entryClosure(bundle) {
|
|
59
|
+
const chunks = Object.values(bundle).filter(
|
|
60
|
+
(output) => output.type === "chunk",
|
|
61
|
+
);
|
|
62
|
+
const byName = new Map(chunks.map((chunk) => [chunk.fileName, chunk]));
|
|
63
|
+
const seen = new Set();
|
|
64
|
+
const visit = (chunk) => {
|
|
65
|
+
if (!chunk || seen.has(chunk.fileName)) return;
|
|
66
|
+
seen.add(chunk.fileName);
|
|
67
|
+
for (const file of chunk.imports ?? []) visit(byName.get(file));
|
|
68
|
+
};
|
|
69
|
+
for (const chunk of chunks) if (chunk.isEntry) visit(chunk);
|
|
70
|
+
return [...seen].map((file) => byName.get(file));
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* The bytes of the entry each owner accounts for. Each chunk's bytes are
|
|
75
|
+
* shared out among the modules it holds in proportion to the code Rolldown
|
|
76
|
+
* rendered for each (`renderedLength`, before minification): an estimate that
|
|
77
|
+
* needs nothing the bundle does not already hold.
|
|
78
|
+
*
|
|
79
|
+
* @returns {{ files: string[], bytes: number, owners: Record<string, number> }}
|
|
80
|
+
*/
|
|
81
|
+
export function attributeEntry(bundle, modules) {
|
|
82
|
+
const files = entryClosure(bundle);
|
|
83
|
+
const owners = {};
|
|
84
|
+
let bytes = 0;
|
|
85
|
+
for (const chunk of files) {
|
|
86
|
+
const size = Buffer.byteLength(chunk.code);
|
|
87
|
+
bytes += size;
|
|
88
|
+
const rendered = Object.entries(chunk.modules ?? {});
|
|
89
|
+
const total = rendered.reduce(
|
|
90
|
+
(sum, [, info]) => sum + info.renderedLength,
|
|
91
|
+
0,
|
|
92
|
+
);
|
|
93
|
+
if (!total) {
|
|
94
|
+
owners[SHARED_OWNER] = (owners[SHARED_OWNER] ?? 0) + size;
|
|
95
|
+
continue;
|
|
96
|
+
}
|
|
97
|
+
for (const [id, info] of rendered) {
|
|
98
|
+
const owner = entryOwner(id, modules);
|
|
99
|
+
owners[owner] =
|
|
100
|
+
(owners[owner] ?? 0) + (size * info.renderedLength) / total;
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
for (const owner of Object.keys(owners))
|
|
104
|
+
owners[owner] = Math.round(owners[owner]);
|
|
105
|
+
return { files: files.map((chunk) => chunk.fileName), bytes, owners };
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
/**
|
|
109
|
+
* Records, in the client build, what each frontend module adds to the entry
|
|
110
|
+
* (`ENTRY_WEIGHT_FILE`). Server builds are left alone: their bundle is no
|
|
111
|
+
* page's entry.
|
|
112
|
+
*/
|
|
113
|
+
export function recordEntryWeight(modules) {
|
|
114
|
+
let ssrBuild = false;
|
|
115
|
+
return {
|
|
116
|
+
name: "dms-entry-weight",
|
|
117
|
+
configResolved(config) {
|
|
118
|
+
ssrBuild = Boolean(config.build.ssr);
|
|
119
|
+
},
|
|
120
|
+
// Once the bundle is written: plugins that generate the bundle after this
|
|
121
|
+
// one still rewrite the chunks' code.
|
|
122
|
+
writeBundle(options, bundle) {
|
|
123
|
+
if (ssrBuild || !options.dir) return;
|
|
124
|
+
const file = join(options.dir, ENTRY_WEIGHT_FILE);
|
|
125
|
+
mkdirSync(dirname(file), { recursive: true });
|
|
126
|
+
writeFileSync(
|
|
127
|
+
file,
|
|
128
|
+
`${JSON.stringify(attributeEntry(bundle, modules), null, 2)}\n`,
|
|
129
|
+
);
|
|
130
|
+
},
|
|
131
|
+
};
|
|
132
|
+
}
|