@antelopejs/dms-frontend 0.3.7 → 0.3.9

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (81) hide show
  1. package/README.md +82 -23
  2. package/dist/cancellation.d.ts +8 -0
  3. package/dist/cancellation.js +23 -0
  4. package/dist/child-output.d.ts +45 -0
  5. package/dist/child-output.js +183 -0
  6. package/dist/commands/build-action.d.ts +21 -0
  7. package/dist/commands/build-action.js +144 -0
  8. package/dist/commands/build.js +54 -62
  9. package/dist/commands/clean-action.d.ts +8 -0
  10. package/dist/commands/clean-action.js +97 -0
  11. package/dist/commands/clean.js +52 -39
  12. package/dist/commands/dev-action.d.ts +8 -0
  13. package/dist/commands/dev-action.js +120 -0
  14. package/dist/commands/dev.js +55 -115
  15. package/dist/commands/prepare-action.d.ts +8 -0
  16. package/dist/commands/prepare-action.js +73 -0
  17. package/dist/commands/prepare.js +55 -54
  18. package/dist/commands/start-action.d.ts +5 -0
  19. package/dist/commands/start-action.js +77 -0
  20. package/dist/commands/start.js +48 -48
  21. package/dist/commands/verify-source-action.d.ts +10 -0
  22. package/dist/commands/verify-source-action.js +104 -0
  23. package/dist/commands/verify-source.d.ts +0 -1
  24. package/dist/commands/verify-source.js +53 -31
  25. package/dist/commands/workspace-task.d.ts +2 -0
  26. package/dist/commands/workspace-task.js +26 -0
  27. package/dist/commands/workspaces-action.d.ts +25 -0
  28. package/dist/commands/workspaces-action.js +71 -0
  29. package/dist/commands/workspaces.d.ts +2 -0
  30. package/dist/commands/workspaces.js +60 -0
  31. package/dist/common.d.ts +1 -0
  32. package/dist/common.js +1 -0
  33. package/dist/config.d.ts +13 -4
  34. package/dist/config.js +115 -18
  35. package/dist/derived-outputs.d.ts +6 -1
  36. package/dist/derived-outputs.js +12 -3
  37. package/dist/discovery.d.ts +2 -1
  38. package/dist/discovery.js +28 -11
  39. package/dist/env-file.js +5 -2
  40. package/dist/help.d.ts +5 -0
  41. package/dist/help.js +70 -0
  42. package/dist/index.js +98 -38
  43. package/dist/layer-watch.d.ts +2 -1
  44. package/dist/layer-watch.js +129 -26
  45. package/dist/layers.d.ts +2 -2
  46. package/dist/layers.js +71 -23
  47. package/dist/manifest-secrets.d.ts +3 -2
  48. package/dist/manifest-secrets.js +25 -21
  49. package/dist/manifest.d.ts +22 -5
  50. package/dist/manifest.js +184 -28
  51. package/dist/materialize.js +2 -1
  52. package/dist/output.d.ts +41 -0
  53. package/dist/output.js +197 -0
  54. package/dist/ports.d.ts +2 -0
  55. package/dist/ports.js +23 -13
  56. package/dist/renderer-range.d.ts +3 -1
  57. package/dist/renderer-range.js +49 -17
  58. package/dist/server-process.d.ts +39 -0
  59. package/dist/server-process.js +221 -0
  60. package/dist/typecheck-output.d.ts +13 -0
  61. package/dist/typecheck-output.js +87 -0
  62. package/dist/update-check.d.ts +14 -2
  63. package/dist/update-check.js +95 -29
  64. package/dist/verify-source-result.d.ts +11 -0
  65. package/dist/verify-source-result.js +17 -0
  66. package/dist/verify-source-runner.js +266 -131
  67. package/dist/vite-output.d.ts +14 -0
  68. package/dist/vite-output.js +99 -0
  69. package/dist/workspace-setup.d.ts +17 -3
  70. package/dist/workspace-setup.js +119 -35
  71. package/dist/workspace.d.ts +8 -2
  72. package/dist/workspace.js +47 -12
  73. package/package.json +4 -12
  74. package/templates/vue/frontend-module.ts +32 -10
  75. package/templates/vue/package.json +4 -0
  76. package/templates/vue/server/cli-report.mjs +91 -0
  77. package/templates/vue/server.mjs +45 -21
  78. package/templates/vue/vite.config.ts +8 -1
  79. package/templates/vue/vite.email.config.ts +5 -1
  80. package/dist/utils/cli-ui.d.ts +0 -27
  81. package/dist/utils/cli-ui.js +0 -178
