@antelopejs/dms-frontend 0.10.0 → 0.10.1

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.
Files changed (74) hide show
  1. package/README.md +34 -11
  2. package/dist/commands/build-action.d.ts +3 -0
  3. package/dist/commands/build-action.js +74 -6
  4. package/dist/commands/build.js +7 -0
  5. package/dist/commands/clean-action.js +1 -1
  6. package/dist/commands/clean.js +1 -1
  7. package/dist/commands/dev-action.js +2 -2
  8. package/dist/commands/dev.js +1 -1
  9. package/dist/config.js +1 -0
  10. package/dist/derived-outputs.d.ts +1 -0
  11. package/dist/derived-outputs.js +3 -1
  12. package/dist/help.js +1 -1
  13. package/dist/internal/prepared-workspace.d.ts +12 -0
  14. package/dist/internal/prepared-workspace.js +74 -0
  15. package/dist/layer-watch.js +1 -1
  16. package/dist/locale-files.d.ts +4 -2
  17. package/dist/locale-files.js +11 -9
  18. package/dist/manifest.js +2 -1
  19. package/dist/ports.d.ts +3 -1
  20. package/dist/ports.js +1 -1
  21. package/dist/project-modules.d.ts +5 -1
  22. package/dist/project-modules.js +10 -7
  23. package/dist/verify-source-runner.js +2 -44
  24. package/dist/verify-source-workspace.d.ts +2 -0
  25. package/dist/verify-source-workspace.js +111 -0
  26. package/dist/workspace-setup.d.ts +1 -0
  27. package/dist/workspace-setup.js +3 -2
  28. package/package.json +7 -5
  29. package/templates/vue/backend-boundary.mjs +132 -0
  30. package/templates/vue/client-chunking.mjs +2 -0
  31. package/templates/vue/entry-weight.mjs +9 -5
  32. package/templates/vue/frontend-build.ts +4 -3
  33. package/templates/vue/head-order.mjs +3 -2
  34. package/templates/vue/icon-api.mjs +2 -0
  35. package/templates/vue/server/auth/backend.mjs +4 -4
  36. package/templates/vue/server/auth/establish-endpoints.mjs +2 -2
  37. package/templates/vue/server/auth/session.mjs +5 -2
  38. package/templates/vue/server/auth-declarations.mjs +46 -0
  39. package/templates/vue/server/auth-hooks.mjs +41 -3
  40. package/templates/vue/server/backend-proxies.mjs +16 -12
  41. package/templates/vue/server/backend-response.mjs +3 -3
  42. package/templates/vue/server/cli-report.mjs +22 -1
  43. package/templates/vue/server/client-error.mjs +5 -0
  44. package/templates/vue/server/content.mjs +24 -11
  45. package/templates/vue/server/email-bundle-watch.mjs +9 -3
  46. package/templates/vue/server/email.mjs +5 -3
  47. package/templates/vue/server/generated-files.mjs +16 -6
  48. package/templates/vue/server/icons.mjs +15 -11
  49. package/templates/vue/server/in-place-render.mjs +3 -3
  50. package/templates/vue/server/inertia.mjs +17 -3
  51. package/templates/vue/server/module-event.mjs +28 -16
  52. package/templates/vue/server/module-route-types.mjs +2 -2
  53. package/templates/vue/server/module-routes.mjs +24 -76
  54. package/templates/vue/server/module-server.mjs +27 -17
  55. package/templates/vue/server/module-testing.mjs +23 -8
  56. package/templates/vue/server/page-props.mjs +6 -5
  57. package/templates/vue/server/page-redirects.mjs +1 -1
  58. package/templates/vue/server/path-patterns.mjs +22 -11
  59. package/templates/vue/server/proxy-headers.mjs +1 -1
  60. package/templates/vue/server/proxy-redirects.mjs +1 -0
  61. package/templates/vue/server/proxy-request.mjs +2 -1
  62. package/templates/vue/server/proxy-transfers.mjs +17 -11
  63. package/templates/vue/server/public-endpoints.mjs +12 -16
  64. package/templates/vue/server/request-failure.mjs +1 -1
  65. package/templates/vue/server/request-routing.mjs +6 -1
  66. package/templates/vue/server/request-target.mjs +22 -9
  67. package/templates/vue/server/route-rules.mjs +12 -6
  68. package/templates/vue/server/static-files.mjs +12 -7
  69. package/templates/vue/server/vite-errors.mjs +13 -2
  70. package/templates/vue/server/web-body.mjs +2 -1
  71. package/templates/vue/server-module.ts +2 -1
  72. package/templates/vue/server.mjs +41 -20
  73. package/templates/vue/ui-inertia-page.mjs +1 -1
  74. package/templates/vue/vite.config.ts +9 -0
package/README.md CHANGED
@@ -1,20 +1,14 @@
1
- <div align="center">
1
+ <a href="https://github.com/AntelopeJS/dms"><img data-illustration src="./.github/readme/strip.svg" width="100%" alt="DMS FRONTEND: part of AntelopeJS DMS, the open-source back office. A glowing button reads Star AntelopeJS DMS."></a>
2
2
 
