@antelopejs/dms-frontend 0.9.3 → 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 (140) hide show
  1. package/README.md +46 -29
  2. package/dist/commands/build-action.d.ts +3 -0
  3. package/dist/commands/build-action.js +77 -8
  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 +4 -3
  8. package/dist/commands/dev.js +1 -1
  9. package/dist/commands/prepare-action.js +1 -1
  10. package/dist/commands/start-action.js +3 -24
  11. package/dist/commands/workspace-task.js +2 -1
  12. package/dist/config.js +1 -0
  13. package/dist/derived-outputs.d.ts +1 -0
  14. package/dist/derived-outputs.js +6 -4
  15. package/dist/discovery.js +17 -6
  16. package/dist/entry-weight.js +15 -0
  17. package/dist/fs-sync.js +7 -4
  18. package/dist/help.js +1 -1
  19. package/dist/index.js +2 -1
  20. package/dist/internal/built-workspace.d.ts +4 -0
  21. package/dist/internal/built-workspace.js +43 -0
  22. package/dist/internal/ensure.d.ts +1 -0
  23. package/dist/internal/ensure.js +8 -0
  24. package/dist/internal/locale-catalogs.d.ts +8 -3
  25. package/dist/internal/locale-catalogs.js +16 -6
  26. package/dist/internal/manifest-decoder.d.ts +7 -0
  27. package/dist/internal/manifest-decoder.js +81 -0
  28. package/dist/internal/own-release.d.ts +5 -0
  29. package/dist/internal/own-release.js +20 -0
  30. package/dist/internal/plain-object.d.ts +1 -0
  31. package/dist/internal/plain-object.js +6 -0
  32. package/dist/internal/prepared-workspace.d.ts +12 -0
  33. package/dist/internal/prepared-workspace.js +74 -0
  34. package/dist/internal/text-list.d.ts +1 -0
  35. package/dist/internal/text-list.js +10 -0
  36. package/dist/internal/vite-manifest.d.ts +6 -0
  37. package/dist/internal/vite-manifest.js +24 -0
  38. package/dist/layer-watch.js +25 -24
  39. package/dist/layers.d.ts +1 -1
  40. package/dist/layers.js +3 -1
  41. package/dist/locale-files.d.ts +6 -3
  42. package/dist/locale-files.js +15 -9
  43. package/dist/manifest.d.ts +1 -1
  44. package/dist/manifest.js +46 -32
  45. package/dist/materialize.js +6 -2
  46. package/dist/module-set.js +2 -1
  47. package/dist/node-options.d.ts +1 -0
  48. package/dist/node-options.js +8 -0
  49. package/dist/output.d.ts +15 -9
  50. package/dist/output.js +30 -18
  51. package/dist/ports.d.ts +3 -1
  52. package/dist/ports.js +1 -1
  53. package/dist/private-values.d.ts +1 -1
  54. package/dist/private-values.js +5 -3
  55. package/dist/project-modules.d.ts +5 -1
  56. package/dist/project-modules.js +23 -15
  57. package/dist/renderer-range.js +5 -3
  58. package/dist/server-process.js +1 -1
  59. package/dist/verify-source-build.js +10 -2
  60. package/dist/verify-source-client-bundle.d.ts +1 -0
  61. package/dist/verify-source-client-bundle.js +30 -0
  62. package/dist/verify-source-runner.js +36 -94
  63. package/dist/verify-source-sources.js +49 -11
  64. package/dist/verify-source-workspace.d.ts +2 -0
  65. package/dist/verify-source-workspace.js +111 -0
  66. package/dist/workspace-setup.d.ts +1 -0
  67. package/dist/workspace-setup.js +4 -3
  68. package/package.json +11 -7
  69. package/skills/dms-frontend/SKILL.md +1 -1
  70. package/templates/vue/app-runtime.ts +2 -9
  71. package/templates/vue/backend-boundary.mjs +132 -0
  72. package/templates/vue/client-chunking.mjs +2 -0
  73. package/templates/vue/client-server-guard.mjs +16 -0
  74. package/templates/vue/compress-assets.mjs +9 -1
  75. package/templates/vue/entry-weight.mjs +29 -3
  76. package/templates/vue/frontend-build.ts +4 -3
  77. package/templates/vue/frontend-module.ts +1 -0
  78. package/templates/vue/frontend-runtime/module-registration.ts +2 -2
  79. package/templates/vue/frontend-runtime/state.ts +2 -2
  80. package/templates/vue/frontend-runtime/types.ts +8 -2
  81. package/templates/vue/head-order.mjs +10 -2
  82. package/templates/vue/icon-api.mjs +2 -0
  83. package/templates/vue/icon-scan.mjs +4 -0
  84. package/templates/vue/server/auth/backend.mjs +42 -5
  85. package/templates/vue/server/auth/client-ip.mjs +52 -4
  86. package/templates/vue/server/auth/establish-endpoints.mjs +11 -7
  87. package/templates/vue/server/auth/session.mjs +86 -4
  88. package/templates/vue/server/auth-declarations.mjs +46 -0
  89. package/templates/vue/server/auth-hooks.mjs +55 -7
  90. package/templates/vue/server/backend-proxies.mjs +78 -18
  91. package/templates/vue/server/backend-request.mjs +61 -0
  92. package/templates/vue/server/backend-response.mjs +23 -6
  93. package/templates/vue/server/built-files.mjs +7 -0
  94. package/templates/vue/server/cli-report.mjs +75 -12
  95. package/templates/vue/server/client-error.mjs +5 -0
  96. package/templates/vue/server/client-manifest.mjs +23 -1
  97. package/templates/vue/server/content.mjs +40 -10
  98. package/templates/vue/server/dev-private-files.mjs +17 -1
  99. package/templates/vue/server/dev-styles.mjs +38 -0
  100. package/templates/vue/server/email-bundle-watch.mjs +9 -3
  101. package/templates/vue/server/email.mjs +14 -6
  102. package/templates/vue/server/favicon.mjs +3 -0
  103. package/templates/vue/server/generated-files.mjs +221 -0
  104. package/templates/vue/server/homepage.mjs +21 -9
  105. package/templates/vue/server/html-tag.mjs +4 -0
  106. package/templates/vue/server/icons.mjs +93 -16
  107. package/templates/vue/server/in-place-render.mjs +35 -0
  108. package/templates/vue/server/inertia.mjs +76 -3
  109. package/templates/vue/server/module-event.mjs +118 -25
  110. package/templates/vue/server/module-route-types.mjs +78 -0
  111. package/templates/vue/server/module-routes.mjs +151 -96
  112. package/templates/vue/server/module-server.mjs +121 -40
  113. package/templates/vue/server/module-testing.mjs +36 -12
  114. package/templates/vue/server/page-props.mjs +94 -3
  115. package/templates/vue/server/page-redirects.mjs +37 -0
  116. package/templates/vue/server/path-patterns.mjs +35 -12
  117. package/templates/vue/server/proxy-compression.mjs +22 -0
  118. package/templates/vue/server/proxy-headers.mjs +12 -2
  119. package/templates/vue/server/proxy-redirects.mjs +21 -5
  120. package/templates/vue/server/proxy-request.mjs +58 -0
  121. package/templates/vue/server/proxy-transfers.mjs +44 -10
  122. package/templates/vue/server/public-endpoints.mjs +139 -28
  123. package/templates/vue/server/public-options.mjs +20 -6
  124. package/templates/vue/server/request-failure.mjs +24 -1
  125. package/templates/vue/server/request-routing.mjs +32 -10
  126. package/templates/vue/server/request-target.mjs +30 -11
  127. package/templates/vue/server/route-rules.mjs +48 -18
  128. package/templates/vue/server/ssr-module.mjs +32 -0
  129. package/templates/vue/server/static-files.mjs +35 -7
  130. package/templates/vue/server/type-guards.mjs +71 -0
  131. package/templates/vue/server/vite-errors.mjs +45 -7
  132. package/templates/vue/server/web-body.mjs +5 -1
  133. package/templates/vue/server-module.ts +12 -2
  134. package/templates/vue/server.mjs +162 -157
  135. package/templates/vue/sfc-global-types.mjs +112 -13
  136. package/templates/vue/ssr-renderer.ts +2 -2
  137. package/templates/vue/ui-inertia-page.mjs +16 -1
  138. package/templates/vue/ui-kbd.mjs +2 -1
  139. package/templates/vue/ui-request-state.mjs +2 -1
  140. package/templates/vue/vite.config.ts +9 -0
