@antelopejs/dms-frontend 0.7.0 → 0.8.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.
Files changed (75) hide show
  1. package/README.md +4 -4
  2. package/dist/commands/build-action.d.ts +8 -5
  3. package/dist/commands/build-action.js +13 -41
  4. package/dist/commands/build.js +9 -15
  5. package/dist/commands/dev-action.js +5 -1
  6. package/dist/commands/start-action.d.ts +2 -0
  7. package/dist/commands/start-action.js +36 -49
  8. package/dist/commands/start.js +8 -6
  9. package/dist/config.d.ts +2 -0
  10. package/dist/config.js +6 -0
  11. package/dist/derived-outputs.js +2 -2
  12. package/dist/fs-sync.d.ts +1 -0
  13. package/dist/fs-sync.js +6 -0
  14. package/dist/help.js +8 -4
  15. package/dist/index.js +4 -6
  16. package/dist/layer-watch.js +4 -0
  17. package/dist/layers.d.ts +1 -3
  18. package/dist/layers.js +1 -95
  19. package/dist/manifest.d.ts +8 -8
  20. package/dist/manifest.js +15 -39
  21. package/dist/materialize.js +6 -0
  22. package/dist/module-set.d.ts +14 -12
  23. package/dist/module-set.js +39 -31
  24. package/dist/private-values.d.ts +2 -2
  25. package/dist/private-values.js +1 -6
  26. package/dist/project-modules.d.ts +46 -0
  27. package/dist/project-modules.js +233 -0
  28. package/dist/workspace-setup.d.ts +3 -7
  29. package/dist/workspace-setup.js +41 -62
  30. package/dist/workspace.d.ts +2 -4
  31. package/dist/workspace.js +1 -1
  32. package/package.json +5 -7
  33. package/skills/dms-frontend/SKILL.md +2 -2
  34. package/templates/vue/client-chunking.mjs +61 -0
  35. package/templates/vue/email-renderer.ts +6 -1
  36. package/templates/vue/frontend-build-loader.ts +1 -1
  37. package/templates/vue/frontend-module.ts +24 -0
  38. package/templates/vue/main.ts +11 -2
  39. package/templates/vue/package.json +2 -2
  40. package/templates/vue/prepare-types.mjs +2 -7
  41. package/templates/vue/public-options.ts +19 -0
  42. package/templates/vue/server/auth/backend.mjs +20 -3
  43. package/templates/vue/server/auth/client-ip.mjs +64 -22
  44. package/templates/vue/server/auth/session.mjs +4 -11
  45. package/templates/vue/server/backend-proxies.mjs +13 -2
  46. package/templates/vue/server/backend-response.mjs +2 -13
  47. package/templates/vue/server/cli-report.mjs +42 -18
  48. package/templates/vue/server/client-manifest.mjs +22 -58
  49. package/templates/vue/server/dev-private-files.mjs +1 -1
  50. package/templates/vue/server/dev-styles.mjs +16 -5
  51. package/templates/vue/server/homepage.mjs +4 -1
  52. package/templates/vue/server/module-event.mjs +23 -8
  53. package/templates/vue/server/module-server.mjs +4 -4
  54. package/templates/vue/server/module-testing.mjs +5 -3
  55. package/templates/vue/server/proxy-compression.mjs +15 -8
  56. package/templates/vue/server/proxy-headers.mjs +12 -1
  57. package/templates/vue/server/proxy-redirects.mjs +66 -0
  58. package/templates/vue/server/proxy-transfers.mjs +84 -0
  59. package/templates/vue/server/public-endpoints.mjs +2 -2
  60. package/templates/vue/server/public-options.mjs +49 -0
  61. package/templates/vue/server/request-routing.mjs +20 -0
  62. package/templates/vue/server/web-body.mjs +24 -0
  63. package/templates/vue/server-module.ts +19 -7
  64. package/templates/vue/server.mjs +59 -49
  65. package/templates/vue/ssr-renderer.ts +19 -1
  66. package/templates/vue/vite.config.ts +72 -46
  67. package/templates/vue/vite.email.config.ts +6 -3
  68. package/templates/vue/vite.server.config.ts +5 -2
  69. package/dist/commands/snapshot-action.d.ts +0 -6
  70. package/dist/commands/snapshot-action.js +0 -53
  71. package/dist/commands/snapshot.d.ts +0 -2
  72. package/dist/commands/snapshot.js +0 -55
  73. package/dist/snapshot.d.ts +0 -25
  74. package/dist/snapshot.js +0 -90
  75. package/templates/vue/server/backend-wake.mjs +0 -129
