@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
@@ -1,6 +1,7 @@
1
1
  import { readFileSync, renameSync, writeFileSync } from "node:fs";
2
2
  import { createRequire } from "node:module";
3
3
  import { pathToFileURL } from "node:url";
4
+ import { isFunction, isObjectLike, isString } from "./server/type-guards.mjs";
4
5
 
5
6
  /**
6
7
  * The very unimport the auto-import plugin scans with, so both read the
@@ -12,14 +13,83 @@ const unimportUrl = pathToFileURL(
12
13
  createRequire(import.meta.url).resolve("unplugin-auto-import/package.json"),
13
14
  ).resolve("unimport"),
14
15
  );
15
- const { builtinPresets, resolvePreset, scanDirExports } = await import(
16
- unimportUrl.href
17
- );
16
+ /** @type {unknown} */
17
+ const unimport = await import(unimportUrl.href);
18
+
19
+ /**
20
+ * An export of unimport, which this package does not depend on and so has no
21
+ * types for.
22
+ *
23
+ * @param {string} name
24
+ * @returns {unknown}
25
+ */
26
+ function unimportExport(name) {
27
+ return isObjectLike(unimport) ? unimport[name] : undefined;
28
+ }
29
+
30
+ /**
31
+ * The function unimport exports as `name`.
32
+ *
33
+ * @param {string} name
34
+ * @returns {Function}
35
+ */
36
+ function unimportFunction(name) {
37
+ const exported = unimportExport(name);
38
+ if (!isFunction(exported))
39
+ throw new Error(`unimport exports no function ${name}`);
40
+ return exported;
41
+ }
42
+
43
+ /**
44
+ * A name unimport scanned or resolved: where it comes from, what it is called
45
+ * and whether it is only a type.
46
+ *
47
+ * @typedef {object} ImportedName
48
+ * @property {string} name
49
+ * @property {string} from
50
+ * @property {string} [as]
51
+ */
52
+
53
+ /**
54
+ * @param {unknown} entry
55
+ * @returns {entry is ImportedName}
56
+ */
57
+ function isImportedName(entry) {
58
+ return (
59
+ isObjectLike(entry) &&
60
+ isString(entry.name) &&
61
+ isString(entry.from) &&
62
+ (entry.as === undefined || isString(entry.as))
63
+ );
64
+ }
65
+
66
+ /**
67
+ * The names of type declarations among `entries`, as unimport lists them.
68
+ *
69
+ * @param {unknown} entries
70
+ * @returns {ImportedName[]}
71
+ */
72
+ function typeNames(entries) {
73
+ if (!Array.isArray(entries)) throw new Error("unimport listed no names");
74
+ /** @type {unknown[]} */
75
+ const listed = entries;
76
+ /** @type {ImportedName[]} */
77
+ const names = [];
78
+ for (const entry of listed) {
79
+ if (!isObjectLike(entry)) throw new Error("unimport listed a non-name");
80
+ if (!entry.type) continue;
81
+ if (!isImportedName(entry))
82
+ throw new Error("unimport listed a type without a name and a source");
83
+ names.push(entry);
84
+ }
85
+ return names;
86
+ }
18
87
 
19
88
  const HEADER =
20
89
  "// Generated by @antelopejs/dms-frontend from the auto-imported types, for\n" +
21
90
  "// Vue's SFC compiler (script.globalTypeFiles). Not read by TypeScript.\n";
22
91
 
92
+ /** @param {string} path */
23
93
  const withoutExtension = (path) => path.replace(/\.[cm]?[jt]sx?$/, "");
24
94
 
25
95
  /**
@@ -45,13 +115,22 @@ const withoutExtension = (path) => path.replace(/\.[cm]?[jt]sx?$/, "");
45
115
  * @param {(string | { glob: string, types?: boolean })[]} dirs the auto-import
46
116
  * directories, as handed to the auto-import plugin
47
117
  * @param {string} cwd the workspace
118
+ * @returns {Promise<string>}
48
119
  */
