@antelopejs/dms-frontend 0.3.11 → 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +85 -5
- package/dist/commands/verify-source-action.js +2 -10
- package/dist/commands/verify-source.js +11 -7
- package/dist/config.d.ts +1 -0
- package/dist/config.js +5 -1
- package/dist/derived-outputs.d.ts +2 -0
- package/dist/derived-outputs.js +22 -3
- package/dist/help.js +4 -0
- package/dist/layers.js +1 -0
- package/dist/verify-source-runner.js +6 -42
- package/dist/verify-source-sources.d.ts +13 -0
- package/dist/verify-source-sources.js +189 -0
- package/dist/workspace.d.ts +1 -0
- package/package.json +1 -1
- package/templates/vue/DmsDynamicPage.vue +3 -1
- package/templates/vue/app-runtime.ts +42 -26
- package/templates/vue/frontend-build-loader.ts +78 -0
- package/templates/vue/frontend-build.ts +41 -0
- package/templates/vue/frontend-module.ts +166 -22
- package/templates/vue/icon-hydration.ts +78 -0
- package/templates/vue/server/auth/backend.mjs +20 -6
- package/templates/vue/server/auth/client-ip.mjs +28 -0
- package/templates/vue/server/auth/routes.mjs +1 -0
- package/templates/vue/server/auth/session.mjs +6 -0
- package/templates/vue/server/icons.mjs +43 -1
- package/templates/vue/server.mjs +14 -13
- package/templates/vue/ssr-renderer.ts +18 -1
- package/templates/vue/vite.config.ts +25 -23
package/README.md
CHANGED
|
@@ -100,7 +100,7 @@ generated server read, from the environment or the project's `.env`.
|
|
|
100
100
|
need a backend URL through `-b` or `DMS_API_BASE_URL`, and exit 2 without one.
|
|
101
101
|
`dev` also discovers it from the enclosing antelope project's
|
|
102
102
|
`.antelope/dev.json`, and exits 1 when there is none to discover.
|
|
103
|
-
`verify-source` needs
|
|
103
|
+
`verify-source` needs no backend: it runs in the project that installs `@antelopejs/dms`.
|
|
104
104
|
|
|
105
105
|
`prepare` runs anywhere: when it cannot prepare the workspace it warns on
|
|
106
106
|
stderr and exits 0, so a frontend module's `postinstall` hook never fails an
|
|
@@ -188,6 +188,61 @@ The SDK also exposes `use` for Vue plugins. Entries execute by descending manife
|
|
|
188
188
|
|
|
189
189
|
Within one setup, the first module to register a name keeps it. `ajs-dms dev` sets every module up again each time a change to a layer reaches the server renderer, into empty registries, so the next server render uses the edited component without a restart. A `setup` may therefore run more than once in a process: it should only register, and keep no state of its own between runs.
|
|
190
190
|
|
|
191
|
+
### Private components
|
|
192
|
+
|
|
193
|
+
A component registers as public by default: it joins the application's global components and resolves by name anywhere. A component that only exists to render the module's own backend pages registers as private instead:
|
|
194
|
+
|
|
195
|
+
```ts
|
|
196
|
+
sdk.registerComponent("BillingTotals", BillingTotals, { private: true });
|
|
197
|
+
```
|
|
198
|
+
|
|
199
|
+
A private component is not registered with `app.component`, so no template or other module reaches it by name. `resolveDmsComponent(name, owner)` returns it only when `owner` is the registering module, and then ahead of a public component of the same name; without an owner, only public components resolve. The generic page renderer passes the page's owner, so a backend page tree naming the component renders it, and the page preloader and the server render's hydration handover cover it too.
|
|
200
|
+
|
|
201
|
+
The owner is the page payload's `module` field: the name of the frontend module that owns the page's component tree, as the backend added it (the module's `name` in the frontend manifest, which the generated loader hands each module's setup). A backend that does not send `module` reaches no private component, whatever the tree names.
|
|
202
|
+
|
|
203
|
+
### Auto-imports (`dms.frontend.build.ts`)
|
|
204
|
+
|
|
205
|
+
Nothing a module ships is auto-imported unless the module declares it, in an optional `dms.frontend.build.ts` next to its `dms.frontend.ts`:
|
|
206
|
+
|
|
207
|
+
```ts
|
|
208
|
+
import { defineDmsFrontendBuild } from "#dms/frontend-build";
|
|
209
|
+
|
|
210
|
+
export default defineDmsFrontendBuild((build) => {
|
|
211
|
+
build.registerAutoImports([
|
|
212
|
+
"layers/*/app/composables",
|
|
213
|
+
"layers/*/app/utils",
|
|
214
|
+
"layers/*/app/types",
|
|
215
|
+
]);
|
|
216
|
+
});
|
|
217
|
+
```
|
|
218
|
+
|
|
219
|
+
Each entry is a directory relative to the module root, glob patterns allowed; every export of the files under it becomes available to the whole application without an import, in scripts and templates. A module without the file has nothing auto-imported. `app/build/` holds a module's private code and is never auto-imported: it is left out of what a broader entry covers, and an entry naming it, or a directory outside the module, is ignored with a warning when Vite starts.
|
|
220
|
+
|
|
221
|
+
The file runs in Node.js when Vite starts, under `ajs dms dev` and `ajs dms build`, not in the application: it may import `#dms/frontend-build` and Node.js built-ins, never the application's code. Vite restarts when one changes.
|
|
222
|
+
|
|
223
|
+
Up to 0.3, every module's `app/composables`, `app/types`, `app/utils`, `app/build/composables` and `app/build/types` were auto-imported. A module that relies on auto-imports declares its public directories in `dms.frontend.build.ts`, and imports its `app/build/` code by path.
|
|
224
|
+
|
|
225
|
+
### Page loading state
|
|
226
|
+
|
|
227
|
+
After a client navigation, the page renders under a `<Suspense>` until its chunk and any async `setup` resolve. `useDmsPageLoading()` (auto-imported) is `true` meanwhile, so a layout can show its own placeholder instead of an empty page body:
|
|
228
|
+
|
|
229
|
+
```vue
|
|
230
|
+
<script setup lang="ts">
|
|
231
|
+
const pageLoading = useDmsPageLoading();
|
|
232
|
+
</script>
|
|
233
|
+
|
|
234
|
+
<template>
|
|
235
|
+
<slot />
|
|
236
|
+
<PageSkeleton v-if="pageLoading" />
|
|
237
|
+
</template>
|
|
238
|
+
```
|
|
239
|
+
|
|
240
|
+
It stays `false` on the server and during hydration, which render the page resolved, for a page that does not suspend, and once a pending page is left.
|
|
241
|
+
|
|
242
|
+
### Interface language
|
|
243
|
+
|
|
244
|
+
The interface language follows the session (`useUserSession().user.language`), not the props of the page on screen: the first render takes it from the session the page arrived with, and it changes when the session's language does, after `useUserSession().fetch()` or a visit with a newer session. A page Inertia restores from history or serves from its prefetch cache carries the session it was fetched with: the frontend server stamps every session it writes (`session.updatedAt`), and a page carrying an older stamp of the same account updates neither the session nor the language. A session without a language, such as a signed-out one, keeps the language on screen; `$i18n.setLocale()` still switches it at any time.
|
|
245
|
+
|
|
191
246
|
### Declaring the loader releases a module supports
|
|
192
247
|
|
|
193
248
|
A frontend module names the `@antelopejs/dms-frontend` releases it runs on in its own `package.json`, the way a package names the Node.js versions it supports:
|
|
@@ -412,7 +467,8 @@ The other files the workspace derives from every module follow their sources
|
|
|
412
467
|
the same way. A file added to, changed in or removed from a module's `public/`
|
|
413
468
|
is served, or no longer served, at once. Adding or removing an
|
|
414
469
|
`app/config/shortcuts-registry.ts` or a `dms.frontend.ts` regenerates the
|
|
415
|
-
aggregated shortcuts or the module loader, and the page reloads.
|
|
470
|
+
aggregated shortcuts or the module loader, and the page reloads. Adding,
|
|
471
|
+
changing or removing a `dms.frontend.build.ts` restarts Vite. A layer
|
|
416
472
|
directory added to or removed from `layers/` is the exception: Vite reads the
|
|
417
473
|
`#<layer>` aliases and the auto-imported directories at startup only, so the
|
|
418
474
|
dev server says it needs a restart.
|
|
@@ -436,6 +492,14 @@ needs no exception for the Iconify API. Icons reach the page two ways:
|
|
|
436
492
|
answers `404`, and a name outside Iconify's naming rule `400`. `ajs dms dev`
|
|
437
493
|
and `ajs dms start` run the same server, so both serve the route.
|
|
438
494
|
|
|
495
|
+
The server render draws every icon, bundled or not: the frontend server loads
|
|
496
|
+
each installed `@iconify-json/*` collection once and hands it to the renderer,
|
|
497
|
+
and an icon is drawn in its first render as soon as its data is loaded, instead
|
|
498
|
+
of after the component mounts. The data of each icon a render drew travels in
|
|
499
|
+
the page, so the browser draws the same icons as it hydrates; an icon no
|
|
500
|
+
installed collection holds stays empty on the server and is fetched in the
|
|
501
|
+
browser as before.
|
|
502
|
+
|
|
439
503
|
## Options
|
|
440
504
|
|
|
441
505
|
| Option | Environment | Purpose |
|
|
@@ -449,6 +513,7 @@ needs no exception for the Iconify API. Icons reach the page two ways:
|
|
|
449
513
|
| | `HOST` | Address the frontend server binds to (default `0.0.0.0`) |
|
|
450
514
|
| | `DMS_COOKIE_SECURE` | Secure cookies (`true` by default; `ajs dms dev` defaults to `false`) |
|
|
451
515
|
| | `DMS_TRUSTED_PROXY_HOPS` | Number of trusted, rightmost reverse-proxy hops (default `0`) |
|
|
516
|
+
| | `DMS_COUNTRY_HEADER` | Client country header the server relays to the backend, set by a trusted reverse proxy (default `CF-IPCountry`; empty relays none). Name the same header in the backend's `auth.signInCountry.header`. Relayed only when `DMS_TRUSTED_PROXY_HOPS` is above `0`, like the client address |
|
|
452
517
|
| | `DMS_SESSION_SECRET` | Session cookie encryption key, 32 characters or more (required for login) |
|
|
453
518
|
| | `DMS_HTML_RENDER_SECRET` | Secret that verifies the backend's signed HTML/email render requests; without it, e-mail renders are refused. Defaults to the `htmlRender.serviceSecret` the backend publishes in the frontend manifest |
|
|
454
519
|
| | `DMS_OAUTH_RELAY_SECRET` | Secret the server presents to the backend's OAuth endpoints; without it, the backend refuses OAuth sign-in. Defaults to the `oauth.relaySecret` the backend publishes in the frontend manifest |
|
|
@@ -463,15 +528,30 @@ Use pnpm for all repository and workspace operations.
|
|
|
463
528
|
|
|
464
529
|
## Verify local frontend modules
|
|
465
530
|
|
|
466
|
-
Use the packaged source verifier to validate
|
|
531
|
+
Use the packaged source verifier to validate unpublished DMS frontend modules against this exact adapter version. Run it from the project:
|
|
532
|
+
|
|
533
|
+
```bash
|
|
534
|
+
ajs dms verify-source
|
|
535
|
+
```
|
|
536
|
+
|
|
537
|
+
The modules are verified on top of the DMS core layer, the `frontend-vue` directory of `@antelopejs/dms`. The verifier finds the `@antelopejs/dms` the project installs the way Node does, from the current directory, and names it before it starts: `Verifying against @antelopejs/dms 0.5.5 (installed in this project)`. When the project does not install it, the verifier stops with the command that adds it as a development dependency (`pnpm add -D @antelopejs/dms`, or the equivalent for the project's package manager).
|
|
538
|
+
|
|
539
|
+
Without `--module`, the verifier takes the current directory when it is a frontend module (it contains `dms.frontend.ts`), and its `frontend-vue` directory otherwise. `--module` names other ones and is repeatable, as is `--local-package`, which binds a local build of a package into the generated workspace. All supplied frontend packages are copied into the generated workspace and bound with `workspace:*`.
|
|
540
|
+
|
|
541
|
+
```bash
|
|
542
|
+
ajs dms verify-source --module ./admin-vue --module ./shop-vue
|
|
543
|
+
```
|
|
544
|
+
|
|
545
|
+
`--layer` is for the DMS core layer's own development: it verifies an unpublished core layer instead of the installed one, here against a local build of `@antelopejs/dms`:
|
|
467
546
|
|
|
468
547
|
```bash
|
|
469
548
|
ajs dms verify-source \
|
|
470
549
|
--layer /path/to/dms/frontend-vue \
|
|
471
|
-
--module /path/to/module/frontend-vue \
|
|
472
550
|
--local-package @antelopejs/dms=/path/to/dms
|
|
473
551
|
```
|
|
474
552
|
|
|
475
|
-
|
|
553
|
+
The checks look for what the core layer ships: its lazily loaded chart and rich-text libraries, the pages every DMS serves and its e-mail templates. A `--layer` that holds no core layer, such as a project's own `frontend-vue`, is refused before anything is installed, with exit code 2 and the command that verifies the same folder with `--module` instead.
|
|
554
|
+
|
|
555
|
+
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 through `pnpm test:real-source`, with `DMS_LAYER_SOURCE` set to a core layer root and `DMS_MODULE_SOURCES` to a JSON array of module roots.
|
|
476
556
|
|
|
477
557
|
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.
|
|
@@ -9,8 +9,6 @@ const node_path_1 = require("node:path");
|
|
|
9
9
|
const cli_1 = require("@antelopejs/core/cli");
|
|
10
10
|
const common_1 = require("../common");
|
|
11
11
|
const verify_source_result_1 = require("../verify-source-result");
|
|
12
|
-
const COMMAND_PATH = "ajs dms verify-source";
|
|
13
|
-
const LAYER_OPTION = "-l, --layer <path>";
|
|
14
12
|
function localPackageSeparator(value) {
|
|
15
13
|
const separator = value.indexOf("=");
|
|
16
14
|
return separator <= 0 || separator === value.length - 1 ? -1 : separator;
|
|
@@ -37,15 +35,10 @@ function directoryProblem(path, label) {
|
|
|
37
35
|
return undefined;
|
|
38
36
|
return { title: `${label} not found: ${path}`, fixes: [verify_source_result_1.LAYER_PATH_FIX] };
|
|
39
37
|
}
|
|
40
|
-
const MISSING_LAYER = {
|
|
41
|
-
title: `Required option '${LAYER_OPTION}' not specified`,
|
|
42
|
-
reason: `Usage: ${COMMAND_PATH} [options]`,
|
|
43
|
-
fixes: [`Run ${COMMAND_PATH} --help for usage`],
|
|
44
|
-
};
|
|
45
38
|
function usageProblems(options) {
|
|
46
39
|
const problems = [
|
|
47
40
|
options.layer === undefined
|
|
48
|
-
?
|
|
41
|
+
? undefined
|
|
49
42
|
: directoryProblem(options.layer, "Layer path"),
|
|
50
43
|
...(options.module ?? []).map((path) => directoryProblem(path, "Module path")),
|
|
51
44
|
...(options.localPackage ?? []).map(localPackageProblem),
|
|
@@ -71,7 +64,6 @@ function channelClosed(child) {
|
|
|
71
64
|
async function runVerifySource(options) {
|
|
72
65
|
const startedAt = Date.now();
|
|
73
66
|
assertValidUsage(usageProblems(options));
|
|
74
|
-
const layer = options.layer ?? "";
|
|
75
67
|
const modules = options.module ?? [];
|
|
76
68
|
const localPackages = parseLocalPackages(options.localPackage ?? []);
|
|
77
69
|
const runner = (0, node_path_1.join)(__dirname, "..", `verify-source-runner${(0, node_path_1.extname)(__filename)}`);
|
|
@@ -83,7 +75,7 @@ async function runVerifySource(options) {
|
|
|
83
75
|
stdio: ["inherit", "inherit", "inherit", "ipc"],
|
|
84
76
|
env: {
|
|
85
77
|
...process.env,
|
|
86
|
-
DMS_LAYER_SOURCE: (0, node_path_1.resolve)(layer),
|
|
78
|
+
DMS_LAYER_SOURCE: options.layer === undefined ? undefined : (0, node_path_1.resolve)(options.layer),
|
|
87
79
|
DMS_MODULE_SOURCES: JSON.stringify(modules.map((path) => (0, node_path_1.resolve)(path))),
|
|
88
80
|
DMS_LOCAL_PACKAGES: JSON.stringify(localPackages),
|
|
89
81
|
},
|
|
@@ -41,20 +41,24 @@ function collectOption(value, values = []) {
|
|
|
41
41
|
}
|
|
42
42
|
const VERIFY_SOURCE_EXAMPLES = [
|
|
43
43
|
{
|
|
44
|
-
description: "
|
|
45
|
-
command: "ajs dms verify-source
|
|
44
|
+
description: "The project's frontend module: the current directory or ./frontend-vue",
|
|
45
|
+
command: "ajs dms verify-source",
|
|
46
46
|
},
|
|
47
47
|
{
|
|
48
|
-
description: "
|
|
49
|
-
command: "ajs dms verify-source -
|
|
48
|
+
description: "Frontend modules in other directories",
|
|
49
|
+
command: "ajs dms verify-source -m ./admin-vue -m ./shop-vue",
|
|
50
|
+
},
|
|
51
|
+
{
|
|
52
|
+
description: "An unpublished DMS core layer, with a local build of @antelopejs/dms",
|
|
53
|
+
command: "ajs dms verify-source -l . --local-package @antelopejs/dms=..",
|
|
50
54
|
},
|
|
51
55
|
];
|
|
52
56
|
function cmdVerifySource() {
|
|
53
57
|
const command = new commander_1.Command("verify-source")
|
|
54
58
|
.summary("Build and type-check unpublished frontend sources")
|
|
55
|
-
.description("Build and type-check unpublished DMS frontend
|
|
56
|
-
.option("-l, --layer <path>", "Root of
|
|
57
|
-
.option("-m, --module <path>", "Root of
|
|
59
|
+
.description("Build and type-check unpublished DMS frontend modules against this loader version, on top of the DMS core layer of the @antelopejs/dms installed in the project. Runs in a temporary workspace removed when the run ends, and starts no backend.")
|
|
60
|
+
.option("-l, --layer <path>", "Root of an unpublished DMS core layer to verify instead of the installed one")
|
|
61
|
+
.option("-m, --module <path>", "Root of a frontend module to verify (repeatable); without -l or -m, the current directory or ./frontend-vue", collectOption)
|
|
58
62
|
.option("--local-package <name=path>", "Bind a local package into the generated workspace (repeatable)", collectOption)
|
|
59
63
|
.action(async (options) => (await Promise.resolve().then(() => __importStar(require("./verify-source-action")))).runVerifySource(options));
|
|
60
64
|
return (0, cli_1.withExamples)(command, VERIFY_SOURCE_EXAMPLES);
|
package/dist/config.d.ts
CHANGED
|
@@ -9,6 +9,7 @@ export declare function writeSecretBearingFile(file: string, content: string): v
|
|
|
9
9
|
export declare const TEMPLATE_FILES: ReadonlyArray<readonly [string, string]>;
|
|
10
10
|
export declare const LAYERS_SUBDIR = "frontend-modules";
|
|
11
11
|
export declare const FRONTEND_MODULE_ENTRY = "dms.frontend.ts";
|
|
12
|
+
export declare const FRONTEND_BUILD_ENTRY = "dms.frontend.build.ts";
|
|
12
13
|
export declare const AUTH_ESTABLISH_FILE = "generated-auth-establish.json";
|
|
13
14
|
export declare const LAYER_COPY_BLOCKLIST: readonly string[];
|
|
14
15
|
export declare const layerCopyIgnore: ignore.Ignore;
|
package/dist/config.js
CHANGED
|
@@ -3,7 +3,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
3
3
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
4
|
};
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
-
exports.Options = exports.UsageError = exports.TAILWIND_SOURCE_GLOB = exports.PNPM_LIFECYCLE_SCRIPTS = exports.layerCopyIgnore = exports.LAYER_COPY_BLOCKLIST = exports.AUTH_ESTABLISH_FILE = exports.FRONTEND_MODULE_ENTRY = exports.LAYERS_SUBDIR = exports.TEMPLATE_FILES = exports.WORKSPACE_DIR_MODE = exports.DEPS_HASH_FILE = exports.DMS_FRONTEND_HOME = void 0;
|
|
6
|
+
exports.Options = exports.UsageError = exports.TAILWIND_SOURCE_GLOB = exports.PNPM_LIFECYCLE_SCRIPTS = exports.layerCopyIgnore = exports.LAYER_COPY_BLOCKLIST = exports.AUTH_ESTABLISH_FILE = exports.FRONTEND_BUILD_ENTRY = exports.FRONTEND_MODULE_ENTRY = exports.LAYERS_SUBDIR = exports.TEMPLATE_FILES = exports.WORKSPACE_DIR_MODE = exports.DEPS_HASH_FILE = exports.DMS_FRONTEND_HOME = void 0;
|
|
7
7
|
exports.writeSecretBearingFile = writeSecretBearingFile;
|
|
8
8
|
exports.parseBackendUrl = parseBackendUrl;
|
|
9
9
|
exports.requireBackendUrl = requireBackendUrl;
|
|
@@ -45,6 +45,9 @@ exports.TEMPLATE_FILES = [
|
|
|
45
45
|
["ui-app-config.ts", "ui-app-config.ts"],
|
|
46
46
|
["ssr-renderer.ts", "ssr-renderer.ts"],
|
|
47
47
|
["frontend-module.ts", "frontend-module.ts"],
|
|
48
|
+
["frontend-build.ts", "frontend-build.ts"],
|
|
49
|
+
["frontend-build-loader.ts", "frontend-build-loader.ts"],
|
|
50
|
+
["icon-hydration.ts", "icon-hydration.ts"],
|
|
48
51
|
["globals.d.ts", "globals.d.ts"],
|
|
49
52
|
["compress-assets.mjs", "compress-assets.mjs"],
|
|
50
53
|
["head-order.mjs", "head-order.mjs"],
|
|
@@ -66,6 +69,7 @@ exports.TEMPLATE_FILES = [
|
|
|
66
69
|
];
|
|
67
70
|
exports.LAYERS_SUBDIR = "frontend-modules";
|
|
68
71
|
exports.FRONTEND_MODULE_ENTRY = "dms.frontend.ts";
|
|
72
|
+
exports.FRONTEND_BUILD_ENTRY = "dms.frontend.build.ts";
|
|
69
73
|
exports.AUTH_ESTABLISH_FILE = "generated-auth-establish.json";
|
|
70
74
|
exports.LAYER_COPY_BLOCKLIST = [
|
|
71
75
|
"node_modules",
|
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
import { FrontendModuleOptions, ResolvedLayer } from "./workspace";
|
|
2
2
|
export interface FrontendModuleRegistryEntry {
|
|
3
3
|
id: string;
|
|
4
|
+
name?: string;
|
|
4
5
|
packageName?: string;
|
|
5
6
|
root: string;
|
|
6
7
|
priority: number;
|
|
7
8
|
entry?: string;
|
|
9
|
+
build?: string;
|
|
8
10
|
options: FrontendModuleOptions;
|
|
9
11
|
}
|
|
10
12
|
export interface FrontendModuleRegistry {
|
package/dist/derived-outputs.js
CHANGED
|
@@ -21,6 +21,8 @@ function validateFrontendModuleRegistry(registry, workspaceDir) {
|
|
|
21
21
|
throw new Error(`Invalid frontend module priority: ${module.id}`);
|
|
22
22
|
if (module.entry !== undefined && module.entry !== config_1.FRONTEND_MODULE_ENTRY)
|
|
23
23
|
throw new Error(`Invalid frontend module entry: ${module.id}`);
|
|
24
|
+
if (module.build !== undefined && module.build !== config_1.FRONTEND_BUILD_ENTRY)
|
|
25
|
+
throw new Error(`Invalid frontend module build entry: ${module.id}`);
|
|
24
26
|
if (!(0, node_path_1.resolve)(module.root).startsWith(moduleRoot))
|
|
25
27
|
throw new Error(`Frontend module root escapes the workspace: ${module.id}`);
|
|
26
28
|
ids.add(module.id);
|
|
@@ -29,13 +31,16 @@ function validateFrontendModuleRegistry(registry, workspaceDir) {
|
|
|
29
31
|
function createFrontendModuleRegistry(workspaceDir, layers) {
|
|
30
32
|
const modules = layers.map((layer) => {
|
|
31
33
|
const sourceEntry = (0, node_path_1.join)(layer.path, config_1.FRONTEND_MODULE_ENTRY);
|
|
34
|
+
const buildEntry = (0, node_path_1.join)(layer.path, config_1.FRONTEND_BUILD_ENTRY);
|
|
32
35
|
const options = layer.options ?? {};
|
|
33
36
|
return {
|
|
34
37
|
id: (0, layers_1.getLayerSafeName)(layer),
|
|
38
|
+
name: layer.name ?? layer.packageName,
|
|
35
39
|
packageName: layer.packageName,
|
|
36
40
|
root: (0, layers_1.getLayerWorkspacePath)(workspaceDir, layer),
|
|
37
41
|
priority: layer.priority ?? 0,
|
|
38
42
|
entry: (0, node_fs_1.existsSync)(sourceEntry) ? config_1.FRONTEND_MODULE_ENTRY : undefined,
|
|
43
|
+
build: (0, node_fs_1.existsSync)(buildEntry) ? config_1.FRONTEND_BUILD_ENTRY : undefined,
|
|
39
44
|
options: layer.configKey ? { [layer.configKey]: options } : options,
|
|
40
45
|
};
|
|
41
46
|
});
|
|
@@ -47,6 +52,7 @@ function createFrontendModuleRegistry(workspaceDir, layers) {
|
|
|
47
52
|
function writeFrontendTypePaths(workspaceDir, registry) {
|
|
48
53
|
const paths = {
|
|
49
54
|
"#dms/frontend-module": ["./frontend-module.ts"],
|
|
55
|
+
"#dms/frontend-build": ["./frontend-build.ts"],
|
|
50
56
|
"@frontend/*": ["./frontend-modules/*"],
|
|
51
57
|
...(0, layer_aliases_1.frontendLayerTypePaths)([...registry.modules].reverse().map((module) => module.root), { relativeTo: workspaceDir }),
|
|
52
58
|
};
|
|
@@ -59,14 +65,27 @@ function writeFrontendModuleLoader(workspaceDir, registry) {
|
|
|
59
65
|
.map((module, index) => `import module${index} from "@frontend/${module.id}/${module.entry}";`)
|
|
60
66
|
.join("\n");
|
|
61
67
|
const registrations = modules
|
|
62
|
-
.map((module, index) => `{ module: module${index}, options: { public: ${JSON.stringify(module.options)} } }`)
|
|
68
|
+
.map((module, index) => `{ name: ${JSON.stringify(module.name)}, module: module${index}, options: { public: ${JSON.stringify(module.options)} } }`)
|
|
63
69
|
.join(", ");
|
|
64
70
|
const content = `${imports}\n\nimport type { DmsFrontendModuleRegistration } from "./frontend-module";\n\nexport const frontendModules: DmsFrontendModuleRegistration[] = [${registrations}];\n`;
|
|
65
71
|
return (0, fs_sync_1.applyContent)(content, (0, node_path_1.join)(workspaceDir, "frontend-modules.generated.ts"));
|
|
66
72
|
}
|
|
73
|
+
function writeFrontendBuildLoader(workspaceDir, registry) {
|
|
74
|
+
const modules = registry.modules.filter((module) => module.build);
|
|
75
|
+
const imports = modules
|
|
76
|
+
.map((module, index) => `import build${index} from "./${config_1.LAYERS_SUBDIR}/${module.id}/${module.build}";`)
|
|
77
|
+
.join("\n");
|
|
78
|
+
const declarations = modules
|
|
79
|
+
.map((module, index) => `{ id: ${JSON.stringify(module.id)}, setup: build${index} }`)
|
|
80
|
+
.join(", ");
|
|
81
|
+
const content = `${imports}\n\nimport type { FrontendBuildDeclaration } from "./frontend-build-loader";\n\nexport const frontendBuilds: FrontendBuildDeclaration[] = [${declarations}];\n`;
|
|
82
|
+
return (0, fs_sync_1.applyContent)(content, (0, node_path_1.join)(workspaceDir, "frontend-builds.generated.ts"));
|
|
83
|
+
}
|
|
67
84
|
function writeModuleRegistry(workspaceDir, registry) {
|
|
68
85
|
const wroteRegistry = (0, fs_sync_1.applyContent)(`${JSON.stringify(registry, null, 2)}\n`, (0, node_path_1.join)(workspaceDir, "generated-frontend-modules.json"));
|
|
69
|
-
|
|
86
|
+
const wroteModules = writeFrontendModuleLoader(workspaceDir, registry);
|
|
87
|
+
const wroteBuilds = writeFrontendBuildLoader(workspaceDir, registry);
|
|
88
|
+
return wroteRegistry || wroteModules || wroteBuilds;
|
|
70
89
|
}
|
|
71
90
|
function moduleAssetRoots(moduleRoot) {
|
|
72
91
|
const layersRoot = (0, node_path_1.join)(moduleRoot, "layers");
|
|
@@ -278,7 +297,7 @@ function writeShortcutExports(workspaceDir, registry) {
|
|
|
278
297
|
exports.DERIVED_OUTPUTS = [
|
|
279
298
|
{
|
|
280
299
|
name: "module registry",
|
|
281
|
-
affects: (path) => (0, fs_sync_1.toPosixPath)(path)
|
|
300
|
+
affects: (path) => [config_1.FRONTEND_MODULE_ENTRY, config_1.FRONTEND_BUILD_ENTRY].includes((0, fs_sync_1.toPosixPath)(path)),
|
|
282
301
|
write: writeModuleRegistry,
|
|
283
302
|
},
|
|
284
303
|
{
|
package/dist/help.js
CHANGED
|
@@ -25,6 +25,10 @@ exports.ENVIRONMENT_VARIABLES = [
|
|
|
25
25
|
"DMS_TRUSTED_PROXY_HOPS",
|
|
26
26
|
"Trusted reverse-proxy hops, counted from the right (default: 0)",
|
|
27
27
|
],
|
|
28
|
+
[
|
|
29
|
+
"DMS_COUNTRY_HEADER",
|
|
30
|
+
"Proxy country header relayed to the backend when a proxy is trusted (default: CF-IPCountry; empty: none)",
|
|
31
|
+
],
|
|
28
32
|
[
|
|
29
33
|
"DMS_HTML_RENDER_SECRET",
|
|
30
34
|
"Overrides the e-mail render secret the backend publishes",
|
package/dist/layers.js
CHANGED
|
@@ -43,6 +43,7 @@ const output_1 = require("./output");
|
|
|
43
43
|
const temporary_workspace_1 = require("./temporary-workspace");
|
|
44
44
|
const typecheck_output_1 = require("./typecheck-output");
|
|
45
45
|
const verify_source_result_1 = require("./verify-source-result");
|
|
46
|
+
const verify_source_sources_1 = require("./verify-source-sources");
|
|
46
47
|
const vite_output_1 = require("./vite-output");
|
|
47
48
|
const EMAIL_BUILD_SIZE_CEILING_BYTES = 256 * 1024;
|
|
48
49
|
const NON_EMAIL_RUNTIME_PATTERN = /DisplayRichText|ApexCharts|Tiptap|FlowCanvas|app\/.*table|components\/table/i;
|
|
@@ -58,44 +59,6 @@ function ensure(condition, title, reason) {
|
|
|
58
59
|
if (!condition)
|
|
59
60
|
throw new cli_1.CliError({ title, reason });
|
|
60
61
|
}
|
|
61
|
-
function readLayer(root) {
|
|
62
|
-
const packagePath = (0, node_path_1.join)(root, "package.json");
|
|
63
|
-
if (!(0, node_fs_1.existsSync)(packagePath))
|
|
64
|
-
throw new common_1.UsageError({
|
|
65
|
-
title: `No package.json in ${(0, output_1.showPath)(root)}`,
|
|
66
|
-
fixes: [verify_source_result_1.LAYER_PATH_FIX],
|
|
67
|
-
});
|
|
68
|
-
const { name } = readJson(packagePath);
|
|
69
|
-
return { path: root, sourcePath: root, packageName: name };
|
|
70
|
-
}
|
|
71
|
-
function readSources() {
|
|
72
|
-
const layerSource = process.env.DMS_LAYER_SOURCE;
|
|
73
|
-
if (!layerSource)
|
|
74
|
-
throw new common_1.UsageError({
|
|
75
|
-
title: "DMS_LAYER_SOURCE is not set",
|
|
76
|
-
fixes: [
|
|
77
|
-
`Set it to the root of a DMS frontend package, or run ${verify_source_result_1.VERIFY_SOURCE_COMMAND} -l <path>`,
|
|
78
|
-
],
|
|
79
|
-
});
|
|
80
|
-
const sourceRoot = (0, node_path_1.resolve)(layerSource);
|
|
81
|
-
if (!(0, node_fs_1.statSync)(sourceRoot, { throwIfNoEntry: false })?.isDirectory())
|
|
82
|
-
throw new common_1.UsageError({
|
|
83
|
-
title: `Layer path not found: ${(0, output_1.showPath)(sourceRoot)}`,
|
|
84
|
-
fixes: [verify_source_result_1.LAYER_PATH_FIX],
|
|
85
|
-
});
|
|
86
|
-
const extraSources = JSON.parse(process.env.DMS_MODULE_SOURCES ?? "[]");
|
|
87
|
-
const layerRoots = (0, node_fs_1.existsSync)((0, node_path_1.join)(sourceRoot, "dms.frontend.ts"))
|
|
88
|
-
? [sourceRoot]
|
|
89
|
-
: (0, node_fs_1.readdirSync)(sourceRoot, { withFileTypes: true })
|
|
90
|
-
.filter((entry) => entry.isDirectory())
|
|
91
|
-
.map((entry) => (0, node_path_1.join)(sourceRoot, entry.name));
|
|
92
|
-
if (layerRoots.length === 0)
|
|
93
|
-
throw new common_1.UsageError({
|
|
94
|
-
title: `No frontend package in ${(0, output_1.showPath)(sourceRoot)}`,
|
|
95
|
-
fixes: [verify_source_result_1.LAYER_PATH_FIX],
|
|
96
|
-
});
|
|
97
|
-
return [...layerRoots, ...extraSources.map((root) => (0, node_path_1.resolve)(root))].map(readLayer);
|
|
98
|
-
}
|
|
99
62
|
function removeDevelopmentDependencies(root) {
|
|
100
63
|
const packagePath = (0, node_path_1.join)(root, "package.json");
|
|
101
64
|
if (!(0, node_fs_1.existsSync)(packagePath))
|
|
@@ -104,7 +67,7 @@ function removeDevelopmentDependencies(root) {
|
|
|
104
67
|
delete packageData.devDependencies;
|
|
105
68
|
(0, node_fs_1.writeFileSync)(packagePath, `${JSON.stringify(packageData, null, 2)}\n`);
|
|
106
69
|
}
|
|
107
|
-
function materializeWorkspace(workspace, layers) {
|
|
70
|
+
function materializeWorkspace(workspace, layers, localPackages) {
|
|
108
71
|
const templateRoot = (0, node_path_1.join)((0, common_1.getPackageRoot)(), "templates", "vue");
|
|
109
72
|
const templateFiles = (0, node_fs_1.readdirSync)(templateRoot).filter((file) => !file.startsWith("npmrc"));
|
|
110
73
|
for (const file of templateFiles)
|
|
@@ -121,7 +84,6 @@ function materializeWorkspace(workspace, layers) {
|
|
|
121
84
|
(0, common_1.writeWorkspacePackageJson)(workspace, layers);
|
|
122
85
|
const packagePath = (0, node_path_1.join)(workspace, "package.json");
|
|
123
86
|
const workspacePackage = readJson(packagePath);
|
|
124
|
-
const localPackages = JSON.parse(process.env.DMS_LOCAL_PACKAGES ?? "{}");
|
|
125
87
|
workspacePackage.pnpm = {
|
|
126
88
|
overrides: {
|
|
127
89
|
...Object.fromEntries(layers.map((layer) => [layer.packageName, "workspace:*"])),
|
|
@@ -314,8 +276,10 @@ function describeError(error, isVerbose) {
|
|
|
314
276
|
};
|
|
315
277
|
}
|
|
316
278
|
async function verifySources() {
|
|
317
|
-
const
|
|
279
|
+
const options = (0, verify_source_sources_1.readSourceOptions)();
|
|
280
|
+
const { layers, coreLayer } = (0, verify_source_sources_1.readSources)(options);
|
|
318
281
|
(0, output_1.writeHeader)("verify-source", [(0, cli_1.pluralize)(layers.length, "module")]);
|
|
282
|
+
(0, cli_1.getProcessUi)().message("info", `Verifying against ${coreLayer}`);
|
|
319
283
|
(0, common_1.assertLayersSupportRenderer)(layers);
|
|
320
284
|
const workspace = (0, temporary_workspace_1.createTemporaryWorkspace)("dms-frontend-real-source-");
|
|
321
285
|
const isVerbose = (0, cli_1.isVerboseRun)();
|
|
@@ -327,7 +291,7 @@ async function verifySources() {
|
|
|
327
291
|
env: { ...process.env, [vite_output_1.VITE_LOG_LEVEL_VARIABLE]: (0, vite_output_1.viteLogLevel)(isVerbose) },
|
|
328
292
|
};
|
|
329
293
|
const tasks = (0, cli_1.getProcessTasks)();
|
|
330
|
-
await tasks.run(`Materializing ${(0, cli_1.pluralize)(layers.length, "module")}`, async () => materializeWorkspace(workspace, layers), { done: `Materialized ${(0, cli_1.pluralize)(layers.length, "module")}` });
|
|
294
|
+
await tasks.run(`Materializing ${(0, cli_1.pluralize)(layers.length, "module")}`, async () => materializeWorkspace(workspace, layers, options.localPackages), { done: `Materialized ${(0, cli_1.pluralize)(layers.length, "module")}` });
|
|
331
295
|
await (0, common_1.installDeps)(workspace, context.mapLine, ["--ignore-scripts"]);
|
|
332
296
|
await buildBundles(context);
|
|
333
297
|
await tasks.run("Checking the client bundle", async () => checkClientBundle(workspace), { done: "Client bundle checked", failed: "Client bundle check failed" });
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { type ResolvedLayer } from "./common";
|
|
2
|
+
export interface SourceOptions {
|
|
3
|
+
projectDir: string;
|
|
4
|
+
layerSource?: string;
|
|
5
|
+
moduleSources: string[];
|
|
6
|
+
localPackages: Record<string, string>;
|
|
7
|
+
}
|
|
8
|
+
export interface Sources {
|
|
9
|
+
layers: ResolvedLayer[];
|
|
10
|
+
coreLayer: string;
|
|
11
|
+
}
|
|
12
|
+
export declare function readSourceOptions(): SourceOptions;
|
|
13
|
+
export declare function readSources(options: SourceOptions): Sources;
|