@@ -0,0 +1,91 @@
1
+ // What the server tells whoever started it. Started by `ajs dms dev` or
2
+ // `ajs dms start`, it reports over IPC and the CLI words what the user reads.
3
+ // Started on its own, it prints the line itself.
4
+
5
+ import { resolve } from "node:path";
6
+
7
+ const WILDCARD_ADDRESSES = ["0.0.0.0", "::"];
8
+
9
+ async function report(message, line, write) {
10
+ if (!process.send) return write(line);
11
+ await new Promise((sent) => process.send(message, () => sent()));
12
+ }
13
+
14
+ /** The server answers at `address`:`port`. */
15
+ export async function reportReady(address, port) {
16
+ const host = WILDCARD_ADDRESSES.includes(address)
17
+ ? "localhost"
18
+ : address.includes(":")
19
+ ? `[${address}]`
20
+ : address;
21
+ await report(
22
+ { type: "dms:ready", address, port },
23
+ `✓ Server ready on http://${host}:${port}`,
24
+ console.log,
25
+ );
26
+ }
27
+
28
+ /**
29
+ * The server cannot listen: a port taken by another process, an address this
30
+ * host does not have. Reported instead of Node's unhandled-error dump, then
31
+ * the server exits.
32
+ */
33
+ export async function reportListenError(error, host, port) {
34
+ await report(
35
+ {
36
+ type: "dms:listen-error",
37
+ code: error.code,
38
+ message: error.message,
39
+ host,
40
+ port,
41
+ },
42
+ `DMS server cannot listen on ${host}:${port}: ${error.message}`,
43
+ console.error,
44
+ );
45
+ process.exit(1);
46
+ }
47
+
48
+ /**
49
+ * Where a startup error points, when it says: esbuild's location for a config
50
+ * it could not bundle, Rollup's for a plugin that failed on a file.
51
+ */
52
+ function errorLocation(error) {
53
+ const location = error?.errors?.[0]?.location;
54
+ if (location?.file) {
55
+ return {
56
+ file: resolve(location.file),
57
+ line: location.line,
58
+ column: location.column,
59
+ lineText: location.lineText,
60
+ };
61
+ }
62
+ const file = error?.loc?.file ?? error?.id;
63
+ if (typeof file !== "string") return undefined;
64
+ return { file, line: error.loc?.line, column: error.loc?.column };
65
+ }
66
+
67
+ /** The first line of what went wrong, without esbuild's error count. */
68
+ function errorText(error) {
69
+ const text = error?.errors?.[0]?.text;
70
+ if (typeof text === "string") return text;
71
+ const message = error instanceof Error ? error.message : String(error);
72
+ return message.split(/\r?\n/)[0];
73
+ }
74
+
75
+ /**
76
+ * Vite could not start, so no page can be served: the error and where it
77
+ * points instead of a stack trace, then the server exits.
78
+ */
79
+ export async function reportStartError(error) {
80
+ const message = errorText(error);
81
+ const location = errorLocation(error);
82
+ const where = location
83
+ ? `${location.file}${location.line ? `:${location.line}:${location.column ?? 0}` : ""}: `
84
+ : "";
85
+ await report(
86
+ { type: "dms:start-error", message, code: error?.code, location },
87
+ `DMS development server failed to start: ${where}${message}`,
88
+ console.error,
89
+ );
90
+ process.exit(1);
91
+ }
@@ -20,6 +20,11 @@ import {
20
20
  UNEXPECTED_ERROR_BODY,
21
21
  } from "./server/backend-response.mjs";
22
22
  import { handleClientError } from "./server/client-error.mjs";
23
+ import {
24
+ reportListenError,
25
+ reportReady,
26
+ reportStartError,
27
+ } from "./server/cli-report.mjs";
23
28
  import { productionHtmlTemplate } from "./server/client-manifest.mjs";
24
29
  import { writeContent } from "./server/content.mjs";