3
- <picture>
4
- <source media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/AntelopeJS/dms/main/packages/dms/frontend-vue/layers/dms-layout/public/images/antelope-logo/dark.svg">
5
- <img alt="AntelopeJS DMS" src="https://raw.githubusercontent.com/AntelopeJS/dms/main/packages/dms/frontend-vue/layers/dms-layout/public/images/antelope-logo/light.svg" data-illustration height="72">
6
- </picture>
3
+ <div align="center">
7
4
 
8
5
  # `@antelopejs/dms-frontend`
9
6
 
10
- <a href="https://www.npmjs.com/package/@antelopejs/dms-frontend"><img alt="npm" src="https://img.shields.io/npm/v/@antelopejs/dms-frontend?style=for-the-badge&labelColor=000000&color=000000" data-illustration></a>
11
- <a href="./LICENSE"><img alt="License" src="https://img.shields.io/npm/l/@antelopejs/dms-frontend?style=for-the-badge&labelColor=000000&color=000000" data-illustration></a>
12
- <a href="https://discord.gg/g4pFt6eXqu"><img alt="Discord" src="https://img.shields.io/badge/Discord-18181B?logo=discord&style=for-the-badge&color=000000" data-illustration></a>
13
- <a href="https://dms.antelopejs.com/docs/frontend"><img alt="Documentation" src="https://img.shields.io/badge/Docs-18181B?style=for-the-badge&color=000000" data-illustration></a>
7
+ <a href="https://www.npmjs.com/package/@antelopejs/dms-frontend"><img alt="npm" src="https://img.shields.io/npm/v/@antelopejs/dms-frontend?style=for-the-badge&labelColor=000000&color=000000" data-illustration></a> <a href="./LICENSE"><img alt="License" src="https://img.shields.io/npm/l/@antelopejs/dms-frontend?style=for-the-badge&labelColor=000000&color=000000" data-illustration></a> <a href="https://discord.gg/g4pFt6eXqu"><img alt="Discord" src="https://img.shields.io/badge/Discord-18181B?logo=discord&style=for-the-badge&color=000000" data-illustration></a> <a href="https://dms.antelopejs.com/docs/frontend"><img alt="Documentation" src="https://img.shields.io/badge/Docs-18181B?style=for-the-badge&color=000000" data-illustration></a> <a href="https://github.com/AntelopeJS/dms"><img data-illustration alt="Star AntelopeJS DMS" src="https://img.shields.io/github/stars/AntelopeJS/dms?label=Star%20AntelopeJS%20DMS&style=for-the-badge&labelColor=000000&color=000000&logo=github"></a>
14
8
 
15
9
  **The frontend of the AntelopeJS DMS: the `ajs dms` CLI, the Vue renderer and the frontend-module SDK.**
16
10
 
