@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
package/README.md
CHANGED
|
@@ -71,6 +71,8 @@ pnpm add @antelopejs/core @antelopejs/dms-frontend
|
|
|
71
71
|
pnpm add -g @antelopejs/core @antelopejs/dms-frontend
|
|
72
72
|
```
|
|
73
73
|
|
|
74
|
+
The plugin prints through the output module of `@antelopejs/core`
|
|
75
|
+
(`@antelopejs/core/cli`), so it needs `@antelopejs/core` 1.13.0 or later.
|
|
74
76
|
`npm install -g` works too; this repository and every generated workspace use
|
|
75
77
|
pnpm. Inside a package script, `ajs` resolves from `node_modules/.bin`, and the
|
|
76
78
|
`dms` command it delegates to resolves the project-local plugin, so a script
|
|
@@ -83,25 +85,68 @@ ajs dms prepare -b http://localhost:5010
|
|
|
83
85
|
ajs dms dev -b http://localhost:5010 -p 3001
|
|
84
86
|
ajs dms build -b https://dms.example.com
|
|
85
87
|
ajs dms start -b https://dms.example.com -p 3001
|
|
88
|
+
ajs dms workspaces
|
|
89
|
+
ajs dms workspaces --json
|
|
86
90
|
ajs dms clean -b https://dms.example.com
|
|
87
91
|
ajs dms clean --all
|
|
92
|
+
ajs dms help environment
|
|
88
93
|
```
|
|
89
94
|
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
95
|
+
`ajs dms <command> --help` shows what a command does, its options and a few
|
|
96
|
+
examples. `ajs dms help environment` lists every variable the CLI and the
|
|
97
|
+
generated server read, from the environment or the project's `.env`.
|
|
98
|
+
|
|
99
|
+
`--help`, `--version`, `workspaces` and `clean` run from any directory. `build` and `start`
|
|
100
|
+
need a backend URL through `-b` or `DMS_API_BASE_URL`, and exit 2 without one.
|
|
101
|
+
`dev` also discovers it from the enclosing antelope project's
|
|
102
|
+
`.antelope/dev.json`, and exits 1 when there is none to discover.
|
|
96
103
|
`verify-source` needs `--layer` instead.
|
|
97
104
|
|
|
105
|
+
`prepare` runs anywhere: when it cannot prepare the workspace it warns on
|
|
106
|
+
stderr and exits 0, so a frontend module's `postinstall` hook never fails an
|
|
107
|
+
install. The warning says whether prepare was skipped or failed. Skipped means
|
|
108
|
+
no backend is in reach (no URL, an unreachable backend, `--offline` without a
|
|
109
|
+
cached manifest), as in CI: the generated types are refreshed later from a
|
|
110
|
+
development environment. Failed means something a later run will not fix: a
|
|
111
|
+
refused credential, an incompatible manifest or renderer range, an invalid URL.
|
|
112
|
+
A step that needs the types, such as a CI type-check, passes `--strict` (or
|
|
113
|
+
sets `DMS_PREPARE_STRICT=1`): prepare then exits 1 in every one of these cases,
|
|
114
|
+
and 2 when the backend URL is missing or invalid. On success, it says how many
|
|
115
|
+
modules it found and how long it took.
|
|
116
|
+
|
|
117
|
+
`build` ends with where it wrote the production frontend (the `dist`
|
|
118
|
+
directory of the workspace), how long it took, and the `ajs dms start` command
|
|
119
|
+
to run next, with the same backend URL and `DMS_SESSION_SECRET`. Its output is
|
|
120
|
+
deployed, so it never builds silently from the cache: when the backend cannot
|
|
121
|
+
be reached or fails, `build` exits 1, says whether a cached manifest exists and
|
|
122
|
+
how old it is, and builds from it only when asked with `--offline`. `dev` and
|
|
123
|
+
`prepare` still fall back to the cache, with a warning.
|
|
124
|
+
|
|
125
|
+
The commands follow the output contract of `ajs`: what the CLI itself prints
|
|
126
|
+
on stdout is only the help and the version, and everything else (progress,
|
|
127
|
+
warnings, errors) goes to stderr. The dependency install and each step of the
|
|
128
|
+
production build run as one progress line each: their output is kept and its
|
|
129
|
+
last lines are shown when they fail, with paths into the generated workspace
|
|
130
|
+
pointing at the frontend-module sources instead. The server `dev` and `start`
|
|
131
|
+
run keeps its own output. A command exits 0 on success, 1 on a failure, 2 on a
|
|
132
|
+
usage error (an unknown command or option, a missing or invalid argument, a
|
|
133
|
+
missing configuration value) and 130 when stopped with Ctrl+C. `--no-color` or
|
|
134
|
+
`NO_COLOR=1` turns colors off, and terminals that cannot draw Unicode, such as
|
|
135
|
+
`TERM=dumb`, get ASCII symbols. `--verbose` or `ANTELOPEJS_VERBOSE` streams
|
|
136
|
+
the full `pnpm` and Vite output, each line behind the name of the step, and adds
|
|
137
|
+
the stack trace of the underlying error to a failure. `ajs --no-color dms …`,
|
|
138
|
+
`ajs --verbose dms …` and `ajs --verbose=<channels> dms …` set those variables
|
|
139
|
+
for the plugin.
|
|
140
|
+
|
|
98
141
|
The CLI checks npm for a newer release at most once a day and prints a one-line
|
|
99
|
-
notice on stderr
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
142
|
+
notice on stderr, only to a terminal: as the last line of a command that
|
|
143
|
+
succeeded, or under the ready block of `dev` and `start`. Help, `--version`,
|
|
144
|
+
`--json`, failed commands, pipes and `TERM=dumb` never show it. The lookup starts
|
|
145
|
+
with the command and never delays its exit; a lookup that comes back empty —
|
|
146
|
+
offline or throttled — is retried after an hour instead of counting as the day's
|
|
147
|
+
attempt. The throttle stamp lives at
|
|
148
|
+
`~/.antelopejs/dms-frontend/update-check.json`. Set `NO_UPDATE_NOTIFIER=1`, pass
|
|
149
|
+
`--no-update-check`, or run under `CI` to turn the check off.
|
|
105
150
|
|
|
106
151
|
Manifest negotiation and module materialization are the renderer contract described in [Renderers](#renderers).
|
|
107
152
|
|
|
@@ -151,7 +196,7 @@ A frontend module names the `@antelopejs/dms-frontend` releases it runs on in it
|
|
|
151
196
|
}
|
|
152
197
|
```
|
|
153
198
|
|
|
154
|
-
`prepare`, `dev`, `build` and `verify-source` read it before they generate anything, whether the manifest was just fetched or replayed from the cache, and stop when this release falls outside a module's range: `dev`, `build` and `verify-source` exit 1, and `prepare` warns and exits 0, as it does for any setup it cannot complete. The message names each such module, its range and this release, rather than letting the build, the type-check or a render fail later on an API the release no longer has, or does not have yet. A prerelease is checked as the release it leads to: `0.4.0-next.1` is outside `<0.4.0`. A module that declares no range still loads, and a warning names it once. `start` checks nothing: it runs what `build` produced.
|
|
199
|
+
`prepare`, `dev`, `build` and `verify-source` read it before they generate anything, whether the manifest was just fetched or replayed from the cache, and stop when this release falls outside a module's range: `dev`, `build` and `verify-source` exit 1, and `prepare` warns and exits 0, as it does for any setup it cannot complete, unless `--strict` is given. The message names each such module, its range and this release, rather than letting the build, the type-check or a render fail later on an API the release no longer has, or does not have yet. A prerelease is checked as the release it leads to: `0.4.0-next.1` is outside `<0.4.0`. A module that declares no range still loads, and a warning names it once. `start` checks nothing: it runs what `build` produced.
|
|
155
200
|
|
|
156
201
|
Cap the range below the next breaking release, which is the next minor release while this package is 0.x, and widen it once the module has been checked against that release. A range left open to `<1.0.0` admits the very release that removes something the module calls.
|
|
157
202
|
|
|
@@ -213,7 +258,7 @@ which is the order the loader itself materializes them in.
|
|
|
213
258
|
|
|
214
259
|
In development, `ajs dms` discovers the backend from the nearest live `.antelope/dev.json`. It reads the local bootstrap credential from `.antelope/dms-dev.json` only when that discovered backend matches the destination URL. For production and CI, set `DMS_API_BASE_URL` and `DMS_BOOTSTRAP_SECRET` in the environment, or in the project's `.env`, rather than passing credentials on the command line.
|
|
215
260
|
|
|
216
|
-
Each canonical backend URL gets an owner-only workspace under `~/.antelopejs/dms-frontend` (see [Workspaces](#workspaces) for how the key is derived). Manifest caches, private module configuration, and extracted archives retain restrictive permissions. `--offline` reuses the last successful manifest and archive;
|
|
261
|
+
Each canonical backend URL gets an owner-only workspace under `~/.antelopejs/dms-frontend` (see [Workspaces](#workspaces) for how the key is derived). Manifest caches, private module configuration, and extracted archives retain restrictive permissions. `--offline` reuses the last successful manifest and archive; `dev` and `prepare` also fall back to them when the backend cannot be reached or fails, and `build` only with `--offline`. An authorization failure never falls back to privileged cached data.
|
|
217
262
|
|
|
218
263
|
## Configuration
|
|
219
264
|
|
|
@@ -324,9 +369,21 @@ different port between runs and re-keying on the URL would discard
|
|
|
324
369
|
`node_modules`, the manifest cache and the client-side appId scope every time it
|
|
325
370
|
does. The consequence is that `ajs dms dev` followed by `ajs dms build -b <url>`
|
|
326
371
|
against the same backend creates two workspaces of their own — several hundred
|
|
327
|
-
megabytes each. Pass `-b` to `dev` to share a single one. `
|
|
328
|
-
both
|
|
329
|
-
reaches the URL-keyed one.
|
|
372
|
+
megabytes each. Pass `-b` to `dev` to share a single one. `workspaces` lists
|
|
373
|
+
both, with the project a workspace is keyed on, its size and its last use;
|
|
374
|
+
`clean -b <url>` only reaches the URL-keyed one. `clean` takes its target from
|
|
375
|
+
`-b` or `--all` only: it never reads `DMS_API_BASE_URL` from the environment or
|
|
376
|
+
`.env`, so running it without either is an error rather than a deletion.
|
|
377
|
+
|
|
378
|
+
`workspaces` prints an aligned table on a terminal. Piped, it prints one
|
|
379
|
+
tab-separated line per workspace, without a header: id, backend URL, key type
|
|
380
|
+
(`url` or `project`), project directory, size in bytes, last use (ISO 8601) and
|
|
381
|
+
workspace directory. `--json` prints one JSON document with the same fields;
|
|
382
|
+
everything else the command says goes to stderr.
|
|
383
|
+
|
|
384
|
+
`clean --all` lists what it removes and asks before removing it. Without a
|
|
385
|
+
terminal to ask on (a pipe, CI), it exits 2 unless `--yes` is given. Both forms
|
|
386
|
+
of `clean` say how much space they freed.
|
|
330
387
|
|
|
331
388
|
A `DMS_API_BASE_URL` line in `.env` counts as an explicit backend, so a project
|
|
332
389
|
that configures one gets the single shared workspace and gives up autodiscovery
|
|
@@ -378,11 +435,13 @@ needs no exception for the Iconify API. Icons reach the page two ways:
|
|
|
378
435
|
|
|
379
436
|
| Option | Environment | Purpose |
|
|
380
437
|
| --- | --- | --- |
|
|
381
|
-
| `-b, --backend-url` | `DMS_API_BASE_URL` | DMS backend URL |
|
|
382
|
-
| `-p, --port` | `PORT` | Frontend port, default `3001` |
|
|
383
|
-
| `-f, --force` | | Reinstall workspace dependencies |
|
|
384
|
-
| `--offline` | `DMS_OFFLINE` | Reuse cached manifest and archives |
|
|
438
|
+
| `-b, --backend-url` | `DMS_API_BASE_URL` (except `clean`) | DMS backend URL, `http://` or `https://` |
|
|
439
|
+
| `-p, --port` | `PORT` | Frontend port from 1 to 65535, default `3001`; `dev` moves to the next free port, `start` stops when it is in use |
|
|
440
|
+
| `-f, --force` | | Reinstall workspace dependencies; `build` also extracts the layers archive from scratch |
|
|
441
|
+
| `--offline` | `DMS_OFFLINE` | Reuse cached manifest and archives; the only way `build` uses them |
|
|
442
|
+
| `--strict` | `DMS_PREPARE_STRICT` | Make `prepare` fail instead of warning; see [Commands](#commands) |
|
|
385
443
|
| `--bootstrap-secret` | `DMS_BOOTSTRAP_SECRET` | Backend bootstrap credential |
|
|
444
|
+
| | `HOST` | Address the frontend server binds to (default `0.0.0.0`) |
|
|
386
445
|
| | `DMS_COOKIE_SECURE` | Secure cookies (`true` by default; `ajs dms dev` defaults to `false`) |
|
|
387
446
|
| | `DMS_TRUSTED_PROXY_HOPS` | Number of trusted, rightmost reverse-proxy hops (default `0`) |
|
|
388
447
|
| | `DMS_SESSION_SECRET` | Session cookie encryption key, 32 characters or more (required for login) |
|
|
@@ -393,7 +452,7 @@ needs no exception for the Iconify API. Icons reach the page two ways:
|
|
|
393
452
|
|
|
394
453
|
All of these can be set in the project's `.env` instead of the environment; see [Configuration](#configuration).
|
|
395
454
|
|
|
396
|
-
`ajs dms dev` takes the two manifest secrets from the manifest it fetches, and `ajs dms start` from the manifest cached by `ajs dms build`. An explicitly set variable always wins over the manifest value. Before the server starts, both commands
|
|
455
|
+
`ajs dms dev` takes the two manifest secrets from the manifest it fetches, and `ajs dms start` from the manifest cached by `ajs dms build`. An explicitly set variable always wins over the manifest value. Before the server starts, both commands warn about each secret that is not set and what stops working without it; once the server is ready, they list where the others come from (the environment, the manifest or the build-time manifest), never their values. When several modules publish the same secret, the first one in manifest-priority order wins, and a module publishing a different value is named in a warning.
|
|
397
456
|
|
|
398
457
|
Use pnpm for all repository and workspace operations.
|
|
399
458
|
|
|
@@ -408,6 +467,6 @@ ajs dms verify-source \
|
|
|
408
467
|
--local-package @antelopejs/dms=/path/to/dms
|
|
409
468
|
```
|
|
410
469
|
|
|
411
|
-
A supplied package whose declared range excludes this adapter version is refused before anything is installed (see [Declaring the loader releases a module supports](#declaring-the-loader-releases-a-module-supports)). The verifier
|
|
470
|
+
A supplied package whose declared range excludes this adapter version is refused before anything is installed (see [Declaring the loader releases a module supports](#declaring-the-loader-releases-a-module-supports)). The verifier generates a temporary workspace, installs it, builds client, SSR, and email bundles, checks what the client bundle loads, runs `vue-tsc`, renders eight DMS email templates, and checks that the email bundle excludes browser-only modules. Each check is one line that ends in ✔ or ✖, and the run ends with `Verified <n> modules` and its duration, or with the first failure: a type or Vite error points at the file in the package you passed (`./frontend-vue/app/components/Callout.vue:17:7`), not at its copy in the workspace, and the command exits 1. `--verbose` streams the pnpm, Vite and `vue-tsc` output. The workspace is removed when the run ends. It does not start a backend or publish packages. Repository development can invoke the same runner with `DMS_LAYER_SOURCE` through `pnpm test:real-source`.
|
|
412
471
|
|
|
413
472
|
Refresh rotation is single-flight within one frontend process. Horizontally scaled deployments must use sticky routing so a browser reaches the same process, or replace this process-local behavior with an external session adapter. It does not provide a distributed rotation guarantee.
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
export declare function exitCodeForSignal(signal: NodeJS.Signals): number;
|
|
2
|
+
export declare class CancelledError extends Error {
|
|
3
|
+
readonly signal: NodeJS.Signals;
|
|
4
|
+
readonly exitCode: number;
|
|
5
|
+
readonly stopped: string;
|
|
6
|
+
readonly context: string[];
|
|
7
|
+
constructor(signal: NodeJS.Signals, stopped?: string, context?: string);
|
|
8
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.CancelledError = void 0;
|
|
4
|
+
exports.exitCodeForSignal = exitCodeForSignal;
|
|
5
|
+
const node_os_1 = require("node:os");
|
|
6
|
+
const cli_1 = require("@antelopejs/core/cli");
|
|
7
|
+
function exitCodeForSignal(signal) {
|
|
8
|
+
const number = node_os_1.constants.signals[signal];
|
|
9
|
+
return 128 + (typeof number === "number" ? number : 15);
|
|
10
|
+
}
|
|
11
|
+
class CancelledError extends Error {
|
|
12
|
+
constructor(signal, stopped = "Stopped", context) {
|
|
13
|
+
const cause = signal === "SIGINT" ? "" : ` (${signal})`;
|
|
14
|
+
const parts = [`${stopped}${cause}`, ...(context ? [context] : [])];
|
|
15
|
+
super(parts.join((0, cli_1.getProcessUi)().symbols.separator));
|
|
16
|
+
this.signal = signal;
|
|
17
|
+
this.name = "CancelledError";
|
|
18
|
+
this.exitCode = exitCodeForSignal(signal);
|
|
19
|
+
this.stopped = parts[0];
|
|
20
|
+
this.context = parts.slice(1);
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
exports.CancelledError = CancelledError;
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { type CliProblem, type Ui } from "@antelopejs/core/cli";
|
|
2
|
+
import type { ResolvedLayer } from "./workspace";
|
|
3
|
+
export declare const VERBOSE_OUTPUT_HINT = "Run with --verbose for the full output.";
|
|
4
|
+
export declare function stripAnsi(text: string): string;
|
|
5
|
+
export declare class LineSplitter {
|
|
6
|
+
private readonly onLine;
|
|
7
|
+
private pending;
|
|
8
|
+
constructor(onLine: (line: string) => void);
|
|
9
|
+
push(chunk: string): void;
|
|
10
|
+
flush(): void;
|
|
11
|
+
}
|
|
12
|
+
export declare class OutputTail {
|
|
13
|
+
private readonly capacity;
|
|
14
|
+
private readonly kept;
|
|
15
|
+
constructor(capacity?: number);
|
|
16
|
+
push(line: string): void;
|
|
17
|
+
lines(): string[];
|
|
18
|
+
}
|
|
19
|
+
export declare function failureTail(lines: string[], count?: number, keepStack?: boolean): string[];
|
|
20
|
+
export interface ChildFailure {
|
|
21
|
+
title: string;
|
|
22
|
+
command: string;
|
|
23
|
+
code: number;
|
|
24
|
+
lines: string[];
|
|
25
|
+
isVerbose: boolean;
|
|
26
|
+
}
|
|
27
|
+
export declare function describeChildFailure(failure: ChildFailure): CliProblem;
|
|
28
|
+
export type PathMapper = (line: string) => string;
|
|
29
|
+
export interface PathMapperOptions {
|
|
30
|
+
showSource?: (path: string) => string;
|
|
31
|
+
shownWorkspace?: string;
|
|
32
|
+
platform?: NodeJS.Platform;
|
|
33
|
+
}
|
|
34
|
+
export declare function createFileNamer(layers: ResolvedLayer[]): (file: string) => string;
|
|
35
|
+
export declare function createPathMapper(workspaceDir: string, layers: ResolvedLayer[], options?: PathMapperOptions): PathMapper;
|
|
36
|
+
export declare function writeChildLine(name: string, line: string, ui?: Ui): void;
|
|
37
|
+
export declare class InstallProgress {
|
|
38
|
+
private resolved?;
|
|
39
|
+
private added?;
|
|
40
|
+
private installed?;
|
|
41
|
+
private isUpToDate;
|
|
42
|
+
read(line: string): boolean;
|
|
43
|
+
get label(): string;
|
|
44
|
+
get doneLabel(): string;
|
|
45
|
+
}
|
|
@@ -0,0 +1,183 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.InstallProgress = exports.OutputTail = exports.LineSplitter = exports.VERBOSE_OUTPUT_HINT = void 0;
|
|
4
|
+
exports.stripAnsi = stripAnsi;
|
|
5
|
+
exports.failureTail = failureTail;
|
|
6
|
+
exports.describeChildFailure = describeChildFailure;
|
|
7
|
+
exports.createFileNamer = createFileNamer;
|
|
8
|
+
exports.createPathMapper = createPathMapper;
|
|
9
|
+
exports.writeChildLine = writeChildLine;
|
|
10
|
+
const node_fs_1 = require("node:fs");
|
|
11
|
+
const cli_1 = require("@antelopejs/core/cli");
|
|
12
|
+
const layers_1 = require("./layers");
|
|
13
|
+
const output_1 = require("./output");
|
|
14
|
+
const ESC = "\u001b";
|
|
15
|
+
const ANSI_SEQUENCE = new RegExp(`${ESC}\\[[0-9;?]*[ -/]*[@-~]`, "g");
|
|
16
|
+
const LINE_BREAK = /\r?\n/;
|
|
17
|
+
const STACK_FRAME = /^\s+at\s/;
|
|
18
|
+
const NODE_DEPRECATION_LINES = [
|
|
19
|
+
/^\(node:\d+\) \[DEP\d+\]/,
|
|
20
|
+
/^\(Use `node --trace-/,
|
|
21
|
+
];
|
|
22
|
+
const GUTTER_BARS = { unicode: "│", ascii: "|" };
|
|
23
|
+
const KEPT_OUTPUT_LINES = 200;
|
|
24
|
+
const FAILURE_TAIL_LINES = 15;
|
|
25
|
+
exports.VERBOSE_OUTPUT_HINT = "Run with --verbose for the full output.";
|
|
26
|
+
function stripAnsi(text) {
|
|
27
|
+
return text.replace(ANSI_SEQUENCE, "");
|
|
28
|
+
}
|
|
29
|
+
class LineSplitter {
|
|
30
|
+
constructor(onLine) {
|
|
31
|
+
this.onLine = onLine;
|
|
32
|
+
this.pending = "";
|
|
33
|
+
}
|
|
34
|
+
push(chunk) {
|
|
35
|
+
const lines = `${this.pending}${chunk}`.split(LINE_BREAK);
|
|
36
|
+
this.pending = lines.pop() ?? "";
|
|
37
|
+
lines.forEach((line) => this.onLine(line));
|
|
38
|
+
}
|
|
39
|
+
flush() {
|
|
40
|
+
if (this.pending !== "")
|
|
41
|
+
this.onLine(this.pending);
|
|
42
|
+
this.pending = "";
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
exports.LineSplitter = LineSplitter;
|
|
46
|
+
class OutputTail {
|
|
47
|
+
constructor(capacity = KEPT_OUTPUT_LINES) {
|
|
48
|
+
this.capacity = capacity;
|
|
49
|
+
this.kept = [];
|
|
50
|
+
}
|
|
51
|
+
push(line) {
|
|
52
|
+
this.kept.push(line);
|
|
53
|
+
if (this.kept.length > this.capacity)
|
|
54
|
+
this.kept.shift();
|
|
55
|
+
}
|
|
56
|
+
lines() {
|
|
57
|
+
return [...this.kept];
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
exports.OutputTail = OutputTail;
|
|
61
|
+
function failureTail(lines, count = FAILURE_TAIL_LINES, keepStack = false) {
|
|
62
|
+
const shown = lines
|
|
63
|
+
.map((line) => stripAnsi(line).trimEnd())
|
|
64
|
+
.filter((line) => line.trim() !== "")
|
|
65
|
+
.filter((line) => !NODE_DEPRECATION_LINES.some((notice) => notice.test(line)))
|
|
66
|
+
.filter((line) => keepStack || !STACK_FRAME.test(line));
|
|
67
|
+
return shown.slice(Math.max(0, shown.length - count));
|
|
68
|
+
}
|
|
69
|
+
function describeChildFailure(failure) {
|
|
70
|
+
const { title, command, code, lines, isVerbose } = failure;
|
|
71
|
+
if (isVerbose) {
|
|
72
|
+
return {
|
|
73
|
+
title,
|
|
74
|
+
reason: `${command} exited with code ${code}; its output is above.`,
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
return {
|
|
78
|
+
title,
|
|
79
|
+
reason: `${command} exited with code ${code}.`,
|
|
80
|
+
details: [...failureTail(lines), exports.VERBOSE_OUTPUT_HINT],
|
|
81
|
+
};
|
|
82
|
+
}
|
|
83
|
+
const PATH_END = "(?![\\w.@-])";
|
|
84
|
+
const RELATIVE_START = `(?:(?<=${ESC}\\[[0-9;]*m)|(?<![\\w.@/\\\\-]))`;
|
|
85
|
+
const LAYERS_SUBDIR = "frontend-modules";
|
|
86
|
+
function escapeRegExp(text) {
|
|
87
|
+
return text.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
88
|
+
}
|
|
89
|
+
function pathPattern(path) {
|
|
90
|
+
return path
|
|
91
|
+
.split(/[\\/]/)
|
|
92
|
+
.map((segment) => escapeRegExp(segment))
|
|
93
|
+
.join("[\\\\/]");
|
|
94
|
+
}
|
|
95
|
+
function hasLocalSource(layer) {
|
|
96
|
+
return !!layer.sourcePath && (0, node_fs_1.existsSync)(layer.sourcePath);
|
|
97
|
+
}
|
|
98
|
+
function shownSource(layer, showSource) {
|
|
99
|
+
if (hasLocalSource(layer))
|
|
100
|
+
return showSource(layer.sourcePath ?? "");
|
|
101
|
+
return layer.packageName ?? (0, layers_1.getLayerSafeName)(layer);
|
|
102
|
+
}
|
|
103
|
+
function createFileNamer(layers) {
|
|
104
|
+
const packages = layers
|
|
105
|
+
.filter((layer) => layer.packageName && !hasLocalSource(layer))
|
|
106
|
+
.map((layer) => shownSource(layer, output_1.showPath));
|
|
107
|
+
return (file) => {
|
|
108
|
+
const owner = packages.find((name) => ["/", "\\"].some((separator) => file.startsWith(`${name}${separator}`)));
|
|
109
|
+
if (!owner)
|
|
110
|
+
return file;
|
|
111
|
+
return `${file.slice(owner.length + 1)} in ${owner}`;
|
|
112
|
+
};
|
|
113
|
+
}
|
|
114
|
+
function createPathMapper(workspaceDir, layers, options = {}) {
|
|
115
|
+
const { showSource = output_1.showPath, shownWorkspace = (0, output_1.showWorkspace)(workspaceDir), platform = process.platform, } = options;
|
|
116
|
+
const flags = platform === "win32" ? "gi" : "g";
|
|
117
|
+
const layerRules = layers
|
|
118
|
+
.filter((layer) => layer.packageName)
|
|
119
|
+
.flatMap((layer) => {
|
|
120
|
+
const shown = shownSource(layer, showSource);
|
|
121
|
+
const copy = pathPattern((0, layers_1.getLayerWorkspacePath)(workspaceDir, layer));
|
|
122
|
+
const relative = `${RELATIVE_START}${pathPattern(`${LAYERS_SUBDIR}/${(0, layers_1.getLayerSafeName)(layer)}`)}`;
|
|
123
|
+
return [copy, relative].map((pattern) => ({
|
|
124
|
+
pattern: new RegExp(`${pattern}${PATH_END}`, flags),
|
|
125
|
+
shown,
|
|
126
|
+
}));
|
|
127
|
+
});
|
|
128
|
+
const rules = [
|
|
129
|
+
...layerRules,
|
|
130
|
+
{
|
|
131
|
+
pattern: new RegExp(`${pathPattern(workspaceDir)}${PATH_END}`, flags),
|
|
132
|
+
shown: shownWorkspace,
|
|
133
|
+
},
|
|
134
|
+
];
|
|
135
|
+
return (line) => rules.reduce((mapped, { pattern, shown }) => mapped.replace(pattern, () => shown), line);
|
|
136
|
+
}
|
|
137
|
+
function hasFeedbackColor() {
|
|
138
|
+
return (0, cli_1.detectCapabilities)((0, cli_1.processCapabilityContext)()).colors.feedback;
|
|
139
|
+
}
|
|
140
|
+
function writeChildLine(name, line, ui = (0, cli_1.getProcessUi)()) {
|
|
141
|
+
const isUnicode = ui.symbols === cli_1.SYMBOL_SETS.unicode;
|
|
142
|
+
const bar = isUnicode ? GUTTER_BARS.unicode : GUTTER_BARS.ascii;
|
|
143
|
+
const text = hasFeedbackColor() ? line : stripAnsi(line);
|
|
144
|
+
(0, cli_1.getProcessTasks)().write(process.stderr, `${ui.palette().dim(`${name} ${bar}`)} ${text}\n`);
|
|
145
|
+
}
|
|
146
|
+
const PROGRESS_LINE = /^Progress: resolved (\d+), reused \d+, downloaded \d+, added (\d+)/;
|
|
147
|
+
const PACKAGES_LINE = /^Packages: \+(\d+)/;
|
|
148
|
+
const UP_TO_DATE_LINE = /^Already up to date/;
|
|
149
|
+
class InstallProgress {
|
|
150
|
+
constructor() {
|
|
151
|
+
this.isUpToDate = false;
|
|
152
|
+
}
|
|
153
|
+
read(line) {
|
|
154
|
+
const progress = PROGRESS_LINE.exec(line);
|
|
155
|
+
if (progress) {
|
|
156
|
+
this.resolved = Number(progress[1]);
|
|
157
|
+
this.added = Number(progress[2]);
|
|
158
|
+
return true;
|
|
159
|
+
}
|
|
160
|
+
const packages = PACKAGES_LINE.exec(line);
|
|
161
|
+
if (packages)
|
|
162
|
+
this.installed = Number(packages[1]);
|
|
163
|
+
if (UP_TO_DATE_LINE.test(line))
|
|
164
|
+
this.isUpToDate = true;
|
|
165
|
+
return false;
|
|
166
|
+
}
|
|
167
|
+
get label() {
|
|
168
|
+
if (this.resolved === undefined)
|
|
169
|
+
return "Installing dependencies";
|
|
170
|
+
return [
|
|
171
|
+
"Installing dependencies",
|
|
172
|
+
`${this.resolved} resolved, ${this.added ?? 0} added`,
|
|
173
|
+
].join((0, cli_1.getProcessUi)().symbols.separator);
|
|
174
|
+
}
|
|
175
|
+
get doneLabel() {
|
|
176
|
+
if (this.installed !== undefined)
|
|
177
|
+
return `Installed ${(0, cli_1.pluralize)(this.installed, "package")}`;
|
|
178
|
+
if (this.isUpToDate)
|
|
179
|
+
return "Dependencies up to date";
|
|
180
|
+
return "Installed the dependencies";
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
exports.InstallProgress = InstallProgress;
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { CliError } from "@antelopejs/core/cli";
|
|
2
|
+
import { type FramedCommandResult } from "../common";
|
|
3
|
+
export interface BuildOptions {
|
|
4
|
+
backendUrl?: string;
|
|
5
|
+
force?: boolean;
|
|
6
|
+
offline?: boolean;
|
|
7
|
+
bootstrapSecret?: string;
|
|
8
|
+
}
|
|
9
|
+
interface BuildStep {
|
|
10
|
+
script: string;
|
|
11
|
+
name: string;
|
|
12
|
+
subject: string;
|
|
13
|
+
running: string;
|
|
14
|
+
done: string;
|
|
15
|
+
failed: string;
|
|
16
|
+
stopped: string;
|
|
17
|
+
}
|
|
18
|
+
export declare const BUILD_STEPS: BuildStep[];
|
|
19
|
+
export declare function buildStepFailure(step: BuildStep, result: FramedCommandResult, isVerbose: boolean, rerun?: string, nameFile?: (file: string) => string): CliError;
|
|
20
|
+
export declare function runBuild(options: BuildOptions): Promise<void>;
|
|
21
|
+
export {};
|
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.BUILD_STEPS = void 0;
|
|
4
|
+
exports.buildStepFailure = buildStepFailure;
|
|
5
|
+
exports.runBuild = runBuild;
|
|
6
|
+
const node_path_1 = require("node:path");
|
|
7
|
+
const cli_1 = require("@antelopejs/core/cli");
|
|
8
|
+
const child_output_1 = require("../child-output");
|
|
9
|
+
const common_1 = require("../common");
|
|
10
|
+
const output_1 = require("../output");
|
|
11
|
+
const vite_output_1 = require("../vite-output");
|
|
12
|
+
const workspace_task_1 = require("./workspace-task");
|
|
13
|
+
exports.BUILD_STEPS = [
|
|
14
|
+
{
|
|
15
|
+
script: "build:client",
|
|
16
|
+
name: "client",
|
|
17
|
+
subject: "client bundle",
|
|
18
|
+
running: "Building the client bundle",
|
|
19
|
+
done: "Built the client bundle",
|
|
20
|
+
failed: "Client bundle failed",
|
|
21
|
+
stopped: "Client bundle stopped",
|
|
22
|
+
},
|
|
23
|
+
{
|
|
24
|
+
script: "build:compress",
|
|
25
|
+
name: "compress",
|
|
26
|
+
subject: "compressed assets",
|
|
27
|
+
running: "Compressing the client assets",
|
|
28
|
+
done: "Compressed the client assets",
|
|
29
|
+
failed: "Asset compression failed",
|
|
30
|
+
stopped: "Asset compression stopped",
|
|
31
|
+
},
|
|
32
|
+
{
|
|
33
|
+
script: "build:ssr",
|
|
34
|
+
name: "ssr",
|
|
35
|
+
subject: "SSR bundle",
|
|
36
|
+
running: "Building the SSR bundle",
|
|
37
|
+
done: "Built the SSR bundle",
|
|
38
|
+
failed: "SSR bundle failed",
|
|
39
|
+
stopped: "SSR bundle stopped",
|
|
40
|
+
},
|
|
41
|
+
{
|
|
42
|
+
script: "build:email",
|
|
43
|
+
name: "email",
|
|
44
|
+
subject: "e-mail bundle",
|
|
45
|
+
running: "Building the e-mail bundle",
|
|
46
|
+
done: "Built the e-mail bundle",
|
|
47
|
+
failed: "E-mail bundle failed",
|
|
48
|
+
stopped: "E-mail bundle stopped",
|
|
49
|
+
},
|
|
50
|
+
];
|
|
51
|
+
function buildStepFailure(step, result, isVerbose, rerun, nameFile) {
|
|
52
|
+
const problem = (0, vite_output_1.describeViteFailure)(result.lines, step.subject, isVerbose, rerun, nameFile) ??
|
|
53
|
+
(0, child_output_1.describeChildFailure)({
|
|
54
|
+
title: "The production build failed",
|
|
55
|
+
command: `pnpm run ${step.script}`,
|
|
56
|
+
code: result.code,
|
|
57
|
+
lines: result.lines,
|
|
58
|
+
isVerbose,
|
|
59
|
+
});
|
|
60
|
+
return new cli_1.CliError(problem);
|
|
61
|
+
}
|
|
62
|
+
async function runBuildStep(step, context) {
|
|
63
|
+
const task = (0, cli_1.getProcessTasks)().start(step.running);
|
|
64
|
+
let result;
|
|
65
|
+
try {
|
|
66
|
+
result = await (0, common_1.runFramedCommand)("pnpm", ["--silent", "run", step.script], {
|
|
67
|
+
name: step.name,
|
|
68
|
+
cwd: context.workspaceDir,
|
|
69
|
+
env: context.env,
|
|
70
|
+
mapLine: context.mapLine,
|
|
71
|
+
onLine: (line) => context.warnings.read(line),
|
|
72
|
+
});
|
|
73
|
+
}
|
|
74
|
+
catch (error) {
|
|
75
|
+
(0, output_1.endInterruptedTask)(task, error, step.stopped);
|
|
76
|
+
throw error;
|
|
77
|
+
}
|
|
78
|
+
if (result.code !== 0) {
|
|
79
|
+
task.fail(step.failed);
|
|
80
|
+
throw buildStepFailure(step, result, context.isVerbose, undefined, context.nameFile);
|
|
81
|
+
}
|
|
82
|
+
task.succeed(step.done);
|
|
83
|
+
}
|
|
84
|
+
async function runBuild(options) {
|
|
85
|
+
const startedAt = Date.now();
|
|
86
|
+
try {
|
|
87
|
+
await buildProduction(options, startedAt);
|
|
88
|
+
}
|
|
89
|
+
catch (error) {
|
|
90
|
+
if (!(error instanceof common_1.CancelledError))
|
|
91
|
+
throw error;
|
|
92
|
+
throw new common_1.CancelledError(error.signal, "Stopped the production build", `ran ${(0, cli_1.formatDuration)(Date.now() - startedAt)}`);
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
async function buildProduction(options, startedAt) {
|
|
96
|
+
const backendUrl = (0, common_1.requireBackendUrl)(options.backendUrl);
|
|
97
|
+
const sessionSecret = (0, common_1.resolveSessionSecret)("build");
|
|
98
|
+
const bootstrapSecret = (0, common_1.normalizeBootstrapSecret)(options.bootstrapSecret);
|
|
99
|
+
const ui = (0, cli_1.getProcessUi)();
|
|
100
|
+
(0, output_1.writeHeader)("build", [backendUrl, "production"]);
|
|
101
|
+
const { workspaceDir, layers, manifestFromCache, manifestFetchedAt } = await (0, workspace_task_1.setUpWorkspace)({
|
|
102
|
+
backendUrl,
|
|
103
|
+
force: !!options.force,
|
|
104
|
+
mode: "build",
|
|
105
|
+
offline: (0, common_1.flagOrEnv)(options.offline, "DMS_OFFLINE"),
|
|
106
|
+
bootstrapSecret,
|
|
107
|
+
});
|
|
108
|
+
if (manifestFromCache) {
|
|
109
|
+
ui.message("warn", `Building from the manifest and layers archive${(0, output_1.cachedAge)(manifestFetchedAt)}`, { detail: "The output may not match the current backend." });
|
|
110
|
+
}
|
|
111
|
+
ui.message("info", "Building for production", {
|
|
112
|
+
detail: `Workspace ${(0, output_1.showWorkspace)(workspaceDir)}`,
|
|
113
|
+
});
|
|
114
|
+
const isVerbose = (0, cli_1.isVerboseRun)();
|
|
115
|
+
const context = {
|
|
116
|
+
workspaceDir,
|
|
117
|
+
isVerbose,
|
|
118
|
+
warnings: new vite_output_1.ViteWarnings(),
|
|
119
|
+
mapLine: (0, child_output_1.createPathMapper)(workspaceDir, layers),
|
|
120
|
+
nameFile: (0, child_output_1.createFileNamer)(layers),
|
|
121
|
+
env: {
|
|
122
|
+
...process.env,
|
|
123
|
+
DMS_SESSION_SECRET: sessionSecret,
|
|
124
|
+
NODE_OPTIONS: "--max-old-space-size=4096",
|
|
125
|
+
NODE_PATH: (0, node_path_1.join)(workspaceDir, "node_modules"),
|
|
126
|
+
[vite_output_1.VITE_LOG_LEVEL_VARIABLE]: (0, vite_output_1.viteLogLevel)(isVerbose),
|
|
127
|
+
},
|
|
128
|
+
};
|
|
129
|
+
for (const step of exports.BUILD_STEPS)
|
|
130
|
+
await runBuildStep(step, context);
|
|
131
|
+
(0, vite_output_1.reportViteWarnings)(context.warnings, isVerbose, ui);
|
|
132
|
+
(0, output_1.writeBlankLine)();
|
|
133
|
+
ui.summary({
|
|
134
|
+
headline: "Built the production frontend",
|
|
135
|
+
artifact: `${(0, output_1.showWorkspace)(workspaceDir)}${node_path_1.sep}dist`,
|
|
136
|
+
durationMs: Date.now() - startedAt,
|
|
137
|
+
nextSteps: [
|
|
138
|
+
{
|
|
139
|
+
command: `ajs dms start -b ${backendUrl}`,
|
|
140
|
+
description: "with the DMS_SESSION_SECRET this build used",
|
|
141
|
+
},
|
|
142
|
+
],
|
|
143
|
+
});
|
|
144
|
+
}
|