@@ -37,90 +37,29 @@ const node_fs_1 = require("node:fs");
37
37
  const node_path_1 = require("node:path");
38
38
  const cli_1 = require("@antelopejs/core/cli");
39
39
  const child_output_1 = require("./child-output");
40
+ const ensure_1 = require("./internal/ensure");
41
+ const guards_1 = require("./internal/guards");
42
+ const vite_manifest_1 = require("./internal/vite-manifest");
40
43
  const common_1 = require("./common");
41
44
  const output_1 = require("./output");
42
45
  const temporary_workspace_1 = require("./temporary-workspace");
46
+ const verify_source_workspace_1 = require("./verify-source-workspace");
47
+ const verify_source_client_bundle_1 = require("./verify-source-client-bundle");
43
48
  const verify_source_build_1 = require("./verify-source-build");
44
49
  const verify_source_result_1 = require("./verify-source-result");
45
50
  const verify_source_sources_1 = require("./verify-source-sources");
46
51
  const vite_output_1 = require("./vite-output");
47
52
  const EMAIL_BUILD_SIZE_CEILING_BYTES = 256 * 1024;
48
53
  const NON_EMAIL_RUNTIME_PATTERN = /DisplayRichText|ApexCharts|Tiptap|FlowCanvas|app\/.*table|components\/table/i;