17
- [Documentation](https://dms.antelopejs.com/docs/frontend) · [DMS](https://github.com/AntelopeJS/dms) · [Modules](https://dms.antelopejs.com/docs/modules)
11
+ [Documentation](https://dms.antelopejs.com/docs/frontend) · [All modules](https://github.com/AntelopeJS/dms#modules)
18
12
 
19
13
  </div>
20
14
 
@@ -59,7 +53,7 @@ pnpm exec ajs dms start -b https://api.example.com -p 3001
59
53
 
60
54
  | Package | Supported | Tested with |
61
55
  | ------------------ | ------------- | ----------- |
62
- | `@antelopejs/core` | `>=1.16.0 <2` | `1.20.0` |
56
+ | `@antelopejs/core` | `>=1.16.0 <2` | `1.21.0` |
63
57
 
64
58
  <!-- antelopejs-docs-compat:end -->
65
59
 
@@ -91,6 +85,35 @@ pnpm test
91
85
 
92
86
  `pnpm docs:reference` regenerates the environment-variable table of `docs/4.configuration.md` from the list `ajs dms help environment` prints. `pnpm lint` fails when the table is stale.
93
87
 
88
+ ## Part of AntelopeJS DMS
89
+
90
+ This package is the frontend of [AntelopeJS DMS](https://github.com/AntelopeJS/dms), the open-source back office. It renders the dashboard that every module adds its pages to, behind the same sign-in and roles.
91
+
92
+ <!-- antelopejs-dms-modules:start -->
93
+
94
+ | Module | What it adds |
95
+ | ---------------------------------------------------------------- | ---------------------------------------------------------------------------------------- |
96
+ | [`dms-builder`](https://github.com/AntelopeJS/dms-builder) | Edit pages, tables and data sources by clicking, as TypeScript that compiles |
97
+ | [`dms-ai`](https://github.com/AntelopeJS/dms-ai) | An assistant chat in the dashboard, on your own Claude Code or Codex |
98
+ | [`dms-database`](https://github.com/AntelopeJS/dms-database) | Schemas, relation diagrams, a data browser and a query console |
99
+ | [`dms-api`](https://github.com/AntelopeJS/dms-api) | API health, request logs, per-route statistics and a request tester |
100
+ | [`dms-automation`](https://github.com/AntelopeJS/dms-automation) | Node-graph automations on schedules, webhooks and DMS events, with a trace for every run |
101
+ | [`dms-mailing`](https://github.com/AntelopeJS/dms-mailing) | E-mail templates in a block editor, versions, a send log and delivery metrics |
102
+ | [`dms-lang`](https://github.com/AntelopeJS/dms-lang) | Translation coverage, a translation matrix and a translation queue |
103
+ | [`dms-media`](https://github.com/AntelopeJS/dms-media) | A media library with folders, per-folder permissions, image sizes and light editing |
104
+ | [`dms-saas`](https://github.com/AntelopeJS/dms-saas) | Self-service sign-up, plans that gate features, and Stripe billing |
105
+ | [`dms-marketing`](https://github.com/AntelopeJS/dms-marketing) | Traffic, conversion funnels with A/B splits and click heatmaps, without cookies |
106
+ | [`dms-frontend`](https://github.com/AntelopeJS/dms-frontend) | The `ajs dms` CLI, the Vue renderer and the SDK every frontend module builds on |
107
+
108
+ <!-- antelopejs-dms-modules:end -->
109
+
94
110
  ## License
95
111
 
96
112
  [Apache-2.0](./LICENSE)
113
+
114
+ ## Requirements
115
+
116
+ <!-- antelopejs-docs-node:start -->
117
+
118
+ This package requires Node.js `>=20.19.0`.
119
+ <!-- antelopejs-docs-node:end -->
@@ -5,6 +5,9 @@ export interface BuildOptions {
5
5
  env: string;
6
6
  workspace?: string;
7
7
  force?: boolean;
8
+ setupOnly?: boolean;
9
+ install?: boolean;
10
+ prepared?: boolean;
8
11
  }
9
12
  export declare const DEFAULT_WORKSPACE_DIR = ".dms-frontend";
10
13
  export declare function productionWorkspace(options: {
@@ -6,6 +6,8 @@ exports.buildStepFailure = buildStepFailure;
6
6
  exports.runBuild = runBuild;
7
7
  const node_path_1 = require("node:path");
8
8
  const cli_1 = require("@antelopejs/core/cli");
9
+ const config_1 = require("../config");
10
+ const prepared_workspace_1 = require("../internal/prepared-workspace");
9
11
  const child_output_1 = require("../child-output");
10
12
  const common_1 = require("../common");
11
13
  const output_1 = require("../output");
@@ -13,6 +15,68 @@ const vite_output_1 = require("../vite-output");
13
15
  const entry_weight_1 = require("../entry-weight");
14
16
  const workspace_task_1 = require("./workspace-task");
15
17
  const node_options_1 = require("../node-options");
18
+ const NOT_PREPARED_EXIT_CODE = 3;
19
+ function buildPlan(options) {
20
+ const install = options.install !== false;
21
+ if (options.setupOnly)
22
+ return { kind: "setup", install };
23
+ if (!install)
24
+ throw new config_1.UsageError({
25
+ title: "--no-install needs --setup-only",
26
+ fixes: ["Run ajs dms build --setup-only --no-install"],
27
+ });
28
+ return options.prepared ? { kind: "prepared" } : { kind: "full" };
29
+ }
30
+ function workspaceFlag(options) {
31
+ return options.workspace ? ` --workspace ${options.workspace}` : "";
32
+ }
33
+ async function setUpProductionWorkspace(options, workspace, install) {
34
+ (0, prepared_workspace_1.forgetPreparedLayers)(workspace);
35
+ const { workspaceDir, layers } = await (0, workspace_task_1.setUpWorkspace)({
36
+ force: !!options.force,
37
+ mode: "build",
38
+ workspaceDir: workspace,
39
+ project: { projectDir: options.project, environment: options.env },
40
+ install,
41
+ });
42
+ (0, prepared_workspace_1.recordPreparedLayers)(workspaceDir, layers);
43
+ return { workspaceDir, layers };
44
+ }
45
+ function setupSummary(options, install, workspaceDir, startedAt) {
46
+ const flag = workspaceFlag(options);
47
+ return {
48
+ headline: install
49
+ ? "Set up the production workspace"
50
+ : "Wrote the production workspace without installing it",
51
+ artifact: (0, output_1.showWorkspace)(workspaceDir),
52
+ durationMs: Date.now() - startedAt,
53
+ nextSteps: [
54
+ install
55
+ ? {
56
+ command: `ajs dms build --prepared${flag}`,
57
+ description: "builds this workspace as it is",
58
+ }
59
+ : {
60
+ command: `ajs dms build --setup-only${flag}`,
61
+ description: "installs its dependencies",
62
+ },
63
+ ],
64
+ };
65
+ }
66
+ function preparedProductionWorkspace(options, workspace) {
67
+ const workspaceDir = (0, node_path_1.resolve)(workspace);
68
+ const found = (0, prepared_workspace_1.readPreparedWorkspace)(workspaceDir);
69
+ if (found.prepared)
70
+ return { workspaceDir, layers: found.layers };
71
+ throw new cli_1.CliError({
72
+ title: `${(0, output_1.showWorkspace)(workspaceDir)} is not prepared`,
73
+ reason: found.reason,
74
+ fixes: [
75
+ `Run ajs dms build --setup-only${workspaceFlag(options)} first, or build without --prepared`,
76
+ ],
77
+ exitCode: NOT_PREPARED_EXIT_CODE,
78
+ });
79
+ }
16
80
  exports.DEFAULT_WORKSPACE_DIR = ".dms-frontend";
17
81
  function productionWorkspace(options) {
18
82
  return options.workspace ?? (0, node_path_1.join)(options.project, exports.DEFAULT_WORKSPACE_DIR);
@@ -109,16 +173,20 @@ async function runBuild(options) {
109
173
  }
110
174
  }
111
175
  async function buildProduction(options, startedAt) {
176
+ const plan = buildPlan(options);
112
177
  (0, entry_weight_1.entryWeightThreshold)(process.env);
113
178
  const ui = (0, cli_1.getProcessUi)();
114
179
  const workspace = productionWorkspace(options);
115
180
  (0, output_1.writeHeader)("build", [options.project, options.env]);
116
- const { workspaceDir, layers } = await (0, workspace_task_1.setUpWorkspace)({
117
- force: !!options.force,
118
- mode: "build",
119
- workspaceDir: workspace,
120
- project: { projectDir: options.project, environment: options.env },
121
- });
181
+ if (plan.kind === "setup") {
182
+ const { workspaceDir } = await setUpProductionWorkspace(options, workspace, plan.install);
183
+ (0, output_1.writeBlankLine)();
184
+ ui.summary(setupSummary(options, plan.install, workspaceDir, startedAt));
185
+ return;
186
+ }
187
+ const { workspaceDir, layers } = plan.kind === "prepared"
188
+ ? preparedProductionWorkspace(options, workspace)
189
+ : await setUpProductionWorkspace(options, workspace, true);
122
190
  ui.message("info", "Building for production", {
123
191
  detail: `Workspace ${(0, output_1.showWorkspace)(workspaceDir)}`,
124
192
  });
@@ -46,6 +46,10 @@ const BUILD_EXAMPLES = [
46
46
  description: "With another environment's configuration, into a directory",
47
47
  command: "ajs dms build -e staging --workspace /srv/dms-frontend",
48
48
  },
49
+ {
50
+ description: "Set the workspace up and install it, then build it later as it is",
51
+ command: "ajs dms build --setup-only && ajs dms build --prepared",
52
+ },
49
53
  ];
50
54
  function cmdBuild() {
51
55
  const command = new commander_1.Command("build")
@@ -55,6 +59,9 @@ function cmdBuild() {
55
59
  .addOption(config_1.Options.env)
56
60
  .addOption(config_1.Options.workspace("Build into this directory instead of <project>/.dms-frontend"))
57
61
  .addOption(config_1.Options.force("Reinstall the workspace dependencies"))
62
+ .addOption(new commander_1.Option("--setup-only", "Set the workspace up and install its dependencies, then stop before the bundles"))
63
+ .addOption(new commander_1.Option("--no-install", "With --setup-only, write the workspace without installing its dependencies"))
64
+ .addOption(new commander_1.Option("--prepared", "Build the workspace --setup-only set up, as it is, without setting it up again; exit 3 when it is not prepared").conflicts(["setupOnly", "force"]))
58
65
  .action(async (options) => (await Promise.resolve().then(() => __importStar(require("./build-action")))).runBuild(options));
59
66
  return (0, cli_1.withExamples)(command, BUILD_EXAMPLES);
60
67
  }
@@ -79,7 +79,7 @@ async function runClean(options) {
79
79
  title: "Nothing to clean: pass -b <url> or --all",
80
80
  reason: "clean never uses DMS_API_BASE_URL from the environment or ./.env.",
81
81
  fixes: [
82
- "Remove the workspace build, start and dev -b share for a URL: ajs dms clean -b <url>",
82
+ "Remove the workspace dev -b and prepare use for a URL: ajs dms clean -b <url>",
83
83
  "Remove every workspace, including those dev created without -b: ajs dms clean --all",
84
84
  "List the workspaces: ajs dms workspaces",
85
85
  ],
@@ -38,7 +38,7 @@ const commander_1 = require("commander");
38
38
  const cli_1 = require("@antelopejs/core/cli");
39
39
  const CLEAN_EXAMPLES = [
40
40
  {
41
- description: "The workspace build, start and dev -b share for a backend",
41
+ description: "The workspace dev -b and prepare use for a backend",
42
42
  command: "ajs dms clean -b https://dms.example.com",
43
43
  },
44
44
  {
@@ -66,7 +66,7 @@ async function runDev(options) {
66
66
  bootstrapSecret,
67
67
  project,
68
68
  });
69
- if (port !== requestedPort) {
69
+ if (reserved.isFallback) {
70
70
  ui.message("warn", `Port ${requestedPort} is busy, using ${port}`);
71
71
  }
72
72
  if (manifestFromCache) {
@@ -120,7 +120,7 @@ function backendModuleSet(setup) {
120
120
  function reportServed(session, setup) {
121
121
  return (0, module_set_1.reportFrontendReady)(session.backendUrl, setup.layers.map((layer) => ({
122
122
  name: layer.name ?? layer.path,
123
- version: (0, project_modules_1.readShippingVersion)(layer.path),
123
+ version: (0, project_modules_1.readShippingPackage)(layer.path).version,
124
124
  })), session.bootstrapSecret);
125
125
  }
126
126
  async function serveWorkspace(session, setup) {
@@ -43,7 +43,7 @@ const DEV_EXAMPLES = [
43
43
  command: "ajs dms dev",
44
44
  },
45
45
  {
46
- description: "Share one workspace with build and start",
46
+ description: "Run in the workspace prepare uses for that backend",
47
47
  command: "ajs dms dev -b http://localhost:5010 -p 3001",
48
48
  },
49
49
  ];
package/dist/config.js CHANGED
@@ -67,6 +67,7 @@ exports.TEMPLATE_FILES = [
67
67
  ["head-order.mjs", "head-order.mjs"],
68
68
  ["client-chunking.mjs", "client-chunking.mjs"],
69
69
  ["entry-weight.mjs", "entry-weight.mjs"],
70
+ ["backend-boundary.mjs", "backend-boundary.mjs"],
70
71
  ["client-server-guard.mjs", "client-server-guard.mjs"],
71
72
  ["ui-inertia-page.mjs", "ui-inertia-page.mjs"],
72
73
  ["ui-request-state.mjs", "ui-request-state.mjs"],
@@ -4,6 +4,7 @@ export interface FrontendModuleRegistryEntry {
4
4
  name?: string;
5
5
  packageName?: string;
6
6
  version?: string;
7
+ backendPackage?: string;
7
8
  root: string;
8
9
  priority: number;
9
10
  entry?: string;
@@ -41,11 +41,13 @@ function createFrontendModuleRegistry(workspaceDir, layers) {
41
41
  const buildEntry = (0, node_path_1.join)(layer.path, config_1.FRONTEND_BUILD_ENTRY);
42
42
  const serverEntry = (0, node_path_1.join)(layer.path, config_1.SERVER_MODULE_ENTRY);
43
43
  const options = layer.options ?? {};
44
+ const shipping = (0, project_modules_1.readShippingPackage)(layer.sourcePath ?? layer.path);
44
45
  return {
45
46
  id: (0, layers_1.getLayerSafeName)(layer),
46
47
  name: layer.name ?? layer.packageName,
47
48
  packageName: layer.packageName,
48
- version: (0, project_modules_1.readShippingVersion)(layer.sourcePath ?? layer.path),
49
+ version: shipping.version,
50
+ backendPackage: shipping.name,
49
51
  root: (0, layers_1.getLayerWorkspacePath)(workspaceDir, layer),
50
52
  priority: layer.priority ?? 0,
51
53
  entry: (0, node_fs_1.existsSync)(sourceEntry) ? config_1.FRONTEND_MODULE_ENTRY : undefined,
package/dist/help.js CHANGED
@@ -81,7 +81,7 @@ exports.ENVIRONMENT_VARIABLES = [
81
81
  ];
82
82
  const ENVIRONMENT_NOTES = [
83
83
  "A variable set in the shell wins over both files, and .env.local wins over .env. Only the current directory is read: the generated workspace never loads a .env of its own.",
84
- "Workspaces live in ~/.antelopejs/dms-frontend, one per backend URL. dev without -b keys its own on the project directory instead; pass -b to share one with build and start. ajs dms workspaces lists them.",
84
+ "dev and prepare keep their workspaces in ~/.antelopejs/dms-frontend, one per backend URL, and ajs dms workspaces lists them. dev without -b keys its own on the project directory instead. build and start use <project>/.dms-frontend, or --workspace.",
85
85
  ];
86
86
  function subcommandTerm(command) {
87
87
  const args = command.registeredArguments.map((argument) => argument.required ? `<${argument.name()}>` : `[${argument.name()}]`);
@@ -0,0 +1,12 @@
1
+ import type { ResolvedLayer } from "../workspace";
2
+ export type RecordedLayer = Pick<ResolvedLayer, "path" | "name" | "sourcePath" | "packageName">;
3
+ export type PreparedWorkspace = {
4
+ prepared: true;
5
+ layers: RecordedLayer[];
6
+ } | {
7
+ prepared: false;
8
+ reason: string;
9
+ };
10
+ export declare function forgetPreparedLayers(workspaceDir: string): void;
11
+ export declare function recordPreparedLayers(workspaceDir: string, layers: readonly ResolvedLayer[]): void;
12
+ export declare function readPreparedWorkspace(workspaceDir: string): PreparedWorkspace;
@@ -0,0 +1,74 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.forgetPreparedLayers = forgetPreparedLayers;
4
+ exports.recordPreparedLayers = recordPreparedLayers;
5
+ exports.readPreparedWorkspace = readPreparedWorkspace;
6
+ const node_fs_1 = require("node:fs");
7
+ const node_path_1 = require("node:path");
8
+ const config_1 = require("../config");
9
+ const workspace_setup_1 = require("../workspace-setup");
10
+ const guards_1 = require("./guards");
11
+ const plain_object_1 = require("./plain-object");
12
+ const PREPARED_LAYERS_FILE = ".dms-build-layers.json";
13
+ const OPTIONAL_TEXT_KEYS = ["name", "sourcePath", "packageName"];
14
+ function isRecordedLayer(value) {
15
+ return ((0, plain_object_1.isPlainObject)(value) &&
16
+ Object.prototype.hasOwnProperty.call(value, "path") &&
17
+ (0, guards_1.isText)(value.path) &&
18
+ OPTIONAL_TEXT_KEYS.every((key) => !Object.prototype.hasOwnProperty.call(value, key) || (0, guards_1.isText)(value[key])));
19
+ }
20
+ function readRecord(workspaceDir) {
21
+ let parsed;
22
+ try {
23
+ parsed = JSON.parse((0, node_fs_1.readFileSync)((0, node_path_1.join)(workspaceDir, PREPARED_LAYERS_FILE), "utf8"));
24
+ }
25
+ catch {
26
+ return undefined;
27
+ }
28
+ if (!(0, plain_object_1.isPlainObject)(parsed) ||
29
+ !Object.prototype.hasOwnProperty.call(parsed, "installed"))
30
+ return undefined;
31
+ const { installed } = parsed;
32
+ if (!(0, guards_1.isFlag)(installed) ||
33
+ !Object.prototype.hasOwnProperty.call(parsed, "layers"))
34
+ return undefined;
35
+ const { layers } = parsed;
36
+ if (!Array.isArray(layers))
37
+ return undefined;
38
+ const listed = layers;
39
+ return listed.every(isRecordedLayer)
40
+ ? { installed, layers: listed }
41
+ : undefined;
42
+ }
43
+ function forgetPreparedLayers(workspaceDir) {
44
+ (0, node_fs_1.rmSync)((0, node_path_1.join)(workspaceDir, PREPARED_LAYERS_FILE), { force: true });
45
+ }
46
+ function recordPreparedLayers(workspaceDir, layers) {
47
+ const record = {
48
+ installed: !(0, workspace_setup_1.needsInstall)(workspaceDir, layers.map((layer) => layer.path), false),
49
+ layers: layers.map(({ path, name, sourcePath, packageName }) => ({
50
+ path,
51
+ name,
52
+ sourcePath,
53
+ packageName,
54
+ })),
55
+ };
56
+ (0, node_fs_1.writeFileSync)((0, node_path_1.join)(workspaceDir, PREPARED_LAYERS_FILE), `${JSON.stringify(record, null, 2)}\n`);
57
+ }
58
+ function readPreparedWorkspace(workspaceDir) {
59
+ const record = readRecord(workspaceDir);
60
+ if (!record)
61
+ return {
62
+ prepared: false,
63
+ reason: "No production setup finished in it.",
64
+ };
65
+ const installed = record.installed &&
66
+ (0, node_fs_1.existsSync)((0, node_path_1.join)(workspaceDir, config_1.DEPS_HASH_FILE)) &&
67
+ (0, node_fs_1.existsSync)((0, node_path_1.join)(workspaceDir, "node_modules"));
68
+ if (!installed)
69
+ return {
70
+ prepared: false,
71
+ reason: "Its dependencies are not installed, or changed since they were.",
72
+ };
73
+ return { prepared: true, layers: record.layers };
74
+ }
@@ -172,7 +172,7 @@ function createDerivedOutputRefresher(workspaceDir, layers, reporter) {
172
172
  const file = reporter.show(err.path);
173
173
  fail(output, {
174
174
  text: `${file} is not a valid locale file`,
175
- options: { details: [...err.reason.split("\n"), kept] },
175
+ options: { details: [err.reason, ...err.details, kept] },
176
176
  fixed: [`${file} fixed`, `${output.name} regenerated`].join((0, cli_1.getProcessUi)().symbols.separator),
177
177
  });
178
178
  }
@@ -1,7 +1,9 @@
1
+ import { CliError } from "@antelopejs/core/cli";
1
2
  import { type LocaleMessages } from "./internal/locale-catalogs";
2
- export declare class InvalidLocaleFileError extends Error {
3
+ export declare class InvalidLocaleFileError extends CliError {
3
4
  readonly path: string;
4
5
  readonly reason: string;
5
- constructor(path: string, reason: string);
6
+ readonly details: readonly string[];
7
+ constructor(path: string, reason: string, details?: readonly string[]);
6
8
  }
7
9
  export declare function readLocaleFile(path: string): LocaleMessages;
@@ -3,18 +3,24 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.InvalidLocaleFileError = void 0;
4
4
  exports.readLocaleFile = readLocaleFile;
5
5
  const node_fs_1 = require("node:fs");
6
+ const cli_1 = require("@antelopejs/core/cli");
6
7
  const message_compiler_1 = require("@intlify/message-compiler");
7
8
  const guards_1 = require("./internal/guards");
8
9
  const locale_catalogs_1 = require("./internal/locale-catalogs");
9
- class InvalidLocaleFileError extends Error {
10
- constructor(path, reason) {
11
- super(`Invalid locale file ${path}: ${reason}`);
10
+ class InvalidLocaleFileError extends cli_1.CliError {
11
+ constructor(path, reason, details = []) {
12
+ super({
13
+ title: `Invalid locale file ${path}: ${reason}`,
14
+ details: [...details],
15
+ });
12
16
  this.path = path;
13
17
  this.reason = reason;
18
+ this.details = details;
14
19
  this.name = "InvalidLocaleFileError";
15
20
  }
16
21
  }
17
22
  exports.InvalidLocaleFileError = InvalidLocaleFileError;
23
+ const LINE_BREAKS = /\r?\n/g;
18
24
  const LITERAL_ESCAPES = {
19
25
  "@": "{'@'}",
20
26
  "{": "{'{'}",
@@ -67,18 +73,14 @@ function readLocaleFile(path) {
67
73
  }
68
74
  catch (error) {
69
75
  const reason = error instanceof Error ? error.message : String(error);
70
- throw new InvalidLocaleFileError(path, reason);
76
+ throw new InvalidLocaleFileError(path, reason.replace(LINE_BREAKS, "\\n"));
71
77
  }
72
78
  if (!(0, locale_catalogs_1.isLocaleValue)(value) || !(0, locale_catalogs_1.isMessageGroup)(value)) {
73
79
  throw new InvalidLocaleFileError(path, "a locale file must hold a JSON object of messages");
74
80
  }
75
81
  const problems = messageProblems(value, "");
76
82
  if (problems.length) {
77
- const reason = [
78
- `${problems.length} message(s) vue-i18n cannot compile:`,
79
- ...problems.map(describeProblem),
80
- ].join("\n");
81
- throw new InvalidLocaleFileError(path, reason);
83
+ throw new InvalidLocaleFileError(path, `${problems.length} message(s) vue-i18n cannot compile:`, problems.map(describeProblem));
82
84
  }
83
85
  return value;
84
86
  }
package/dist/manifest.js CHANGED
@@ -86,7 +86,8 @@ function displayUrl(url) {
86
86
  }
87
87
  function backendEndpoint(backendUrl, path) {
88
88
  try {
89
- return new URL(`${(0, config_1.parseBackendUrl)(backendUrl)}${path}`);
89
+ const base = (0, config_1.parseBackendUrl)(backendUrl).replace(/\/+$/, "");
90
+ return new URL(`${base}${path}`);
90
91
  }
91
92
  catch (err) {
92
93
  if (!(err instanceof config_1.UsageError))
package/dist/ports.d.ts CHANGED
@@ -4,5 +4,7 @@ export interface ReservedPort {
4
4
  port: number;
5
5
  release: () => Promise<void>;
6
6
  }
7
- export declare function reserveFreePort(preferred: number, range?: number): Promise<ReservedPort>;
7
+ export declare function reserveFreePort(preferred: number, range?: number): Promise<ReservedPort & {
8
+ isFallback: boolean;
9
+ }>;
8
10
  export declare function reservePort(port: number): Promise<ReservedPort | undefined>;
package/dist/ports.js CHANGED
@@ -21,7 +21,7 @@ async function reserveFreePort(preferred, range = exports.PORT_FALLBACK_RANGE) {
21
21
  for (let port = preferred; port <= end; port++) {
22
22
  const reserved = await reservePort(port);
23
23
  if (reserved)
24
- return reserved;
24
+ return { ...reserved, isFallback: port !== preferred };
25
25
  }
26
26
  throw new cli_1.CliError({
27
27
  title: `No free port found between ${preferred} and ${end}`,
@@ -42,6 +42,10 @@ export interface ProjectModulesOptions {
42
42
  dev?: boolean;
43
43
  }
44
44
  export declare function loadProjectConfig(projectDir: string, environment: string): Promise<ProjectConfig>;
45
- export declare function readShippingVersion(sourcePath: string): string | undefined;
45
+ export interface ShippingPackage {
46
+ name?: string;
47
+ version?: string;
48
+ }
49
+ export declare function readShippingPackage(sourcePath: string): ShippingPackage;
46
50
  export declare function readProjectFrontendModules(options: ProjectModulesOptions): Promise<ProjectFrontendModule[]>;
47
51
  export {};
@@ -35,7 +35,7 @@ var __importStar = (this && this.__importStar) || (function () {
35
35
  Object.defineProperty(exports, "__esModule", { value: true });
36
36
  exports.FRONTEND_DECLARATION_FIELD = void 0;
37
37
  exports.loadProjectConfig = loadProjectConfig;
38
- exports.readShippingVersion = readShippingVersion;
38
+ exports.readShippingPackage = readShippingPackage;
39
39
  exports.readProjectFrontendModules = readProjectFrontendModules;
40
40
  const node_fs_1 = require("node:fs");
41
41
  const fsPromises = __importStar(require("node:fs/promises"));
@@ -191,15 +191,18 @@ function moduleFolder(projectDir, cacheFolder, source) {
191
191
  : [];
192
192
  return candidates.find((folder) => (0, node_fs_1.existsSync)((0, node_path_1.join)(folder, "package.json")));
193
193
  }
194
- function readShippingVersion(sourcePath) {
194
+ function readShippingPackage(sourcePath) {
195
195
  try {
196
196
  const pkg = JSON.parse((0, node_fs_1.readFileSync)((0, node_path_1.join)((0, node_path_1.dirname)(sourcePath), "package.json"), "utf-8"));
197
- return (0, guards_1.isObjectLike)(pkg) && "version" in pkg && (0, guards_1.isText)(pkg.version)
198
- ? pkg.version
199
- : undefined;
197
+ if (!(0, guards_1.isObjectLike)(pkg))
198
+ return {};
199
+ return {
200
+ name: "name" in pkg && (0, guards_1.isText)(pkg.name) ? pkg.name : undefined,
201
+ version: "version" in pkg && (0, guards_1.isText)(pkg.version) ? pkg.version : undefined,
202
+ };
200
203
  }
201
204
  catch {
202
- return undefined;
205
+ return {};
203
206
  }
204
207
  }
205
208
  function toLayer(registration) {
@@ -274,6 +277,6 @@ async function readProjectFrontendModules(options) {
274
277
  .sort((left, right) => (right.priority ?? 0) - (left.priority ?? 0))
275
278
  .map((registration) => ({
276
279
  layer: toLayer(registration),
277
- version: readShippingVersion(registration.sourcePath),
280
+ version: readShippingPackage(registration.sourcePath).version,
278
281
  }));
279
282
  }
@@ -39,11 +39,11 @@ const cli_1 = require("@antelopejs/core/cli");
39
39
  const child_output_1 = require("./child-output");
40
40
  const ensure_1 = require("./internal/ensure");
41
41
  const guards_1 = require("./internal/guards");
42
- const plain_object_1 = require("./internal/plain-object");
43
42
  const vite_manifest_1 = require("./internal/vite-manifest");
44
43
  const common_1 = require("./common");
45
44
  const output_1 = require("./output");
46
45
  const temporary_workspace_1 = require("./temporary-workspace");
46
+ const verify_source_workspace_1 = require("./verify-source-workspace");
47
47
  const verify_source_client_bundle_1 = require("./verify-source-client-bundle");
48
48
  const verify_source_build_1 = require("./verify-source-build");
49
49
  const verify_source_result_1 = require("./verify-source-result");
@@ -51,48 +51,6 @@ const verify_source_sources_1 = require("./verify-source-sources");
51
51
  const vite_output_1 = require("./vite-output");
52
52
  const EMAIL_BUILD_SIZE_CEILING_BYTES = 256 * 1024;
53
53
  const NON_EMAIL_RUNTIME_PATTERN = /DisplayRichText|ApexCharts|Tiptap|FlowCanvas|app\/.*table|components\/table/i;
54
- function removeDevelopmentDependencies(root) {
55
- const packagePath = (0, node_path_1.join)(root, "package.json");
56
- if (!(0, node_fs_1.existsSync)(packagePath))
57
- return;
58
- const packageData = JSON.parse((0, node_fs_1.readFileSync)(packagePath, "utf8"));
59
- if (!(0, plain_object_1.isPlainObject)(packageData))
60
- return;
61
- delete packageData.devDependencies;
62
- (0, node_fs_1.writeFileSync)(packagePath, `${JSON.stringify(packageData, null, 2)}\n`);
63
- }
64
- function materializeWorkspace(workspace, layers, localPackages) {
65
- const templateRoot = (0, node_path_1.join)((0, common_1.getPackageRoot)(), "templates", "vue");
66
- const templateFiles = (0, node_fs_1.readdirSync)(templateRoot).filter((file) => !file.startsWith("npmrc"));
67
- for (const file of templateFiles)
68
- (0, node_fs_1.cpSync)((0, node_path_1.join)(templateRoot, file), (0, node_path_1.join)(workspace, file), {
69
- recursive: true,
70
- });
71
- (0, common_1.materializeLayers)(workspace, layers);
72
- const registry = (0, common_1.createFrontendModuleRegistry)(workspace, layers);
73
- registry.modules.forEach((module) => {
74
- removeDevelopmentDependencies(module.root);
75
- });
76
- (0, common_1.writeFrontendModuleRegistry)(workspace, layers);
77
- (0, node_fs_1.writeFileSync)((0, node_path_1.join)(workspace, "dms-main.css"), '@import "tailwindcss";\n@import "@nuxt/ui";\n');
78
- (0, common_1.writeWorkspacePackageJson)(workspace, layers);
79
- const packagePath = (0, node_path_1.join)(workspace, "package.json");
80
- const workspacePackage = JSON.parse((0, node_fs_1.readFileSync)(packagePath, "utf8"));
81
- (0, ensure_1.ensure)((0, plain_object_1.isPlainObject)(workspacePackage), "The workspace package.json is not a JSON object");
82
- workspacePackage.pnpm = {
83
- overrides: {
84
- ...Object.fromEntries(layers.map((layer) => [
85
- String(layer.packageName),
86
- "workspace:*",
87
- ])),
88
- ...Object.fromEntries(Object.entries(localPackages).map(([name, path]) => [
89
- name,
90
- `link:${(0, node_path_1.resolve)(path)}`,
91
- ])),
92
- },
93
- };
94
- (0, node_fs_1.writeFileSync)(packagePath, `${JSON.stringify(workspacePackage, null, 2)}\n`);
95
- }
96
54
  async function checkServerModulesBundle(workspace) {
97
55
  const root = (0, node_path_1.join)(workspace, "dist", "server-modules");
98
56
  const javascript = (0, node_fs_1.readdirSync)(root, { recursive: true, encoding: "utf8" })
@@ -216,7 +174,7 @@ async function verifySources() {
216
174
  env: { ...process.env, [vite_output_1.VITE_LOG_LEVEL_VARIABLE]: (0, vite_output_1.viteLogLevel)(isVerbose) },
217
175
  };
218
176
  const tasks = (0, cli_1.getProcessTasks)();
219
- 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")}` });
177
+ await tasks.run(`Materializing ${(0, cli_1.pluralize)(layers.length, "module")}`, async () => (0, verify_source_workspace_1.materializeWorkspace)(workspace, layers, options.localPackages), { done: `Materialized ${(0, cli_1.pluralize)(layers.length, "module")}` });
220
178
  await (0, common_1.installDeps)(workspace, context.mapLine, ["--ignore-scripts"]);
221
179
  await (0, verify_source_build_1.prepareTypes)(context);
222
180
  await (0, verify_source_build_1.buildAndTypeCheck)(context);
@@ -0,0 +1,2 @@
1
+ import { type ResolvedLayer } from "./common";
2
+ export declare function materializeWorkspace(workspace: string, layers: ResolvedLayer[], localPackages: Record<string, string>): void;