49
120
  export async function sfcGlobalTypes(dirs, cwd) {
50
- const vue = await resolvePreset(builtinPresets.vue);
51
- const scanned = await scanDirExports(dirs, { cwd, types: true });
121
+ const presets = unimportExport("builtinPresets");
122
+ /** @type {unknown} */
123
+ const vue = await unimportFunction("resolvePreset")(
124
+ isObjectLike(presets) ? presets.vue : undefined,
125
+ );
126
+ /** @type {unknown} */
127
+ const scanned = await unimportFunction("scanDirExports")(dirs, {
128
+ cwd,
129
+ types: true,
130
+ });
131
+ /** @type {Map<string, string>} */
52
132
  const aliases = new Map();
53
- for (const entry of [...vue, ...scanned]) {
54
- if (!entry.type) continue;
133
+ for (const entry of [...typeNames(vue), ...typeNames(scanned)]) {
55
134
  const name = entry.as ?? entry.name;
56
135
  const source = JSON.stringify(withoutExtension(entry.from));
57
136
  aliases.set(
@@ -66,8 +145,13 @@ export async function sfcGlobalTypes(dirs, cwd) {
66
145
  * Written through a temporary file: a build starting meanwhile reads the
67
146
  * previous declarations or the new ones, never a half-written file the
68
147
  * compiler would then keep in its cache.
148
+ *
149
+ * @param {string} file
150
+ * @param {string} content
151
+ * @returns {boolean}
69
152
  */
70
153
  function writeIfChanged(file, content) {
154
+ /** @type {string | undefined} */
71
155
  let current;
72
156
  try {
73
157
  current = readFileSync(file, "utf8");
@@ -81,7 +165,14 @@ function writeIfChanged(file, content) {
81
165
  return true;
82
166
  }
83
167
 
84
- /** Writes the declarations once, before Vite's config is returned. */
168
+ /**
169
+ * Writes the declarations once, before Vite's config is returned.
170
+ *
171
+ * @param {(string | { glob: string, types?: boolean })[]} dirs
172
+ * @param {string} cwd
173
+ * @param {string} file
174
+ * @returns {Promise<void>}
175
+ */
85
176
  export async function writeSfcGlobalTypes(dirs, cwd, file) {
86
177
  writeIfChanged(file, await sfcGlobalTypes(dirs, cwd));
87
178
  }
@@ -94,24 +185,32 @@ export async function writeSfcGlobalTypes(dirs, cwd, file) {
94
185
  * The file is under the workspace, which Vite watches: rewriting it is the
95
186
  * change the Vue plugin reloads the dependent components on. A failed scan or
96
187
  * write keeps the previous declarations, since it must not end the dev server.
188
+ *
189
+ * @param {(string | { glob: string, types?: boolean })[]} dirs
190
+ * @param {string} cwd
191
+ * @param {string} file
192
+ * @returns {import("vite").Plugin}
97
193
  */
98
194
  export function sfcGlobalTypesWatcher(dirs, cwd, file) {
99
195
  return {
100
196
  name: "dms-sfc-global-types",
101
197
  apply: "serve",
102
198
  configureServer(server) {
199
+ /** @type {ReturnType<typeof setTimeout> | undefined} */
103
200
  let pending;
201
+ /** @param {string} path */
104
202
  const refresh = (path) => {
105
203
  if (!/\.[cm]?[jt]sx?$/.test(path) || path === file) return;
106
204
  clearTimeout(pending);
107
205
  pending = setTimeout(() => {
108
206
  sfcGlobalTypes(dirs, cwd)
109
207
  .then((content) => writeIfChanged(file, content))
110
- .catch((error) =>
111
- server.config.logger.warn(
112
- `Could not update ${file}, the types Vue's SFC compiler ` +
113
- `reads: ${error instanceof Error ? error.message : error}`,
114
- ),
208
+ .catch(
209
+ /** @param {unknown} error */ (error) =>
210
+ server.config.logger.warn(
211
+ `Could not update ${file}, the types Vue's SFC compiler ` +
212
+ `reads: ${error instanceof Error ? error.message : String(error)}`,
213
+ ),
115
214
  );
116
215
  }, 100);
117
216
  };
@@ -7,7 +7,6 @@ import { createSSRApp, h, readonly } from "vue";
7
7
  import { appConfigs } from "./app-configs.generated";
8
8
  import {
9
9
  DMS_PAYLOAD_ID,
10
- type DmsServerFetch,
11
10
  SSR_ASYNC_COMPONENTS_ID,
12
11
  configureDmsApp,
13
12
  resolveDmsInertiaPage,
@@ -19,6 +18,7 @@ import {
19
18
  type DmsInertiaPage,
20
19
  type DmsPageProps,
21
20
  type DmsPayload,
21
+ type DmsServerFetch,
22
22
  createDmsFrontendRuntime,
23
23
  hasDmsPage,
24
24
  pageErrorData,
@@ -166,7 +166,7 @@ export async function renderDmsPage(
166
166
  requestCookies?: string,
167
167
  ): Promise<DmsSsrResult> {
168
168
  const runtime = createDmsFrontendRuntime(
169
- serverFetch as typeof import("ofetch").ofetch,
169
+ serverFetch,
170
170
  {},
171
171
  true,
172
172
  requestCookies,
@@ -6,6 +6,20 @@ const DMS_PAGE_IMPORT =
6
6
  const ANY_INERTIA_PAGE_IMPORT =
7
7
  /import\s*\{[^}]*\busePage\b[^}]*\}\s*from\s*["']@inertiajs\/vue3["']|import\s*\*\s*as\s+\w+\s+from\s*["']@inertiajs\/vue3["']/;
8
8
 
9
+ /**
10
+ * A Vite plugin that rewrites the source of Nuxt UI files.
11
+ *
12
+ * @typedef {{
13
+ * name: string,
14
+ * enforce: "pre",
15
+ * transform: (
16
+ * this: Pick<import("vite").Rollup.TransformPluginContext, "error">,
17
+ * code: string,
18
+ * id: string,
19
+ * ) => { code: string, map: null } | null,
20
+ * }} NuxtUiRewrite
21
+ */
22
+
9
23
  /**
10
24
  * The rewrite, as a Vite plugin.
11
25
  *
@@ -16,7 +30,8 @@ const ANY_INERTIA_PAGE_IMPORT =
16
30
  * a form this rewrite does not know, rather than letting the leak come back
17
31
  * unnoticed with a Nuxt UI release.
18
32
  *
19
- * @param isNuxtUiFile `(id) => boolean`, whether a module is Nuxt UI's
33
+ * @param {(id: string) => boolean} isNuxtUiFile whether a module is Nuxt UI's
34
+ * @returns {NuxtUiRewrite}
20
35
  */
21
36
  export function uiInertiaPage(isNuxtUiFile) {
22
37
  return {
@@ -21,7 +21,8 @@ const ROUTED_EXPORT =
21
21
  * changes the form rewritten here, rather than letting the mismatch come back
22
22
  * unnoticed.
23
23
  *
24
- * @param isNuxtUiFile `(id) => boolean`, whether a module is Nuxt UI's
24
+ * @param {(id: string) => boolean} isNuxtUiFile whether a module is Nuxt UI's
25
+ * @returns {import("./ui-inertia-page.mjs").NuxtUiRewrite}
25
26
  */
26
27
  export function uiKbd(isNuxtUiFile) {
27
28
  return {
@@ -21,7 +21,8 @@ const APPLICATION_STATE =
21
21
  * UI file defines `useState`, rather than letting the leak come back unnoticed
22
22
  * with a Nuxt UI release.
23
23
  *
24
- * @param isNuxtUiFile `(id) => boolean`, whether a module is Nuxt UI's
24
+ * @param {(id: string) => boolean} isNuxtUiFile whether a module is Nuxt UI's
25
+ * @returns {import("./ui-inertia-page.mjs").NuxtUiRewrite}
25
26
  */
26
27
  export function uiRequestState(isNuxtUiFile) {
27
28
  return {
@@ -11,6 +11,7 @@ import {
11
11
  } from "vite";
12
12
  import { clientCodeSplitting, onBuildLog } from "./client-chunking.mjs";
13
13
  import { recordEntryWeight } from "./entry-weight.mjs";
14
+ import { rejectBackendSources } from "./backend-boundary.mjs";
14
15
  import { rejectServerSourcesInClient } from "./client-server-guard.mjs";
15
16
  import { uiInertiaPage } from "./ui-inertia-page.mjs";
16
17
  import { uiKbd } from "./ui-kbd.mjs";
@@ -29,6 +30,7 @@ interface FrontendModuleRegistryEntry {
29
30
  id: string;
30
31
  name?: string;
31
32
  packageName?: string;
33
+ backendPackage?: string;
32
34
  root: string;
33
35
  priority: number;
34
36
  }
@@ -45,6 +47,9 @@ const registry = JSON.parse(
45
47
  readFileSync(registryPath, "utf8"),
46
48
  ) as FrontendModuleRegistry;
47
49
  const moduleRoots = registry.modules.map((module) => module.root);
50
+ const backendPackages = registry.modules.flatMap((module) =>
51
+ module.backendPackage === undefined ? [] : [module.backendPackage],
52
+ );
48
53
  const frontendSourceRoots = moduleRoots.flatMap((root) => {
49
54
  const layersRoot = resolve(root, "layers");
50
55
  if (!existsSync(layersRoot)) return [root];
@@ -175,6 +180,9 @@ export default defineConfig({
175
180
  // Fails a client build that imports a dms.server.ts or a file under a
176
181
  // module's server/ directory, however it is imported.
177
182
  rejectServerSourcesInClient(moduleRoots),
183
+ // Fails a build, a worker build or a dev request that resolves a file
184
+ // under the src of the package that ships a module.
185
+ rejectBackendSources(backendPackages),
178
186
  // What each module adds to the client entry, which `ajs dms build` reports.
179
187
  recordEntryWeight(registry.modules),
180
188
  {
@@ -267,6 +275,7 @@ export default defineConfig({
267
275
  paintBeforeHydrate(),
268
276
  ...vitePlugins(registry.modules, frontendBuilds),
269
277
  ],
278
+ worker: { plugins: () => vitePlugins(registry.modules, frontendBuilds) },
270
279
  resolve: {
271
280
  // `@iconify/vue` keeps its API providers in module state: `main.ts` and
272
281
  // Nuxt UI's `Icon` must share one copy for the same-origin provider to apply.