@akanjs/cli 3.0.0-beta.23 → 3.0.0-beta.25

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 (73) hide show
  1. package/.build-stamp +1 -1
  2. package/{CodeAgent-0dh9fzzg.js → CodeAgent-p65jja35.js} +2 -2
  3. package/{CodeTui-drwdvwg9.js → CodeTui-5xr8kxx4.js} +1 -1
  4. package/TunnelShare-cwh9ezsm.js +9 -0
  5. package/{agent.command-ttn09rx3.js → agent.command-rp505zxa.js} +7 -7
  6. package/akanCode.js +2 -2
  7. package/{application.command-b6rck8d6.js → application.command-2x9jqy0k.js} +7 -7
  8. package/{applicationBuildRunner-vh74vrhg.js → applicationBuildRunner-tm0vba5t.js} +4 -4
  9. package/buildBatch.proc.js +99 -12
  10. package/{cloud.command-gwdypy7k.js → cloud.command-j6m607bp.js} +12 -12
  11. package/cloud.script-r5t7nhee.js +20 -0
  12. package/{code.command-1jgk14tc.js → code.command-jhrjttm1.js} +7 -7
  13. package/{context.command-4vpjmg22.js → context.command-xncdvv7n.js} +14 -14
  14. package/context.runner-pjy89k2s.js +21 -0
  15. package/{dependencyScanner-ye2e6pdz.js → dependencyScanner-06z2dmrr.js} +2 -2
  16. package/{fileSys-5ngz93ga.js → fileSys-wzzrntkw.js} +1 -1
  17. package/{guideline.command-7jk47fgq.js → guideline.command-d5qmxx29.js} +4 -4
  18. package/guidelines/agentRule/agentRule.instruction.md +7 -0
  19. package/guidelines/conventions/conventions.instruction.md +7 -5
  20. package/incrementalBuilder.proc.js +544 -72
  21. package/{index-ga6hygxr.js → index-0n9q1f23.js} +3 -3
  22. package/{index-3t7g9e4r.js → index-2ahj896a.js} +1 -1
  23. package/{index-smme4r47.js → index-2wc41k5w.js} +1 -1
  24. package/{index-khpfpcsd.js → index-48k96p55.js} +27 -7
  25. package/{index-776sswhv.js → index-4mgbzgb5.js} +281 -651
  26. package/{index-2gh9sndr.js → index-50rq2qms.js} +4 -4
  27. package/{index-5b6tavv3.js → index-52w0fvh8.js} +15 -1
  28. package/{index-yjkte9aj.js → index-56xwsgyn.js} +3 -3
  29. package/{index-rmcw8yc8.js → index-58cyna2s.js} +2 -2
  30. package/{index-1db2cw0y.js → index-5mm3g24t.js} +2 -2
  31. package/{index-wp43xrjv.js → index-6dt6zft6.js} +4 -4
  32. package/{index-vj1cwwy5.js → index-7yxndzea.js} +7 -7
  33. package/{index-q44jbw48.js → index-860zjnr1.js} +1 -1
  34. package/{index-jn07hxrr.js → index-8k93esfr.js} +6 -6
  35. package/{index-22d57v4c.js → index-94s080qm.js} +3 -3
  36. package/{index-yy4s1j72.js → index-d65fzxvd.js} +1 -1
  37. package/{index-m0ms3n1c.js → index-da72pfdv.js} +7 -7
  38. package/index-dt1x62ec.js +4108 -0
  39. package/{index-4dn1p115.js → index-ggfhz9ab.js} +4 -4
  40. package/{index-9629w7ns.js → index-hzy4pe24.js} +3 -3
  41. package/{index-jg5e15db.js → index-pb10njz7.js} +192 -58
  42. package/{index-mgfx3mfs.js → index-per2kqy9.js} +4 -4
  43. package/{index-aaz7n8s2.js → index-qm4fppfk.js} +4 -4
  44. package/{index-ya5qbmyg.js → index-t02g6dpn.js} +3 -3
  45. package/{index-hb8sgepf.js → index-tpdn30ya.js} +1 -1
  46. package/{index-rnt66vym.js → index-v4hnkqz9.js} +1 -1
  47. package/{index-pp54p5zk.js → index-xfgdmm5d.js} +2 -2
  48. package/{index-tgjy8xf3.js → index-zazvn45w.js} +4 -4
  49. package/{index-0bq14vr1.js → index-zzvvc845.js} +1 -1
  50. package/index.js +22 -22
  51. package/{library.command-ccb1cp92.js → library.command-sv6g1h2g.js} +4 -4
  52. package/{localRegistry.command-cxvgn7fr.js → localRegistry.command-4kj56531.js} +11 -11
  53. package/{module.command-5k6h407d.js → module.command-ccdeder9.js} +6 -6
  54. package/{package.command-e6ntvpez.js → package.command-xmw605b1.js} +5 -5
  55. package/package.json +4 -4
  56. package/{page.command-v4f90wb8.js → page.command-ky7vafsn.js} +4 -4
  57. package/{primitive.command-g6km57q5.js → primitive.command-bh6j8nfs.js} +7 -7
  58. package/{prompter-ptaa3nk0.js → prompter-76k56b37.js} +2 -2
  59. package/{quality.command-de3p36pz.js → quality.command-rqqrpe98.js} +3 -3
  60. package/{repair.command-d3x1javq.js → repair.command-n9nsm9qe.js} +6 -6
  61. package/{scalar.command-7ezrac2b.js → scalar.command-t09gmck8.js} +5 -5
  62. package/{subspace.command-0cep72ce.js → subspace.command-ygfdqpbj.js} +6 -6
  63. package/templates/workspaceRoot/package.json.template +1 -3
  64. package/{tunnel.command-hsrv2mny.js → tunnel.command-4g95vhsn.js} +5 -5
  65. package/{workflow.command-z2p32nmf.js → workflow.command-b91ph8sx.js} +11 -11
  66. package/{workspace.command-axwscbnn.js → workspace.command-p44d21qf.js} +24 -24
  67. package/README.ko.md +0 -72
  68. package/README.md +0 -88
  69. package/TunnelShare-scf4e4pd.js +0 -9
  70. package/cloud.script-at2beffs.js +0 -20
  71. package/context.runner-ehjs419v.js +0 -21
  72. package/index-fgq75haa.js +0 -2084
  73. package/templates/workspaceRoot/patches/react-dom@19.2.7.patch.template +0 -62
@@ -1,95 +1,29 @@
1
1
  // @bun
2
2
  import {
3
- AsyncDefaultExportDetector,
4
3
  resolveSsrPageEntriesForApp
5
- } from "./index-ga6hygxr.js";
4
+ } from "./index-0n9q1f23.js";
6
5
  import {
7
- SOURCE_EXTS
8
- } from "./index-khpfpcsd.js";
9
- import {
10
- loaderFor,
6
+ computeRouteSeedIndex,
7
+ saveRouteSeedIndex,
11
8
  BarrelAnalyzer,
12
- createBarrelImportsPlugin,
13
9
  createTsconfigPackageResolver,
14
10
  rewriteBarrelImports,
15
11
  GraphClientEntryDiscovery,
16
- transformUseClient,
17
12
  bundleDefine,
18
13
  ClientEntriesBundler,
19
14
  VENDOR_SPECIFIERS,
20
- RouteClientBuilder
21
- } from "./index-fgq75haa.js";
22
- var __require = import.meta.require;
23
-
24
- // pkgs/@akanjs/devkit/artifact/routeSeedIndex.ts
25
- import path from "path";
26
- import { assertUniqueRoutePatterns, compareRouteSpecificity, parseRouteModuleKey } from "akanjs/common";
15
+ RouteClientBuilder,
16
+ CsrEntryFiles,
17
+ PagesEntrySourceGenerator,
18
+ PagesBundleBuilder
19
+ } from "./index-dt1x62ec.js";
27
20
  import {
28
- ROUTE_SEED_INDEX_JSON
29
- } from "akanjs/server/artifact/routeSeedIndexStore";
30
- function computeRouteSeedIndex(pageEntries) {
31
- const layoutsByPrefix = new Map;
32
- const pagesBySegments = [];
33
- for (const { key, moduleAbsPath, seedAbsPaths } of pageEntries) {
34
- const parsed = parseRouteModuleKey(key);
35
- const files = [path.resolve(moduleAbsPath), ...(seedAbsPaths ?? []).map((seed) => path.resolve(seed))];
36
- if (parsed.kind === "layout" || parsed.kind === "overrides") {
37
- layoutsByPrefix.getOrInsert(parsed.routeSegments.join("/"), []).push(...files);
38
- } else if (parsed.kind === "page") {
39
- pagesBySegments.push({
40
- key,
41
- pattern: parsed.pattern,
42
- segments: parsed.routeSegments,
43
- files,
44
- includeOwnLayout: parsed.leaf === "_index"
45
- });
46
- }
47
- }
48
- assertUniqueRoutePatterns(pagesBySegments);
49
- const globalLayoutFiles = layoutsByPrefix.get("") ?? [];
50
- const seedEntries = [];
51
- for (const { pattern, segments, files, includeOwnLayout } of pagesBySegments) {
52
- const layouts = [];
53
- const maxPrefixLength = includeOwnLayout ? segments.length : Math.max(segments.length - 1, 0);
54
- for (let i = 0;i <= maxPrefixLength; i++) {
55
- const prefix = segments.slice(0, i).join("/");
56
- const layoutFiles = layoutsByPrefix.get(prefix);
57
- if (layoutFiles)
58
- layouts.push(...layoutFiles);
59
- }
60
- const seeds = Array.from(new Set([...layouts, ...files]));
61
- const routeId = pattern || "/";
62
- seedEntries.push({ routeId, pattern: routeId, seeds });
63
- }
64
- seedEntries.sort((a, b) => compareRouteSpecificity(a.pattern, b.pattern));
65
- return { entries: seedEntries, globalLayoutFiles };
66
- }
67
- function serializeRouteSeedIndexForArtifact(index, artifactDir, options = {}) {
68
- const normalizedArtifactDir = path.resolve(artifactDir);
69
- if (options.production)
70
- return { entries: index.entries.map((entry) => ({ routeId: entry.routeId })) };
71
- return {
72
- entries: index.entries.map((entry) => ({
73
- ...entry,
74
- seeds: entry.seeds.map((seed) => serializeArtifactPath(seed, normalizedArtifactDir))
75
- })),
76
- globalLayoutFiles: index.globalLayoutFiles.map((file) => serializeArtifactPath(file, normalizedArtifactDir))
77
- };
78
- }
79
- async function saveRouteSeedIndex(artifactDir, index, options = {}) {
80
- const absPath = path.join(path.resolve(artifactDir), ROUTE_SEED_INDEX_JSON);
81
- await Bun.write(absPath, `${JSON.stringify(serializeRouteSeedIndexForArtifact(index, artifactDir, options), null, 2)}
82
- `);
83
- return absPath;
84
- }
85
- function serializeArtifactPath(artifactPath, artifactDir) {
86
- if (!path.isAbsolute(artifactPath))
87
- return artifactPath;
88
- return path.relative(artifactDir, artifactPath).split(path.sep).join("/");
89
- }
21
+ SOURCE_EXTS
22
+ } from "./index-48k96p55.js";
23
+ var __require = import.meta.require;
90
24
 
