@antelopejs/dms-frontend 0.3.7 → 0.3.9
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 +82 -23
- package/dist/cancellation.d.ts +8 -0
- package/dist/cancellation.js +23 -0
- package/dist/child-output.d.ts +45 -0
- package/dist/child-output.js +183 -0
- package/dist/commands/build-action.d.ts +21 -0
- package/dist/commands/build-action.js +144 -0
- package/dist/commands/build.js +54 -62
- package/dist/commands/clean-action.d.ts +8 -0
- package/dist/commands/clean-action.js +97 -0
- package/dist/commands/clean.js +52 -39
- package/dist/commands/dev-action.d.ts +8 -0
- package/dist/commands/dev-action.js +120 -0
- package/dist/commands/dev.js +55 -115
- package/dist/commands/prepare-action.d.ts +8 -0
- package/dist/commands/prepare-action.js +73 -0
- package/dist/commands/prepare.js +55 -54
- package/dist/commands/start-action.d.ts +5 -0
- package/dist/commands/start-action.js +77 -0
- package/dist/commands/start.js +48 -48
- package/dist/commands/verify-source-action.d.ts +10 -0
- package/dist/commands/verify-source-action.js +104 -0
- package/dist/commands/verify-source.d.ts +0 -1
- package/dist/commands/verify-source.js +53 -31
- package/dist/commands/workspace-task.d.ts +2 -0
- package/dist/commands/workspace-task.js +26 -0
- package/dist/commands/workspaces-action.d.ts +25 -0
- package/dist/commands/workspaces-action.js +71 -0
- package/dist/commands/workspaces.d.ts +2 -0
- package/dist/commands/workspaces.js +60 -0
- package/dist/common.d.ts +1 -0
- package/dist/common.js +1 -0
- package/dist/config.d.ts +13 -4
- package/dist/config.js +115 -18
- package/dist/derived-outputs.d.ts +6 -1
- package/dist/derived-outputs.js +12 -3
- package/dist/discovery.d.ts +2 -1
- package/dist/discovery.js +28 -11
- package/dist/env-file.js +5 -2
- package/dist/help.d.ts +5 -0
- package/dist/help.js +70 -0
- package/dist/index.js +98 -38
- package/dist/layer-watch.d.ts +2 -1
- package/dist/layer-watch.js +129 -26
- package/dist/layers.d.ts +2 -2
- package/dist/layers.js +71 -23
- package/dist/manifest-secrets.d.ts +3 -2
- package/dist/manifest-secrets.js +25 -21
- package/dist/manifest.d.ts +22 -5
- package/dist/manifest.js +184 -28
- package/dist/materialize.js +2 -1
- package/dist/output.d.ts +41 -0
- package/dist/output.js +197 -0
- package/dist/ports.d.ts +2 -0
- package/dist/ports.js +23 -13
- package/dist/renderer-range.d.ts +3 -1
- package/dist/renderer-range.js +49 -17
- package/dist/server-process.d.ts +39 -0
- package/dist/server-process.js +221 -0
- package/dist/typecheck-output.d.ts +13 -0
- package/dist/typecheck-output.js +87 -0
- package/dist/update-check.d.ts +14 -2
- package/dist/update-check.js +95 -29
- package/dist/verify-source-result.d.ts +11 -0
- package/dist/verify-source-result.js +17 -0
- package/dist/verify-source-runner.js +266 -131
- package/dist/vite-output.d.ts +14 -0
- package/dist/vite-output.js +99 -0
- package/dist/workspace-setup.d.ts +17 -3
- package/dist/workspace-setup.js +119 -35
- package/dist/workspace.d.ts +8 -2
- package/dist/workspace.js +47 -12
- package/package.json +4 -12
- package/templates/vue/frontend-module.ts +32 -10
- package/templates/vue/package.json +4 -0
- package/templates/vue/server/cli-report.mjs +91 -0
- package/templates/vue/server.mjs +45 -21
- package/templates/vue/vite.config.ts +8 -1
- package/templates/vue/vite.email.config.ts +5 -1
- package/dist/utils/cli-ui.d.ts +0 -27
- package/dist/utils/cli-ui.js +0 -178
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { type CliProblem, type Ui } from "@antelopejs/core/cli";
|
|
2
|
+
export declare const VITE_LOG_LEVEL_VARIABLE = "DMS_VITE_LOG_LEVEL";
|
|
3
|
+
export declare function viteLogLevel(isVerbose: boolean): string;
|
|
4
|
+
export declare const VERBOSE_VITE_HINT = "Run with --verbose for the full Vite output.";
|
|
5
|
+
export declare class ViteWarnings {
|
|
6
|
+
private readonly kept;
|
|
7
|
+
private opened;
|
|
8
|
+
private isAfterPluginTag;
|
|
9
|
+
read(line: string): void;
|
|
10
|
+
get count(): number;
|
|
11
|
+
lines(): string[];
|
|
12
|
+
}
|
|
13
|
+
export declare function reportViteWarnings(warnings: ViteWarnings, isVerbose: boolean, ui: Ui): void;
|
|
14
|
+
export declare function describeViteFailure(lines: string[], subject: string, isVerbose: boolean, rerun?: string, nameFile?: (file: string) => string): CliProblem | undefined;
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.ViteWarnings = exports.VERBOSE_VITE_HINT = exports.VITE_LOG_LEVEL_VARIABLE = void 0;
|
|
4
|
+
exports.viteLogLevel = viteLogLevel;
|
|
5
|
+
exports.reportViteWarnings = reportViteWarnings;
|
|
6
|
+
exports.describeViteFailure = describeViteFailure;
|
|
7
|
+
const cli_1 = require("@antelopejs/core/cli");
|
|
8
|
+
const output_1 = require("./output");
|
|
9
|
+
exports.VITE_LOG_LEVEL_VARIABLE = "DMS_VITE_LOG_LEVEL";
|
|
10
|
+
function viteLogLevel(isVerbose) {
|
|
11
|
+
return isVerbose ? "info" : "warn";
|
|
12
|
+
}
|
|
13
|
+
exports.VERBOSE_VITE_HINT = "Run with --verbose for the full Vite output.";
|
|
14
|
+
const WARNING_START = /^(\(!\)|\[plugin[ :][^\]]*\]|\(node:\d+\))/;
|
|
15
|
+
const PLUGIN_TAG_LINE = /^\[plugin[ :][^\]]*\]$/;
|
|
16
|
+
class ViteWarnings {
|
|
17
|
+
constructor() {
|
|
18
|
+
this.kept = [];
|
|
19
|
+
this.opened = 0;
|
|
20
|
+
this.isAfterPluginTag = false;
|
|
21
|
+
}
|
|
22
|
+
read(line) {
|
|
23
|
+
const text = line.trim();
|
|
24
|
+
if (text === "")
|
|
25
|
+
return;
|
|
26
|
+
this.kept.push(line.trimEnd());
|
|
27
|
+
const continuesTag = this.isAfterPluginTag && text.startsWith("(!)");
|
|
28
|
+
if (WARNING_START.test(text) && !continuesTag)
|
|
29
|
+
this.opened += 1;
|
|
30
|
+
this.isAfterPluginTag = PLUGIN_TAG_LINE.test(text);
|
|
31
|
+
}
|
|
32
|
+
get count() {
|
|
33
|
+
if (this.opened === 0 && this.kept.length > 0)
|
|
34
|
+
return 1;
|
|
35
|
+
return this.opened;
|
|
36
|
+
}
|
|
37
|
+
lines() {
|
|
38
|
+
return [...this.kept];
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
exports.ViteWarnings = ViteWarnings;
|
|
42
|
+
function reportViteWarnings(warnings, isVerbose, ui) {
|
|
43
|
+
if (isVerbose || warnings.count === 0)
|
|
44
|
+
return;
|
|
45
|
+
const title = (0, cli_1.pluralize)(warnings.count, "Vite warning");
|
|
46
|
+
const details = (0, output_1.isTerminalFeedback)()
|
|
47
|
+
? [`${ui.symbols.levels.hint} Run with --verbose to list them`]
|
|
48
|
+
: warnings.lines();
|
|
49
|
+
ui.message("warn", title, { details });
|
|
50
|
+
}
|
|
51
|
+
const ERROR_START = "error during build:";
|
|
52
|
+
const FILE_LINE = /^file:\s+(.+)$/;
|
|
53
|
+
const CODE_FRAME_LINE = /^\s*\d*\s*\|/;
|
|
54
|
+
const STACK_FRAME = /^\s+at\s/;
|
|
55
|
+
const LOCATION_SUFFIX = /\s*\(\d+:\d+\)$/;
|
|
56
|
+
const VITE_PLUGIN_TAG = /^\[vite:[^\]]+\]\s+(?=\S)/;
|
|
57
|
+
function errorBlock(lines) {
|
|
58
|
+
let start = -1;
|
|
59
|
+
lines.forEach((line, index) => {
|
|
60
|
+
if (line.trim() === ERROR_START)
|
|
61
|
+
start = index;
|
|
62
|
+
});
|
|
63
|
+
if (start < 0)
|
|
64
|
+
return undefined;
|
|
65
|
+
const block = [];
|
|
66
|
+
for (const line of lines.slice(start + 1)) {
|
|
67
|
+
if (STACK_FRAME.test(line))
|
|
68
|
+
break;
|
|
69
|
+
if (line.trim() !== "")
|
|
70
|
+
block.push(line.trimEnd());
|
|
71
|
+
}
|
|
72
|
+
return block;
|
|
73
|
+
}
|
|
74
|
+
function describeViteFailure(lines, subject, isVerbose, rerun = "ajs dms build", nameFile = (file) => file) {
|
|
75
|
+
const block = errorBlock(lines);
|
|
76
|
+
if (!block)
|
|
77
|
+
return undefined;
|
|
78
|
+
const [message = "", ...rest] = block;
|
|
79
|
+
const reason = message
|
|
80
|
+
.replace(LOCATION_SUFFIX, "")
|
|
81
|
+
.replace(VITE_PLUGIN_TAG, "");
|
|
82
|
+
const hint = isVerbose ? [] : [exports.VERBOSE_VITE_HINT];
|
|
83
|
+
const file = rest
|
|
84
|
+
.map((line) => FILE_LINE.exec(line.trim())?.[1])
|
|
85
|
+
.find((match) => match !== undefined);
|
|
86
|
+
if (file) {
|
|
87
|
+
return {
|
|
88
|
+
title: `Vite could not compile ${nameFile(file)}`,
|
|
89
|
+
reason,
|
|
90
|
+
fixes: [`Fix the file and run ${rerun} again`],
|
|
91
|
+
details: [...rest.filter((line) => CODE_FRAME_LINE.test(line)), ...hint],
|
|
92
|
+
};
|
|
93
|
+
}
|
|
94
|
+
return {
|
|
95
|
+
title: `Vite could not build the ${subject}`,
|
|
96
|
+
reason,
|
|
97
|
+
details: [...rest, ...hint],
|
|
98
|
+
};
|
|
99
|
+
}
|
|
@@ -1,11 +1,24 @@
|
|
|
1
|
-
import { type SpawnOptions } from "node:child_process";
|
|
1
|
+
import { type ChildProcess, type SpawnOptions } from "node:child_process";
|
|
2
|
+
import { type PathMapper } from "./child-output";
|
|
2
3
|
import { type ManifestSecrets } from "./manifest-secrets";
|
|
3
4
|
import { ResolvedLayer } from "./workspace";
|
|
4
5
|
export declare function computeDepsHash(layerPaths: string[], workspacePackage?: string, installInputs?: readonly string[]): string;
|
|
5
6
|
export declare function needsInstall(workspaceDir: string, layerPaths: string[], force: boolean): boolean;
|
|
6
7
|
export declare function saveDepsHash(workspaceDir: string, layerPaths: string[]): void;
|
|
7
|
-
export declare function installDeps(workspaceDir: string): void
|
|
8
|
-
export declare function runCommand(command: string, args: string[], options?: SpawnOptions): Promise<number>;
|
|
8
|
+
export declare function installDeps(workspaceDir: string, mapLine?: PathMapper, extraArgs?: string[]): Promise<void>;
|
|
9
|
+
export declare function runCommand(command: string, args: string[], options?: SpawnOptions, onSpawn?: (child: ChildProcess) => void): Promise<number>;
|
|
10
|
+
export interface FramedCommandOptions {
|
|
11
|
+
name: string;
|
|
12
|
+
cwd: string;
|
|
13
|
+
env?: NodeJS.ProcessEnv;
|
|
14
|
+
mapLine?: PathMapper;
|
|
15
|
+
onLine?: (line: string) => void;
|
|
16
|
+
}
|
|
17
|
+
export interface FramedCommandResult {
|
|
18
|
+
code: number;
|
|
19
|
+
lines: string[];
|
|
20
|
+
}
|
|
21
|
+
export declare function runFramedCommand(command: string, args: string[], options: FramedCommandOptions): Promise<FramedCommandResult>;
|
|
9
22
|
export interface SetupWorkspaceOptions {
|
|
10
23
|
backendUrl: string;
|
|
11
24
|
force: boolean;
|
|
@@ -14,6 +27,7 @@ export interface SetupWorkspaceOptions {
|
|
|
14
27
|
clientUrl?: string;
|
|
15
28
|
workspaceKey?: string;
|
|
16
29
|
bootstrapSecret?: string;
|
|
30
|
+
beforeInstall?: () => Promise<void>;
|
|
17
31
|
}
|
|
18
32
|
export interface SetupWorkspaceResult {
|
|
19
33
|
workspaceDir: string;
|
package/dist/workspace-setup.js
CHANGED
|
@@ -5,18 +5,22 @@ exports.needsInstall = needsInstall;
|
|
|
5
5
|
exports.saveDepsHash = saveDepsHash;
|
|
6
6
|
exports.installDeps = installDeps;
|
|
7
7
|
exports.runCommand = runCommand;
|
|
8
|
+
exports.runFramedCommand = runFramedCommand;
|
|
8
9
|
exports.setupWorkspace = setupWorkspace;
|
|
9
10
|
const node_child_process_1 = require("node:child_process");
|
|
10
11
|
const node_crypto_1 = require("node:crypto");
|
|
11
12
|
const node_fs_1 = require("node:fs");
|
|
12
|
-
const node_os_1 = require("node:os");
|
|
13
13
|
const node_path_1 = require("node:path");
|
|
14
|
+
const cli_1 = require("@antelopejs/core/cli");
|
|
15
|
+
const child_output_1 = require("./child-output");
|
|
14
16
|
const layers_1 = require("./layers");
|
|
15
17
|
const manifest_1 = require("./manifest");
|
|
16
18
|
const manifest_secrets_1 = require("./manifest-secrets");
|
|
17
19
|
const renderer_range_1 = require("./renderer-range");
|
|
18
20
|
const materialize_1 = require("./materialize");
|
|
19
21
|
const workspace_1 = require("./workspace");
|
|
22
|
+
const cancellation_1 = require("./cancellation");
|
|
23
|
+
const output_1 = require("./output");
|
|
20
24
|
const config_1 = require("./config");
|
|
21
25
|
const WORKSPACE_PNPM_CONFIG = "pnpm-workspace.yaml";
|
|
22
26
|
const WORKSPACE_PATCHES_DIR = "patches";
|
|
@@ -61,48 +65,77 @@ function saveDepsHash(workspaceDir, layerPaths) {
|
|
|
61
65
|
const hash = computeDepsHash(layerPaths, (0, node_path_1.join)(workspaceDir, "package.json"), workspaceInstallInputs(workspaceDir));
|
|
62
66
|
(0, node_fs_1.writeFileSync)((0, node_path_1.join)(workspaceDir, config_1.DEPS_HASH_FILE), hash);
|
|
63
67
|
}
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
68
|
+
const PNPM_INSTALL_ARGS = ["install", "--reporter=append-only"];
|
|
69
|
+
async function installDeps(workspaceDir, mapLine, extraArgs = []) {
|
|
70
|
+
const progress = new child_output_1.InstallProgress();
|
|
71
|
+
const task = (0, cli_1.getProcessTasks)().start(progress.label);
|
|
72
|
+
try {
|
|
73
|
+
const { code, lines } = await runFramedCommand("pnpm", [...PNPM_INSTALL_ARGS, ...extraArgs], {
|
|
74
|
+
name: "pnpm",
|
|
75
|
+
cwd: workspaceDir,
|
|
76
|
+
env: { ...process.env, npm_config_update_notifier: "false" },
|
|
77
|
+
mapLine,
|
|
78
|
+
onLine: (line) => {
|
|
79
|
+
if (progress.read(line))
|
|
80
|
+
task.update(progress.label);
|
|
81
|
+
},
|
|
82
|
+
});
|
|
83
|
+
if (code !== 0) {
|
|
84
|
+
throw new cli_1.CliError((0, child_output_1.describeChildFailure)({
|
|
85
|
+
title: "Dependency install failed",
|
|
86
|
+
command: "pnpm install",
|
|
87
|
+
code,
|
|
88
|
+
lines,
|
|
89
|
+
isVerbose: (0, cli_1.isVerboseRun)(),
|
|
90
|
+
}));
|
|
91
|
+
}
|
|
92
|
+
task.succeed(progress.doneLabel);
|
|
93
|
+
}
|
|
94
|
+
catch (error) {
|
|
95
|
+
(0, output_1.endInterruptedTask)(task, error, "Dependency install stopped");
|
|
96
|
+
throw error;
|
|
97
|
+
}
|
|
70
98
|
}
|
|
71
99
|
const FORWARDED_SIGNALS = ["SIGINT", "SIGTERM", "SIGHUP"];
|
|
72
100
|
const CHILD_SHUTDOWN_TIMEOUT_MS = 5000;
|
|
73
|
-
function
|
|
74
|
-
const number = node_os_1.constants.signals[signal];
|
|
75
|
-
return 128 + (typeof number === "number" ? number : 15);
|
|
76
|
-
}
|
|
77
|
-
function runCommand(command, args, options) {
|
|
101
|
+
function runCommand(command, args, options, onSpawn) {
|
|
78
102
|
const isWindows = process.platform === "win32";
|
|
79
103
|
return new Promise((resolve, reject) => {
|
|
80
104
|
const child = (0, node_child_process_1.spawn)(command, args, {
|
|
81
105
|
stdio: "inherit",
|
|
82
106
|
shell: isWindows,
|
|
83
107
|
...options,
|
|
108
|
+
detached: !isWindows,
|
|
84
109
|
});
|
|
110
|
+
onSpawn?.(child);
|
|
85
111
|
let escalation;
|
|
86
112
|
let forwarded;
|
|
87
113
|
const alive = () => child.exitCode === null && child.signalCode === null;
|
|
88
|
-
const
|
|
89
|
-
if (
|
|
114
|
+
const signalTree = (signal) => {
|
|
115
|
+
if (child.pid === undefined)
|
|
90
116
|
return;
|
|
91
117
|
try {
|
|
92
|
-
|
|
118
|
+
if (isWindows) {
|
|
119
|
+
(0, node_child_process_1.spawnSync)("taskkill", ["/pid", String(child.pid), "/T", "/F"], {
|
|
120
|
+
stdio: "ignore",
|
|
121
|
+
});
|
|
122
|
+
}
|
|
123
|
+
else {
|
|
124
|
+
process.kill(-child.pid, signal);
|
|
125
|
+
}
|
|
93
126
|
}
|
|
94
127
|
catch {
|
|
95
128
|
}
|
|
129
|
+
};
|
|
130
|
+
const stopChild = (signal) => {
|
|
131
|
+
if (!alive())
|
|
132
|
+
return;
|
|
133
|
+
signalTree(signal);
|
|
96
134
|
if (escalation || isWindows)
|
|
97
135
|
return;
|
|
98
136
|
escalation = setTimeout(() => {
|
|
99
|
-
if (alive())
|
|
100
|
-
|
|
101
|
-
child.kill("SIGKILL");
|
|
102
|
-
}
|
|
103
|
-
catch {
|
|
104
|
-
}
|
|
105
|
-
}
|
|
137
|
+
if (alive())
|
|
138
|
+
signalTree("SIGKILL");
|
|
106
139
|
}, CHILD_SHUTDOWN_TIMEOUT_MS);
|
|
107
140
|
escalation.unref();
|
|
108
141
|
};
|
|
@@ -111,13 +144,8 @@ function runCommand(command, args, options) {
|
|
|
111
144
|
stopChild(signal);
|
|
112
145
|
};
|
|
113
146
|
const onParentExit = () => {
|
|
114
|
-
if (alive())
|
|
115
|
-
|
|
116
|
-
child.kill("SIGKILL");
|
|
117
|
-
}
|
|
118
|
-
catch {
|
|
119
|
-
}
|
|
120
|
-
}
|
|
147
|
+
if (alive())
|
|
148
|
+
signalTree("SIGKILL");
|
|
121
149
|
};
|
|
122
150
|
for (const signal of FORWARDED_SIGNALS)
|
|
123
151
|
process.on(signal, onSignal);
|
|
@@ -135,22 +163,72 @@ function runCommand(command, args, options) {
|
|
|
135
163
|
});
|
|
136
164
|
child.on("exit", (code, signal) => {
|
|
137
165
|
cleanup();
|
|
166
|
+
if (forwarded) {
|
|
167
|
+
if (!isWindows)
|
|
168
|
+
signalTree("SIGKILL");
|
|
169
|
+
return reject(new cancellation_1.CancelledError(forwarded));
|
|
170
|
+
}
|
|
138
171
|
if (code !== null)
|
|
139
172
|
return resolve(code);
|
|
140
|
-
resolve(exitCodeForSignal(signal ??
|
|
173
|
+
resolve((0, cancellation_1.exitCodeForSignal)(signal ?? "SIGTERM"));
|
|
141
174
|
});
|
|
142
175
|
});
|
|
143
176
|
}
|
|
177
|
+
const OUTPUT_DRAIN_TIMEOUT_MS = 2000;
|
|
178
|
+
function drained(stream) {
|
|
179
|
+
if (!stream)
|
|
180
|
+
return Promise.resolve();
|
|
181
|
+
return new Promise((settle) => {
|
|
182
|
+
stream.once("end", settle);
|
|
183
|
+
stream.once("error", settle);
|
|
184
|
+
});
|
|
185
|
+
}
|
|
186
|
+
function drainTimeout() {
|
|
187
|
+
return new Promise((settle) => {
|
|
188
|
+
setTimeout(settle, OUTPUT_DRAIN_TIMEOUT_MS).unref();
|
|
189
|
+
});
|
|
190
|
+
}
|
|
191
|
+
async function runFramedCommand(command, args, options) {
|
|
192
|
+
const { name, cwd, env, mapLine = (line) => line, onLine } = options;
|
|
193
|
+
const isVerbose = (0, cli_1.isVerboseRun)();
|
|
194
|
+
const tails = [new child_output_1.OutputTail(), new child_output_1.OutputTail()];
|
|
195
|
+
const readLine = (tail, line) => {
|
|
196
|
+
const mapped = mapLine(line);
|
|
197
|
+
if (isVerbose)
|
|
198
|
+
(0, child_output_1.writeChildLine)(name, mapped);
|
|
199
|
+
const plain = (0, child_output_1.stripAnsi)(mapped);
|
|
200
|
+
tail.push(plain);
|
|
201
|
+
onLine?.(plain);
|
|
202
|
+
};
|
|
203
|
+
let ended = Promise.resolve();
|
|
204
|
+
const code = await runCommand(command, args, { cwd, env, stdio: ["ignore", "pipe", "pipe"] }, (child) => {
|
|
205
|
+
const streams = [child.stdout, child.stderr];
|
|
206
|
+
for (const [index, stream] of streams.entries()) {
|
|
207
|
+
const splitter = new child_output_1.LineSplitter((line) => readLine(tails[index], line));
|
|
208
|
+
stream?.setEncoding("utf8");
|
|
209
|
+
stream?.on("data", (chunk) => splitter.push(chunk));
|
|
210
|
+
stream?.once("end", () => splitter.flush());
|
|
211
|
+
}
|
|
212
|
+
ended = Promise.all(streams.map((stream) => drained(stream)));
|
|
213
|
+
});
|
|
214
|
+
await Promise.race([ended, drainTimeout()]);
|
|
215
|
+
return { code, lines: tails.flatMap((tail) => tail.lines()) };
|
|
216
|
+
}
|
|
217
|
+
function manifestCacheUse(mode, offline) {
|
|
218
|
+
if (offline)
|
|
219
|
+
return "offline";
|
|
220
|
+
return mode === "build" ? "never" : "fallback";
|
|
221
|
+
}
|
|
144
222
|
async function setupWorkspace(opts) {
|
|
145
223
|
const { backendUrl, force, mode, offline, clientUrl, bootstrapSecret } = opts;
|
|
146
224
|
const workspaceKey = opts.workspaceKey ?? (0, config_1.canonicalizeBackendUrl)(backendUrl);
|
|
147
225
|
const workspaceDir = (0, workspace_1.ensureWorkspace)(workspaceKey);
|
|
148
226
|
(0, workspace_1.writeWorkspaceMeta)(workspaceDir, backendUrl, workspaceKey);
|
|
149
|
-
const resolved = await (0, manifest_1.resolveManifest)(workspaceDir, backendUrl,
|
|
227
|
+
const resolved = await (0, manifest_1.resolveManifest)(workspaceDir, backendUrl, manifestCacheUse(mode, offline), clientUrl, bootstrapSecret);
|
|
150
228
|
const { manifest, fromCache, fetchedAt } = resolved;
|
|
151
229
|
let layers;
|
|
152
230
|
if (mode === "dev") {
|
|
153
|
-
(0, layers_1.assertLayerPathsServed)(manifest.modules);
|
|
231
|
+
(0, layers_1.assertLayerPathsServed)(manifest.modules, backendUrl, bootstrapSecret);
|
|
154
232
|
if (fromCache) {
|
|
155
233
|
(0, manifest_1.assertCachedLayerPathsExist)(manifest.modules, fetchedAt);
|
|
156
234
|
}
|
|
@@ -160,7 +238,12 @@ async function setupWorkspace(opts) {
|
|
|
160
238
|
const cacheDir = (0, node_path_1.join)(workspaceDir, ".layers-cache");
|
|
161
239
|
if (fromCache) {
|
|
162
240
|
if (!(0, node_fs_1.existsSync)(cacheDir)) {
|
|
163
|
-
throw new
|
|
241
|
+
throw new cli_1.CliError({
|
|
242
|
+
title: "No layers archive was downloaded for this workspace",
|
|
243
|
+
fixes: [
|
|
244
|
+
"Run once without --offline, with the backend reachable, first",
|
|
245
|
+
],
|
|
246
|
+
});
|
|
164
247
|
}
|
|
165
248
|
(0, layers_1.assertCachedArchivesExist)(manifest.modules, cacheDir, fetchedAt);
|
|
166
249
|
}
|
|
@@ -169,7 +252,7 @@ async function setupWorkspace(opts) {
|
|
|
169
252
|
}
|
|
170
253
|
layers = await (0, layers_1.buildLayersFromCache)(manifest.modules, cacheDir);
|
|
171
254
|
}
|
|
172
|
-
(0, renderer_range_1.assertLayersSupportRenderer)(layers);
|
|
255
|
+
(0, renderer_range_1.assertLayersSupportRenderer)(layers, { workspaceDir });
|
|
173
256
|
(0, materialize_1.copyStaticTemplates)(workspaceDir);
|
|
174
257
|
(0, materialize_1.writeWorkspacePackageJson)(workspaceDir, layers);
|
|
175
258
|
(0, materialize_1.materializeLayers)(workspaceDir, layers);
|
|
@@ -178,7 +261,8 @@ async function setupWorkspace(opts) {
|
|
|
178
261
|
const layerPaths = layers.map((layer) => layer.path);
|
|
179
262
|
const shouldInstall = needsInstall(workspaceDir, layerPaths, force);
|
|
180
263
|
if (shouldInstall) {
|
|
181
|
-
|
|
264
|
+
await opts.beforeInstall?.();
|
|
265
|
+
await installDeps(workspaceDir, (0, child_output_1.createPathMapper)(workspaceDir, layers));
|
|
182
266
|
saveDepsHash(workspaceDir, layerPaths);
|
|
183
267
|
}
|
|
184
268
|
return {
|
package/dist/workspace.d.ts
CHANGED
|
@@ -27,6 +27,7 @@ export interface FrontendManifest {
|
|
|
27
27
|
}
|
|
28
28
|
export interface ResolvedLayer {
|
|
29
29
|
path: string;
|
|
30
|
+
sourcePath?: string;
|
|
30
31
|
packageName?: string;
|
|
31
32
|
priority?: number;
|
|
32
33
|
configKey?: string;
|
|
@@ -42,9 +43,14 @@ export interface WorkspaceEntry {
|
|
|
42
43
|
dir: string;
|
|
43
44
|
backendUrl: string;
|
|
44
45
|
workspaceKey?: string;
|
|
46
|
+
lastUsedAt: string;
|
|
45
47
|
}
|
|
46
|
-
export
|
|
47
|
-
|
|
48
|
+
export interface WorkspaceListing {
|
|
49
|
+
workspaces: WorkspaceEntry[];
|
|
50
|
+
skipped: string[];
|
|
51
|
+
}
|
|
52
|
+
export declare function listWorkspaces(): WorkspaceListing;
|
|
53
|
+
export declare function directorySize(dir: string): number;
|
|
48
54
|
export declare function writeWorkspaceMeta(workspaceDir: string, backendUrl: string, workspaceKey: string): void;
|
|
49
55
|
export declare const FRONTEND_MANIFEST_VERSION = 1;
|
|
50
56
|
export declare function bootstrapHeaders(secret?: string): Record<string, string>;
|
package/dist/workspace.js
CHANGED
|
@@ -6,8 +6,8 @@ exports.projectWorkspaceKey = projectWorkspaceKey;
|
|
|
6
6
|
exports.projectDirFromWorkspaceKey = projectDirFromWorkspaceKey;
|
|
7
7
|
exports.getWorkspaceDir = getWorkspaceDir;
|
|
8
8
|
exports.ensureWorkspace = ensureWorkspace;
|
|
9
|
-
exports.describeWorkspace = describeWorkspace;
|
|
10
9
|
exports.listWorkspaces = listWorkspaces;
|
|
10
|
+
exports.directorySize = directorySize;
|
|
11
11
|
exports.writeWorkspaceMeta = writeWorkspaceMeta;
|
|
12
12
|
exports.bootstrapHeaders = bootstrapHeaders;
|
|
13
13
|
exports.isUnauthorized = isUnauthorized;
|
|
@@ -38,32 +38,28 @@ function ensureWorkspace(workspaceKey) {
|
|
|
38
38
|
(0, node_fs_1.chmodSync)(dir, config_1.WORKSPACE_DIR_MODE);
|
|
39
39
|
return dir;
|
|
40
40
|
}
|
|
41
|
-
function describeWorkspace(entry) {
|
|
42
|
-
const projectDir = projectDirFromWorkspaceKey(entry.workspaceKey);
|
|
43
|
-
return projectDir
|
|
44
|
-
? `${entry.backendUrl}, keyed on project ${projectDir}`
|
|
45
|
-
: entry.backendUrl;
|
|
46
|
-
}
|
|
47
41
|
function listWorkspaces() {
|
|
42
|
+
const listing = { workspaces: [], skipped: [] };
|
|
48
43
|
if (!(0, node_fs_1.existsSync)(config_1.DMS_FRONTEND_HOME))
|
|
49
|
-
return
|
|
50
|
-
const workspaces = [];
|
|
44
|
+
return listing;
|
|
51
45
|
for (const entry of (0, node_fs_1.readdirSync)(config_1.DMS_FRONTEND_HOME, { withFileTypes: true })) {
|
|
52
46
|
if (!entry.isDirectory())
|
|
53
47
|
continue;
|
|
54
48
|
const dir = (0, node_path_1.join)(config_1.DMS_FRONTEND_HOME, entry.name);
|
|
55
49
|
const meta = readWorkspaceMeta(dir);
|
|
56
50
|
if (meta?.backendUrl === undefined) {
|
|
57
|
-
|
|
51
|
+
listing.skipped.push(dir);
|
|
58
52
|
continue;
|
|
59
53
|
}
|
|
60
|
-
workspaces.push({
|
|
54
|
+
listing.workspaces.push({
|
|
61
55
|
dir,
|
|
62
56
|
backendUrl: meta.backendUrl,
|
|
63
57
|
workspaceKey: meta.workspaceKey,
|
|
58
|
+
lastUsedAt: meta.updatedAt ?? metaModifiedAt(dir),
|
|
64
59
|
});
|
|
65
60
|
}
|
|
66
|
-
|
|
61
|
+
listing.workspaces.sort((a, b) => b.lastUsedAt.localeCompare(a.lastUsedAt));
|
|
62
|
+
return listing;
|
|
67
63
|
}
|
|
68
64
|
function readWorkspaceMeta(dir) {
|
|
69
65
|
let meta;
|
|
@@ -79,8 +75,47 @@ function readWorkspaceMeta(dir) {
|
|
|
79
75
|
workspaceKey: typeof record?.workspaceKey === "string"
|
|
80
76
|
? record.workspaceKey
|
|
81
77
|
: undefined,
|
|
78
|
+
updatedAt: typeof record?.updatedAt === "string" &&
|
|
79
|
+
!Number.isNaN(Date.parse(record.updatedAt))
|
|
80
|
+
? new Date(record.updatedAt).toISOString()
|
|
81
|
+
: undefined,
|
|
82
82
|
};
|
|
83
83
|
}
|
|
84
|
+
function metaModifiedAt(dir) {
|
|
85
|
+
return (0, node_fs_1.statSync)((0, node_path_1.join)(dir, WORKSPACE_META_FILE)).mtime.toISOString();
|
|
86
|
+
}
|
|
87
|
+
function directorySize(dir) {
|
|
88
|
+
const seen = new Set();
|
|
89
|
+
const pending = [dir];
|
|
90
|
+
let total = 0;
|
|
91
|
+
for (let current = pending.pop(); current; current = pending.pop()) {
|
|
92
|
+
let entries;
|
|
93
|
+
try {
|
|
94
|
+
entries = (0, node_fs_1.readdirSync)(current, { withFileTypes: true });
|
|
95
|
+
}
|
|
96
|
+
catch {
|
|
97
|
+
continue;
|
|
98
|
+
}
|
|
99
|
+
for (const entry of entries) {
|
|
100
|
+
const path = (0, node_path_1.join)(current, entry.name);
|
|
101
|
+
if (entry.isDirectory()) {
|
|
102
|
+
pending.push(path);
|
|
103
|
+
continue;
|
|
104
|
+
}
|
|
105
|
+
try {
|
|
106
|
+
const stats = (0, node_fs_1.lstatSync)(path);
|
|
107
|
+
const inode = `${stats.dev}:${stats.ino}`;
|
|
108
|
+
if (stats.nlink > 1 && seen.has(inode))
|
|
109
|
+
continue;
|
|
110
|
+
seen.add(inode);
|
|
111
|
+
total += stats.size;
|
|
112
|
+
}
|
|
113
|
+
catch {
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
return total;
|
|
118
|
+
}
|
|
84
119
|
function writeWorkspaceMeta(workspaceDir, backendUrl, workspaceKey) {
|
|
85
120
|
(0, node_fs_1.writeFileSync)((0, node_path_1.join)(workspaceDir, WORKSPACE_META_FILE), JSON.stringify({
|
|
86
121
|
backendUrl: (0, config_1.canonicalizeBackendUrl)(backendUrl),
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@antelopejs/dms-frontend",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.9",
|
|
4
4
|
"description": "Frontend-agnostic loader for AntelopeJS DMS, shipping the Vue 3 renderer (Vite, Inertia, SSR)",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"antelope",
|
|
@@ -55,7 +55,7 @@
|
|
|
55
55
|
"scripts": {
|
|
56
56
|
"build": "rimraf dist && tsc",
|
|
57
57
|
"prepack": "pnpm run build",
|
|
58
|
-
"release": "pnpm run lint && pnpm run
|
|
58
|
+
"release": "pnpm run lint && pnpm run build && pnpm run test && release-it",
|
|
59
59
|
"test": "node --import tsx --test tests/*.test.ts tests/*.test.mts",
|
|
60
60
|
"test:real-source": "pnpm run build && node dist/verify-source-runner.js",
|
|
61
61
|
"format": "oxfmt .",
|
|
@@ -66,21 +66,18 @@
|
|
|
66
66
|
"knip:all": "knip"
|
|
67
67
|
},
|
|
68
68
|
"dependencies": {
|
|
69
|
-
"boxen": "^5.1.2",
|
|
70
|
-
"chalk": "^4.1.2",
|
|
71
69
|
"chokidar": "^4.0.3",
|
|
72
70
|
"commander": "^13.1.0",
|
|
73
|
-
"figlet": "^1.9.4",
|
|
74
71
|
"ignore": "^7.0.5",
|
|
75
72
|
"semver": "^7.7.4",
|
|
76
73
|
"unzipper": "^0.12.3"
|
|
77
74
|
},
|
|
78
75
|
"devDependencies": {
|
|
76
|
+
"@antelopejs/core": "^1.13.0",
|
|
79
77
|
"@antelopejs/tooling-configs": ">=0.0.7 <1.0.0",
|
|
80
78
|
"@iconify/utils": "^3.1.0",
|
|
81
79
|
"@iconify/vue": "^5.0.1",
|
|
82
80
|
"@inertiajs/vue3": "^3.7.1",
|
|
83
|
-
"@types/figlet": "^1.7.0",
|
|
84
81
|
"@types/node": "^24.8.1",
|
|
85
82
|
"@types/semver": "^7.7.1",
|
|
86
83
|
"@types/unzipper": "^0.10.11",
|
|
@@ -104,12 +101,7 @@
|
|
|
104
101
|
"vue-i18n": "^11.2.7"
|
|
105
102
|
},
|
|
106
103
|
"peerDependencies": {
|
|
107
|
-
"@antelopejs/core": ">=1.
|
|
108
|
-
},
|
|
109
|
-
"peerDependenciesMeta": {
|
|
110
|
-
"@antelopejs/core": {
|
|
111
|
-
"optional": true
|
|
112
|
-
}
|
|
104
|
+
"@antelopejs/core": ">=1.13.0 <2"
|
|
113
105
|
},
|
|
114
106
|
"optionalDependencies": {
|
|
115
107
|
"@oxc-parser/binding-darwin-arm64": "^0.95.0",
|
|
@@ -333,6 +333,11 @@ export interface DmsFrontendRuntime {
|
|
|
333
333
|
pendingNavigation?: Promise<void>;
|
|
334
334
|
hasNavigationListener: boolean;
|
|
335
335
|
pageVersion: number;
|
|
336
|
+
/**
|
|
337
|
+
* Identifies the page on screen (its path, or the error page shown at that
|
|
338
|
+
* path), whose meta `route.meta` holds.
|
|
339
|
+
*/
|
|
340
|
+
pageKey?: string;
|
|
336
341
|
/** The `Cookie` header of the request a server runtime renders. */
|
|
337
342
|
requestCookies?: string;
|
|
338
343
|
/**
|
|
@@ -495,7 +500,10 @@ function parseRoute(runtime: DmsFrontendRuntime, url: string): DmsRoute {
|
|
|
495
500
|
}
|
|
496
501
|
|
|
497
502
|
function updateRoute(runtime: DmsFrontendRuntime, url: string): void {
|
|
498
|
-
|
|
503
|
+
// The meta is the page's, not the URL's: a page declares it, then reads its
|
|
504
|
+
// route, and the middleware the meta selected must still find it.
|
|
505
|
+
const { meta: _pageMeta, ...location } = parseRoute(runtime, url);
|
|
506
|
+
Object.assign(runtime.route, location);
|
|
499
507
|
}
|
|
500
508
|
|
|
501
509
|
function locationToUrl(
|
|
@@ -578,16 +586,22 @@ async function visit(
|
|
|
578
586
|
export function useDmsRoute(pattern?: string): DmsRoute {
|
|
579
587
|
const runtime = useDmsRuntime();
|
|
580
588
|
runtime.currentRoutePattern = pattern;
|
|
589
|
+
// A server render answers one request, whose URL the runtime was given with
|
|
590
|
+
// its page, and never changes it. Inertia's page is one ref for the whole
|
|
591
|
+
// process, written by each render as it starts: after an await it may hold
|
|
592
|
+
// another request's URL, and while the plugins set up, the previous one's.
|
|
593
|
+
if (runtime.isServer) {
|
|
594
|
+
updateRoute(runtime, runtime.route.fullPath);
|
|
595
|
+
return runtime.route;
|
|
596
|
+
}
|
|
581
597
|
const page = usePage();
|
|
582
|
-
|
|
583
|
-
//
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
(url) => updateRoute(runtime, url),
|
|
590
|
-
);
|
|
598
|
+
// The browser's plugins set up before Inertia has a page: until then the
|
|
599
|
+
// runtime's route is the initial page's.
|
|
600
|
+
if (page.url !== undefined) updateRoute(runtime, page.url);
|
|
601
|
+
watch(
|
|
602
|
+
() => page.url,
|
|
603
|
+
(url) => updateRoute(runtime, url),
|
|
604
|
+
);
|
|
591
605
|
return runtime.route;
|
|
592
606
|
}
|
|
593
607
|
|
|
@@ -1360,6 +1374,14 @@ export function getDmsLayoutProps(
|
|
|
1360
1374
|
export function hydrateDmsPageProps(props: DmsPageProps, url?: string): void {
|
|
1361
1375
|
const runtime = useDmsRuntime();
|
|
1362
1376
|
if (url) updateRoute(runtime, url);
|
|
1377
|
+
// Pages are keyed by path: another path mounts another page, which declares
|
|
1378
|
+
// its own meta, and so does an error page shown in place of the page. A
|
|
1379
|
+
// visit that only changes the query keeps the page, and so its meta.
|
|
1380
|
+
const pageKey = props.error ? `error:${props.path}` : props.path;
|
|
1381
|
+
if (pageKey !== runtime.pageKey) {
|
|
1382
|
+
runtime.pageKey = pageKey;
|
|
1383
|
+
runtime.route.meta = {};
|
|
1384
|
+
}
|
|
1363
1385
|
runtime.pageVersion++;
|
|
1364
1386
|
runtime.currentError.value = null;
|
|
1365
1387
|
useDmsState<DmsUser | null>("dms-user", () => null).value =
|
|
@@ -5,6 +5,10 @@
|
|
|
5
5
|
"scripts": {
|
|
6
6
|
"dev": "vite",
|
|
7
7
|
"build": "vite build && node compress-assets.mjs && vite build --ssr ssr-renderer.ts --outDir dist/ssr && vite build --config vite.email.config.ts",
|
|
8
|
+
"build:client": "vite build",
|
|
9
|
+
"build:compress": "node compress-assets.mjs",
|
|
10
|
+
"build:ssr": "vite build --ssr ssr-renderer.ts --outDir dist/ssr",
|
|
11
|
+
"build:email": "vite build --config vite.email.config.ts",
|
|
8
12
|
"typecheck": "node --import ./typecheck-loader.mjs ./node_modules/vue-tsc/bin/vue-tsc.js --noEmit",
|
|
9
13
|
"start": "node server.mjs"
|
|
10
14
|
},
|