@@ -4,6 +4,7 @@ import { extname, join, resolve, sep } from "node:path";
4
4
  import { fileURLToPath } from "node:url";
5
5
  import { ofetch } from "ofetch";
6
6
  import {
7
+ CLIENT_HEADERS,
7
8
  relayedHeaders,
8
9
  RequestBodyError,
9
10
  UpstreamError,
@@ -17,7 +18,6 @@ import {
17
18
  redirectAccessRefusal,
18
19
  UNEXPECTED_ERROR_BODY,
19
20
  } from "./server/backend-response.mjs";
20
- import { fetchWaking } from "./server/backend-wake.mjs";
21
21
  import { handleClientError } from "./server/client-error.mjs";
22
22
  import {
23
23
  reportListenError,
@@ -37,7 +37,11 @@ import { documentStyleTags } from "./server/dev-styles.mjs";
37
37
  import { watchEmailBundle } from "./server/email.mjs";
38
38
  import { withFaviconPlaceholder } from "./server/favicon.mjs";
39
39
  import { DEV_FS_DENY, denyPrivateFiles } from "./server/dev-private-files.mjs";
40
- import { ownerLabel, requestOwner } from "./server/request-routing.mjs";
40
+ import {
41
+ ownerLabel,
42
+ reachesBackend,
43
+ requestOwner,
44
+ } from "./server/request-routing.mjs";
41
45
  import { routingReport } from "./server/backend-proxies.mjs";
42
46
  import { routeRuleFor, ruleHeaders } from "./server/route-rules.mjs";
43
47
  import {
@@ -45,6 +49,7 @@ import {
45
49
  loadServerModules,
46
50
  } from "./server/module-server.mjs";
47
51
  import { HOMEPAGE } from "./server/homepage.mjs";
52
+ import { publicOptionsScript } from "./server/public-options.mjs";
48
53
  import { loadPageProps } from "./server/page-props.mjs";
49
54
  import { htmlTag } from "./server/html-tag.mjs";
50
55
  import { listenHost } from "./server/listen-host.mjs";
@@ -55,6 +60,7 @@ import {
55
60
  relayAnswer,
56
61
  } from "./server/proxy-compression.mjs";
57
62
  import { backendUrl, refuseForeignTarget } from "./server/request-target.mjs";
63
+ import { sendWithinLimit, SERVER_OPTIONS } from "./server/proxy-transfers.mjs";
58
64
  import {
59
65
  createInertiaPage,
60
66
  htmlHeaders,
@@ -73,7 +79,12 @@ const SAFE_METHODS = new Set(["GET", "HEAD", "OPTIONS"]);
73
79
  const LOCAL_ROUTES = [[icons.isIconRequest, icons.handleIcons]];
74
80
  const CLIENT_ROOT = join(PROJECT_ROOT, "dist/client");
75
81
  const SOURCE_TEMPLATE_PATH = join(PROJECT_ROOT, "index.html");
76
- const BUILT_SSR_RENDERER_PATH = join(PROJECT_ROOT, "dist/ssr/ssr-renderer.js");
82
+ // A URL, not a path: on Windows, `import()` reads `C:\...` as a URL whose
83
+ // scheme is `c:` and refuses it.
84
+ const BUILT_SSR_RENDERER_URL = new URL(
85
+ "dist/ssr/ssr-renderer.js",
86
+ import.meta.url,
87
+ );
77
88
  // The SSR bundle keeps vue-i18n external, so Node loads its esm-bundler build,
78
89
  // which reads compile-time flags only a bundler replaces. Vite inlines them in
79
90
  // the client bundle; without the same value here, installing vue-i18n under
@@ -94,8 +105,7 @@ async function developmentServer() {
94
105
  // The generated secrets and the modules' server code, never served.
95
106
  fs: { deny: DEV_FS_DENY },
96
107
  };
97
- if (frontendHttpServer)
98
- serverOptions.hmr = { server: frontendHttpServer };
108
+ if (frontendHttpServer) serverOptions.ws = { server: frontendHttpServer };
99
109
  return createViteServer({
100
110
  server: serverOptions,
101
111
  appType: "custom",
@@ -127,9 +137,7 @@ function backendHeaders(request, access = { anonymous: false, headers: [] }) {
127
137
  "if-match",
128
138
  "origin",
129
139
  "referer",
130
- "user-agent",
131
- "x-content-language",
132
- "x-realtime-session",
140
+ ...CLIENT_HEADERS,
133
141
  ...access.headers,
134
142
  ]),
135
143
  ...forwardedHeaders(request),
@@ -155,10 +163,9 @@ function serverComponentFetch(request) {
155
163
  }
156
164
 
157
165
  async function backendJson(path, request) {
158
- const response = await fetchWaking(
159
- new URL(path, process.env.DMS_API_BASE_URL),
160
- { headers: backendHeaders(request), signal: request.dmsAbandoned },
161
- );
166
+ const response = await fetch(new URL(path, process.env.DMS_API_BASE_URL), {
167
+ headers: backendHeaders(request),
168
+ });
162
169
  if (!response.ok) {
163
170
  throw await backendResponseError(response);
164
171
  }
@@ -202,25 +209,45 @@ function redirectSignedOutPrivatePage(request, response, props, auth) {
202
209
  return true;
203
210
  }
204
211
 
205
- /** Sends a request to the backend through the proxy it reached. */
206
- async function proxy(request, response, backendProxy) {
212
+ /**
213
+ * Sends a request to the backend through the proxy it reached.
214
+ * `isProxiedPath` says which backend paths a relayed `Location` may name on
215
+ * the frontend's origin.
216
+ */
217
+ async function proxy(request, response, backendProxy, isProxiedPath) {
207
218
  const access = proxyAccess(request, SAFE_METHODS, backendProxy);
208
219
  if (access.isRefused) {
209
220
  refuseCrossOrigin(response);
210
221
  return true;
211
222
  }
223
+ // A client that left while the request was routed closed the response
224
+ // already, and would never abort the request below.
225
+ if (response.closed) return;
212
226
  // Cancelling the body alone misses a client that leaves before the backend
213
227
  // answers: its stream, an SSE feed above all, then stayed open for good.
214
228
  const abandoned = new AbortController();
215
229
  response.once("close", () => abandoned.abort());
216
- const upstream = await fetchWaking(backendUrl(request.url), {
230
+ const init = {
217
231
  signal: abandoned.signal,
218
232
  method: request.method,
219
233
  headers: { ...backendHeaders(request, access), ...PLAIN_ANSWER_HEADERS },
220
- body: ["GET", "HEAD"].includes(request.method) ? undefined : request,
221
234
  duplex: "half",
235
+ // A backend redirect is the browser's to follow: Media sends a file's
236
+ // link to the storage's signed URL, on the public origin the server
237
+ // itself may not reach, which turned every download into a 502.
238
+ redirect: "manual",
239
+ };
240
+ const upstream = await sendWithinLimit(
241
+ request,
242
+ response,
243
+ backendProxy.maxBodyBytes,
244
+ (body) => fetch(backendUrl(request.url), { ...init, body }),
245
+ );
246
+ if (!upstream) return true;
247
+ await relayAnswer(request, response, upstream, {
248
+ fetched: true,
249
+ isProxiedPath,
222
250
  });
223
- await relayAnswer(request, response, upstream, { fetched: true });
224
251
  }
225
252
 
226
253
  /**
@@ -240,7 +267,7 @@ function serveAsset(pathname, request, response) {
240
267
  async function ssrRenderer(devServer) {
241
268
  const renderer = devServer
242
269
  ? await devServer.ssrLoadModule("/ssr-renderer.ts")
243
- : await (productionSsrRenderer ??= import(BUILT_SSR_RENDERER_PATH));
270
+ : await (productionSsrRenderer ??= import(BUILT_SSR_RENDERER_URL.href));
244
271
  await icons.provideIconCollections(renderer);
245
272
  return renderer;
246
273
  }
@@ -250,6 +277,7 @@ function clientRenderedShell(page) {
250
277
  body: inertiaAppHtml(page),
251
278
  head: { headTags: "", htmlAttrs: "", bodyAttrs: "" },
252
279
  overlays: "",
280
+ modules: [],
253
281
  };
254
282
  }
255
283
 
@@ -290,7 +318,9 @@ export async function renderHtml(
290
318
  : await serverRendered(page, serverFetch, requestCookies, devServer);
291
319
  if (rendered.redirect)
292
320
  return { html: "", status: 200, redirect: rendered.redirect };
293
- const preloads = await documentStyleTags(devServer, page, template);
321
+ const headEnd =
322
+ (await documentStyleTags(devServer, rendered.modules, template)) +
323
+ publicOptionsScript();
294
324
  const html = template
295
325
  .replace("<title>Antelope DMS</title>", () =>
296
326
  withFaviconPlaceholder(rendered.head.headTags),
@@ -299,7 +329,7 @@ export async function renderHtml(
299
329
  htmlTag(attributes, rendered.head.htmlAttrs),
300
330
  )
301
331
  .replace("<body", `<body ${rendered.head.bodyAttrs}`)
302
- .replace("</head>", `${preloads}</head>`)
332
+ .replace("</head>", `${headEnd}</head>`)
303
333
  .replace(
304
334
  '<div id="dms-overlays" class="isolate"></div>',
305
335
  `<div id="dms-overlays" class="isolate">${rendered.overlays}</div>`,
@@ -332,9 +362,6 @@ async function writeBackendError(error, request, response) {
332
362
  if (await redirectAccessRefusal(error, request, response, loadRenderer))
333
363
  return;
334
364
  console.error("DMS backend request failed", error);
335
- const retryHeaders = error?.retryAfter
336
- ? { "retry-after": error.retryAfter }
337
- : {};
338
365
  if (isFrontendVisit(request)) {
339
366
  const props = {
340
367
  path: pathname,
@@ -348,24 +375,18 @@ async function writeBackendError(error, request, response) {
348
375
  request,
349
376
  response,
350
377
  status,
351
- { ...inertiaHeaders(page.version), ...retryHeaders },
378
+ inertiaHeaders(page.version),
352
379
  JSON.stringify(page),
353
380
  );
354
381
  }
355
382
  const { html } = await renderRequestHtml(page, request);
356
- return writeContent(
357
- request,
358
- response,
359
- status,
360
- { ...htmlHeaders(), ...retryHeaders },
361
- html,
362
- );
383
+ return writeContent(request, response, status, htmlHeaders(), html);
363
384
  }
364
385
  const payload =
365
386
  error instanceof UpstreamError || error instanceof RequestBodyError
366
387
  ? { message: error.message }
367
388
  : UNEXPECTED_ERROR_BODY;
368
- response.writeHead(status, { "content-type": JSON_TYPE, ...retryHeaders });
389
+ response.writeHead(status, { "content-type": JSON_TYPE });
369
390
  response.end(JSON.stringify(payload));
370
391
  }
371
392
 
@@ -425,15 +446,16 @@ export async function handleRequest(request, response) {
425
446
  if (localRoute) return localRoute[1](request, response);
426
447
  const devServer = await developmentServer();
427
448
  const modules = await loadServerModules(devServer);
428
- const owner = requestOwner(request.method, pathname, modules, (path) =>
429
- hasRendererFile(devServer, path),
430
- );
449
+ const hasFile = (path) => hasRendererFile(devServer, path);
450
+ const owner = requestOwner(request.method, pathname, modules, hasFile);
431
451
  if (devServer) response.setHeader("x-dms-route-owner", ownerLabel(owner));
432
452
  if (owner.kind === "renderer")
433
453
  return serveRendererFile(devServer, request, response, pathname);
434
454
  if (owner.kind === "module")
435
455
  return handleModuleRoute(request, response, pathname, modules);
436
- if (owner.kind === "backend") return proxy(request, response, owner.proxy);
456
+ const isProxiedPath = (path) => reachesBackend(path, modules, hasFile);
457
+ if (owner.kind === "backend")
458
+ return proxy(request, response, owner.proxy, isProxiedPath);
437
459
  if (owner.kind === "not-found") return writeNotFound(response);
438
460
  if (requestFailure.hasMalformedPath(request.url))
439
461
  return requestFailure.writeBadRequest(response);
@@ -487,19 +509,7 @@ async function writeRequestFailure(error, request, response) {
487
509
  await writeBackendError(error, request, response);
488
510
  }
489
511
 
490
- /**
491
- * Aborted once the client is gone, ending a wait for a waking backend. An
492
- * embedder's response that emits no events never aborts it.
493
- */
494
- function abandonedSignal(response) {
495
- const abandoned = new AbortController();
496
- if (typeof response.once === "function")
497
- response.once("close", () => abandoned.abort());
498
- return abandoned.signal;
499
- }
500
-
501
512
  export async function handleRequestSafely(request, response) {
502
- request.dmsAbandoned = abandonedSignal(response);
503
513
  try {
504
514
  if (refuseForeignTarget(request, response)) return;
505
515
  await handleRequest(request, response);
@@ -515,7 +525,7 @@ if (
515
525
  process.argv[1] &&
516
526
  import.meta.url === new URL(`file://${process.argv[1]}`).href
517
527
  ) {
518
- frontendHttpServer = createServer(handleRequestSafely);
528
+ frontendHttpServer = createServer(SERVER_OPTIONS, handleRequestSafely);
519
529
  frontendHttpServer.on("clientError", (error, socket) =>
520
530
  handleClientError(error, socket, maxHeaderSize),
521
531
  );
@@ -28,7 +28,9 @@ import {
28
28
  setDmsServerRuntimeResolver,
29
29
  setupFrontendModules,
30
30
  trackDmsServerScopes,
31
+ withPublicOptions,
31
32
  } from "./frontend-module";
33
+ import { parsePublicOptions } from "./public-options";
32
34
  import { frontendModules } from "./frontend-modules.generated";
33
35
  import {
34
36
  SSR_ICONS_ID,
@@ -53,6 +55,12 @@ export interface DmsSsrResult {
53
55
  body: string;
54
56
  head: DmsSsrHead;
55
57
  overlays: string;
58
+ /**
59
+ * The id of every single-file component the render created, its path from
60
+ * the workspace root as the client manifest keys it: the server links their
61
+ * stylesheets.
62
+ */
63
+ modules: string[];
56
64
  error?: DmsErrorData;
57
65
  redirect?: string;
58
66
  }
@@ -64,6 +72,8 @@ export interface DmsSsrHead {
64
72
  }
65
73
 
66
74
  interface DmsSsrContext {
75
+ /** Filled by `@vitejs/plugin-vue`: each component adds its id as it sets up. */
76
+ modules?: Set<string>;
67
77
  teleports?: Record<string, string>;
68
78
  }
69
79
 
@@ -122,7 +132,13 @@ function iconsScript(names: Set<string>): string {
122
132
  return `<script id="${SSR_ICONS_ID}" type="application/json">${value}</script>`;
123
133
  }
124
134
 
125
- await setupFrontendModules(frontendModules, appConfigs);
135
+ await setupFrontendModules(
136
+ withPublicOptions(
137
+ frontendModules,
138
+ parsePublicOptions(process.env.DMS_PUBLIC_OPTIONS),
139
+ ),
140
+ appConfigs,
141
+ );
126
142
 
127
143
  export function isDmsFrontendPage(path: string): boolean {
128
144
  return hasDmsPage(path);
@@ -208,6 +224,7 @@ async function renderDmsPageWithRuntime(
208
224
  body: "",
209
225
  head: { bodyAttrs: "", headTags: "", htmlAttrs: "" },
210
226
  overlays: "",
227
+ modules: [],
211
228
  redirect: runtime.serverRedirect,
212
229
  };
213
230
  }
@@ -238,6 +255,7 @@ async function renderDmsPageWithRuntime(
238
255
  htmlAttrs: renderedHead.htmlAttrs,
239
256
  },
240
257
  overlays: ssrContext.teleports?.["#dms-overlays"] ?? "",
258
+ modules: [...(ssrContext.modules ?? [])],
241
259
  };
242
260
  } finally {
243
261
  // Only once the head and the async data are serialized: both still read
@@ -9,11 +9,12 @@ import {
9
9
  normalizePath,
10
10
  type Plugin,
11
11
  } from "vite";
12
+ import { clientCodeSplitting, onBuildLog } from "./client-chunking.mjs";
12
13
  import { rejectServerSourcesInClient } from "./client-server-guard.mjs";
13
14
  import { uiInertiaPage } from "./ui-inertia-page.mjs";
14
15
  import { uiRequestState } from "./ui-request-state.mjs";
15
- import { autoImportDirectories } from "./frontend-build-loader";
16
- import { frontendBuilds } from "./frontend-builds.generated";
16
+ import { autoImportDirectories } from "./frontend-build-loader.ts";
17
+ import { frontendBuilds } from "./frontend-builds.generated.ts";
17
18
  import { orderHeadForFirstPaint } from "./head-order.mjs";
18
19
  import { iconScanGlobs } from "./icon-scan.mjs";
19
20
 
@@ -27,7 +28,10 @@ interface FrontendModuleRegistry {
27
28
  modules: FrontendModuleRegistryEntry[];
28
29
  }
29
30
 
30
- const registryPath = resolve(__dirname, "generated-frontend-modules.json");
31
+ const registryPath = resolve(
32
+ import.meta.dirname,
33
+ "generated-frontend-modules.json",
34
+ );
31
35
  const registry = JSON.parse(
32
36
  readFileSync(registryPath, "utf8"),
33
37
  ) as FrontendModuleRegistry;
@@ -109,7 +113,7 @@ const optimizedDependencies = [
109
113
  * `normalizePath` is Vite's own answer to this, the identity on POSIX.
110
114
  */
111
115
  const optimizerEntries = [
112
- resolve(__dirname, "main.ts"),
116
+ resolve(import.meta.dirname, "main.ts"),
113
117
  ...moduleRoots.map((root) => resolve(root, "dms.frontend.ts")),
114
118
  ...frontendSourceRoots.flatMap((root) => [
115
119
  resolve(root, "app/**/*.vue"),
@@ -128,7 +132,7 @@ function paintBeforeHydrate(): Plugin {
128
132
 
129
133
  const uiLinkImport = "@nuxt/ui/components/Link.vue";
130
134
  const uiInertiaLinkImport = resolve(
131
- __dirname,
135
+ import.meta.dirname,
132
136
  "node_modules/@nuxt/ui/dist/runtime/vue/overrides/inertia/Link.vue",
133
137
  );
134
138
  /**
@@ -163,7 +167,12 @@ export default defineConfig({
163
167
  router: "inertia",
164
168
  icon: {
165
169
  clientBundle: {
166
- scan: { globInclude: iconScanGlobs(__dirname, frontendSourceRoots) },
170
+ scan: {
171
+ globInclude: iconScanGlobs(
172
+ import.meta.dirname,
173
+ frontendSourceRoots,
174
+ ),
175
+ },
167
176
  },
168
177
  },
169
178
  components: { dirs: [] },
@@ -179,42 +188,43 @@ export default defineConfig({
179
188
  // backslashes intact, where `\U`, `\M` and `\f` are read as escape
180
189
  // sequences: the specifier the bundler then resolves is a mangled
181
190
  // path that cannot exist. Keep it POSIX.
182
- [normalizePath(resolve(__dirname, "frontend-module.ts"))]: [
183
- "$fetch",
184
- "abortDmsNavigation",
185
- "clearDmsError",
186
- "createDmsError",
187
- "dmsRequestKey",
188
- "defineDmsAppConfig",
189
- "defineDmsPlugin",
190
- "defineDmsMiddleware",
191
- "defineDmsPageMeta",
192
- "navigateDms",
193
- "preloadDmsComponents",
194
- "refreshDmsData",
195
- "refreshDmsPageData",
196
- "setDmsAsyncDataScope",
197
- "showDmsError",
198
- "useDmsAsyncData",
199
- "useDmsData",
200
- "useDmsCookie",
201
- "useDmsFetch",
202
- "useHead",
203
- "useI18n",
204
- "useDmsError",
205
- "useDmsPagedData",
206
- "useDmsPageLoading",
207
- "useDmsApp",
208
- "useDmsRoute",
209
- "useDmsRouter",
210
- "useDmsRuntimeConfig",
211
- "useSeoMeta",
212
- "useDmsState",
213
- "useDmsAppConfig",
214
- "DmsClientOnly",
215
- "DmsLink",
216
- "useDmsUserSession",
217
- ],
191
+ [normalizePath(resolve(import.meta.dirname, "frontend-module.ts"))]:
192
+ [
193
+ "$fetch",
194
+ "abortDmsNavigation",
195
+ "clearDmsError",
196
+ "createDmsError",
197
+ "dmsRequestKey",
198
+ "defineDmsAppConfig",
199
+ "defineDmsPlugin",
200
+ "defineDmsMiddleware",
201
+ "defineDmsPageMeta",
202
+ "navigateDms",
203
+ "preloadDmsComponents",
204
+ "refreshDmsData",
205
+ "refreshDmsPageData",
206
+ "setDmsAsyncDataScope",
207
+ "showDmsError",
208
+ "useDmsAsyncData",
209
+ "useDmsData",
210
+ "useDmsCookie",
211
+ "useDmsFetch",
212
+ "useHead",
213
+ "useI18n",
214
+ "useDmsError",
215
+ "useDmsPagedData",
216
+ "useDmsPageLoading",
217
+ "useDmsApp",
218
+ "useDmsRoute",
219
+ "useDmsRouter",
220
+ "useDmsRuntimeConfig",
221
+ "useSeoMeta",
222
+ "useDmsState",
223
+ "useDmsAppConfig",
224
+ "DmsClientOnly",
225
+ "DmsLink",
226
+ "useDmsUserSession",
227
+ ],
218
228
  },
219
229
  ],
220
230
  dts: "auto-imports.d.ts",
@@ -228,13 +238,16 @@ export default defineConfig({
228
238
  // Nuxt UI's `Icon` must share one copy for the same-origin provider to apply.
229
239
  dedupe: ["vue", "reka-ui", "@nuxt/ui", "@vueuse/core", "@iconify/vue"],
230
240
  alias: {
231
- "#dms/frontend-module": resolve(__dirname, "frontend-module.ts"),
241
+ "#dms/frontend-module": resolve(
242
+ import.meta.dirname,
243
+ "frontend-module.ts",
244
+ ),
232
245
  // For the server's development loader only: no browser module imports
233
246
  // a `dms.server.ts`.
234
- "#dms/server": resolve(__dirname, "server-module.ts"),
247
+ "#dms/server": resolve(import.meta.dirname, "server-module.ts"),
235
248
  "#build/nuxt-icon-client-bundle": "virtual:nuxt-ui-icons",
236
249
  "#shortcuts-aggregated": resolve(
237
- __dirname,
250
+ import.meta.dirname,
238
251
  "shortcuts-aggregated.generated.ts",
239
252
  ),
240
253
  ...stableModuleAliases,
@@ -252,5 +265,18 @@ export default defineConfig({
252
265
  },
253
266
  ssr: { noExternal: ["@nuxt/icon", "@nuxt/ui"] },
254
267
  server: { strictPort: true, allowedHosts: [".onamp.dev"] },
255
- build: { manifest: true, outDir: "dist/client" },
268
+ build: {
269
+ manifest: true,
270
+ outDir: "dist/client",
271
+ rolldownOptions: { onLog: onBuildLog },
272
+ },
273
+ // The browser bundle only: the server bundles are single Node programs, for
274
+ // which a file count means nothing.
275
+ environments: {
276
+ client: {
277
+ build: {
278
+ rolldownOptions: { output: { codeSplitting: clientCodeSplitting } },
279
+ },
280
+ },
281
+ },
256
282
  });
@@ -14,7 +14,10 @@ interface FrontendModuleRegistry {
14
14
  }
15
15
 
16
16
  const registry = JSON.parse(
17
- readFileSync(resolve(__dirname, "generated-frontend-modules.json"), "utf8"),
17
+ readFileSync(
18
+ resolve(import.meta.dirname, "generated-frontend-modules.json"),
19
+ "utf8",
20
+ ),
18
21
  ) as FrontendModuleRegistry;
19
22
  const moduleAliases = Object.fromEntries(
20
23
  registry.modules.map((module) => [`@frontend/${module.id}`, module.root]),
@@ -42,7 +45,7 @@ export default defineConfig({
42
45
  {
43
46
  name: "dms-email-locale-assets",
44
47
  buildStart() {
45
- const directory = resolve(__dirname, "locales.generated");
48
+ const directory = resolve(import.meta.dirname, "locales.generated");
46
49
  for (const file of readdirSync(directory).filter((name) =>
47
50
  name.endsWith(".json"),
48
51
  )) {
@@ -66,7 +69,7 @@ export default defineConfig({
66
69
  // Inlined verbatim as a module specifier in the generated import, so
67
70
  // it has to be POSIX: a native Windows path would reach the parser
68
71
  // with its backslashes read as string escapes. See vite.config.ts.
69
- [normalizePath(resolve(__dirname, "email-runtime.ts"))]: [
72
+ [normalizePath(resolve(import.meta.dirname, "email-runtime.ts"))]: [
70
73
  "useDmsAppConfig",
71
74
  "useDmsRuntimeConfig",
72
75
  "defineDmsAppConfig",
@@ -12,7 +12,10 @@ interface FrontendModuleRegistry {
12
12
  }
13
13
 
14
14
  const registry = JSON.parse(
15
- readFileSync(resolve(__dirname, "generated-frontend-modules.json"), "utf8"),
15
+ readFileSync(
16
+ resolve(import.meta.dirname, "generated-frontend-modules.json"),
17
+ "utf8",
18
+ ),
16
19
  ) as FrontendModuleRegistry;
17
20
  const moduleAliases = Object.fromEntries(
18
21
  registry.modules.map((module) => [`@frontend/${module.id}`, module.root]),
@@ -44,7 +47,7 @@ export default defineConfig({
44
47
  logLevel,
45
48
  resolve: {
46
49
  alias: {
47
- "#dms/server": resolve(__dirname, "server-module.ts"),
50
+ "#dms/server": resolve(import.meta.dirname, "server-module.ts"),
48
51
  ...stableModuleAliases,
49
52
  ...moduleAliases,
50
53
  },
@@ -1,6 +0,0 @@
1
- export interface SnapshotOptions {
2
- backendUrl?: string;
3
- output: string;
4
- bootstrapSecret?: string;
5
- }
6
- export declare function runSnapshot(options: SnapshotOptions): Promise<void>;
@@ -1,53 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.runSnapshot = runSnapshot;
4
- const node_fs_1 = require("node:fs");
5
- const node_os_1 = require("node:os");
6
- const node_path_1 = require("node:path");
7
- const cli_1 = require("@antelopejs/core/cli");
8
- const common_1 = require("../common");
9
- const output_1 = require("../output");
10
- const snapshot_1 = require("../snapshot");
11
- async function runSnapshot(options) {
12
- const startedAt = Date.now();
13
- const backendUrl = (0, common_1.requireBackendUrl)(options.backendUrl);
14
- const bootstrapSecret = (0, common_1.normalizeBootstrapSecret)(options.bootstrapSecret);
15
- if (!bootstrapSecret) {
16
- throw new common_1.UsageError({
17
- title: "A snapshot needs the bootstrap credential",
18
- reason: "Without it the backend withholds the modules' backend proxies, and a build from the snapshot would not route them.",
19
- fixes: [
20
- "Set DMS_BOOTSTRAP_SECRET to the backend's frontend.bootstrapSecret",
21
- ],
22
- });
23
- }
24
- (0, output_1.writeHeader)("snapshot", [backendUrl]);
25
- const task = (0, cli_1.getProcessTasks)().start("Fetching the manifest and layers");
26
- const scratch = (0, node_fs_1.mkdtempSync)((0, node_path_1.join)((0, node_os_1.tmpdir)(), "dms-snapshot-"));
27
- try {
28
- const manifest = await (0, common_1.fetchManifest)(backendUrl, undefined, bootstrapSecret);
29
- const layers = (0, node_path_1.join)(scratch, snapshot_1.SNAPSHOT_LAYERS_DIR);
30
- await (0, common_1.downloadAndExtractLayers)(backendUrl, manifest.pack, layers, true, bootstrapSecret);
31
- const snapshot = (0, snapshot_1.writeSnapshot)(options.output, backendUrl, manifest, layers);
32
- task.succeed(`Fetched ${manifest.modules.length} frontend module${manifest.modules.length === 1 ? "" : "s"}`);
33
- (0, output_1.writeBlankLine)();
34
- (0, cli_1.getProcessUi)().summary({
35
- headline: `Took a snapshot of modules ${snapshot.digest}, without private values`,
36
- artifact: options.output,
37
- durationMs: Date.now() - startedAt,
38
- nextSteps: [
39
- {
40
- command: `ajs dms build --from ${options.output} --workspace .dms-frontend`,
41
- description: "without the backend or any secret",
42
- },
43
- ],
44
- });
45
- }
46
- catch (error) {
47
- (0, output_1.endInterruptedTask)(task, error, "Snapshot stopped");
48
- throw error;
49
- }
50
- finally {
51
- (0, node_fs_1.rmSync)(scratch, { recursive: true, force: true });
52
- }
53
- }
@@ -1,2 +0,0 @@
1
- import { Command } from "commander";
2
- export declare function cmdSnapshot(): Command;