49
- const LAZY_LIBRARIES = [
50
- { name: "ApexCharts", pattern: /ApexCharts/ },
51
- { name: "Tiptap", pattern: /tiptap/i },
52
- ];
53
- const RUNTIME_MARKERS = ["auth/login", "DefaultLayout", "error.500.title"];
54
- function readJson(path) {
55
- return JSON.parse((0, node_fs_1.readFileSync)(path, "utf8"));
56
- }
57
- function ensure(condition, title, reason) {
58
- if (!condition)
59
- throw new cli_1.CliError({ title, reason });
60
- }
61
- function removeDevelopmentDependencies(root) {
62
- const packagePath = (0, node_path_1.join)(root, "package.json");
63
- if (!(0, node_fs_1.existsSync)(packagePath))
64
- return;
65
- const packageData = readJson(packagePath);
66
- delete packageData.devDependencies;
67
- (0, node_fs_1.writeFileSync)(packagePath, `${JSON.stringify(packageData, null, 2)}\n`);
68
- }
69
- function materializeWorkspace(workspace, layers, localPackages) {
70
- const templateRoot = (0, node_path_1.join)((0, common_1.getPackageRoot)(), "templates", "vue");
71
- const templateFiles = (0, node_fs_1.readdirSync)(templateRoot).filter((file) => !file.startsWith("npmrc"));
72
- for (const file of templateFiles)
73
- (0, node_fs_1.cpSync)((0, node_path_1.join)(templateRoot, file), (0, node_path_1.join)(workspace, file), {
74
- recursive: true,
75
- });
76
- (0, common_1.materializeLayers)(workspace, layers);
77
- const registry = (0, common_1.createFrontendModuleRegistry)(workspace, layers);
78
- registry.modules.forEach((module) => {
79
- removeDevelopmentDependencies(module.root);
80
- });
81
- (0, common_1.writeFrontendModuleRegistry)(workspace, layers);
82
- (0, node_fs_1.writeFileSync)((0, node_path_1.join)(workspace, "dms-main.css"), '@import "tailwindcss";\n@import "@nuxt/ui";\n');
83
- (0, common_1.writeWorkspacePackageJson)(workspace, layers);
84
- const packagePath = (0, node_path_1.join)(workspace, "package.json");
85
- const workspacePackage = readJson(packagePath);
86
- workspacePackage.pnpm = {
87
- overrides: {
88
- ...Object.fromEntries(layers.map((layer) => [layer.packageName, "workspace:*"])),
89
- ...Object.fromEntries(Object.entries(localPackages).map(([name, path]) => [
90
- name,
91
- `link:${(0, node_path_1.resolve)(path)}`,
92
- ])),
93
- },
94
- };
95
- (0, node_fs_1.writeFileSync)(packagePath, `${JSON.stringify(workspacePackage, null, 2)}\n`);
96
- }
97
- function checkClientBundle(workspace) {
98
- const clientRoot = (0, node_path_1.join)(workspace, "dist", "client");
99
- const manifest = readJson((0, node_path_1.join)(clientRoot, ".vite", "manifest.json"));
100
- const mainEntry = Object.values(manifest).find((entry) => entry.isEntry);
101
- ensure(mainEntry !== undefined, "The Vite manifest names no entry chunk");
102
- const mainJavascript = (0, node_fs_1.readFileSync)((0, node_path_1.join)(clientRoot, mainEntry.file), "utf8");
103
- const javascript = (0, node_fs_1.readdirSync)((0, node_path_1.join)(clientRoot, "assets"))
104
- .filter((file) => file.endsWith(".js"))
105
- .map((file) => (0, node_fs_1.readFileSync)((0, node_path_1.join)(clientRoot, "assets", file), "utf8"))
106
- .join("\n");
107
- for (const { name, pattern } of LAZY_LIBRARIES) {
108
- ensure(!pattern.test(mainJavascript), `The main chunk bundles ${name}`, "It must stay in a lazily loaded chunk.");
109
- ensure(pattern.test(javascript), `No client chunk bundles ${name}`);
110
- }
111
- ensure(Object.keys(manifest).some((source) => source.endsWith("/kpi/KpiCard.vue")), "DmsKpiCard is not reachable at runtime");
112
- for (const marker of RUNTIME_MARKERS)
113
- ensure(javascript.includes(marker), `${marker} is not reachable at runtime`);
114
- }
115
54
  async function checkServerModulesBundle(workspace) {
116
55
  const root = (0, node_path_1.join)(workspace, "dist", "server-modules");
117
56
  const javascript = (0, node_fs_1.readdirSync)(root, { recursive: true, encoding: "utf8" })
118
57
  .filter((file) => file.endsWith(".js"))
119
58
  .map((file) => (0, node_fs_1.readFileSync)((0, node_path_1.join)(root, file), "utf8"))
120
59
  .join("\n");
121
- ensure(!/\.vue["'?]|createApp\(|DefaultLayout/.test(javascript), "The server modules bundle loads application code", "A dms.server.ts imports server code only, never .vue files or dms.frontend.ts.");
122
- const { serverModules } = await Promise.resolve(`${(0, node_path_1.join)(root, "server-modules.generated.js")}`).then(s => __importStar(require(s)));
123
- ensure(Array.isArray(serverModules), "The server modules bundle exports none");
60
+ (0, ensure_1.ensure)(!/\.vue["'?]|createApp\(|DefaultLayout/.test(javascript), "The server modules bundle loads application code", "A dms.server.ts imports server code only, never .vue files or dms.frontend.ts.");
61
+ const serverModules = (await Promise.resolve(`${(0, node_path_1.join)(root, "server-modules.generated.js")}`).then(s => __importStar(require(s)))).serverModules;
62
+ (0, ensure_1.ensure)(Array.isArray(serverModules), "The server modules bundle exports none");
124
63
  return `${(0, cli_1.pluralize)(serverModules.length, "server module")}, ${(0, output_1.formatSize)(Buffer.byteLength(javascript))} JS`;
125
64
  }
126
65
  const EMAIL_CASES = [
@@ -177,19 +116,19 @@ const EMAIL_CASES = [
177
116
  ];
178
117
  const BROWSER_ONLY_REASON = "It must not load rich text, charts, tables or flows.";
179
118
  function checkLayoutEmail(html) {
180
- ensure(/<meta charset="utf-8"/.test(html), "EmailLayout declares no UTF-8 charset");
181
- ensure(/images\/antelope-logo\/light\.svg/.test(html), "EmailLayout renders no logo");
182
- ensure(!/<h1[^>]*\bas=/.test(html), "EmailLayout leaks an `as` attribute onto its heading");
119
+ (0, ensure_1.ensure)(/<meta charset="utf-8"/.test(html), "EmailLayout declares no UTF-8 charset");
120
+ (0, ensure_1.ensure)(/images\/antelope-logo\/light\.svg/.test(html), "EmailLayout renders no logo");
121
+ (0, ensure_1.ensure)(!/<h1[^>]*\bas=/.test(html), "EmailLayout leaks an `as` attribute onto its heading");
183
122
  }
184
123
  async function checkEmailContracts(workspace) {
185
124
  const emailRoot = (0, node_path_1.join)(workspace, "dist", "server");
186
125
  const emailManifestContent = (0, node_fs_1.readFileSync)((0, node_path_1.join)(emailRoot, ".vite", "manifest.json"), "utf8");
187
- ensure(!NON_EMAIL_RUNTIME_PATTERN.test(emailManifestContent), "The e-mail manifest references browser-only modules", BROWSER_ONLY_REASON);
188
- const emailManifest = JSON.parse(emailManifestContent);
126
+ (0, ensure_1.ensure)(!NON_EMAIL_RUNTIME_PATTERN.test(emailManifestContent), "The e-mail manifest references browser-only modules", BROWSER_ONLY_REASON);
127
+ const emailManifest = (0, vite_manifest_1.parseViteManifest)(emailManifestContent, "e-mail build manifest");
189
128
  const emailJavascriptFiles = [
190
129
  ...new Set([
191
130
  "email-renderer.js",
192
- ...Object.values(emailManifest)
131
+ ...emailManifest
193
132
  .map((entry) => entry.file)
194
133
  .filter((file) => file.endsWith(".js")),
195
134
  ]),
@@ -197,18 +136,18 @@ async function checkEmailContracts(workspace) {
197
136
  const emailJavascript = emailJavascriptFiles
198
137
  .map((file) => (0, node_fs_1.readFileSync)((0, node_path_1.join)(emailRoot, file), "utf8"))
199
138
  .join("\n");
200
- ensure(!NON_EMAIL_RUNTIME_PATTERN.test(emailJavascript), "The e-mail bundle loads browser-only modules", BROWSER_ONLY_REASON);
139
+ (0, ensure_1.ensure)(!NON_EMAIL_RUNTIME_PATTERN.test(emailJavascript), "The e-mail bundle loads browser-only modules", BROWSER_ONLY_REASON);
201
140
  const emailBuildSize = Buffer.byteLength(emailJavascript);
202
141
  const emailLocaleBytes = (0, node_fs_1.readdirSync)((0, node_path_1.join)(emailRoot, "locales")).reduce((total, file) => total + (0, node_fs_1.readFileSync)((0, node_path_1.join)(emailRoot, "locales", file)).byteLength, 0);
203
142
  const limit = (0, output_1.formatSize)(EMAIL_BUILD_SIZE_CEILING_BYTES);
204
- ensure(emailBuildSize <= EMAIL_BUILD_SIZE_CEILING_BYTES, `The e-mail bundle is ${(0, output_1.formatSize)(emailBuildSize)} of JavaScript`, `The limit is ${limit}.`);
143
+ (0, ensure_1.ensure)(emailBuildSize <= EMAIL_BUILD_SIZE_CEILING_BYTES, `The e-mail bundle is ${(0, output_1.formatSize)(emailBuildSize)} of JavaScript`, `The limit is ${limit}.`);
205
144
  const { renderEmail } = await Promise.resolve(`${(0, node_path_1.join)(emailRoot, "email-renderer.js")}`).then(s => __importStar(require(s)));
206
145
  for (const emailCase of EMAIL_CASES) {
207
146
  const html = await renderEmail(emailCase.name, emailCase.props);
208
- ensure(html.startsWith("<!doctype html>"), `${emailCase.name} does not render a full HTML document`);
147
+ (0, ensure_1.ensure)((0, guards_1.isText)(html) && html.startsWith("<!doctype html>"), `${emailCase.name} does not render a full HTML document`);
209
148
  if (emailCase.name === "EmailLayout")
210
149
  checkLayoutEmail(html);
211
- ensure(html.includes(emailCase.marker), `${emailCase.name} does not render its props`, `Its HTML lacks ${emailCase.marker}.`);
150
+ (0, ensure_1.ensure)(html.includes(emailCase.marker), `${emailCase.name} does not render its props`, `Its HTML lacks ${emailCase.marker}.`);
212
151
  }
213
152
  return [
214
153
  "E-mail contracts hold",
@@ -219,18 +158,6 @@ async function checkEmailContracts(workspace) {
219
158
  }
220
159
  const LINE_BREAK = /\r?\n/;
221
160
  const STACK_TRACE_HINT = "Run with --verbose for the full trace.";
222
- function describeError(error, isVerbose) {
223
- if (error instanceof cli_1.CliError)
224
- return error.problem;
225
- if (!(error instanceof Error))
226
- return { title: String(error) };
227
- const [title] = error.message.split(LINE_BREAK);
228
- const stack = (error.stack ?? "").split(LINE_BREAK);
229
- return {
230
- title: title || error.name,
231
- details: isVerbose ? stack : [STACK_TRACE_HINT],
232
- };
233
- }
234
161
  async function verifySources() {
235
162
  const options = (0, verify_source_sources_1.readSourceOptions)();
236
163
  const { layers, coreLayer } = (0, verify_source_sources_1.readSources)(options);
@@ -247,11 +174,11 @@ async function verifySources() {
247
174
  env: { ...process.env, [vite_output_1.VITE_LOG_LEVEL_VARIABLE]: (0, vite_output_1.viteLogLevel)(isVerbose) },
248
175
  };
249
176
  const tasks = (0, cli_1.getProcessTasks)();
250
- 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")}` });
251
178
  await (0, common_1.installDeps)(workspace, context.mapLine, ["--ignore-scripts"]);
252
179
  await (0, verify_source_build_1.prepareTypes)(context);
253
180
  await (0, verify_source_build_1.buildAndTypeCheck)(context);
254
- await tasks.run("Checking the client bundle", async () => checkClientBundle(workspace), { done: "Client bundle checked", failed: "Client bundle check failed" });
181
+ await tasks.run("Checking the client bundle", async () => (0, verify_source_client_bundle_1.checkClientBundle)(workspace), { done: "Client bundle checked", failed: "Client bundle check failed" });
255
182
  await tasks.run("Checking the e-mail contracts", async () => checkEmailContracts(workspace), { done: (line) => line, failed: "E-mail contracts failed" });
256
183
  await tasks.run("Checking the server modules", async () => checkServerModulesBundle(workspace), { done: (line) => line, failed: "Server modules check failed" });
257
184
  return { ok: true, modules: layers.length };
@@ -276,7 +203,22 @@ async function main() {
276
203
  process.exitCode = error.exitCode;
277
204
  return;
278
205
  }
279
- result = { ok: false, problem: describeError(error, (0, cli_1.isVerboseRun)()) };
206
+ let problem;
207
+ if (error instanceof cli_1.CliError) {
208
+ problem = error.problem;
209
+ }
210
+ else if (error instanceof Error) {
211
+ const [title] = error.message.split(LINE_BREAK);
212
+ const stack = (error.stack ?? "").split(LINE_BREAK);
213
+ problem = {
214
+ title: title || error.name,
215
+ details: (0, cli_1.isVerboseRun)() ? stack : [STACK_TRACE_HINT],
216
+ };
217
+ }
218
+ else {
219
+ problem = { title: String(error) };
220
+ }
221
+ result = { ok: false, problem };
280
222
  }
281
223
  if (process.send) {
282
224
  process.exitCode = result.ok ? 0 : 1;
@@ -5,18 +5,20 @@ exports.readSources = readSources;
5
5
  const node_fs_1 = require("node:fs");
6
6
  const node_path_1 = require("node:path");
7
7
  const common_1 = require("./common");
8
+ const guards_1 = require("./internal/guards");
9
+ const plain_object_1 = require("./internal/plain-object");
8
10
  const output_1 = require("./output");
9
11
  const verify_source_result_1 = require("./verify-source-result");
10
12
  const DMS_PACKAGE = "@antelopejs/dms";
11
13
  const DMS_CORE_LAYER_PACKAGE = "@antelopejs/dms-frontend-vue";
12
14
  const CORE_LAYER_DIRECTORY = "frontend-vue";
13
15
  const FRONTEND_ENTRY = "dms.frontend.ts";
14
- const ADD_DEV_DEPENDENCY = {
15
- pnpm: "pnpm add -D",
16
- npm: "npm install -D",
17
- yarn: "yarn add -D",
18
- bun: "bun add -d",
19
- };
16
+ const ADD_DEV_DEPENDENCY = new Map([
17
+ ["pnpm", "pnpm add -D"],
18
+ ["npm", "npm install -D"],
19
+ ["yarn", "yarn add -D"],
20
+ ["bun", "bun add -d"],
21
+ ]);
20
22
  const LOCKFILES = [
21
23
  ["pnpm-lock.yaml", "pnpm"],
22
24
  ["package-lock.json", "npm"],
@@ -24,16 +26,46 @@ const LOCKFILES = [
24
26
  ["bun.lock", "bun"],
25
27
  ["bun.lockb", "bun"],
26
28
  ];
29
+ function malformedVariable(variable, expected) {
30
+ return new common_1.UsageError({
31
+ title: `${variable} is not ${expected}`,
32
+ reason: `\`ajs dms verify-source\` starts the runner with ${variable} set to ${expected}.`,
33
+ });
34
+ }
35
+ function readTextList(variable) {
36
+ const value = JSON.parse(process.env[variable] ?? "[]");
37
+ if (Array.isArray(value) && value.every(guards_1.isText))
38
+ return value;
39
+ throw malformedVariable(variable, "a JSON list of paths");
40
+ }
41
+ function readTextRecord(variable) {
42
+ const value = JSON.parse(process.env[variable] ?? "{}");
43
+ if ((0, plain_object_1.isPlainObject)(value)) {
44
+ const entries = Object.entries(value);
45
+ if (entries.every((entry) => (0, guards_1.isText)(entry[1]))) {
46
+ return Object.fromEntries(entries);
47
+ }
48
+ }
49
+ throw malformedVariable(variable, "a JSON record of paths by package name");
50
+ }
27
51
  function readSourceOptions() {
28
52
  return {
29
53
  projectDir: process.cwd(),
30
54
  layerSource: process.env.DMS_LAYER_SOURCE || undefined,
31
- moduleSources: JSON.parse(process.env.DMS_MODULE_SOURCES ?? "[]"),
32
- localPackages: JSON.parse(process.env.DMS_LOCAL_PACKAGES ?? "{}"),
55
+ moduleSources: readTextList("DMS_MODULE_SOURCES"),
56
+ localPackages: readTextRecord("DMS_LOCAL_PACKAGES"),
33
57
  };
34
58
  }
35
59
  function readManifest(packagePath) {
36
- return JSON.parse((0, node_fs_1.readFileSync)(packagePath, "utf8"));
60
+ const parsed = JSON.parse((0, node_fs_1.readFileSync)(packagePath, "utf8"));
61
+ if (!(0, plain_object_1.isPlainObject)(parsed))
62
+ return {};
63
+ const { name, version, packageManager } = parsed;
64
+ return {
65
+ name: (0, guards_1.isText)(name) ? name : undefined,
66
+ version: (0, guards_1.isText)(version) ? version : undefined,
67
+ packageManager: (0, guards_1.isText)(packageManager) ? packageManager : undefined,
68
+ };
37
69
  }
38
70
  function readLayer(root) {
39
71
  const packagePath = (0, node_path_1.join)(root, "package.json");
@@ -68,7 +100,7 @@ function packageManager(projectDir) {
68
100
  const packagePath = (0, node_path_1.join)(directory, "package.json");
69
101
  if ((0, node_fs_1.existsSync)(packagePath)) {
70
102
  const [name] = (readManifest(packagePath).packageManager ?? "").split("@");
71
- if (name && ADD_DEV_DEPENDENCY[name])
103
+ if (name && ADD_DEV_DEPENDENCY.has(name))
72
104
  return name;
73
105
  const lockfile = LOCKFILES.find(([file]) => (0, node_fs_1.existsSync)((0, node_path_1.join)(directory, file)));
74
106
  if (lockfile)
@@ -79,7 +111,7 @@ function packageManager(projectDir) {
79
111
  }
80
112
  }
81
113
  function addDmsCommand(projectDir, version = "") {
82
- return `${ADD_DEV_DEPENDENCY[packageManager(projectDir)]} ${DMS_PACKAGE}${version}`;
114
+ return `${ADD_DEV_DEPENDENCY.get(packageManager(projectDir))} ${DMS_PACKAGE}${version}`;
83
115
  }
84
116
  function describeDirectory(directory) {
85
117
  return (0, node_path_1.resolve)(directory) === process.cwd()
@@ -107,6 +139,12 @@ function installedCoreLayer(projectDir) {
107
139
  ],
108
140
  });
109
141
  const { version } = readManifest(packagePath);
142
+ if (version === undefined)
143
+ throw new common_1.UsageError({
144
+ title: `${DMS_PACKAGE} has no text "version" in its package.json`,
145
+ reason: `${(0, output_1.showPath)(packagePath)} has no "version" that is text, and the verification names the installed ${DMS_PACKAGE} by it.`,
146
+ fixes: [`Reinstall it: ${addDmsCommand(projectDir, "@latest")}`],
147
+ });
110
148
  const root = (0, node_path_1.join)((0, node_path_1.dirname)(packagePath), CORE_LAYER_DIRECTORY);
111
149
  const layer = (0, node_fs_1.existsSync)((0, node_path_1.join)(root, "package.json"))
112
150
  ? readLayer(root)
@@ -0,0 +1,2 @@
1
+ import { type ResolvedLayer } from "./common";
2
+ export declare function materializeWorkspace(workspace: string, layers: ResolvedLayer[], localPackages: Record<string, string>): void;
@@ -0,0 +1,111 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.materializeWorkspace = materializeWorkspace;
4
+ const node_fs_1 = require("node:fs");
5
+ const node_path_1 = require("node:path");
6
+ const yaml_1 = require("yaml");
7
+ const common_1 = require("./common");
8
+ const ensure_1 = require("./internal/ensure");
9
+ const guards_1 = require("./internal/guards");
10
+ const plain_object_1 = require("./internal/plain-object");
11
+ const output_1 = require("./output");
12
+ function removeDevelopmentDependencies(root) {
13
+ const packagePath = (0, node_path_1.join)(root, "package.json");
14
+ if (!(0, node_fs_1.existsSync)(packagePath))
15
+ return;
16
+ const packageData = JSON.parse((0, node_fs_1.readFileSync)(packagePath, "utf8"));
17
+ if (!(0, plain_object_1.isPlainObject)(packageData))
18
+ return;
19
+ delete packageData.devDependencies;
20
+ (0, node_fs_1.writeFileSync)(packagePath, `${JSON.stringify(packageData, null, 2)}\n`);
21
+ }
22
+ const PATH_PROTOCOLS = ["file:", "link:"];
23
+ const WORKSPACE_PROTOCOL = "workspace:";
24
+ const LOCKFILE = "pnpm-lock.yaml";
25
+ const WORKSPACE_FILE = "pnpm-workspace.yaml";
26
+ const OWN_LOCKFILES = /^\s*shared-workspace-lockfile\s*=\s*false\s*$/m;
27
+ function findUp(directory, file) {
28
+ if ((0, node_fs_1.existsSync)((0, node_path_1.join)(directory, file)))
29
+ return directory;
30
+ const parent = (0, node_path_1.dirname)(directory);
31
+ return parent === directory ? undefined : findUp(parent, file);
32
+ }
33
+ function sharesLockfile(root) {
34
+ const npmrc = (0, node_path_1.join)(root, ".npmrc");
35
+ if ((0, node_fs_1.existsSync)(npmrc) && OWN_LOCKFILES.test((0, node_fs_1.readFileSync)(npmrc, "utf8")))
36
+ return false;
37
+ const settings = (0, yaml_1.parse)((0, node_fs_1.readFileSync)((0, node_path_1.join)(root, WORKSPACE_FILE), "utf8"));
38
+ return !((0, plain_object_1.isPlainObject)(settings) && settings.sharedWorkspaceLockfile === false);
39
+ }
40
+ function findLockfile(directory) {
41
+ const workspaceRoot = findUp(directory, WORKSPACE_FILE);
42
+ const lockfileRoot = workspaceRoot && sharesLockfile(workspaceRoot)
43
+ ? workspaceRoot
44
+ : findUp(directory, LOCKFILE);
45
+ if (!lockfileRoot)
46
+ return undefined;
47
+ const lockfile = (0, node_path_1.join)(lockfileRoot, LOCKFILE);
48
+ const outsideWorkspace = workspaceRoot !== undefined &&
49
+ (0, node_path_1.relative)(workspaceRoot, lockfileRoot).startsWith("..");
50
+ return (0, node_fs_1.existsSync)(lockfile) && !outsideWorkspace ? lockfile : undefined;
51
+ }
52
+ function readLayerOverrides(layer) {
53
+ const lockfile = findLockfile(layer.path);
54
+ if (!lockfile)
55
+ return [];
56
+ const root = (0, node_path_1.dirname)(lockfile);
57
+ const overrides = [];
58
+ for (const document of (0, yaml_1.parseAllDocuments)((0, node_fs_1.readFileSync)(lockfile, "utf8"))) {
59
+ const [error] = document.errors;
60
+ (0, ensure_1.ensure)(error === undefined, `${(0, output_1.showPath)(lockfile)} is not a pnpm lockfile`, error?.message);
61
+ const lock = document.toJS();
62
+ if (!(0, plain_object_1.isPlainObject)(lock) || !(0, plain_object_1.isPlainObject)(lock.overrides))
63
+ continue;
64
+ for (const [name, spec] of Object.entries(lock.overrides)) {
65
+ if (!(0, guards_1.isText)(spec) || spec.startsWith(WORKSPACE_PROTOCOL))
66
+ continue;
67
+ overrides.push([name, resolveSpec(root, spec)]);
68
+ }
69
+ }
70
+ return overrides;
71
+ }
72
+ function resolveSpec(root, spec) {
73
+ const protocol = PATH_PROTOCOLS.find((prefix) => spec.startsWith(prefix));
74
+ if (!protocol)
75
+ return spec;
76
+ const path = spec.slice(protocol.length);
77
+ return (0, node_path_1.isAbsolute)(path) ? spec : `${protocol}${(0, node_path_1.resolve)(root, path)}`;
78
+ }
79
+ function materializeWorkspace(workspace, layers, localPackages) {
80
+ const templateRoot = (0, node_path_1.join)((0, common_1.getPackageRoot)(), "templates", "vue");
81
+ const templateFiles = (0, node_fs_1.readdirSync)(templateRoot).filter((file) => !file.startsWith("npmrc"));
82
+ for (const file of templateFiles)
83
+ (0, node_fs_1.cpSync)((0, node_path_1.join)(templateRoot, file), (0, node_path_1.join)(workspace, file), {
84
+ recursive: true,
85
+ });
86
+ (0, common_1.materializeLayers)(workspace, layers);
87
+ const registry = (0, common_1.createFrontendModuleRegistry)(workspace, layers);
88
+ registry.modules.forEach((module) => {
89
+ removeDevelopmentDependencies(module.root);
90
+ });
91
+ (0, common_1.writeFrontendModuleRegistry)(workspace, layers);
92
+ (0, node_fs_1.writeFileSync)((0, node_path_1.join)(workspace, "dms-main.css"), '@import "tailwindcss";\n@import "@nuxt/ui";\n');
93
+ (0, common_1.writeWorkspacePackageJson)(workspace, layers);
94
+ const packagePath = (0, node_path_1.join)(workspace, "package.json");
95
+ const workspacePackage = JSON.parse((0, node_fs_1.readFileSync)(packagePath, "utf8"));
96
+ (0, ensure_1.ensure)((0, plain_object_1.isPlainObject)(workspacePackage), "The workspace package.json is not a JSON object");
97
+ workspacePackage.pnpm = {
98
+ overrides: {
99
+ ...Object.fromEntries(layers.flatMap(readLayerOverrides)),
100
+ ...Object.fromEntries(layers.map((layer) => [
101
+ String(layer.packageName),
102
+ "workspace:*",
103
+ ])),
104
+ ...Object.fromEntries(Object.entries(localPackages).map(([name, path]) => [
105
+ name,
106
+ `link:${(0, node_path_1.resolve)(path)}`,
107
+ ])),
108
+ },
109
+ };
110
+ (0, node_fs_1.writeFileSync)(packagePath, `${JSON.stringify(workspacePackage, null, 2)}\n`);
111
+ }
@@ -31,6 +31,7 @@ export interface SetupWorkspaceOptions {
31
31
  workspaceDir?: string;
32
32
  project?: ProjectModulesOptions;
33
33
  beforeInstall?: () => Promise<void>;
34
+ install?: boolean;
34
35
  }
35
36
  export interface SetupWorkspaceResult {
36
37
  workspaceDir: string;
@@ -95,7 +95,7 @@ async function installDeps(workspaceDir, mapLine, extraArgs = []) {
95
95
  task.succeed(progress.doneLabel);
96
96
  }
97
97
  catch (error) {
98
- (0, output_1.endInterruptedTask)(task, error, "Dependency install stopped");
98
+ (0, output_1.endInterruptedTask)(task, error instanceof cancellation_1.CancelledError ? error : undefined, "Dependency install stopped");
99
99
  throw error;
100
100
  }
101
101
  }
@@ -299,7 +299,8 @@ async function setupWorkspace(opts) {
299
299
  writeDerivedOutputs(workspaceDir, layers);
300
300
  (0, materialize_1.writeDmsMainCss)(workspaceDir, layers);
301
301
  const layerPaths = layers.map((layer) => layer.path);
302
- const shouldInstall = needsInstall(workspaceDir, layerPaths, opts.force);
302
+ const shouldInstall = opts.install !== false &&
303
+ needsInstall(workspaceDir, layerPaths, opts.force);
303
304
  if (shouldInstall) {
304
305
  await opts.beforeInstall?.();
305
306
  await installDeps(workspaceDir, (0, child_output_1.createPathMapper)(workspaceDir, layers));
@@ -321,6 +322,6 @@ function writeDerivedOutputs(workspaceDir, layers) {
321
322
  if (!(error instanceof locale_files_1.InvalidLocaleFileError))
322
323
  throw error;
323
324
  const showSource = (0, child_output_1.createPathMapper)(workspaceDir, layers);
324
- throw new locale_files_1.InvalidLocaleFileError(showSource(error.path), error.reason);
325
+ throw new locale_files_1.InvalidLocaleFileError(showSource(error.path), error.reason, error.details);
325
326
  }
326
327
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@antelopejs/dms-frontend",
3
- "version": "0.9.3",
3
+ "version": "0.10.1",
4
4
  "description": "Frontend-agnostic loader for AntelopeJS DMS, shipping the Vue 3 renderer (Vite, Inertia, SSR)",
5
5
  "keywords": [
6
6
  "antelope",
@@ -66,7 +66,7 @@
66
66
  "test:real-source": "pnpm run build && node dist/verify-source-runner.js",
67
67
  "format": "oxfmt .",
68
68
  "lint": "oxlint && antelopejs-check-interface-ranges && pnpm run docs:reference:check && pnpm run source-checks",
69
- "typecheck": "tsc --noEmit",
69
+ "typecheck": "tsc --noEmit && tsx scripts/typecheck-projects.ts tsconfig.templates.json tsconfig.tests.json",
70
70
  "lint:fix": "oxlint --fix --max-warnings=1000000 && oxfmt .",
71
71
  "knip": "knip --dependencies",
72
72
  "knip:all": "knip",
@@ -76,9 +76,7 @@
76
76
  "prepare": "if git rev-parse --is-inside-work-tree >/dev/null 2>&1 && command -v lefthook >/dev/null 2>&1; then lefthook install; fi",
77
77
  "checks": "antelopejs-checks",
78
78
  "size": "antelopejs-check-size",
79
- "ratchet": "antelopejs-ratchet",
80
79
  "sync": "antelopejs-sync",
81
- "ratchet:update": "antelopejs-ratchet --update",
82
80
  "sync:check": "antelopejs-sync --check",
83
81
  "format:check": "oxfmt --check .",
84
82
  "suppressions": "antelopejs-suppressions",
@@ -89,11 +87,13 @@
89
87
  "chokidar": "^4.0.3",
90
88
  "commander": "^13.1.0",
91
89
  "ignore": "^7.0.5",
92
- "semver": "^7.7.4"
90
+ "semver": "^7.7.4",
91
+ "yaml": "^2.9.1"
93
92
  },
94
93
  "devDependencies": {
95
- "@antelopejs/core": "^1.16.0",
96
- "@antelopejs/tooling-configs": "^0.2.17",
94
+ "@antelopejs/core": "^1.21.0",
95
+ "@antelopejs/interface-dms": "^0.10.0",
96
+ "@antelopejs/tooling-configs": "^0.3.3",
97
97
  "@iconify/utils": "^3.1.0",
98
98
  "@iconify/vue": "^5.0.1",
99
99
  "@inertiajs/vue3": "^3.7.1",
@@ -105,6 +105,7 @@
105
105
  "@vueuse/core": "14.4.0",
106
106
  "defu": "^6.1.4",
107
107
  "devalue": "^5.9.4",
108
+ "fast-check": "^4.10.2",
108
109
  "knip": "^6.34.0",
109
110
  "lefthook": "^2.2.2",
110
111
  "ofetch": "^1.5.1",
@@ -141,6 +142,9 @@
141
142
  }
142
143
  },
143
144
  "antelopeJs": {
145
+ "docs": {
146
+ "standard": true
147
+ },
144
148
  "skills": [
145
149
  "./skills"
146
150
  ]
@@ -40,7 +40,7 @@ tags: [frontend, cli, renderer, vue, inertia, ssr, deployment, dms-module]
40
40
 
41
41
  | Task | Page |
42
42
  | --- | --- |
43
- | Compatibility with DMS releases | `/docs/frontend/introduction#compatibility` |
43
+ | Where to read the renderer releases a DMS release runs on | `/docs/frontend/introduction#compatibility` |
44
44
  | One command in depth | `/docs/frontend/cli/<command>` |
45
45
  | Variables and secrets | `/docs/frontend/configuration` |
46
46
  | Module files and SDK | `/docs/frontend/frontend-modules/anatomy`, then `server-routes`, `sessions-and-sign-in`, `route-rules`, `emails` |
@@ -9,7 +9,6 @@ import { useHead } from "@unhead/vue";
9
9
  import type { VueHeadClient } from "@unhead/vue/types";
10
10
  import type { IconifyIcon } from "@iconify/vue";
11
11
  import { parse } from "devalue";
12
- import type { FetchOptions } from "ofetch";
13
12
  import {
14
13
  type App,
15
14
  type Component,
@@ -28,6 +27,7 @@ import {
28
27
  type DmsLocaleLoader,
29
28
  type DmsPageProps,
30
29
  type DmsPayload,
30
+ type DmsServerFetch,
31
31
  type DmsUser,
32
32
  createDmsFrontendRuntime,
33
33
  dmsPageSuspenseProps,
@@ -107,11 +107,6 @@ export interface DmsConfiguredApp {
107
107
  runtime: ReturnType<typeof createDmsFrontendRuntime>;
108
108
  }
109
109
 
110
- export type DmsServerFetch = (
111
- request: string,
112
- options?: FetchOptions,
113
- ) => Promise<unknown>;
114
-
115
110
  export const DMS_PAYLOAD_ID = "dms-payload";
116
111
  export const SSR_ASYNC_COMPONENTS_ID = "dms-ssr-async-components";
117
112
  const DEFAULT_LOCALE = "en";
@@ -156,9 +151,7 @@ drawLoadedIconsAtOnce();
156
151
  function createBrowserRuntime(
157
152
  serverFetch?: DmsServerFetch,
158
153
  ): DmsConfiguredApp["runtime"] {
159
- const runtime = createDmsFrontendRuntime(
160
- serverFetch as typeof import("ofetch").ofetch,
161
- );
154
+ const runtime = createDmsFrontendRuntime(serverFetch);
162
155
  // Nothing when the server did not render the page: no hydration then.
163
156
  const payload = readServerRendered<DmsPayload | undefined>(
164
157
  DMS_PAYLOAD_ID,