91
25
  // pkgs/@akanjs/devkit/frontendBuild/allRoutesBuilder.ts
92
- import path2 from "path";
26
+ import path from "path";
93
27
 
94
28
  // pkgs/@akanjs/devkit/frontendBuild/routesManifestArtifactSerializer.ts
95
29
  import { RoutesManifestStore } from "akanjs/server/artifact/routesManifestStore";
@@ -155,7 +89,7 @@ class AllRoutesBuilder {
155
89
  routeIds: this.#routeIds,
156
90
  ...this.#merged
157
91
  };
158
- const manifestPath = path2.join(path2.resolve(this.#artifactDir), "routes-manifest.json");
92
+ const manifestPath = path.join(path.resolve(this.#artifactDir), "routes-manifest.json");
159
93
  await Bun.write(manifestPath, `${JSON.stringify(RoutesManifestArtifactSerializer.serialize(manifest, this.#artifactDir, {
160
94
  production: this.#command === "build"
161
95
  }), null, 2)}
@@ -194,6 +128,222 @@ class AllRoutesBuilder {
194
128
  }
195
129
  }
196
130
 
131
+ // pkgs/@akanjs/devkit/frontendBuild/csrArtifactBuilder.ts
132
+ import { mkdir, rm, unlink } from "fs/promises";
133
+ import path2 from "path";
134
+ class CsrArtifactBuilder {
135
+ #app;
136
+ #command;
137
+ #lang;
138
+ constructor(app, command = "start", lang = "en") {
139
+ this.#app = app;
140
+ this.#command = command;
141
+ this.#lang = lang;
142
+ }
143
+ async build() {
144
+ const pageKeys = await this.#app.getPageKeys();
145
+ if (pageKeys.length === 0) {
146
+ this.#app.log(`[cli] no route files under ${this.#app.cwdPath}/page \u2014 skipping CSR build`);
147
+ return null;
148
+ }
149
+ const pageEntries = await resolveSsrPageEntriesForApp(this.#app, pageKeys);
150
+ const akanConfig = await this.#app.getConfig();
151
+ const cssAssets = await this.#loadCssAssets();
152
+ const basePaths = [...akanConfig.basePaths];
153
+ const htmlBasePaths = basePaths.length > 0 ? basePaths : [""];
154
+ await rm(this.#outputDir, { recursive: true, force: true });
155
+ await mkdir(this.#generatedDir, { recursive: true });
156
+ const generatedFiles = Object.fromEntries((await Promise.all(htmlBasePaths.map(async (basePath) => [
157
+ this.#createHtmlFile(basePath),
158
+ await this.#createEntryFile(basePath, CsrArtifactBuilder.pageEntriesForBasePath(pageEntries, basePath, basePaths))
159
+ ]))).flat());
160
+ const result = await Bun.build({
161
+ target: "browser",
162
+ entrypoints: htmlBasePaths.map((basePath) => this.#generatedPath(CsrArtifactBuilder.htmlFilename(basePath))),
163
+ files: generatedFiles,
164
+ root: this.#generatedDir,
165
+ outdir: this.#outputDir,
166
+ splitting: false,
167
+ minify: true,
168
+ env: "AKAN_PUBLIC_*",
169
+ define: bundleDefine(this.#app, this.#command, "csr"),
170
+ optimizeImports: akanConfig.optimizeImports,
171
+ plugins: [PagesBundleBuilder.createCssStubPlugin()]
172
+ });
173
+ if (!result.success) {
174
+ const logs = result.logs.map((log) => log.message).join(`
175
+ `);
176
+ throw new Error(`[csr-build] failed${logs ? `
177
+ ${logs}` : ""}`);
178
+ }
179
+ await this.#inlineCsrArtifacts(cssAssets);
180
+ this.#app.verbose(`[csr-build] output -> ${this.#outputDir}`);
181
+ return { outputDir: this.#outputDir };
182
+ }
183
+ static pageEntriesForBasePath(pageEntries, basePath, basePaths) {
184
+ return CsrEntryFiles.pageEntriesForBasePath(pageEntries, basePath, basePaths);
185
+ }
186
+ static htmlFilename(basePath) {
187
+ return `${basePath || "index"}.html`;
188
+ }
189
+ static entryFilename(basePath) {
190
+ return CsrEntryFiles.entryFilename(basePath);
191
+ }
192
+ static basePathOfHtml(htmlPath) {
193
+ const name = path2.basename(htmlPath, ".html");
194
+ return name === "index" ? "" : name;
195
+ }
196
+ get #outputDir() {
197
+ return path2.join(this.#command === "build" ? this.#app.dist.cwdPath : this.#app.cwdPath, this.#command === "build" ? "csr" : ".akan/artifact/csr");
198
+ }
199
+ get #generatedDir() {
200
+ return path2.join(this.#app.cwdPath, ".akan/generated/csr");
201
+ }
202
+ get #artifactDir() {
203
+ return path2.join(this.#command === "build" ? this.#app.dist.cwdPath : this.#app.cwdPath, ".akan/artifact");
204
+ }
205
+ #generatedPath(filename) {
206
+ return path2.join(this.#generatedDir, filename);
207
+ }
208
+ async#createEntryFile(basePath, pageEntries) {
209
+ return [
210
+ this.#generatedPath(CsrArtifactBuilder.entryFilename(basePath)),
211
+ `import { bootCsr } from "akanjs/webkit";
212
+ ${await PagesEntrySourceGenerator.generateStatic(pageEntries)}
213
+ void bootCsr(pages);
214
+ `
215
+ ];
216
+ }
217
+ #createHtmlFile(basePath) {
218
+ return [
219
+ this.#generatedPath(CsrArtifactBuilder.htmlFilename(basePath)),
220
+ `<!doctype html>
221
+ <html lang="${this.#lang}">
222
+ <head>
223
+ <meta charset="utf-8" />
224
+ <title>${this.#app.name}</title>
225
+ <base href="/" />
226
+ <meta name="viewport" content="width=device-width, initial-scale=1" />
227
+ </head>
228
+ <body>
229
+ <div id="root"></div>
230
+ <script type="module" src="./${CsrArtifactBuilder.entryFilename(basePath)}"></script>
231
+ </body>
232
+ </html>
233
+ `
234
+ ];
235
+ }
236
+ async#loadCssAssets() {
237
+ const artifactFile = Bun.file(path2.join(this.#artifactDir, "base-artifact.json"));
238
+ if (!await artifactFile.exists())
239
+ return {};
240
+ const artifact = await artifactFile.json();
241
+ return artifact.cssAssets ?? {};
242
+ }
243
+ async#inlineCsrArtifacts(cssAssets) {
244
+ const jsFiles = new Set;
245
+ for (const htmlPath of await this.#listOutputFiles((filePath) => filePath.endsWith(".html"))) {
246
+ const htmlFile = Bun.file(htmlPath);
247
+ if (!await htmlFile.exists())
248
+ continue;
249
+ const basePath = CsrArtifactBuilder.basePathOfHtml(htmlPath);
250
+ const cssAsset = cssAssets[basePath];
251
+ if (!cssAsset) {
252
+ this.#app.logger.warn(`[csr-build] base-artifact.json has no compiled stylesheet for ${basePath || "root"}; ${path2.basename(htmlPath)} ships without CSS`);
253
+ }
254
+ const inlined = await this.#inlineHtmlAssets(await htmlFile.text(), htmlPath, cssAsset);
255
+ for (const filePath of inlined.jsFiles)
256
+ jsFiles.add(filePath);
257
+ await Bun.write(htmlPath, inlined.html);
258
+ }
259
+ for (const filePath of jsFiles)
260
+ await unlink(filePath).catch(() => {
261
+ return;
262
+ });
263
+ const remainingAssets = await this.#listOutputFiles((filePath) => filePath.endsWith(".js") || filePath.endsWith(".css"));
264
+ if (remainingAssets.length > 0) {
265
+ throw new Error(`[csr-build] expected single-file HTML, but CSR assets remain:
266
+ ${remainingAssets.join(`
267
+ `)}`);
268
+ }
269
+ }
270
+ async#inlineHtmlAssets(html, htmlPath, cssAsset) {
271
+ let next = html;
272
+ if (cssAsset) {
273
+ const css = await Bun.file(path2.join(this.#artifactDir, cssAsset.cssRelPath)).text();
274
+ next = CsrArtifactBuilder.injectBeforeHeadEnd(next, CsrArtifactBuilder.createInlineStyle(css));
275
+ }
276
+ const jsFiles = [];
277
+ next = await CsrArtifactBuilder.replaceModuleScriptSrc(next, async (src) => {
278
+ const jsPath = CsrArtifactBuilder.resolveHtmlAssetPath(htmlPath, src);
279
+ jsFiles.push(jsPath);
280
+ return await Bun.file(jsPath).text();
281
+ });
282
+ return { html: next, jsFiles };
283
+ }
284
+ async#listOutputFiles(predicate) {
285
+ const glob = new Bun.Glob("**/*");
286
+ const files = [];
287
+ for await (const filePath of glob.scan({ cwd: this.#outputDir, absolute: true })) {
288
+ if (predicate(filePath))
289
+ files.push(filePath);
290
+ }
291
+ return files.sort();
292
+ }
293
+ static injectBeforeHeadEnd(html, snippet) {
294
+ const scannable = CsrArtifactBuilder.blankEmbeddedContent(html);
295
+ const headEnd = scannable.search(/<\/head\s*>/i);
296
+ if (headEnd !== -1)
297
+ return `${html.slice(0, headEnd)}${snippet}
298
+ ${html.slice(headEnd)}`;
299
+ const bodyStart = scannable.search(/<body(?:\s|>)/i);
300
+ if (bodyStart !== -1)
301
+ return `${html.slice(0, bodyStart)}${snippet}
302
+ ${html.slice(bodyStart)}`;
303
+ return `${html}
304
+ ${snippet}`;
305
+ }
306
+ static blankEmbeddedContent(html) {
307
+ return html.replace(/<script\b[^>]*>[\s\S]*?<\/script\s*>|<style\b[^>]*>[\s\S]*?<\/style\s*>|<!--[\s\S]*?-->/gi, (match) => " ".repeat(match.length));
308
+ }
309
+ static createInlineStyle(css) {
310
+ return `<style data-akan-css="active">
311
+ ${css.replace(/<\/style/gi, "<\\/style")}
312
+ </style>`;
313
+ }
314
+ static async replaceModuleScriptSrc(html, loadScript) {
315
+ const scriptRe = /<script\b(?=[^>]*\btype=["']module["'])(?=[^>]*\bsrc=["']([^"']+)["'])[^>]*>\s*<\/script>/gi;
316
+ let result = "";
317
+ let lastIndex = 0;
318
+ let matched = false;
319
+ for (const match of html.matchAll(scriptRe)) {
320
+ const full = match[0];
321
+ const src = match[1];
322
+ if (match.index === undefined || !src)
323
+ continue;
324
+ matched = true;
325
+ result += html.slice(lastIndex, match.index);
326
+ result += `<script type="module">
327
+ ${CsrArtifactBuilder.escapeInlineScript(await loadScript(src))}
328
+ </script>`;
329
+ lastIndex = match.index + full.length;
330
+ }
331
+ if (!matched)
332
+ return html;
333
+ return result + html.slice(lastIndex);
334
+ }
335
+ static escapeInlineScript(source) {
336
+ return source.replace(/<\/script/gi, "<\\/script");
337
+ }
338
+ static resolveHtmlAssetPath(htmlPath, src) {
339
+ if (/^[a-z][a-z0-9+.-]*:/i.test(src) || src.startsWith("//")) {
340
+ throw new Error(`[csr-build] cannot inline external script: ${src}`);
341
+ }
342
+ const normalized = src.startsWith("/") ? src.slice(1) : src;
343
+ return path2.resolve(path2.dirname(htmlPath), normalized);
344
+ }
345
+ }
346
+
197
347
  // pkgs/@akanjs/devkit/frontendBuild/cssCompiler.ts
198
348
  import path4 from "path";
199
349
  import { Logger } from "akanjs/common";
@@ -721,532 +871,12 @@ function declaredCustomProperties(css) {
721
871
  return [...new Set([...withoutThemeBlocks.matchAll(/(?:^|[\s;{])(--[\w-]+)\s*:/g)].map(([, name]) => name))].filter((name) => !!name);
722
872
  }
723
873
  function getPageKeyBasePath(pageKey, basePaths) {
724
- const normalized = pageKey.split(path4.sep).join("/").replace(/^\.\//, "");
725
- const segments = normalized.split("/");
726
- const firstPublicSegment = segments.find((segment) => segment !== "[lang]" && !/^\(.+\)$/.test(segment));
727
- return firstPublicSegment && basePaths.includes(firstPublicSegment) ? firstPublicSegment : null;
728
- }
729
-
730
- // pkgs/@akanjs/devkit/frontendBuild/pagesBundleBuilder.ts
731
- import path7 from "path";
732
-
733
- // pkgs/@akanjs/devkit/transforms/externalizeFrameworkPlugin.ts
734
- import path5 from "path";
735
- var DEFAULT_INCLUDE = ["akanjs/", "@apps/", "@libs/"];
736
- var DEFAULT_EXCLUDE_EXACT = new Set(["akanjs/webkit", "@akanjs/cli", "@akanjs/devkit"]);
737
- var DEFAULT_EXCLUDE_PREFIX = ["@akanjs/cli/", "@akanjs/devkit/"];
738
- var OPTIONAL_BACKEND_EXTERNAL_EXACT = new Set([
739
- "@libsql/client",
740
- "bullmq",
741
- "ioredis",
742
- "postgres",
743
- "protobufjs"
744
- ]);
745
- var RUNTIME_EXTERNAL_EXACT = new Set([
746
- "react",
747
- "react-dom",
748
- "react/jsx-runtime",
749
- "react/jsx-dev-runtime",
750
- "react-server-dom-webpack",
751
- "react-server-dom-webpack/server.node",
752
- "react-server-dom-webpack/client.node",
753
- "react-server-dom-webpack/client.browser"
754
- ]);
755
- var RUNTIME_EXTERNAL_PREFIX = ["react-dom/", "react-server-dom-webpack/"];
756
- var CANDIDATE_EXTS = [".tsx", ".ts", ".jsx", ".js", ".mjs", ".cjs"];
757
- async function createExternalizeFrameworkPlugin(options) {
758
- const tsconfig = await options.app.getTsConfig();
759
- const includePrefixes = options.include ?? DEFAULT_INCLUDE;
760
- const extraExact = new Set(options.extra ?? []);
761
- const workspaceRoot = options.app.workspace.workspaceRoot;
762
- const tsconfigPaths = tsconfig.compilerOptions.paths ?? {};
763
- const rootEntries = Object.entries(tsconfigPaths).filter(([k]) => !k.endsWith("/*")).map(([k, v]) => ({ pkg: k, entryFile: v[0] ?? null })).filter((e) => e.entryFile !== null);
764
- const wildcardEntries = Object.entries(tsconfigPaths).filter(([k]) => k.endsWith("/*")).map(([k, v]) => ({ prefix: k.slice(0, -1), replacements: v })).sort((a, b) => b.prefix.length - a.prefix.length);
765
- async function resolveWorkspaceSubpath(spec) {
766
- if (!workspaceRoot)
767
- return null;
768
- for (const { prefix, replacements } of wildcardEntries) {
769
- if (!spec.startsWith(prefix))
770
- continue;
771
- const suffix = spec.slice(prefix.length);
772
- for (const repl of replacements) {
773
- const replPath = repl?.endsWith("/*") ? repl.slice(0, -1) : repl ?? "";
774
- if (!replPath)
775
- continue;
776
- const candidate = path5.resolve(workspaceRoot, replPath + suffix);
777
- const hit = await firstExisting(candidate);
778
- if (hit)
779
- return hit;
780
- }
781
- }
782
- for (const { pkg, entryFile } of rootEntries) {
783
- if (!spec.startsWith(`${pkg}/`))
784
- continue;
785
- const suffix = spec.slice(pkg.length + 1);
786
- const pkgDir = path5.dirname(path5.resolve(workspaceRoot, entryFile));
787
- const candidate = path5.join(pkgDir, suffix);
788
- const hit = await firstExisting(candidate);
789
- if (hit)
790
- return hit;
791
- }
792
- return null;
793
- }
794
- return {
795
- name: "akan-externalize-framework",
796
- setup(build) {
797
- build.onResolve({ filter: /.*/ }, async (args) => {
798
- const spec = args.path;
799
- if (spec === "." || spec === ".." || spec.startsWith("./") || spec.startsWith("../") || spec.startsWith("/"))
800
- return;
801
- if (extraExact.has(spec) || DEFAULT_EXCLUDE_EXACT.has(spec))
802
- return { path: spec, external: true };
803
- for (const prefix of DEFAULT_EXCLUDE_PREFIX) {
804
- if (spec.startsWith(prefix))
805
- return { path: spec, external: true };
806
- }
807
- if (OPTIONAL_BACKEND_EXTERNAL_EXACT.has(spec))
808
- return { path: spec, external: true };
809
- if (RUNTIME_EXTERNAL_EXACT.has(spec))
810
- return { path: spec, external: true };
811
- for (const prefix of RUNTIME_EXTERNAL_PREFIX) {
812
- if (spec.startsWith(prefix))
813
- return { path: spec, external: true };
814
- }
815
- if (!includePrefixes.some((prefix) => spec.startsWith(prefix)))
816
- return;
817
- const resolved = await resolveWorkspaceSubpath(spec);
818
- return resolved ? { path: resolved } : undefined;
819
- });
820
- }
821
- };
822
- }
823
- async function firstExisting(basePath) {
824
- if (await Bun.file(basePath).exists())
825
- return basePath;
826
- for (const ext of CANDIDATE_EXTS) {
827
- const candidate = `${basePath}${ext}`;
828
- if (await Bun.file(candidate).exists())
829
- return candidate;
830
- }
831
- for (const ext of CANDIDATE_EXTS) {
832
- const candidate = path5.join(basePath, `index${ext}`);
833
- if (await Bun.file(candidate).exists())
834
- return candidate;
835
- }
836
- return null;
837
- }
838
-
839
- // pkgs/@akanjs/devkit/transforms/useClientBundlePlugin.ts
840
- function createUseClientBundlePlugin(options = {}) {
841
- return {
842
- name: "akan-use-client-bundle",
843
- setup(build) {
844
- build.onLoad({ filter: /\.(tsx|ts|jsx|js)$/ }, async (args) => {
845
- if (args.path.includes("/node_modules/"))
846
- return;
847
- let source;
848
- try {
849
- source = await Bun.file(args.path).text();
850
- } catch {
851
- return;
852
- }
853
- const stubbed = transformUseClient(source, {
854
- path: args.path,
855
- workspaceRoot: options.workspaceRoot
856
- });
857
- if (stubbed === null)
858
- return;
859
- return { contents: stubbed, loader: loaderFor(args.path) };
860
- });
861
- }
862
- };
863
- }
864
-
865
- // pkgs/@akanjs/devkit/frontendBuild/pagesEntrySourceGenerator.ts
866
- import path6 from "path";
867
- class PagesEntrySourceGenerator {
868
- #pageEntries;
869
- constructor(pageEntries) {
870
- this.#pageEntries = pageEntries;
871
- }
872
- static generate(pageEntries) {
873
- return new PagesEntrySourceGenerator(pageEntries).generate();
874
- }
875
- generate() {
876
- const lines = this.#pageEntries.map(({ key, moduleAbsPath }) => {
877
- const specifier = PagesEntrySourceGenerator.#toImportSpecifier(moduleAbsPath);
878
- return ` ${JSON.stringify(key)}: () => import(${JSON.stringify(specifier)}),`;
879
- });
880
- return `export const pages = {
881
- ${lines.join(`
882
- `)}
883
- };
884
- `;
885
- }
886
- static async generateStatic(pageEntries) {
887
- return await new PagesEntrySourceGenerator(pageEntries).generateStatic();
888
- }
889
- async generateStatic() {
890
- const imports = this.#pageEntries.map(({ moduleAbsPath }, index) => {
891
- const specifier = PagesEntrySourceGenerator.#toImportSpecifier(moduleAbsPath);
892
- return `import * as page${index} from ${JSON.stringify(specifier)};`;
893
- });
894
- const entries = await Promise.all(this.#pageEntries.map(async ({ key, moduleAbsPath }, index) => {
895
- const isAsyncDefault = await AsyncDefaultExportDetector.detect(moduleAbsPath);
896
- return ` ${JSON.stringify(key)}: { loader: async () => page${index}, isAsyncDefault: ${isAsyncDefault} },`;
897
- }));
898
- return `${imports.join(`
899
- `)}
900
- export const pages = {
901
- ${entries.join(`
902
- `)}
903
- };
904
- `;
905
- }
906
- static #toImportSpecifier(moduleAbsPath) {
907
- return path6.resolve(moduleAbsPath).split(path6.sep).join("/");
908
- }
909
- }
910
-
911
- // pkgs/@akanjs/devkit/frontendBuild/pagesBundleBuilder.ts
912
- var VIRTUAL_PAGES_ENTRY = "akan-pages-entry";
913
-
914
- class PagesBundleBuilder {
915
- #app;
916
- #command;
917
- #pageEntries;
918
- #started = Date.now();
919
- constructor(app, command = "start", pageEntries) {
920
- this.#app = app;
921
- this.#command = command;
922
- this.#pageEntries = pageEntries;
923
- }
924
- async build() {
925
- const akanConfig = await this.#app.getConfig();
926
- const resolvedEntries = this.#pageEntries ?? await resolveSsrPageEntriesForApp(this.#app, await this.#app.getPageKeys());
927
- const entrySource = PagesEntrySourceGenerator.generate(resolvedEntries);
928
- const workspaceRoot = this.#app.workspace.workspaceRoot;
929
- const result = await Bun.build({
930
- entrypoints: [VIRTUAL_PAGES_ENTRY],
931
- outdir: `${this.#artifactDir}/server`,
932
- target: "bun",
933
- format: "esm",
934
- splitting: this.#splitting,
935
- minify: this.#command === "build",
936
- naming: {
937
- entry: "pages-[hash].[ext]",
938
- chunk: "chunks/[name]-[hash].[ext]",
939
- asset: "assets/[name]-[hash].[ext]"
940
- },
941
- define: bundleDefine(this.#app, this.#command, "ssr"),
942
- plugins: [
943
- PagesBundleBuilder.createPagesEntryPlugin(entrySource),
944
- PagesBundleBuilder.createCssStubPlugin(),
945
- PagesBundleBuilder.createServerUseClientFetchPlugin(),
946
- await createExternalizeFrameworkPlugin({ app: this.#app, extra: akanConfig.externalLibs }),
947
- akanConfig.barrelImports.length > 0 ? await createBarrelImportsPlugin(this.#app, {
948
- pipeAfter: (source, args) => transformUseClient(source, {
949
- path: args.path,
950
- workspaceRoot
951
- })
952
- }) : createUseClientBundlePlugin({ workspaceRoot })
953
- ]
954
- });
955
- if (!result.success)
956
- throw new AggregateError(result.logs, "[PagesBundleBuilder] Bun.build failed");
957
- const entryArtifact = result.outputs.find((a) => a.kind === "entry-point");
958
- if (!entryArtifact)
959
- throw new Error("[PagesBundleBuilder] Bun.build emitted no entry-point artifact");
960
- const bundlePath = path7.resolve(entryArtifact.path);
961
- const buildId = Date.now();
962
- const outputBytes = result.outputs.reduce((sum, output) => sum + output.size, 0);
963
- const chunkCount = result.outputs.filter((output) => output.kind === "chunk").length;
964
- this.#app.verbose(`[PagesBundleBuilder] ${path7.basename(bundlePath)} emitted in ${Date.now() - this.#started}ms splitting=${this.#splitting} entry=${entryArtifact.size} bytes outputs=${result.outputs.length} chunks=${chunkCount} total=${outputBytes} bytes`);
965
- return {
966
- bundlePath,
967
- buildId,
968
- splitting: this.#splitting,
969
- entryBytes: entryArtifact.size,
970
- outputBytes,
971
- outputCount: result.outputs.length,
972
- chunkCount
973
- };
974
- }
975
- get #artifactDir() {
976
- return `${this.#command === "build" ? this.#app.dist.cwdPath : this.#app.cwdPath}/.akan/artifact`;
977
- }
978
- get #splitting() {
979
- return process.env.AKAN_SERVER_PAGES_SPLITTING === "1";
980
- }
981
- static createPagesEntryPlugin(source) {
982
- return {
983
- name: "akan-pages-entry",
984
- setup(build) {
985
- build.onResolve({ filter: /^akan-pages-entry$/ }, () => ({
986
- path: VIRTUAL_PAGES_ENTRY,
987
- namespace: "akan-virtual"
988
- }));
989
- build.onLoad({ filter: /^akan-pages-entry$/, namespace: "akan-virtual" }, () => ({
990
- contents: source,
991
- loader: "tsx"
992
- }));
993
- }
994
- };
995
- }
996
- static createCssStubPlugin() {
997
- return {
998
- name: "akan-css-stub",
999
- setup(build) {
1000
- build.onLoad({ filter: /\.css$/ }, () => ({
1001
- contents: "",
1002
- loader: "js"
1003
- }));
1004
- }
1005
- };
1006
- }
1007
- static createServerUseClientFetchPlugin() {
1008
- return {
1009
- name: "akan-server-use-client-fetch",
1010
- setup(build) {
1011
- build.onLoad({ filter: /[/\\]lib[/\\]useClient\.(ts|tsx|js|jsx)$/ }, async (args) => {
1012
- const source = await Bun.file(args.path).text();
1013
- const transformed = PagesBundleBuilder.transformServerUseClientFetchSource(source);
1014
- if (transformed === source)
1015
- return;
1016
- return { contents: transformed, loader: loaderFor(args.path) };
1017
- });
1018
- }
1019
- };
1020
- }
1021
- static transformServerUseClientFetchSource(source) {
1022
- if (!source.includes(`with { type: "macro" }`) || !source.includes("FetchClient.build"))
1023
- return source;
1024
- return source.replace(/import\s+\{\s*getSerializedSignal\s*\}\s+from\s+["']\.\/sig["']\s+with\s+\{\s*type\s*:\s*["']macro["']\s*\};/, `import { fetch as serverFetch } from "./sig";`).replace(/const\s+fetchProto\s*=\s*FetchClient\.build<[^;]+;/, "const fetchProto = FetchClient.build<typeof signal>(cnst, serverFetch.serializedSignal, { Err: pageProto.Err, base: serverFetch });");
1025
- }
1026
- }
1027
-
1028
- // pkgs/@akanjs/devkit/frontendBuild/csrArtifactBuilder.ts
1029
- import { mkdir, rm, unlink } from "fs/promises";
1030
- import path8 from "path";
1031
- class CsrArtifactBuilder {
1032
- #app;
1033
- #command;
1034
- #lang;
1035
- constructor(app, command = "start", lang = "en") {
1036
- this.#app = app;
1037
- this.#command = command;
1038
- this.#lang = lang;
1039
- }
1040
- async build() {
1041
- const pageKeys = await this.#app.getPageKeys();
1042
- if (pageKeys.length === 0) {
1043
- this.#app.log(`[cli] no route files under ${this.#app.cwdPath}/page \u2014 skipping CSR build`);
1044
- return null;
1045
- }
1046
- const pageEntries = await resolveSsrPageEntriesForApp(this.#app, pageKeys);
1047
- const akanConfig = await this.#app.getConfig();
1048
- const cssAssets = await this.#loadCssAssets();
1049
- const basePaths = [...akanConfig.basePaths];
1050
- const htmlBasePaths = basePaths.length > 0 ? basePaths : [""];
1051
- await rm(this.#outputDir, { recursive: true, force: true });
1052
- await mkdir(this.#generatedDir, { recursive: true });
1053
- const generatedFiles = Object.fromEntries((await Promise.all(htmlBasePaths.map(async (basePath) => [
1054
- this.#createHtmlFile(basePath),
1055
- await this.#createEntryFile(basePath, CsrArtifactBuilder.pageEntriesForBasePath(pageEntries, basePath, basePaths))
1056
- ]))).flat());
1057
- const result = await Bun.build({
1058
- target: "browser",
1059
- entrypoints: htmlBasePaths.map((basePath) => this.#generatedPath(CsrArtifactBuilder.htmlFilename(basePath))),
1060
- files: generatedFiles,
1061
- root: this.#generatedDir,
1062
- outdir: this.#outputDir,
1063
- splitting: false,
1064
- minify: true,
1065
- env: "AKAN_PUBLIC_*",
1066
- define: bundleDefine(this.#app, this.#command, "csr"),
1067
- optimizeImports: akanConfig.optimizeImports,
1068
- plugins: [PagesBundleBuilder.createCssStubPlugin()]
1069
- });
1070
- if (!result.success) {
1071
- const logs = result.logs.map((log) => log.message).join(`
1072
- `);
1073
- throw new Error(`[csr-build] failed${logs ? `
1074
- ${logs}` : ""}`);
1075
- }
1076
- await this.#inlineCsrArtifacts(cssAssets);
1077
- this.#app.verbose(`[csr-build] output -> ${this.#outputDir}`);
1078
- return { outputDir: this.#outputDir };
1079
- }
1080
- static pageEntriesForBasePath(pageEntries, basePath, basePaths) {
1081
- return pageEntries.filter((entry) => {
1082
- const entryBasePath = getPageKeyBasePath(entry.key, basePaths);
1083
- return entryBasePath === null || entryBasePath === basePath;
1084
- });
1085
- }
1086
- static htmlFilename(basePath) {
1087
- return `${basePath || "index"}.html`;
1088
- }
1089
- static entryFilename(basePath) {
1090
- return `${basePath || "index"}.csr.tsx`;
1091
- }
1092
- static basePathOfHtml(htmlPath) {
1093
- const name = path8.basename(htmlPath, ".html");
1094
- return name === "index" ? "" : name;
1095
- }
1096
- get #outputDir() {
1097
- return path8.join(this.#command === "build" ? this.#app.dist.cwdPath : this.#app.cwdPath, this.#command === "build" ? "csr" : ".akan/artifact/csr");
1098
- }
1099
- get #generatedDir() {
1100
- return path8.join(this.#app.cwdPath, ".akan/generated/csr");
1101
- }
1102
- get #artifactDir() {
1103
- return path8.join(this.#command === "build" ? this.#app.dist.cwdPath : this.#app.cwdPath, ".akan/artifact");
1104
- }
1105
- #generatedPath(filename) {
1106
- return path8.join(this.#generatedDir, filename);
1107
- }
1108
- async#createEntryFile(basePath, pageEntries) {
1109
- return [
1110
- this.#generatedPath(CsrArtifactBuilder.entryFilename(basePath)),
1111
- `import { bootCsr } from "akanjs/webkit";
1112
- ${await PagesEntrySourceGenerator.generateStatic(pageEntries)}
1113
- void bootCsr(pages);
1114
- `
1115
- ];
1116
- }
1117
- #createHtmlFile(basePath) {
1118
- return [
1119
- this.#generatedPath(CsrArtifactBuilder.htmlFilename(basePath)),
1120
- `<!doctype html>
1121
- <html lang="${this.#lang}">
1122
- <head>
1123
- <meta charset="utf-8" />
1124
- <title>${this.#app.name}</title>
1125
- <base href="/" />
1126
- <meta name="viewport" content="width=device-width, initial-scale=1" />
1127
- </head>
1128
- <body>
1129
- <div id="root"></div>
1130
- <script type="module" src="./${CsrArtifactBuilder.entryFilename(basePath)}"></script>
1131
- </body>
1132
- </html>
1133
- `
1134
- ];
1135
- }
1136
- async#loadCssAssets() {
1137
- const artifactFile = Bun.file(path8.join(this.#artifactDir, "base-artifact.json"));
1138
- if (!await artifactFile.exists())
1139
- return {};
1140
- const artifact = await artifactFile.json();
1141
- return artifact.cssAssets ?? {};
1142
- }
1143
- async#inlineCsrArtifacts(cssAssets) {
1144
- const jsFiles = new Set;
1145
- for (const htmlPath of await this.#listOutputFiles((filePath) => filePath.endsWith(".html"))) {
1146
- const htmlFile = Bun.file(htmlPath);
1147
- if (!await htmlFile.exists())
1148
- continue;
1149
- const basePath = CsrArtifactBuilder.basePathOfHtml(htmlPath);
1150
- const cssAsset = cssAssets[basePath];
1151
- if (!cssAsset) {
1152
- this.#app.logger.warn(`[csr-build] base-artifact.json has no compiled stylesheet for ${basePath || "root"}; ${path8.basename(htmlPath)} ships without CSS`);
1153
- }
1154
- const inlined = await this.#inlineHtmlAssets(await htmlFile.text(), htmlPath, cssAsset);
1155
- for (const filePath of inlined.jsFiles)
1156
- jsFiles.add(filePath);
1157
- await Bun.write(htmlPath, inlined.html);
1158
- }
1159
- for (const filePath of jsFiles)
1160
- await unlink(filePath).catch(() => {
1161
- return;
1162
- });
1163
- const remainingAssets = await this.#listOutputFiles((filePath) => filePath.endsWith(".js") || filePath.endsWith(".css"));
1164
- if (remainingAssets.length > 0) {
1165
- throw new Error(`[csr-build] expected single-file HTML, but CSR assets remain:
1166
- ${remainingAssets.join(`
1167
- `)}`);
1168
- }
1169
- }
1170
- async#inlineHtmlAssets(html, htmlPath, cssAsset) {
1171
- let next = html;
1172
- if (cssAsset) {
1173
- const css = await Bun.file(path8.join(this.#artifactDir, cssAsset.cssRelPath)).text();
1174
- next = CsrArtifactBuilder.injectBeforeHeadEnd(next, CsrArtifactBuilder.createInlineStyle(css));
1175
- }
1176
- const jsFiles = [];
1177
- next = await CsrArtifactBuilder.replaceModuleScriptSrc(next, async (src) => {
1178
- const jsPath = CsrArtifactBuilder.resolveHtmlAssetPath(htmlPath, src);
1179
- jsFiles.push(jsPath);
1180
- return await Bun.file(jsPath).text();
1181
- });
1182
- return { html: next, jsFiles };
1183
- }
1184
- async#listOutputFiles(predicate) {
1185
- const glob = new Bun.Glob("**/*");
1186
- const files = [];
1187
- for await (const filePath of glob.scan({ cwd: this.#outputDir, absolute: true })) {
1188
- if (predicate(filePath))
1189
- files.push(filePath);
1190
- }
1191
- return files.sort();
1192
- }
1193
- static injectBeforeHeadEnd(html, snippet) {
1194
- const scannable = CsrArtifactBuilder.blankEmbeddedContent(html);
1195
- const headEnd = scannable.search(/<\/head\s*>/i);
1196
- if (headEnd !== -1)
1197
- return `${html.slice(0, headEnd)}${snippet}
1198
- ${html.slice(headEnd)}`;
1199
- const bodyStart = scannable.search(/<body(?:\s|>)/i);
1200
- if (bodyStart !== -1)
1201
- return `${html.slice(0, bodyStart)}${snippet}
1202
- ${html.slice(bodyStart)}`;
1203
- return `${html}
1204
- ${snippet}`;
1205
- }
1206
- static blankEmbeddedContent(html) {
1207
- return html.replace(/<script\b[^>]*>[\s\S]*?<\/script\s*>|<style\b[^>]*>[\s\S]*?<\/style\s*>|<!--[\s\S]*?-->/gi, (match) => " ".repeat(match.length));
1208
- }
1209
- static createInlineStyle(css) {
1210
- return `<style data-akan-css="active">
1211
- ${css.replace(/<\/style/gi, "<\\/style")}
1212
- </style>`;
1213
- }
1214
- static async replaceModuleScriptSrc(html, loadScript) {
1215
- const scriptRe = /<script\b(?=[^>]*\btype=["']module["'])(?=[^>]*\bsrc=["']([^"']+)["'])[^>]*>\s*<\/script>/gi;
1216
- let result = "";
1217
- let lastIndex = 0;
1218
- let matched = false;
1219
- for (const match of html.matchAll(scriptRe)) {
1220
- const full = match[0];
1221
- const src = match[1];
1222
- if (match.index === undefined || !src)
1223
- continue;
1224
- matched = true;
1225
- result += html.slice(lastIndex, match.index);
1226
- result += `<script type="module">
1227
- ${CsrArtifactBuilder.escapeInlineScript(await loadScript(src))}
1228
- </script>`;
1229
- lastIndex = match.index + full.length;
1230
- }
1231
- if (!matched)
1232
- return html;
1233
- return result + html.slice(lastIndex);
1234
- }
1235
- static escapeInlineScript(source) {
1236
- return source.replace(/<\/script/gi, "<\\/script");
1237
- }
1238
- static resolveHtmlAssetPath(htmlPath, src) {
1239
- if (/^[a-z][a-z0-9+.-]*:/i.test(src) || src.startsWith("//")) {
1240
- throw new Error(`[csr-build] cannot inline external script: ${src}`);
1241
- }
1242
- const normalized = src.startsWith("/") ? src.slice(1) : src;
1243
- return path8.resolve(path8.dirname(htmlPath), normalized);
1244
- }
874
+ return CsrEntryFiles.basePathOfPageKey(pageKey, basePaths);
1245
875
  }
1246
876
 
1247
877
  // pkgs/@akanjs/devkit/frontendBuild/fontOptimizer.ts
1248
878
  import { mkdir as mkdir2, stat as stat2 } from "fs/promises";
1249
- import path9 from "path";
879
+ import path5 from "path";
1250
880
  import ts from "typescript";
1251
881
  var FONT_URL_PREFIX = "/_akan/fonts";
1252
882
  var DEFAULT_FONT_SUBSETS = ["latin"];
@@ -1263,7 +893,7 @@ class FontOptimizer {
1263
893
  constructor(app, command = "start") {
1264
894
  this.#app = app;
1265
895
  this.#command = command;
1266
- this.#artifactRoot = path9.join(command === "build" ? app.dist.cwdPath : app.cwdPath, ".akan/artifact");
896
+ this.#artifactRoot = path5.join(command === "build" ? app.dist.cwdPath : app.cwdPath, ".akan/artifact");
1267
897
  }
1268
898
  async optimize() {
1269
899
  const fonts = await this.discoverFonts();
@@ -1288,7 +918,7 @@ class FontOptimizer {
1288
918
  return result;
1289
919
  }
1290
920
  get #cachePath() {
1291
- return path9.join(this.#artifactRoot, "fontCache.json");
921
+ return path5.join(this.#artifactRoot, "fontCache.json");
1292
922
  }
1293
923
  async#buildCacheKey(fonts) {
1294
924
  const sources = [];
@@ -1303,7 +933,7 @@ class FontOptimizer {
1303
933
  sources.push({ optimizedSrc: face.optimizedSrc, ...stamp });
1304
934
  }
1305
935
  for (const filePath of font.subsetFiles ?? []) {
1306
- const abs = path9.isAbsolute(filePath) ? filePath : path9.join(this.#app.cwdPath, filePath);
936
+ const abs = path5.isAbsolute(filePath) ? filePath : path5.join(this.#app.cwdPath, filePath);
1307
937
  const stamp = await this.#fileStamp(abs);
1308
938
  if (!stamp)
1309
939
  return null;
@@ -1329,7 +959,7 @@ class FontOptimizer {
1329
959
  const cache = await Bun.file(this.#cachePath).json().catch(() => null);
1330
960
  if (cache?.version !== FontOptimizer.#cacheVersion || cache.key !== key)
1331
961
  return null;
1332
- const files = cache.files.map((relativePath) => path9.join(this.#artifactRoot, relativePath));
962
+ const files = cache.files.map((relativePath) => path5.join(this.#artifactRoot, relativePath));
1333
963
  for (const filePath of files) {
1334
964
  if (!await Bun.file(filePath).exists())
1335
965
  return null;
@@ -1341,7 +971,7 @@ class FontOptimizer {
1341
971
  version: FontOptimizer.#cacheVersion,
1342
972
  key,
1343
973
  css: result.css,
1344
- files: result.files.map((filePath) => path9.relative(this.#artifactRoot, filePath))
974
+ files: result.files.map((filePath) => path5.relative(this.#artifactRoot, filePath))
1345
975
  };
1346
976
  await Bun.write(this.#cachePath, JSON.stringify(cache));
1347
977
  }
@@ -1349,7 +979,7 @@ class FontOptimizer {
1349
979
  const pageKeys = await this.#app.getPageKeys();
1350
980
  const fonts = [];
1351
981
  await Promise.all(pageKeys.map(async (key) => {
1352
- const filePath = path9.resolve(this.#app.cwdPath, "page", key);
982
+ const filePath = path5.resolve(this.#app.cwdPath, "page", key);
1353
983
  const file = Bun.file(filePath);
1354
984
  if (!await file.exists())
1355
985
  return;
@@ -1368,8 +998,8 @@ class FontOptimizer {
1368
998
  this.#app.logger.warn(`[font] source not found: ${face.src}`);
1369
999
  continue;
1370
1000
  }
1371
- const outputPath = path9.join(this.#artifactRoot, face.optimizedSrc.replace(/^\/_akan\//, ""));
1372
- await mkdir2(path9.dirname(outputPath), { recursive: true });
1001
+ const outputPath = path5.join(this.#artifactRoot, face.optimizedSrc.replace(/^\/_akan\//, ""));
1002
+ await mkdir2(path5.dirname(outputPath), { recursive: true });
1373
1003
  const sourceBuffer = Buffer.from(await Bun.file(sourcePath).arrayBuffer());
1374
1004
  const outputBuffer = await this.#buildFontBuffer(font, sourceBuffer, sourcePath);
1375
1005
  await Bun.write(outputPath, outputBuffer);
@@ -1391,7 +1021,7 @@ class FontOptimizer {
1391
1021
  const entries = Array.isArray(value) ? value : [];
1392
1022
  const fonts = entries.filter((entry) => this.#isReadableFont(entry));
1393
1023
  if (fonts.length !== entries.length || !Array.isArray(value))
1394
- this.#app.logger.warn(`[font] ${path9.relative(this.#app.cwdPath, filePath)} declares fonts the build cannot read without evaluating the module \u2014 write the list inline, or nothing is subset for it and every /_akan/fonts request 404s`);
1024
+ this.#app.logger.warn(`[font] ${path5.relative(this.#app.cwdPath, filePath)} declares fonts the build cannot read without evaluating the module \u2014 write the list inline, or nothing is subset for it and every /_akan/fonts request 404s`);
1395
1025
  return fonts.map((font) => ({ ...font, subsets: font.subsets ?? [...DEFAULT_FONT_SUBSETS] }));
1396
1026
  }
1397
1027
  #isReadableFont(value) {
@@ -1549,8 +1179,8 @@ class FontOptimizer {
1549
1179
  return null;
1550
1180
  const rel = src.replace(/^\//, "");
1551
1181
  const candidates = [
1552
- this.#command === "build" ? path9.join(this.#app.dist.cwdPath, "public", rel) : null,
1553
- path9.join(this.#app.cwdPath, "public", rel),
1182
+ this.#command === "build" ? path5.join(this.#app.dist.cwdPath, "public", rel) : null,
1183
+ path5.join(this.#app.cwdPath, "public", rel),
1554
1184
  this.#resolveWorkspacePublicPath(rel)
1555
1185
  ].filter(Boolean);
1556
1186
  for (const candidate of candidates) {
@@ -1582,7 +1212,7 @@ class FontOptimizer {
1582
1212
  return "woff2";
1583
1213
  if (signature === "OTTO")
1584
1214
  return "otf";
1585
- const ext = path9.extname(sourcePath).slice(1).toLowerCase();
1215
+ const ext = path5.extname(sourcePath).slice(1).toLowerCase();
1586
1216
  if (ext === "otf" || ext === "woff" || ext === "woff2")
1587
1217
  return ext;
1588
1218
  return "ttf";
@@ -1591,7 +1221,7 @@ class FontOptimizer {
1591
1221
  const [root, dep, ...rest] = rel.split("/");
1592
1222
  if (root !== "libs" || !dep || rest.length === 0)
1593
1223
  return null;
1594
- return path9.join(this.#app.workspace.workspaceRoot, "libs", dep, "public", ...rest);
1224
+ return path5.join(this.#app.workspace.workspaceRoot, "libs", dep, "public", ...rest);
1595
1225
  }
1596
1226
  async#getSubsetText(font) {
1597
1227
  const parts = new Set;
@@ -1601,7 +1231,7 @@ class FontOptimizer {
1601
1231
  if (font.subsetText)
1602
1232
  parts.add(font.subsetText);
1603
1233
  for (const filePath of font.subsetFiles ?? []) {
1604
- const abs = path9.isAbsolute(filePath) ? filePath : path9.join(this.#app.cwdPath, filePath);
1234
+ const abs = path5.isAbsolute(filePath) ? filePath : path5.join(this.#app.cwdPath, filePath);
1605
1235
  const file = Bun.file(abs);
1606
1236
  if (await file.exists())
1607
1237
  parts.add(await file.text());
@@ -1621,7 +1251,7 @@ class FontOptimizer {
1621
1251
  }
1622
1252
  async#collectAutoSubsetText() {
1623
1253
  const libPageRoots = (await this.#app.getPageRoots()).filter((root) => root.keyPrefix).map((root) => root.dir);
1624
- const roots = [...["page", "ui", "lib"].map((dir) => path9.join(this.#app.cwdPath, dir)), ...libPageRoots];
1254
+ const roots = [...["page", "ui", "lib"].map((dir) => path5.join(this.#app.cwdPath, dir)), ...libPageRoots];
1625
1255
  const glob = new Bun.Glob("**/*.{ts,tsx,js,jsx,html,md}");
1626
1256
  const parts = [];
1627
1257
  for (const root of [...new Set(roots)].sort()) {
@@ -1735,7 +1365,7 @@ ${declarations.map(([prop, value]) => ` ${prop}: ${value};`).join(`
1735
1365
 
1736
1366
  // pkgs/@akanjs/devkit/frontendBuild/fontPruner.ts
1737
1367
  import { rm as rm2, rmdir, stat as stat3 } from "fs/promises";
1738
- import path10 from "path";
1368
+ import path6 from "path";
1739
1369
  import { isFontOptimizationEnabled } from "akanjs/client";
1740
1370
  var FONT_GLOB = "**/*.{woff2,woff,ttf,otf,ttc,eot}";
1741
1371
  var REFERRER_GLOB = "**/*.{css,js,mjs,cjs,json,html,htm,svg,xml,webmanifest}";
@@ -1748,8 +1378,8 @@ class FontPruner {
1748
1378
  constructor(app, { keepFonts = [] } = {}) {
1749
1379
  this.#app = app;
1750
1380
  this.#keepGlobs = keepFonts;
1751
- this.#publicRoot = path10.join(app.dist.cwdPath, "public");
1752
- this.#artifactRoot = path10.join(app.dist.cwdPath, ".akan/artifact");
1381
+ this.#publicRoot = path6.join(app.dist.cwdPath, "public");
1382
+ this.#artifactRoot = path6.join(app.dist.cwdPath, ".akan/artifact");
1753
1383
  }
1754
1384
  async prune() {
1755
1385
  const empty = { removed: [], kept: [], freedBytes: 0 };
@@ -1764,7 +1394,7 @@ class FontPruner {
1764
1394
  for (const font of fonts) {
1765
1395
  const target = isFontOptimizationEnabled(font) ? optimizedSrcs : unoptimizedSrcs;
1766
1396
  for (const fontPath of font.paths)
1767
- target.add(path10.posix.basename(fontPath.src));
1397
+ target.add(path6.posix.basename(fontPath.src));
1768
1398
  }
1769
1399
  const result = { removed: [], kept: [], freedBytes: 0 };
1770
1400
  const undecided = [];
@@ -1802,10 +1432,10 @@ class FontPruner {
1802
1432
  async#collectCandidates() {
1803
1433
  const candidates = [];
1804
1434
  for await (const rel of new Bun.Glob(FONT_GLOB).scan({ cwd: this.#publicRoot, dot: false })) {
1805
- const entry = await stat3(path10.join(this.#publicRoot, rel)).catch(() => null);
1435
+ const entry = await stat3(path6.join(this.#publicRoot, rel)).catch(() => null);
1806
1436
  if (!entry?.isFile())
1807
1437
  continue;
1808
- candidates.push({ rel: rel.split(path10.sep).join("/"), basename: path10.basename(rel), bytes: entry.size });
1438
+ candidates.push({ rel: rel.split(path6.sep).join("/"), basename: path6.basename(rel), bytes: entry.size });
1809
1439
  }
1810
1440
  return candidates;
1811
1441
  }
@@ -1815,11 +1445,11 @@ class FontPruner {
1815
1445
  const bundled = candidates.filter((candidate) => !optimizedSrcs.has(candidate.basename));
1816
1446
  const roots = [
1817
1447
  { dir: this.#publicRoot, label: "public", needles: authoritative },
1818
- { dir: path10.join(this.#artifactRoot, "styles"), label: ".akan/artifact/styles", needles: authoritative },
1819
- { dir: path10.join(this.#artifactRoot, "client"), label: ".akan/artifact/client", needles: bundled },
1820
- { dir: path10.join(this.#artifactRoot, "client-ssr"), label: ".akan/artifact/client-ssr", needles: bundled },
1821
- { dir: path10.join(this.#artifactRoot, "server"), label: ".akan/artifact/server", needles: bundled },
1822
- { dir: path10.join(this.#app.dist.cwdPath, "csr"), label: "csr", needles: bundled }
1448
+ { dir: path6.join(this.#artifactRoot, "styles"), label: ".akan/artifact/styles", needles: authoritative },
1449
+ { dir: path6.join(this.#artifactRoot, "client"), label: ".akan/artifact/client", needles: bundled },
1450
+ { dir: path6.join(this.#artifactRoot, "client-ssr"), label: ".akan/artifact/client-ssr", needles: bundled },
1451
+ { dir: path6.join(this.#artifactRoot, "server"), label: ".akan/artifact/server", needles: bundled },
1452
+ { dir: path6.join(this.#app.dist.cwdPath, "csr"), label: "csr", needles: bundled }
1823
1453
  ];
1824
1454
  for (const root of roots) {
1825
1455
  const pending = root.needles.filter((candidate) => !referrers.has(candidate.rel));
@@ -1838,20 +1468,20 @@ class FontPruner {
1838
1468
  const remaining = needles.filter((needle) => !referrers.has(needle.rel));
1839
1469
  if (!remaining.length)
1840
1470
  return;
1841
- const text = await Bun.file(path10.join(root.dir, rel)).text().catch(() => "");
1471
+ const text = await Bun.file(path6.join(root.dir, rel)).text().catch(() => "");
1842
1472
  if (!text)
1843
1473
  continue;
1844
1474
  for (const needle of remaining) {
1845
1475
  if (needle.forms.some((form) => text.includes(form)))
1846
- referrers.set(needle.rel, `${root.label}/${rel.split(path10.sep).join("/")}`);
1476
+ referrers.set(needle.rel, `${root.label}/${rel.split(path6.sep).join("/")}`);
1847
1477
  }
1848
1478
  }
1849
1479
  }
1850
1480
  async#remove(files) {
1851
- await Promise.all(files.map((file) => rm2(path10.join(this.#publicRoot, file), { force: true })));
1852
- const dirs = [...new Set(files.map((file) => path10.posix.dirname(file)))].filter((dir) => dir !== "." && dir !== "/").sort((a, b) => b.length - a.length);
1481
+ await Promise.all(files.map((file) => rm2(path6.join(this.#publicRoot, file), { force: true })));
1482
+ const dirs = [...new Set(files.map((file) => path6.posix.dirname(file)))].filter((dir) => dir !== "." && dir !== "/").sort((a, b) => b.length - a.length);
1853
1483
  for (const dir of dirs)
1854
- await rmdir(path10.join(this.#publicRoot, dir)).catch(() => {
1484
+ await rmdir(path6.join(this.#publicRoot, dir)).catch(() => {
1855
1485
  return;
1856
1486
  });
1857
1487
  }
@@ -1899,7 +1529,7 @@ class FontPruner {
1899
1529
 
1900
1530
  // pkgs/@akanjs/devkit/frontendBuild/precompressArtifacts.ts
1901
1531
  import fs from "fs";
1902
- import path11 from "path";
1532
+ import path7 from "path";
1903
1533
  import zlib from "zlib";
1904
1534
  var COMPRESSIBLE_EXTS = new Set([".css", ".html", ".js", ".json", ".svg"]);
1905
1535
  var MIN_COMPRESS_BYTES = 1024;
@@ -1909,8 +1539,8 @@ var GZIP_LEVEL = 9;
1909
1539
  var { formatBytes } = FontPruner;
1910
1540
  async function precompressArtifacts(app) {
1911
1541
  const roots = [
1912
- path11.join(app.dist.cwdPath, ".akan/artifact/client"),
1913
- path11.join(app.dist.cwdPath, ".akan/artifact/styles")
1542
+ path7.join(app.dist.cwdPath, ".akan/artifact/client"),
1543
+ path7.join(app.dist.cwdPath, ".akan/artifact/styles")
1914
1544
  ];
1915
1545
  const result = { files: 0, inputBytes: 0, outputBytes: 0, brotliBytes: 0 };
1916
1546
  await Promise.all(roots.map((root) => precompressRoot(root, result)));
@@ -1929,7 +1559,7 @@ async function precompressRoot(root, result) {
1929
1559
  const bytes = await Bun.file(filePath).bytes();
1930
1560
  const buffer = bytes.buffer.slice(bytes.byteOffset, bytes.byteOffset + bytes.byteLength);
1931
1561
  const gz = Bun.gzipSync(buffer, { level: GZIP_LEVEL });
1932
- const br = brotliCompress(buffer, path11.extname(filePath).toLowerCase());
1562
+ const br = brotliCompress(buffer, path7.extname(filePath).toLowerCase());
1933
1563
  await Promise.all([Bun.write(`${filePath}.gz`, gz), Bun.write(`${filePath}.br`, br)]);
1934
1564
  result.files += 1;
1935
1565
  result.inputBytes += bytes.byteLength;
@@ -1949,7 +1579,7 @@ function brotliCompress(buffer, ext) {
1949
1579
  async function shouldPrecompress(filePath) {
1950
1580
  if (filePath.endsWith(".gz") || filePath.endsWith(".br"))
1951
1581
  return false;
1952
- if (!COMPRESSIBLE_EXTS.has(path11.extname(filePath).toLowerCase()))
1582
+ if (!COMPRESSIBLE_EXTS.has(path7.extname(filePath).toLowerCase()))
1953
1583
  return false;
1954
1584
  const file = Bun.file(filePath);
1955
1585
  if (!await file.exists())
@@ -1958,7 +1588,7 @@ async function shouldPrecompress(filePath) {
1958
1588
  }
1959
1589
 
1960
1590
  // pkgs/@akanjs/devkit/frontendBuild/ssrBaseArtifactBuilder.ts
1961
- import path12 from "path";
1591
+ import path8 from "path";
1962
1592
  import { optimize } from "@tailwindcss/node";
1963
1593
  function prepareCssAsset(command, basePath, cssText) {
1964
1594
  return optimize(cssText, { file: `${basePath || "root"}.css`, minify: command === "build" }).code;
@@ -1974,7 +1604,7 @@ class SsrBaseArtifactBuilder {
1974
1604
  this.#app = app;
1975
1605
  this.#command = command;
1976
1606
  this.#artifactDir = `${command === "build" ? app.dist.cwdPath : app.cwdPath}/.akan/artifact`;
1977
- this.#absArtifactDir = path12.resolve(this.#artifactDir);
1607
+ this.#absArtifactDir = path8.resolve(this.#artifactDir);
1978
1608
  }
1979
1609
  static servedWeb(command, web) {
1980
1610
  return command === "start" ? { ssr: true, csr: true } : web;
@@ -1999,7 +1629,7 @@ class SsrBaseArtifactBuilder {
1999
1629
  rscRuntimeSsrManifest,
2000
1630
  vendorMap,
2001
1631
  cssAssets,
2002
- pagesBundlePath: this.#command === "build" ? path12.relative(this.#absArtifactDir, pagesBundle.bundlePath) : pagesBundle.bundlePath,
1632
+ pagesBundlePath: this.#command === "build" ? path8.relative(this.#absArtifactDir, pagesBundle.bundlePath) : pagesBundle.bundlePath,
2003
1633
  pagesBundleBuildId: pagesBundle.buildId,
2004
1634
  domains: [...akanConfig.domains],
2005
1635
  subRoutes: Object.fromEntries(Array.from(akanConfig.subRoutes.entries()).map(([basePath, domains]) => [basePath, [...domains]])),
@@ -2016,18 +1646,18 @@ class SsrBaseArtifactBuilder {
2016
1646
  androidSha256CertFingerprints: target.deepLinks?.android?.sha256CertFingerprints
2017
1647
  }))
2018
1648
  };
2019
- await Bun.write(path12.join(this.#absArtifactDir, "base-artifact.json"), `${JSON.stringify(artifact, null, 2)}
1649
+ await Bun.write(path8.join(this.#absArtifactDir, "base-artifact.json"), `${JSON.stringify(artifact, null, 2)}
2020
1650
  `);
2021
1651
  this.#app.verbose(`[base-artifact] complete in ${Date.now() - this.#started}ms`);
2022
1652
  return { artifact, seedIndex, cssCompiler, optimizedFonts };
2023
1653
  }
2024
1654
  async#buildRuntimeClientEntries() {
2025
1655
  const akanServerPath = await this.#resolveAkanServerPath();
2026
- const rscClientEntry = path12.resolve(akanServerPath, "rscClient.tsx");
2027
- const rscSegmentOutletEntry = path12.resolve(akanServerPath, "rscSegmentOutlet.tsx");
1656
+ const rscClientEntry = path8.resolve(akanServerPath, "rscClient.tsx");
1657
+ const rscSegmentOutletEntry = path8.resolve(akanServerPath, "rscSegmentOutlet.tsx");
2028
1658
  const vendorEntries = VENDOR_SPECIFIERS.map((specifier) => ({
2029
1659
  specifier,
2030
- absPath: path12.resolve(akanServerPath, "vendor", `${specifier.replaceAll("/", "-").replaceAll(".", "-")}.ts`)
1660
+ absPath: path8.resolve(akanServerPath, "vendor", `${specifier.replaceAll("/", "-").replaceAll(".", "-")}.ts`)
2031
1661
  }));
2032
1662
  const entries = [rscClientEntry, rscSegmentOutletEntry, ...vendorEntries.map((v) => v.absPath)];
2033
1663
  const clientBundle = await new ClientEntriesBundler({ app: this.#app, entries, command: this.#command }).bundle();
@@ -2064,15 +1694,15 @@ class SsrBaseArtifactBuilder {
2064
1694
  async#resolveAkanServerPath() {
2065
1695
  const candidates = [];
2066
1696
  try {
2067
- candidates.push(path12.dirname(Bun.resolveSync("akanjs/server", this.#app.workspace.workspaceRoot)));
1697
+ candidates.push(path8.dirname(Bun.resolveSync("akanjs/server", this.#app.workspace.workspaceRoot)));
2068
1698
  } catch {}
2069
- candidates.push(path12.join(this.#app.workspace.workspaceRoot, "pkgs/akanjs/server"), path12.join(this.#app.workspace.workspaceRoot, "node_modules/akanjs/server"));
1699
+ candidates.push(path8.join(this.#app.workspace.workspaceRoot, "pkgs/akanjs/server"), path8.join(this.#app.workspace.workspaceRoot, "node_modules/akanjs/server"));
2070
1700
  try {
2071
- candidates.push(path12.dirname(Bun.resolveSync("akanjs/server", path12.dirname(Bun.main))));
1701
+ candidates.push(path8.dirname(Bun.resolveSync("akanjs/server", path8.dirname(Bun.main))));
2072
1702
  } catch {}
2073
- candidates.push(path12.join(path12.dirname(Bun.main), "node_modules/akanjs/server"), path12.join(path12.dirname(Bun.main), "../../akanjs/server"), path12.resolve(import.meta.dir, "../../server"), path12.resolve(import.meta.dir, "../server"));
1703
+ candidates.push(path8.join(path8.dirname(Bun.main), "node_modules/akanjs/server"), path8.join(path8.dirname(Bun.main), "../../akanjs/server"), path8.resolve(import.meta.dir, "../../server"), path8.resolve(import.meta.dir, "../server"));
2074
1704
  for (const candidate of candidates) {
2075
- if (await Bun.file(path12.join(candidate, "rscClient.tsx")).exists())
1705
+ if (await Bun.file(path8.join(candidate, "rscClient.tsx")).exists())
2076
1706
  return candidate;
2077
1707
  }
2078
1708
  throw new Error(`[base-artifact] failed to locate akanjs/server; looked in: ${candidates.join(", ")}`);
@@ -2101,7 +1731,7 @@ ${preparedCssText}`).toString(36);
2101
1731
  `styles/${cssAssetName}-${cssHash}.css`,
2102
1732
  `/_akan/styles/${cssAssetName}-${cssHash}.css`
2103
1733
  ];
2104
- await Bun.write(path12.join(this.#absArtifactDir, cssRelPath), preparedCssText);
1734
+ await Bun.write(path8.join(this.#absArtifactDir, cssRelPath), preparedCssText);
2105
1735
  SsrBaseArtifactBuilder.#warnDroppedImports(this.#app, cssRelPath, preparedCssText, imported);
2106
1736
  this.#app.verbose(`[base-artifact] wrote ${preparedCssText.length} bytes of CSS for ${basePath} -> ${cssRelPath}`);
2107
1737
  return [basePath, { cssUrl, cssRelPath }];
@@ -2114,4 +1744,4 @@ ${preparedCssText}`).toString(36);
2114
1744
  }
2115
1745
  }
2116
1746
  }
2117
- export { computeRouteSeedIndex, saveRouteSeedIndex, AllRoutesBuilder, CssCompiler, PagesBundleBuilder, CsrArtifactBuilder, FontOptimizer, FontPruner, precompressArtifacts, SsrBaseArtifactBuilder };
1747
+ export { AllRoutesBuilder, CsrArtifactBuilder, CssCompiler, FontOptimizer, FontPruner, precompressArtifacts, SsrBaseArtifactBuilder };