25
30
  import {
@@ -401,6 +406,22 @@ async function answeredByVite(devServer, request, response) {
401
406
  return result.status !== "unhandled";
402
407
  }
403
408
 
409
+ /**
410
+ * Vite's own URLs (`/@vite/client`, `/@id/…`, `/@fs/…`) and paths with an
411
+ * extension name files: modules, stylesheets, fonts, images, built assets.
412
+ * The backend's routes outside `/api` and `/dms` are mostly the paths of the
413
+ * pages they serve data for, which have none (`/<page>/pagelayout`).
414
+ */
415
+ function isFilePath(pathname) {
416
+ return pathname.startsWith("/@") || extname(pathname) !== "";
417
+ }
418
+
419
+ /** Vite's files in development, the build's in production. */
420
+ async function answeredWithFile(devServer, request, response, pathname) {
421
+ if (!devServer) return serveAsset(pathname, request, response);
422
+ return pathname !== "/" && answeredByVite(devServer, request, response);
423
+ }
424
+
404
425
  export async function handleRequest(request, response) {
405
426
  const url = new URL(request.url, "http://frontend.local");
406
427
  const pathname = url.pathname;
@@ -421,13 +442,18 @@ export async function handleRequest(request, response) {
421
442
  return proxy(request, response);
422
443
  if (requestFailure.hasMalformedPath(request.url))
423
444
  return requestFailure.writeBadRequest(response);
424
- if (!isFrontendVisit(request) && (await proxy(request, response, true)))
425
- return;
426
445
  const devServer = await developmentServer();
427
- if (devServer && pathname !== "/") {
428
- if (await answeredByVite(devServer, request, response)) return;
429
- }
430
- if (!devServer && serveAsset(pathname, request, response)) return;
446
+ const visit = isFrontendVisit(request);
447
+ // A file is looked for among the frontend's own before the backend is asked
448
+ // for it: asking first cost a backend round trip for each of the hundreds of
449
+ // modules a development page loads, and for each built asset. A file the
450
+ // frontend does not have, a media file, is still the backend's.
451
+ const fileFirst = !visit && isFilePath(pathname);
452
+ const serveFile = () =>
453
+ answeredWithFile(devServer, request, response, pathname);
454
+ if (fileFirst && (await serveFile())) return;
455
+ if (!visit && (await proxy(request, response, true))) return;
456
+ if (!fileFirst && (await serveFile())) return;
431
457
  if (handleAssetVersionMismatch(request, response)) return;
432
458
  if (redirectToHomepage(request, response, pathname)) return;
433
459
  const props = await pageProps(request);
@@ -493,32 +519,30 @@ if (
493
519
  frontendHttpServer.on("clientError", (error, socket) =>
494
520
  handleClientError(error, socket, maxHeaderSize),
495
521
  );
496
- frontendHttpServer.listen(
497
- Number(process.env.PORT ?? 3001),
498
- process.env.HOST ?? "0.0.0.0",
499
- announceReady,
500
- );
522
+ const port = Number(process.env.PORT ?? 3001);
523
+ const host = process.env.HOST ?? "0.0.0.0";
524
+ const onListenError = (error) => void reportListenError(error, host, port);
525
+ frontendHttpServer.once("error", onListenError);
526
+ frontendHttpServer.listen(port, host, () => {
527
+ frontendHttpServer.off("error", onListenError);
528
+ void announceReady();
529
+ });
501
530
  }
502
531
 
503
532
  /**
504
- * Print the line the CLI's "Starting … server" announcement waits for. In
505
- * development Vite would otherwise start with the first request, so it is
506
- * started here: "ready" then means the first page is served without that wait.
533
+ * Report the server ready. In development Vite would otherwise start with the
534
+ * first request, so it is started here: "ready" then means the first page is
535
+ * served without that wait.
507
536
  */
508
537
  async function announceReady() {
509
538
  try {
510
539
  await developmentServer();
511
540
  } catch (error) {
512
- console.error("DMS development server failed to start", error);
541
+ await reportStartError(error);
513
542
  return;
514
543
  }
515
544
  const { address, port } = frontendHttpServer.address();
516
- const host = ["0.0.0.0", "::"].includes(address)
517
- ? "localhost"
518
- : address.includes(":")
519
- ? `[${address}]`
520
- : address;
521
- console.log(`✓ Server ready on http://${host}:${port}`);
545
+ await reportReady(address, port);
522
546
  // Only the production build produces the email bundle otherwise. Started
523
547
  // once the server answers, so it never delays the first page.
524
548
  if (process.env.DMS_DEV === "true") watchEmailBundle();
@@ -2,7 +2,7 @@ import { existsSync, readdirSync, readFileSync } from "node:fs";
2
2
  import { basename, resolve } from "node:path";
3
3
  import ui from "@nuxt/ui/vite";
4
4
  import vue from "@vitejs/plugin-vue";
5
- import { defineConfig, normalizePath, type Plugin } from "vite";
5
+ import { defineConfig, type LogLevel, normalizePath, type Plugin } from "vite";
6
6
  import { orderHeadForFirstPaint } from "./head-order.mjs";
7
7
  import { iconScanGlobs } from "./icon-scan.mjs";
8
8
 
@@ -117,7 +117,14 @@ const uiInertiaLinkImport = resolve(
117
117
  "node_modules/@nuxt/ui/dist/runtime/vue/overrides/inertia/Link.vue",
118
118
  );
119
119
 
120
+ /**
121
+ * `ajs dms build` runs Vite at "warn" and reports the steps itself; a verbose
122
+ * run, `dev` and a hand-run build keep Vite's own output.
123
+ */
124
+ const logLevel = (process.env.DMS_VITE_LOG_LEVEL ?? "info") as LogLevel;
125
+
120
126
  export default defineConfig({
127
+ logLevel,
121
128
  plugins: [
122
129
  {
123
130
  name: "dms-ui-inertia-link",
@@ -2,7 +2,7 @@ import { existsSync, readdirSync, readFileSync } from "node:fs";
2
2
  import { basename, resolve } from "node:path";
3
3
  import vue from "@vitejs/plugin-vue";
4
4
  import AutoImport from "unplugin-auto-import/vite";
5
- import { defineConfig, normalizePath } from "vite";
5
+ import { defineConfig, type LogLevel, normalizePath } from "vite";
6
6
 
7
7
  interface FrontendModuleRegistryEntry {
8
8
  id: string;
@@ -33,7 +33,11 @@ const stableModuleAliases = Object.fromEntries(
33
33
  }),
34
34
  );
35
35
 
36
+ /** Set by `ajs dms build`, as in vite.config.ts. */
37
+ const logLevel = (process.env.DMS_VITE_LOG_LEVEL ?? "info") as LogLevel;
38
+
36
39
  export default defineConfig({
40
+ logLevel,
37
41
  plugins: [
38
42
  {
39
43
  name: "dms-email-locale-assets",
@@ -1,27 +0,0 @@
1
- import { type Options as BoxenOptions } from "boxen";
2
- export declare class Spinner {
3
- private text;
4
- private isRunning;
5
- private interval?;
6
- private currentCharIndex;
7
- private isTerminal;
8
- constructor(text: string);
9
- start(text?: string): Promise<Spinner>;
10
- update(text: string): Spinner;
11
- log(stream: NodeJS.WriteStream, message: string): Spinner;
12
- succeed(text?: string): Promise<void>;
13
- fail(text?: string): Promise<void>;
14
- info(text?: string): Promise<void>;
15
- warn(text?: string): Promise<void>;
16
- pause(): Promise<void>;
17
- stop(): Promise<void>;
18
- clear(): Promise<void>;
19
- }
20
- export declare function displayBox(message: string, title?: string, options?: BoxenOptions): void;
21
- export declare function displayBanner(text: string, font?: string): void;
22
- export declare function success(message: string): void;
23
- export declare function error(message: string): void;
24
- export declare function warning(message: string): void;
25
- export declare function info(message: string): void;
26
- export declare function header(text: string): void;
27
- export declare function keyValue(key: string, value: string | number | boolean): string;
@@ -1,178 +0,0 @@
1
- "use strict";
2
- var __importDefault = (this && this.__importDefault) || function (mod) {
3
- return (mod && mod.__esModule) ? mod : { "default": mod };
4
- };
5
- Object.defineProperty(exports, "__esModule", { value: true });
6
- exports.Spinner = void 0;
7
- exports.displayBox = displayBox;
8
- exports.displayBanner = displayBanner;
9
- exports.success = success;
10
- exports.error = error;
11
- exports.warning = warning;
12
- exports.info = info;
13
- exports.header = header;
14
- exports.keyValue = keyValue;
15
- const boxen_1 = __importDefault(require("boxen"));
16
- const chalk_1 = __importDefault(require("chalk"));
17
- const figlet_1 = __importDefault(require("figlet"));
18
- const clearLine = () => process.stdout.write("\r\x1b[K");
19
- const spinnerChars = ["⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏"];
20
- const SPINNER_INTERVAL_MS = 80;
21
- function isTerminalOutput() {
22
- return process.stdout.isTTY ?? false;
23
- }
24
- class Spinner {
25
- constructor(text) {
26
- this.isRunning = false;
27
- this.currentCharIndex = 0;
28
- this.isTerminal = isTerminalOutput();
29
- this.text = text;
30
- }
31
- async start(text) {
32
- if (text) {
33
- this.text = text;
34
- }
35
- if (this.isRunning) {
36
- return this;
37
- }
38
- this.isRunning = true;
39
- this.currentCharIndex = 0;
40
- if (!this.isTerminal) {
41
- console.log(` ${this.text}`);
42
- return this;
43
- }
44
- this.interval = setInterval(() => {
45
- if (this.isRunning) {
46
- const spinnerChar = spinnerChars[this.currentCharIndex];
47
- process.stdout.write(`\r${chalk_1.default.cyan(spinnerChar)} ${this.text}`);
48
- this.currentCharIndex =
49
- (this.currentCharIndex + 1) % spinnerChars.length;
50
- }
51
- }, SPINNER_INTERVAL_MS);
52
- return this;
53
- }
54
- update(text) {
55
- this.text = text;
56
- return this;
57
- }
58
- log(stream, message) {
59
- if (this.isRunning && this.isTerminal) {
60
- clearLine();
61
- stream.write(`${message}\n`);
62
- const spinnerChar = spinnerChars[this.currentCharIndex];
63
- process.stdout.write(`${chalk_1.default.cyan(spinnerChar)} ${this.text}`);
64
- }
65
- else {
66
- stream.write(`${message}\n`);
67
- }
68
- return this;
69
- }
70
- async succeed(text) {
71
- if (!this.isRunning)
72
- return;
73
- await this.stop();
74
- const message = text || this.text;
75
- if (this.isTerminal) {
76
- process.stdout.write(`\r${chalk_1.default.bold.green("✓")} ${message}\n`);
77
- }
78
- else {
79
- console.log(`✓ ${message}`);
80
- }
81
- }
82
- async fail(text) {
83
- if (!this.isRunning)
84
- return;
85
- await this.stop();
86
- const message = text || this.text;
87
- if (this.isTerminal) {
88
- process.stdout.write(`\r${chalk_1.default.bold.red("✗")} ${chalk_1.default.red(message)}\n`);
89
- }
90
- else {
91
- console.log(`✗ ${message}`);
92
- }
93
- }
94
- async info(text) {
95
- if (!this.isRunning)
96
- return;
97
- await this.stop();
98
- const message = text || this.text;
99
- if (this.isTerminal) {
100
- process.stdout.write(`\r${chalk_1.default.bold.blue("ℹ")} ${message}\n`);
101
- }
102
- else {
103
- console.log(`ℹ ${message}`);
104
- }
105
- }
106
- async warn(text) {
107
- if (!this.isRunning)
108
- return;
109
- await this.stop();
110
- const message = text || this.text;
111
- if (this.isTerminal) {
112
- process.stdout.write(`\r${chalk_1.default.bold.yellow("⚠")} ${message}\n`);
113
- }
114
- else {
115
- console.log(`⚠ ${message}`);
116
- }
117
- }
118
- async pause() {
119
- if (this.interval) {
120
- clearInterval(this.interval);
121
- this.interval = undefined;
122
- }
123
- if (this.isTerminal) {
124
- clearLine();
125
- }
126
- }
127
- async stop() {
128
- if (this.interval) {
129
- clearInterval(this.interval);
130
- this.interval = undefined;
131
- }
132
- this.isRunning = false;
133
- if (this.isTerminal) {
134
- clearLine();
135
- }
136
- }
137
- async clear() {
138
- await this.stop();
139
- }
140
- }
141
- exports.Spinner = Spinner;
142
- function displayBox(message, title, options) {
143
- const defaultOptions = {
144
- padding: 1,
145
- margin: 1,
146
- borderStyle: "round",
147
- borderColor: "blue",
148
- title: title,
149
- titleAlignment: "center",
150
- };
151
- console.log((0, boxen_1.default)(message, { ...defaultOptions, ...options }));
152
- }
153
- function displayBanner(text, font) {
154
- const figletText = figlet_1.default.textSync(text, {
155
- font: (font || "Standard"),
156
- });
157
- console.log(chalk_1.default.blue(figletText));
158
- }
159
- function success(message) {
160
- console.log(`${chalk_1.default.green.bold("✓")} ${message}`);
161
- }
162
- function error(message) {
163
- console.log(`${chalk_1.default.red.bold("✗")} ${chalk_1.default.red(message)}`);
164
- }
165
- function warning(message) {
166
- console.log(`${chalk_1.default.yellow.bold("⚠")} ${chalk_1.default.yellow(message)}`);
167
- }
168
- function info(message) {
169
- console.log(`${chalk_1.default.blue.bold("ℹ")} ${message}`);
170
- }
171
- function header(text) {
172
- console.log("");
173
- console.log(chalk_1.default.bold.blue(text));
174
- console.log(chalk_1.default.blue("─".repeat(text.length)));
175
- }
176
- function keyValue(key, value) {
177
- return `${chalk_1.default.cyan(key)}: ${value}`;
178
- }