@akanjs/cli 3.1.0-rc.0 → 3.1.0-rc.10
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.
- package/.build-stamp +1 -1
- package/{CodeAgent-q7a4srt6.js → CodeAgent-rkjyxgec.js} +2 -2
- package/{CodeTui-ak23dxc9.js → CodeTui-h7pyj6xa.js} +1 -1
- package/TunnelShare-661cxz5e.js +14 -0
- package/{agent.command-f3gsc1gd.js → agent.command-3qxf4xcj.js} +5 -5
- package/akanCode.js +2 -2
- package/{application.command-3v2d53gd.js → application.command-m1kcd7jf.js} +54 -42
- package/{applicationBuildRunner-1yaqsrdq.js → applicationBuildRunner-dj0xkrep.js} +26 -33
- package/buildBatch.proc.js +24 -15
- package/{cloud.command-kk2rwkvw.js → cloud.command-9cp1t0f5.js} +63 -65
- package/cloud.script-dgstqt1m.js +24 -0
- package/{code.command-30sca09d.js → code.command-0ad6gw2a.js} +6 -6
- package/{context.command-k7eqacq6.js → context.command-0qx447mb.js} +17 -13
- package/context.runner-1vsjztjg.js +25 -0
- package/{dependencyScanner-06z2dmrr.js → dependencyScanner-3afj2vt1.js} +1 -1
- package/{guideline.command-dtczrqpf.js → guideline.command-cv5bmrx2.js} +2 -2
- package/guidelines/agentRule/agentRule.instruction.md +3 -3
- package/guidelines/conventions/conventions.instruction.md +457 -1069
- package/guidelines/cssRule/cssRule.instruction.md +28 -7
- package/guidelines/fieldRule/fieldRule.instruction.md +62 -0
- package/guidelines/framework/framework.instruction.md +4 -7
- package/guidelines/mcpRule/mcpRule.instruction.md +48 -20
- package/guidelines/modelConstant/modelConstant.instruction.md +2 -1
- package/guidelines/modelStore/modelStore.instruction.md +9 -0
- package/guidelines/pageRule/pageRule.instruction.md +80 -0
- package/guidelines/queryRule/queryRule.instruction.md +44 -11
- package/guidelines/runtimeRule/runtimeRule.instruction.md +113 -17
- package/guidelines/ssrRule/ssrRule.instruction.md +13 -0
- package/guidelines/transportRule/transportRule.instruction.md +135 -13
- package/guidelines/workspaceOnboarding/workspaceOnboarding.instruction.md +4 -4
- package/guidelines/workspaceRecipes/workspaceRecipes.instruction.md +68 -7
- package/incrementalBuilder.proc.js +10 -7
- package/index-192ha4xh.js +1173 -0
- package/{index-c76k723e.js → index-1p98exm2.js} +2 -2
- package/{index-rwq3ekqc.js → index-1xj2t7cb.js} +7 -7
- package/{index-nz15k0b6.js → index-20bxxyvn.js} +80 -80
- package/{index-qt22xstj.js → index-35hc95h5.js} +5 -3
- package/{index-jyv387qw.js → index-4mhy6qme.js} +4 -4
- package/{index-ww9bhp67.js → index-4pf8g7ff.js} +1 -1
- package/{index-2mx692qq.js → index-5edzv8fn.js} +5 -5
- package/{index-k9rrten6.js → index-5nd1wtkv.js} +92 -94
- package/index-62md4dwy.js +260 -0
- package/index-665zp9rs.js +271 -0
- package/{index-2etcv0sc.js → index-86jt2a0e.js} +1 -1
- package/{index-2t4tqd1c.js → index-8fp3b47h.js} +419 -418
- package/{index-c2yrsxdh.js → index-9z426msb.js} +109 -328
- package/{index-ctanya41.js → index-awbdfcf2.js} +4 -4
- package/{index-3hx1z96c.js → index-bqbf654f.js} +1 -1
- package/{index-tn4rpqp1.js → index-bspm21y1.js} +73 -49
- package/index-cpb3nvma.js +667 -0
- package/{index-p9ystjrp.js → index-d47ypebv.js} +97 -55
- package/{index-0bm04n5g.js → index-d72hfmdm.js} +15 -13
- package/{index-e0cvze1s.js → index-fmzcs8v6.js} +4 -4
- package/{index-y8q31011.js → index-fyq1z6kt.js} +68 -177
- package/{index-kjvrq3nc.js → index-g1mbppa9.js} +331 -250
- package/{index-gnyf1fqy.js → index-j237xtew.js} +6 -6
- package/{index-fm46xrf1.js → index-j4mkhz93.js} +3 -3
- package/{index-6jjtnhkw.js → index-jw1zm273.js} +74 -34
- package/{index-q958tqqg.js → index-m9jj544v.js} +1 -1
- package/index-mdjc93e1.js +33 -0
- package/{index-c1x0be5j.js → index-pydbnfez.js} +2 -2
- package/{index-7bymavqk.js → index-qwa5k1j8.js} +1 -1
- package/{index-v29746vb.js → index-tamvyvj3.js} +3 -3
- package/{index-05g3jayg.js → index-trpasv9q.js} +260 -259
- package/{index-2ahj896a.js → index-tvehn2sp.js} +1 -1
- package/{index-55mynnqs.js → index-va06nr10.js} +1434 -1173
- package/index-vk0pa0mn.js +240 -0
- package/{index-aprt7pkz.js → index-w44cy4vs.js} +2271 -3369
- package/index.js +22 -22
- package/{library.command-gaga5bmq.js → library.command-erp2f56n.js} +3 -3
- package/{localRegistry.command-9e84e32q.js → localRegistry.command-y176p558.js} +15 -11
- package/{module.command-7vpf6db4.js → module.command-gs2r615e.js} +5 -5
- package/{package.command-twmvvf69.js → package.command-j10r76ax.js} +4 -4
- package/package.json +21 -21
- package/{page.command-5asq7fdf.js → page.command-4h4a8pnh.js} +3 -3
- package/{pipeline.command-d0v38572.js → pipeline.command-a2nrk26m.js} +221 -220
- package/{primitive.command-dyrs2fm8.js → primitive.command-w6t0asqz.js} +6 -6
- package/{projectTypecheck-cvtkrzp2.js → projectTypecheck-f534enqj.js} +1 -1
- package/{quality.command-6emsqpbg.js → quality.command-77x7vbbx.js} +4 -4
- package/{repair.command-8t6aegv5.js → repair.command-y6sstev7.js} +5 -5
- package/{scalar.command-bnggy1x7.js → scalar.command-20b8cy0k.js} +4 -4
- package/{subspace.command-f02j5tge.js → subspace.command-5c7yasd7.js} +424 -364
- package/templates/app/env/env.client.type.ts +2 -3
- package/templates/app/page/styles.css.template +8 -0
- package/templates/appSample/lib/task/Task.Util.tsx +7 -7
- package/templates/appSample/lib/task/Task.Zone.tsx +1 -1
- package/templates/appSample/ui/QuantityControl.tsx +2 -2
- package/templates/crudSinglePage/page.tsx +1 -1
- package/templates/lib/cnst.ts +0 -1
- package/templates/workspaceRoot/.gitignore.template +23 -52
- package/templates/workspaceRoot/biome.json.template +1 -1
- package/{tunnel.command-9x8nfn75.js → tunnel.command-2kw8vt9g.js} +8 -4
- package/{workflow.command-jx0wag5r.js → workflow.command-2djwmpqf.js} +10 -10
- package/{workspace.command-cmj6mqyk.js → workspace.command-6qfgq4b4.js} +38 -34
- package/TunnelShare-21mm6dr5.js +0 -9
- package/cloud.script-r7eyfey4.js +0 -20
- package/context.runner-hqd1ppqe.js +0 -21
- package/index-6w0rbr9d.js +0 -68
- package/index-n68jx79w.js +0 -120
- package/index-r2tpeb3n.js +0 -392
|
@@ -1,25 +1,28 @@
|
|
|
1
1
|
// @bun
|
|
2
2
|
import {
|
|
3
3
|
resolveSsrPageEntriesForApp
|
|
4
|
-
} from "./index-
|
|
4
|
+
} from "./index-va06nr10.js";
|
|
5
5
|
import {
|
|
6
6
|
computeRouteSeedIndex,
|
|
7
7
|
saveRouteSeedIndex,
|
|
8
|
-
BarrelAnalyzer,
|
|
9
|
-
createTsconfigPackageResolver,
|
|
10
|
-
rewriteBarrelImports,
|
|
11
8
|
GraphClientEntryDiscovery,
|
|
12
|
-
bundleDefine,
|
|
13
9
|
ClientEntriesBundler,
|
|
14
10
|
VENDOR_SPECIFIERS,
|
|
15
|
-
RouteClientBuilder
|
|
11
|
+
RouteClientBuilder
|
|
12
|
+
} from "./index-w44cy4vs.js";
|
|
13
|
+
import {
|
|
14
|
+
SOURCE_EXTS
|
|
15
|
+
} from "./index-5nd1wtkv.js";
|
|
16
|
+
import {
|
|
16
17
|
CsrEntryFiles,
|
|
17
|
-
|
|
18
|
+
BarrelAnalyzer,
|
|
19
|
+
createTsconfigPackageResolver,
|
|
20
|
+
rewriteBarrelImports,
|
|
18
21
|
PagesBundleBuilder
|
|
19
|
-
} from "./index-
|
|
22
|
+
} from "./index-192ha4xh.js";
|
|
20
23
|
import {
|
|
21
|
-
|
|
22
|
-
} from "./index-
|
|
24
|
+
CssBlockParser
|
|
25
|
+
} from "./index-62md4dwy.js";
|
|
23
26
|
var __require = import.meta.require;
|
|
24
27
|
|
|
25
28
|
// pkgs/@akanjs/devkit/frontendBuild/allRoutesBuilder.ts
|
|
@@ -127,233 +130,8 @@ class AllRoutesBuilder {
|
|
|
127
130
|
this.#knownSet.add(abs);
|
|
128
131
|
}
|
|
129
132
|
}
|
|
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
|
-
#cssAssets;
|
|
139
|
-
constructor(app, command = "start", lang = "en", { cssAssets = null } = {}) {
|
|
140
|
-
this.#app = app;
|
|
141
|
-
this.#command = command;
|
|
142
|
-
this.#lang = lang;
|
|
143
|
-
this.#cssAssets = cssAssets;
|
|
144
|
-
}
|
|
145
|
-
async build() {
|
|
146
|
-
const pageKeys = await this.#app.getPageKeys();
|
|
147
|
-
if (pageKeys.length === 0) {
|
|
148
|
-
this.#app.log(`[cli] no route files under ${this.#app.cwdPath}/page \u2014 skipping CSR build`);
|
|
149
|
-
return null;
|
|
150
|
-
}
|
|
151
|
-
const pageEntries = await resolveSsrPageEntriesForApp(this.#app, pageKeys);
|
|
152
|
-
const akanConfig = await this.#app.getConfig();
|
|
153
|
-
const cssAssets = await this.#loadCssAssets();
|
|
154
|
-
const basePaths = [...akanConfig.basePaths];
|
|
155
|
-
const htmlBasePaths = basePaths.length > 0 ? basePaths : [""];
|
|
156
|
-
await rm(this.#outputDir, { recursive: true, force: true });
|
|
157
|
-
await mkdir(this.#generatedDir, { recursive: true });
|
|
158
|
-
const generatedFiles = Object.fromEntries((await Promise.all(htmlBasePaths.map(async (basePath) => [
|
|
159
|
-
this.#createHtmlFile(basePath),
|
|
160
|
-
await this.#createEntryFile(basePath, CsrArtifactBuilder.pageEntriesForBasePath(pageEntries, basePath, basePaths))
|
|
161
|
-
]))).flat());
|
|
162
|
-
const result = await Bun.build({
|
|
163
|
-
...CsrArtifactBuilder.bundleOptions(this.#app, this.#command, akanConfig.optimizeImports),
|
|
164
|
-
entrypoints: htmlBasePaths.map((basePath) => this.#generatedPath(CsrArtifactBuilder.htmlFilename(basePath))),
|
|
165
|
-
files: generatedFiles,
|
|
166
|
-
root: this.#generatedDir,
|
|
167
|
-
outdir: this.#outputDir
|
|
168
|
-
});
|
|
169
|
-
if (!result.success) {
|
|
170
|
-
const logs = result.logs.map((log) => log.message).join(`
|
|
171
|
-
`);
|
|
172
|
-
throw new Error(`[csr-build] failed${logs ? `
|
|
173
|
-
${logs}` : ""}`);
|
|
174
|
-
}
|
|
175
|
-
await this.#inlineCsrArtifacts(cssAssets);
|
|
176
|
-
this.#app.verbose(`[csr-build] output -> ${this.#outputDir}`);
|
|
177
|
-
return { outputDir: this.#outputDir };
|
|
178
|
-
}
|
|
179
|
-
static bundleOptions(app, command, optimizeImports = []) {
|
|
180
|
-
return {
|
|
181
|
-
target: "browser",
|
|
182
|
-
splitting: false,
|
|
183
|
-
minify: true,
|
|
184
|
-
define: bundleDefine(app, command, "csr"),
|
|
185
|
-
optimizeImports,
|
|
186
|
-
plugins: [PagesBundleBuilder.createCssStubPlugin()]
|
|
187
|
-
};
|
|
188
|
-
}
|
|
189
|
-
static pageEntriesForBasePath(pageEntries, basePath, basePaths) {
|
|
190
|
-
return CsrEntryFiles.pageEntriesForBasePath(pageEntries, basePath, basePaths);
|
|
191
|
-
}
|
|
192
|
-
static htmlFilename(basePath) {
|
|
193
|
-
return `${basePath || "index"}.html`;
|
|
194
|
-
}
|
|
195
|
-
static entryFilename(basePath) {
|
|
196
|
-
return CsrEntryFiles.entryFilename(basePath);
|
|
197
|
-
}
|
|
198
|
-
static basePathOfHtml(htmlPath) {
|
|
199
|
-
const name = path2.basename(htmlPath, ".html");
|
|
200
|
-
return name === "index" ? "" : name;
|
|
201
|
-
}
|
|
202
|
-
get #outputDir() {
|
|
203
|
-
return path2.join(this.#command === "build" ? this.#app.dist.cwdPath : this.#app.cwdPath, this.#command === "build" ? "csr" : ".akan/artifact/csr");
|
|
204
|
-
}
|
|
205
|
-
get #generatedDir() {
|
|
206
|
-
return path2.join(this.#app.cwdPath, ".akan/generated/csr");
|
|
207
|
-
}
|
|
208
|
-
get #artifactDir() {
|
|
209
|
-
return path2.join(this.#command === "build" ? this.#app.dist.cwdPath : this.#app.cwdPath, ".akan/artifact");
|
|
210
|
-
}
|
|
211
|
-
#generatedPath(filename) {
|
|
212
|
-
return path2.join(this.#generatedDir, filename);
|
|
213
|
-
}
|
|
214
|
-
async#createEntryFile(basePath, pageEntries) {
|
|
215
|
-
return [
|
|
216
|
-
this.#generatedPath(CsrArtifactBuilder.entryFilename(basePath)),
|
|
217
|
-
`import { bootCsr } from "akanjs/webkit";
|
|
218
|
-
${await PagesEntrySourceGenerator.generateStatic(pageEntries)}
|
|
219
|
-
void bootCsr(pages);
|
|
220
|
-
`
|
|
221
|
-
];
|
|
222
|
-
}
|
|
223
|
-
#createHtmlFile(basePath) {
|
|
224
|
-
return [
|
|
225
|
-
this.#generatedPath(CsrArtifactBuilder.htmlFilename(basePath)),
|
|
226
|
-
`<!doctype html>
|
|
227
|
-
<html lang="${this.#lang}">
|
|
228
|
-
<head>
|
|
229
|
-
<meta charset="utf-8" />
|
|
230
|
-
<title>${this.#app.name}</title>
|
|
231
|
-
<base href="/" />
|
|
232
|
-
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
|
233
|
-
</head>
|
|
234
|
-
<body>
|
|
235
|
-
<div id="root"></div>
|
|
236
|
-
<script type="module" src="./${CsrArtifactBuilder.entryFilename(basePath)}"></script>
|
|
237
|
-
</body>
|
|
238
|
-
</html>
|
|
239
|
-
`
|
|
240
|
-
];
|
|
241
|
-
}
|
|
242
|
-
async#loadCssAssets() {
|
|
243
|
-
if (this.#cssAssets)
|
|
244
|
-
return this.#cssAssets;
|
|
245
|
-
const artifactFile = Bun.file(path2.join(this.#artifactDir, "base-artifact.json"));
|
|
246
|
-
if (!await artifactFile.exists())
|
|
247
|
-
return {};
|
|
248
|
-
const artifact = await artifactFile.json();
|
|
249
|
-
return artifact.cssAssets ?? {};
|
|
250
|
-
}
|
|
251
|
-
async#inlineCsrArtifacts(cssAssets) {
|
|
252
|
-
const jsFiles = new Set;
|
|
253
|
-
for (const htmlPath of await this.#listOutputFiles((filePath) => filePath.endsWith(".html"))) {
|
|
254
|
-
const htmlFile = Bun.file(htmlPath);
|
|
255
|
-
if (!await htmlFile.exists())
|
|
256
|
-
continue;
|
|
257
|
-
const basePath = CsrArtifactBuilder.basePathOfHtml(htmlPath);
|
|
258
|
-
const cssAsset = cssAssets[basePath];
|
|
259
|
-
if (!cssAsset) {
|
|
260
|
-
this.#app.logger.warn(`[csr-build] base-artifact.json has no compiled stylesheet for ${basePath || "root"}; ${path2.basename(htmlPath)} ships without CSS`);
|
|
261
|
-
}
|
|
262
|
-
const inlined = await this.#inlineHtmlAssets(await htmlFile.text(), htmlPath, cssAsset);
|
|
263
|
-
for (const filePath of inlined.jsFiles)
|
|
264
|
-
jsFiles.add(filePath);
|
|
265
|
-
await Bun.write(htmlPath, inlined.html);
|
|
266
|
-
}
|
|
267
|
-
for (const filePath of jsFiles)
|
|
268
|
-
await unlink(filePath).catch(() => {
|
|
269
|
-
return;
|
|
270
|
-
});
|
|
271
|
-
const remainingAssets = await this.#listOutputFiles((filePath) => filePath.endsWith(".js") || filePath.endsWith(".css"));
|
|
272
|
-
if (remainingAssets.length > 0) {
|
|
273
|
-
throw new Error(`[csr-build] expected single-file HTML, but CSR assets remain:
|
|
274
|
-
${remainingAssets.join(`
|
|
275
|
-
`)}`);
|
|
276
|
-
}
|
|
277
|
-
}
|
|
278
|
-
async#inlineHtmlAssets(html, htmlPath, cssAsset) {
|
|
279
|
-
let next = html;
|
|
280
|
-
if (cssAsset) {
|
|
281
|
-
const css = await Bun.file(path2.join(this.#artifactDir, cssAsset.cssRelPath)).text();
|
|
282
|
-
next = CsrArtifactBuilder.injectBeforeHeadEnd(next, CsrArtifactBuilder.createInlineStyle(css));
|
|
283
|
-
}
|
|
284
|
-
const jsFiles = [];
|
|
285
|
-
next = await CsrArtifactBuilder.replaceModuleScriptSrc(next, async (src) => {
|
|
286
|
-
const jsPath = CsrArtifactBuilder.resolveHtmlAssetPath(htmlPath, src);
|
|
287
|
-
jsFiles.push(jsPath);
|
|
288
|
-
return await Bun.file(jsPath).text();
|
|
289
|
-
});
|
|
290
|
-
return { html: next, jsFiles };
|
|
291
|
-
}
|
|
292
|
-
async#listOutputFiles(predicate) {
|
|
293
|
-
const glob = new Bun.Glob("**/*");
|
|
294
|
-
const files = [];
|
|
295
|
-
for await (const filePath of glob.scan({ cwd: this.#outputDir, absolute: true })) {
|
|
296
|
-
if (predicate(filePath))
|
|
297
|
-
files.push(filePath);
|
|
298
|
-
}
|
|
299
|
-
return files.sort();
|
|
300
|
-
}
|
|
301
|
-
static injectBeforeHeadEnd(html, snippet) {
|
|
302
|
-
const scannable = CsrArtifactBuilder.blankEmbeddedContent(html);
|
|
303
|
-
const headEnd = scannable.search(/<\/head\s*>/i);
|
|
304
|
-
if (headEnd !== -1)
|
|
305
|
-
return `${html.slice(0, headEnd)}${snippet}
|
|
306
|
-
${html.slice(headEnd)}`;
|
|
307
|
-
const bodyStart = scannable.search(/<body(?:\s|>)/i);
|
|
308
|
-
if (bodyStart !== -1)
|
|
309
|
-
return `${html.slice(0, bodyStart)}${snippet}
|
|
310
|
-
${html.slice(bodyStart)}`;
|
|
311
|
-
return `${html}
|
|
312
|
-
${snippet}`;
|
|
313
|
-
}
|
|
314
|
-
static blankEmbeddedContent(html) {
|
|
315
|
-
return html.replace(/<script\b[^>]*>[\s\S]*?<\/script\s*>|<style\b[^>]*>[\s\S]*?<\/style\s*>|<!--[\s\S]*?-->/gi, (match) => " ".repeat(match.length));
|
|
316
|
-
}
|
|
317
|
-
static createInlineStyle(css) {
|
|
318
|
-
return `<style data-akan-css="active">
|
|
319
|
-
${css.replace(/<\/style/gi, "<\\/style")}
|
|
320
|
-
</style>`;
|
|
321
|
-
}
|
|
322
|
-
static async replaceModuleScriptSrc(html, loadScript) {
|
|
323
|
-
const scriptRe = /<script\b(?=[^>]*\btype=["']module["'])(?=[^>]*\bsrc=["']([^"']+)["'])[^>]*>\s*<\/script>/gi;
|
|
324
|
-
let result = "";
|
|
325
|
-
let lastIndex = 0;
|
|
326
|
-
let matched = false;
|
|
327
|
-
for (const match of html.matchAll(scriptRe)) {
|
|
328
|
-
const full = match[0];
|
|
329
|
-
const src = match[1];
|
|
330
|
-
if (match.index === undefined || !src)
|
|
331
|
-
continue;
|
|
332
|
-
matched = true;
|
|
333
|
-
result += html.slice(lastIndex, match.index);
|
|
334
|
-
result += `<script type="module">
|
|
335
|
-
${CsrArtifactBuilder.escapeInlineScript(await loadScript(src))}
|
|
336
|
-
</script>`;
|
|
337
|
-
lastIndex = match.index + full.length;
|
|
338
|
-
}
|
|
339
|
-
if (!matched)
|
|
340
|
-
return html;
|
|
341
|
-
return result + html.slice(lastIndex);
|
|
342
|
-
}
|
|
343
|
-
static escapeInlineScript(source) {
|
|
344
|
-
return source.replace(/<\/script/gi, "<\\/script");
|
|
345
|
-
}
|
|
346
|
-
static resolveHtmlAssetPath(htmlPath, src) {
|
|
347
|
-
if (/^[a-z][a-z0-9+.-]*:/i.test(src) || src.startsWith("//")) {
|
|
348
|
-
throw new Error(`[csr-build] cannot inline external script: ${src}`);
|
|
349
|
-
}
|
|
350
|
-
const normalized = src.startsWith("/") ? src.slice(1) : src;
|
|
351
|
-
return path2.resolve(path2.dirname(htmlPath), normalized);
|
|
352
|
-
}
|
|
353
|
-
}
|
|
354
|
-
|
|
355
133
|
// pkgs/@akanjs/devkit/frontendBuild/cssCompiler.ts
|
|
356
|
-
import
|
|
134
|
+
import path3 from "path";
|
|
357
135
|
import { Logger } from "akanjs/common";
|
|
358
136
|
import { compile } from "tailwindcss";
|
|
359
137
|
|
|
@@ -424,7 +202,7 @@ class CssCandidateCache {
|
|
|
424
202
|
}
|
|
425
203
|
|
|
426
204
|
// pkgs/@akanjs/devkit/frontendBuild/cssImportResolver.ts
|
|
427
|
-
import
|
|
205
|
+
import path2 from "path";
|
|
428
206
|
var CSS_IMPORT_EXTS = ["", ".css", "/styles.css", "/index.css"];
|
|
429
207
|
|
|
430
208
|
class CssImportResolver {
|
|
@@ -467,7 +245,7 @@ class CssImportResolver {
|
|
|
467
245
|
const exact = this.#paths[id];
|
|
468
246
|
if (exact) {
|
|
469
247
|
for (const repl of exact) {
|
|
470
|
-
const resolved = await this.#firstExisting(
|
|
248
|
+
const resolved = await this.#firstExisting(path2.resolve(this.#workspaceRoot, repl));
|
|
471
249
|
if (resolved)
|
|
472
250
|
return resolved;
|
|
473
251
|
}
|
|
@@ -478,7 +256,7 @@ class CssImportResolver {
|
|
|
478
256
|
const suffix = id.slice(prefix.length);
|
|
479
257
|
for (const repl of replacements) {
|
|
480
258
|
const replPath = repl.endsWith("/*") ? repl.slice(0, -1) : repl;
|
|
481
|
-
const resolved = await this.#firstExisting(
|
|
259
|
+
const resolved = await this.#firstExisting(path2.resolve(this.#workspaceRoot, replPath + suffix));
|
|
482
260
|
if (resolved)
|
|
483
261
|
return resolved;
|
|
484
262
|
}
|
|
@@ -508,34 +286,34 @@ class CssImportResolver {
|
|
|
508
286
|
try {
|
|
509
287
|
if (!await Bun.file(pkgPath).exists())
|
|
510
288
|
return null;
|
|
511
|
-
const pkgDir =
|
|
289
|
+
const pkgDir = path2.dirname(pkgPath);
|
|
512
290
|
const pkg = await Bun.file(pkgPath).json();
|
|
513
291
|
const subpath = id === pkgName ? "." : `.${id.slice(pkgName.length)}`;
|
|
514
292
|
const exportValue = pkg.exports?.[subpath];
|
|
515
293
|
const exportedEntry = typeof exportValue === "string" ? exportValue : exportValue?.style || exportValue?.import || exportValue?.default;
|
|
516
294
|
if (exportedEntry)
|
|
517
|
-
return await this.#firstExisting(
|
|
295
|
+
return await this.#firstExisting(path2.resolve(pkgDir, exportedEntry));
|
|
518
296
|
if (subpath !== ".")
|
|
519
|
-
return await this.#firstExisting(
|
|
520
|
-
return await this.#firstExisting(
|
|
297
|
+
return await this.#firstExisting(path2.resolve(pkgDir, subpath));
|
|
298
|
+
return await this.#firstExisting(path2.resolve(pkgDir, pkg.exports?.["."]?.style || pkg.style || "index.css"));
|
|
521
299
|
} catch {
|
|
522
300
|
return null;
|
|
523
301
|
}
|
|
524
302
|
}
|
|
525
303
|
#resolutionBases(fromBase) {
|
|
526
|
-
return [fromBase, this.#workspaceRoot,
|
|
304
|
+
return [fromBase, this.#workspaceRoot, path2.dirname(Bun.main), path2.resolve(path2.dirname(Bun.main), "../..")];
|
|
527
305
|
}
|
|
528
306
|
#packageJsonCandidates(pkgName) {
|
|
529
307
|
return [
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
308
|
+
path2.join(this.#workspaceRoot, "pkgs", pkgName, "package.json"),
|
|
309
|
+
path2.join(this.#workspaceRoot, "node_modules", pkgName, "package.json"),
|
|
310
|
+
path2.join(path2.dirname(Bun.main), "node_modules", pkgName, "package.json"),
|
|
311
|
+
path2.join(path2.dirname(Bun.main), "../../", pkgName, "package.json")
|
|
534
312
|
];
|
|
535
313
|
}
|
|
536
314
|
async#firstExisting(basePath) {
|
|
537
315
|
for (const suffix of CSS_IMPORT_EXTS) {
|
|
538
|
-
const candidate =
|
|
316
|
+
const candidate = path2.normalize(`${basePath}${suffix}`);
|
|
539
317
|
if (await Bun.file(candidate).exists())
|
|
540
318
|
return candidate;
|
|
541
319
|
}
|
|
@@ -548,7 +326,7 @@ class CssImportResolver {
|
|
|
548
326
|
return parts[0] ?? null;
|
|
549
327
|
}
|
|
550
328
|
static isCssFile(filePath) {
|
|
551
|
-
return
|
|
329
|
+
return path2.extname(filePath) === ".css";
|
|
552
330
|
}
|
|
553
331
|
}
|
|
554
332
|
|
|
@@ -566,7 +344,7 @@ class CssCompiler {
|
|
|
566
344
|
this.#app = app;
|
|
567
345
|
}
|
|
568
346
|
get #candidateCachePath() {
|
|
569
|
-
return
|
|
347
|
+
return path3.join(this.#app.cwdPath, ".akan/cache/cssCandidates.json");
|
|
570
348
|
}
|
|
571
349
|
#cssText = null;
|
|
572
350
|
#cssTextByBasePath = null;
|
|
@@ -596,7 +374,7 @@ class CssCompiler {
|
|
|
596
374
|
return filePath;
|
|
597
375
|
}
|
|
598
376
|
for (const ext of SOURCE_EXTS) {
|
|
599
|
-
const filePath =
|
|
377
|
+
const filePath = path3.join(absPathNoExt, `index${ext}`);
|
|
600
378
|
if (await this.#fileExists(filePath))
|
|
601
379
|
return filePath;
|
|
602
380
|
}
|
|
@@ -645,14 +423,14 @@ class CssCompiler {
|
|
|
645
423
|
return this.#cssTextByBasePath;
|
|
646
424
|
}
|
|
647
425
|
async#warnUnreachableStylesheets() {
|
|
648
|
-
const pageDir =
|
|
426
|
+
const pageDir = path3.join(this.#app.cwdPath, "page");
|
|
649
427
|
const glob = new Bun.Glob("**/*.css");
|
|
650
428
|
for await (const cssPath of glob.scan({ cwd: pageDir, absolute: true })) {
|
|
651
|
-
if (cssPath.includes(`${
|
|
429
|
+
if (cssPath.includes(`${path3.sep}(libs)${path3.sep}`))
|
|
652
430
|
continue;
|
|
653
431
|
if (this.#discoveredCssPaths.has(cssPath))
|
|
654
432
|
continue;
|
|
655
|
-
this.#logger.warn(`css ${
|
|
433
|
+
this.#logger.warn(`css ${path3.relative(this.#app.cwdPath, cssPath)} is imported by no route and never compiled`);
|
|
656
434
|
}
|
|
657
435
|
}
|
|
658
436
|
async discoverCss({ refresh } = {}) {
|
|
@@ -664,7 +442,7 @@ class CssCompiler {
|
|
|
664
442
|
pageKeys
|
|
665
443
|
} = {}) {
|
|
666
444
|
pageKeys ??= await this.#app.getPageKeys({ refresh });
|
|
667
|
-
const seeds = pageKeys.map((key) =>
|
|
445
|
+
const seeds = pageKeys.map((key) => path3.resolve(this.#app.cwdPath, "page", key));
|
|
668
446
|
const cssFiles = new Set;
|
|
669
447
|
const sourceFiles = new Set;
|
|
670
448
|
const queue = [...seeds];
|
|
@@ -696,7 +474,7 @@ class CssCompiler {
|
|
|
696
474
|
} catch {
|
|
697
475
|
continue;
|
|
698
476
|
}
|
|
699
|
-
const importerDir =
|
|
477
|
+
const importerDir = path3.dirname(filePath);
|
|
700
478
|
for (const imp of imports) {
|
|
701
479
|
const spec = imp.path;
|
|
702
480
|
if (!spec)
|
|
@@ -721,18 +499,18 @@ class CssCompiler {
|
|
|
721
499
|
return { cssPaths, sourcePaths: [...sourceFiles] };
|
|
722
500
|
}
|
|
723
501
|
async#libTokenStylesheets(sourceFiles) {
|
|
724
|
-
const libsRoot =
|
|
502
|
+
const libsRoot = path3.join(this.#app.workspace.workspaceRoot, "libs");
|
|
725
503
|
const libNames = new Set;
|
|
726
504
|
for (const filePath of sourceFiles) {
|
|
727
|
-
const relPath =
|
|
728
|
-
if (relPath.startsWith("..") ||
|
|
505
|
+
const relPath = path3.relative(libsRoot, filePath);
|
|
506
|
+
if (relPath.startsWith("..") || path3.isAbsolute(relPath))
|
|
729
507
|
continue;
|
|
730
|
-
const [libName] = relPath.split(
|
|
508
|
+
const [libName] = relPath.split(path3.sep);
|
|
731
509
|
if (libName)
|
|
732
510
|
libNames.add(libName);
|
|
733
511
|
}
|
|
734
512
|
const tokenPaths = await Promise.all([...libNames].sort().map(async (libName) => {
|
|
735
|
-
const tokensPath =
|
|
513
|
+
const tokensPath = path3.join(libsRoot, libName, "ui/tokens.css");
|
|
736
514
|
return await this.#fileExists(tokensPath) ? tokensPath : null;
|
|
737
515
|
}));
|
|
738
516
|
return tokenPaths.filter((tokensPath) => !!tokensPath);
|
|
@@ -747,7 +525,7 @@ class CssCompiler {
|
|
|
747
525
|
const compileStarted = Date.now();
|
|
748
526
|
const compilers = await Promise.all(cssPaths.map(async (cssPath) => {
|
|
749
527
|
const css = await Bun.file(cssPath).text();
|
|
750
|
-
const base =
|
|
528
|
+
const base = path3.dirname(cssPath);
|
|
751
529
|
const imported = new Map;
|
|
752
530
|
const compiler = await compile(css, {
|
|
753
531
|
base,
|
|
@@ -787,11 +565,11 @@ class CssCompiler {
|
|
|
787
565
|
const content = await Bun.file(p).text();
|
|
788
566
|
imported?.set(p, content);
|
|
789
567
|
this.#logger.verbose(`css import "${id}" from ${fromBase} -> ${p} (${content.length} bytes)`);
|
|
790
|
-
return { path: p, base:
|
|
568
|
+
return { path: p, base: path3.dirname(p), content };
|
|
791
569
|
}
|
|
792
570
|
async#resolveCssImport(id, fromBase) {
|
|
793
571
|
if (id.startsWith(".") || id.startsWith("/")) {
|
|
794
|
-
const filePath =
|
|
572
|
+
const filePath = path3.resolve(fromBase, id);
|
|
795
573
|
if (await this.#fileExists(filePath))
|
|
796
574
|
return filePath;
|
|
797
575
|
throw new Error(`[css] failed to resolve stylesheet import "${id}" from ${fromBase} (no file at ${filePath})`);
|
|
@@ -805,7 +583,7 @@ class CssCompiler {
|
|
|
805
583
|
async#loadModule(id, fromBase) {
|
|
806
584
|
const p = __require.resolve(id, { paths: [fromBase] });
|
|
807
585
|
const mod = await import(p);
|
|
808
|
-
return { path: p, base:
|
|
586
|
+
return { path: p, base: path3.dirname(p), module: mod.default ?? mod };
|
|
809
587
|
}
|
|
810
588
|
#resolveSourceImport(id, fromBase, resolvePackage) {
|
|
811
589
|
const cacheKey = `${fromBase}\x00${id}`;
|
|
@@ -818,7 +596,7 @@ class CssCompiler {
|
|
|
818
596
|
}
|
|
819
597
|
async#resolveSourceImportUncached(id, fromBase, resolvePackage) {
|
|
820
598
|
if (id.startsWith(".") || id.startsWith("/")) {
|
|
821
|
-
const abs = id.startsWith("/") ? id :
|
|
599
|
+
const abs = id.startsWith("/") ? id : path3.resolve(fromBase, id);
|
|
822
600
|
return this.#resolveSourceFileCandidate(abs);
|
|
823
601
|
}
|
|
824
602
|
const pkg = await resolvePackage(id);
|
|
@@ -869,22 +647,28 @@ function resolveSourceWithRequire(id, fromBase) {
|
|
|
869
647
|
}
|
|
870
648
|
}
|
|
871
649
|
function isSourceFile(filePath) {
|
|
872
|
-
return SOURCE_EXTS.has(
|
|
650
|
+
return SOURCE_EXTS.has(path3.extname(filePath));
|
|
873
651
|
}
|
|
874
652
|
function isIgnoredNodeModuleSource(filePath) {
|
|
875
653
|
return NODE_MODULES_RE.test(filePath) && !AKANJS_NODE_MODULE_RE.test(filePath);
|
|
876
654
|
}
|
|
877
655
|
function declaredCustomProperties(css) {
|
|
878
|
-
const
|
|
879
|
-
|
|
656
|
+
const names = new Set;
|
|
657
|
+
CssBlockParser.walk(CssBlockParser.parse(css), true, (node) => {
|
|
658
|
+
if (node.kind === "block")
|
|
659
|
+
return node.atRule === "@theme" ? null : true;
|
|
660
|
+
if (node.property.startsWith("--"))
|
|
661
|
+
names.add(node.property);
|
|
662
|
+
return null;
|
|
663
|
+
});
|
|
664
|
+
return [...names];
|
|
880
665
|
}
|
|
881
666
|
function getPageKeyBasePath(pageKey, basePaths) {
|
|
882
667
|
return CsrEntryFiles.basePathOfPageKey(pageKey, basePaths);
|
|
883
668
|
}
|
|
884
|
-
|
|
885
669
|
// pkgs/@akanjs/devkit/frontendBuild/fontOptimizer.ts
|
|
886
|
-
import { mkdir
|
|
887
|
-
import
|
|
670
|
+
import { mkdir, stat as stat2 } from "fs/promises";
|
|
671
|
+
import path4 from "path";
|
|
888
672
|
import ts from "typescript";
|
|
889
673
|
var FONT_URL_PREFIX = "/_akan/fonts";
|
|
890
674
|
var DEFAULT_FONT_SUBSETS = ["latin"];
|
|
@@ -901,7 +685,7 @@ class FontOptimizer {
|
|
|
901
685
|
constructor(app, command = "start") {
|
|
902
686
|
this.#app = app;
|
|
903
687
|
this.#command = command;
|
|
904
|
-
this.#artifactRoot =
|
|
688
|
+
this.#artifactRoot = path4.join(command === "build" ? app.dist.cwdPath : app.cwdPath, ".akan/artifact");
|
|
905
689
|
}
|
|
906
690
|
async optimize() {
|
|
907
691
|
const fonts = await this.discoverFonts();
|
|
@@ -926,7 +710,7 @@ class FontOptimizer {
|
|
|
926
710
|
return result;
|
|
927
711
|
}
|
|
928
712
|
get #cachePath() {
|
|
929
|
-
return
|
|
713
|
+
return path4.join(this.#artifactRoot, "fontCache.json");
|
|
930
714
|
}
|
|
931
715
|
async#buildCacheKey(fonts) {
|
|
932
716
|
const sources = [];
|
|
@@ -941,7 +725,7 @@ class FontOptimizer {
|
|
|
941
725
|
sources.push({ optimizedSrc: face.optimizedSrc, ...stamp });
|
|
942
726
|
}
|
|
943
727
|
for (const filePath of font.subsetFiles ?? []) {
|
|
944
|
-
const abs =
|
|
728
|
+
const abs = path4.isAbsolute(filePath) ? filePath : path4.join(this.#app.cwdPath, filePath);
|
|
945
729
|
const stamp = await this.#fileStamp(abs);
|
|
946
730
|
if (!stamp)
|
|
947
731
|
return null;
|
|
@@ -967,7 +751,7 @@ class FontOptimizer {
|
|
|
967
751
|
const cache = await Bun.file(this.#cachePath).json().catch(() => null);
|
|
968
752
|
if (cache?.version !== FontOptimizer.#cacheVersion || cache.key !== key)
|
|
969
753
|
return null;
|
|
970
|
-
const files = cache.files.map((relativePath) =>
|
|
754
|
+
const files = cache.files.map((relativePath) => path4.join(this.#artifactRoot, relativePath));
|
|
971
755
|
for (const filePath of files) {
|
|
972
756
|
if (!await Bun.file(filePath).exists())
|
|
973
757
|
return null;
|
|
@@ -979,7 +763,7 @@ class FontOptimizer {
|
|
|
979
763
|
version: FontOptimizer.#cacheVersion,
|
|
980
764
|
key,
|
|
981
765
|
css: result.css,
|
|
982
|
-
files: result.files.map((filePath) =>
|
|
766
|
+
files: result.files.map((filePath) => path4.relative(this.#artifactRoot, filePath))
|
|
983
767
|
};
|
|
984
768
|
await Bun.write(this.#cachePath, JSON.stringify(cache));
|
|
985
769
|
}
|
|
@@ -987,7 +771,7 @@ class FontOptimizer {
|
|
|
987
771
|
const pageKeys = await this.#app.getPageKeys();
|
|
988
772
|
const fonts = [];
|
|
989
773
|
await Promise.all(pageKeys.map(async (key) => {
|
|
990
|
-
const filePath =
|
|
774
|
+
const filePath = path4.resolve(this.#app.cwdPath, "page", key);
|
|
991
775
|
const file = Bun.file(filePath);
|
|
992
776
|
if (!await file.exists())
|
|
993
777
|
return;
|
|
@@ -1006,8 +790,8 @@ class FontOptimizer {
|
|
|
1006
790
|
this.#app.logger.warn(`[font] source not found: ${face.src}`);
|
|
1007
791
|
continue;
|
|
1008
792
|
}
|
|
1009
|
-
const outputPath =
|
|
1010
|
-
await
|
|
793
|
+
const outputPath = path4.join(this.#artifactRoot, face.optimizedSrc.replace(/^\/_akan\//, ""));
|
|
794
|
+
await mkdir(path4.dirname(outputPath), { recursive: true });
|
|
1011
795
|
const sourceBuffer = Buffer.from(await Bun.file(sourcePath).arrayBuffer());
|
|
1012
796
|
const outputBuffer = await this.#buildFontBuffer(font, sourceBuffer, sourcePath);
|
|
1013
797
|
await Bun.write(outputPath, outputBuffer);
|
|
@@ -1029,7 +813,7 @@ class FontOptimizer {
|
|
|
1029
813
|
const entries = Array.isArray(value) ? value : [];
|
|
1030
814
|
const fonts = entries.filter((entry) => this.#isReadableFont(entry));
|
|
1031
815
|
if (fonts.length !== entries.length || !Array.isArray(value))
|
|
1032
|
-
this.#app.logger.warn(`[font] ${
|
|
816
|
+
this.#app.logger.warn(`[font] ${path4.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`);
|
|
1033
817
|
return fonts.map((font) => ({ ...font, subsets: font.subsets ?? [...DEFAULT_FONT_SUBSETS] }));
|
|
1034
818
|
}
|
|
1035
819
|
#isReadableFont(value) {
|
|
@@ -1187,8 +971,8 @@ class FontOptimizer {
|
|
|
1187
971
|
return null;
|
|
1188
972
|
const rel = src.replace(/^\//, "");
|
|
1189
973
|
const candidates = [
|
|
1190
|
-
this.#command === "build" ?
|
|
1191
|
-
|
|
974
|
+
this.#command === "build" ? path4.join(this.#app.dist.cwdPath, "public", rel) : null,
|
|
975
|
+
path4.join(this.#app.cwdPath, "public", rel),
|
|
1192
976
|
this.#resolveWorkspacePublicPath(rel)
|
|
1193
977
|
].filter(Boolean);
|
|
1194
978
|
for (const candidate of candidates) {
|
|
@@ -1220,7 +1004,7 @@ class FontOptimizer {
|
|
|
1220
1004
|
return "woff2";
|
|
1221
1005
|
if (signature === "OTTO")
|
|
1222
1006
|
return "otf";
|
|
1223
|
-
const ext =
|
|
1007
|
+
const ext = path4.extname(sourcePath).slice(1).toLowerCase();
|
|
1224
1008
|
if (ext === "otf" || ext === "woff" || ext === "woff2")
|
|
1225
1009
|
return ext;
|
|
1226
1010
|
return "ttf";
|
|
@@ -1229,7 +1013,7 @@ class FontOptimizer {
|
|
|
1229
1013
|
const [root, dep, ...rest] = rel.split("/");
|
|
1230
1014
|
if (root !== "libs" || !dep || rest.length === 0)
|
|
1231
1015
|
return null;
|
|
1232
|
-
return
|
|
1016
|
+
return path4.join(this.#app.workspace.workspaceRoot, "libs", dep, "public", ...rest);
|
|
1233
1017
|
}
|
|
1234
1018
|
async#getSubsetText(font) {
|
|
1235
1019
|
const parts = new Set;
|
|
@@ -1239,7 +1023,7 @@ class FontOptimizer {
|
|
|
1239
1023
|
if (font.subsetText)
|
|
1240
1024
|
parts.add(font.subsetText);
|
|
1241
1025
|
for (const filePath of font.subsetFiles ?? []) {
|
|
1242
|
-
const abs =
|
|
1026
|
+
const abs = path4.isAbsolute(filePath) ? filePath : path4.join(this.#app.cwdPath, filePath);
|
|
1243
1027
|
const file = Bun.file(abs);
|
|
1244
1028
|
if (await file.exists())
|
|
1245
1029
|
parts.add(await file.text());
|
|
@@ -1259,7 +1043,7 @@ class FontOptimizer {
|
|
|
1259
1043
|
}
|
|
1260
1044
|
async#collectAutoSubsetText() {
|
|
1261
1045
|
const libPageRoots = (await this.#app.getPageRoots()).filter((root) => root.keyPrefix).map((root) => root.dir);
|
|
1262
|
-
const roots = [...["page", "ui", "lib"].map((dir) =>
|
|
1046
|
+
const roots = [...["page", "ui", "lib"].map((dir) => path4.join(this.#app.cwdPath, dir)), ...libPageRoots];
|
|
1263
1047
|
const glob = new Bun.Glob("**/*.{ts,tsx,js,jsx,html,md}");
|
|
1264
1048
|
const parts = [];
|
|
1265
1049
|
for (const root of [...new Set(roots)].sort()) {
|
|
@@ -1269,7 +1053,7 @@ class FontOptimizer {
|
|
|
1269
1053
|
const filePaths = [];
|
|
1270
1054
|
for await (const filePath of glob.scan({ cwd: root, absolute: true }))
|
|
1271
1055
|
filePaths.push(filePath);
|
|
1272
|
-
for (const filePath of filePaths.sort())
|
|
1056
|
+
for (const filePath of filePaths.sort((a, b) => a < b ? -1 : a > b ? 1 : 0))
|
|
1273
1057
|
parts.push(await Bun.file(filePath).text());
|
|
1274
1058
|
}
|
|
1275
1059
|
return parts.join("");
|
|
@@ -1370,10 +1154,9 @@ ${declarations.map(([prop, value]) => ` ${prop}: ${value};`).join(`
|
|
|
1370
1154
|
return JSON.stringify(value);
|
|
1371
1155
|
}
|
|
1372
1156
|
}
|
|
1373
|
-
|
|
1374
1157
|
// pkgs/@akanjs/devkit/frontendBuild/fontPruner.ts
|
|
1375
|
-
import { rm
|
|
1376
|
-
import
|
|
1158
|
+
import { rm, rmdir, stat as stat3 } from "fs/promises";
|
|
1159
|
+
import path5 from "path";
|
|
1377
1160
|
import { isFontOptimizationEnabled } from "akanjs/client";
|
|
1378
1161
|
var FONT_GLOB = "**/*.{woff2,woff,ttf,otf,ttc,eot}";
|
|
1379
1162
|
var REFERRER_GLOB = "**/*.{css,js,mjs,cjs,json,html,htm,svg,xml,webmanifest}";
|
|
@@ -1386,8 +1169,8 @@ class FontPruner {
|
|
|
1386
1169
|
constructor(app, { keepFonts = [] } = {}) {
|
|
1387
1170
|
this.#app = app;
|
|
1388
1171
|
this.#keepGlobs = keepFonts;
|
|
1389
|
-
this.#publicRoot =
|
|
1390
|
-
this.#artifactRoot =
|
|
1172
|
+
this.#publicRoot = path5.join(app.dist.cwdPath, "public");
|
|
1173
|
+
this.#artifactRoot = path5.join(app.dist.cwdPath, ".akan/artifact");
|
|
1391
1174
|
}
|
|
1392
1175
|
async prune() {
|
|
1393
1176
|
const empty = { removed: [], kept: [], freedBytes: 0 };
|
|
@@ -1402,7 +1185,7 @@ class FontPruner {
|
|
|
1402
1185
|
for (const font of fonts) {
|
|
1403
1186
|
const target = isFontOptimizationEnabled(font) ? optimizedSrcs : unoptimizedSrcs;
|
|
1404
1187
|
for (const fontPath of font.paths)
|
|
1405
|
-
target.add(
|
|
1188
|
+
target.add(path5.posix.basename(fontPath.src));
|
|
1406
1189
|
}
|
|
1407
1190
|
const result = { removed: [], kept: [], freedBytes: 0 };
|
|
1408
1191
|
const undecided = [];
|
|
@@ -1440,10 +1223,10 @@ class FontPruner {
|
|
|
1440
1223
|
async#collectCandidates() {
|
|
1441
1224
|
const candidates = [];
|
|
1442
1225
|
for await (const rel of new Bun.Glob(FONT_GLOB).scan({ cwd: this.#publicRoot, dot: false })) {
|
|
1443
|
-
const entry = await stat3(
|
|
1226
|
+
const entry = await stat3(path5.join(this.#publicRoot, rel)).catch(() => null);
|
|
1444
1227
|
if (!entry?.isFile())
|
|
1445
1228
|
continue;
|
|
1446
|
-
candidates.push({ rel: rel.split(
|
|
1229
|
+
candidates.push({ rel: rel.split(path5.sep).join("/"), basename: path5.basename(rel), bytes: entry.size });
|
|
1447
1230
|
}
|
|
1448
1231
|
return candidates;
|
|
1449
1232
|
}
|
|
@@ -1453,11 +1236,11 @@ class FontPruner {
|
|
|
1453
1236
|
const bundled = candidates.filter((candidate) => !optimizedSrcs.has(candidate.basename));
|
|
1454
1237
|
const roots = [
|
|
1455
1238
|
{ dir: this.#publicRoot, label: "public", needles: authoritative },
|
|
1456
|
-
{ dir:
|
|
1457
|
-
{ dir:
|
|
1458
|
-
{ dir:
|
|
1459
|
-
{ dir:
|
|
1460
|
-
{ dir:
|
|
1239
|
+
{ dir: path5.join(this.#artifactRoot, "styles"), label: ".akan/artifact/styles", needles: authoritative },
|
|
1240
|
+
{ dir: path5.join(this.#artifactRoot, "client"), label: ".akan/artifact/client", needles: bundled },
|
|
1241
|
+
{ dir: path5.join(this.#artifactRoot, "client-ssr"), label: ".akan/artifact/client-ssr", needles: bundled },
|
|
1242
|
+
{ dir: path5.join(this.#artifactRoot, "server"), label: ".akan/artifact/server", needles: bundled },
|
|
1243
|
+
{ dir: path5.join(this.#app.dist.cwdPath, "csr"), label: "csr", needles: bundled }
|
|
1461
1244
|
];
|
|
1462
1245
|
for (const root of roots) {
|
|
1463
1246
|
const pending = root.needles.filter((candidate) => !referrers.has(candidate.rel));
|
|
@@ -1476,20 +1259,20 @@ class FontPruner {
|
|
|
1476
1259
|
const remaining = needles.filter((needle) => !referrers.has(needle.rel));
|
|
1477
1260
|
if (!remaining.length)
|
|
1478
1261
|
return;
|
|
1479
|
-
const text = await Bun.file(
|
|
1262
|
+
const text = await Bun.file(path5.join(root.dir, rel)).text().catch(() => "");
|
|
1480
1263
|
if (!text)
|
|
1481
1264
|
continue;
|
|
1482
1265
|
for (const needle of remaining) {
|
|
1483
1266
|
if (needle.forms.some((form) => text.includes(form)))
|
|
1484
|
-
referrers.set(needle.rel, `${root.label}/${rel.split(
|
|
1267
|
+
referrers.set(needle.rel, `${root.label}/${rel.split(path5.sep).join("/")}`);
|
|
1485
1268
|
}
|
|
1486
1269
|
}
|
|
1487
1270
|
}
|
|
1488
1271
|
async#remove(files) {
|
|
1489
|
-
await Promise.all(files.map((file) =>
|
|
1490
|
-
const dirs = [...new Set(files.map((file) =>
|
|
1272
|
+
await Promise.all(files.map((file) => rm(path5.join(this.#publicRoot, file), { force: true })));
|
|
1273
|
+
const dirs = [...new Set(files.map((file) => path5.posix.dirname(file)))].filter((dir) => dir !== "." && dir !== "/").sort((a, b) => b.length - a.length);
|
|
1491
1274
|
for (const dir of dirs)
|
|
1492
|
-
await rmdir(
|
|
1275
|
+
await rmdir(path5.join(this.#publicRoot, dir)).catch(() => {
|
|
1493
1276
|
return;
|
|
1494
1277
|
});
|
|
1495
1278
|
}
|
|
@@ -1534,10 +1317,9 @@ class FontPruner {
|
|
|
1534
1317
|
return await stat3(dir).then((entry) => entry.isDirectory(), () => false);
|
|
1535
1318
|
}
|
|
1536
1319
|
}
|
|
1537
|
-
|
|
1538
1320
|
// pkgs/@akanjs/devkit/frontendBuild/precompressArtifacts.ts
|
|
1539
1321
|
import fs from "fs";
|
|
1540
|
-
import
|
|
1322
|
+
import path6 from "path";
|
|
1541
1323
|
import zlib from "zlib";
|
|
1542
1324
|
var COMPRESSIBLE_EXTS = new Set([".css", ".html", ".js", ".json", ".svg"]);
|
|
1543
1325
|
var MIN_COMPRESS_BYTES = 1024;
|
|
@@ -1547,8 +1329,8 @@ var GZIP_LEVEL = 9;
|
|
|
1547
1329
|
var { formatBytes } = FontPruner;
|
|
1548
1330
|
async function precompressArtifacts(app) {
|
|
1549
1331
|
const roots = [
|
|
1550
|
-
|
|
1551
|
-
|
|
1332
|
+
path6.join(app.dist.cwdPath, ".akan/artifact/client"),
|
|
1333
|
+
path6.join(app.dist.cwdPath, ".akan/artifact/styles")
|
|
1552
1334
|
];
|
|
1553
1335
|
const result = { files: 0, inputBytes: 0, outputBytes: 0, brotliBytes: 0 };
|
|
1554
1336
|
await Promise.all(roots.map((root) => precompressRoot(root, result)));
|
|
@@ -1567,7 +1349,7 @@ async function precompressRoot(root, result) {
|
|
|
1567
1349
|
const bytes = await Bun.file(filePath).bytes();
|
|
1568
1350
|
const buffer = bytes.buffer.slice(bytes.byteOffset, bytes.byteOffset + bytes.byteLength);
|
|
1569
1351
|
const gz = Bun.gzipSync(buffer, { level: GZIP_LEVEL });
|
|
1570
|
-
const br = brotliCompress(buffer,
|
|
1352
|
+
const br = brotliCompress(buffer, path6.extname(filePath).toLowerCase());
|
|
1571
1353
|
await Promise.all([Bun.write(`${filePath}.gz`, gz), Bun.write(`${filePath}.br`, br)]);
|
|
1572
1354
|
result.files += 1;
|
|
1573
1355
|
result.inputBytes += bytes.byteLength;
|
|
@@ -1587,16 +1369,15 @@ function brotliCompress(buffer, ext) {
|
|
|
1587
1369
|
async function shouldPrecompress(filePath) {
|
|
1588
1370
|
if (filePath.endsWith(".gz") || filePath.endsWith(".br"))
|
|
1589
1371
|
return false;
|
|
1590
|
-
if (!COMPRESSIBLE_EXTS.has(
|
|
1372
|
+
if (!COMPRESSIBLE_EXTS.has(path6.extname(filePath).toLowerCase()))
|
|
1591
1373
|
return false;
|
|
1592
1374
|
const file = Bun.file(filePath);
|
|
1593
1375
|
if (!await file.exists())
|
|
1594
1376
|
return false;
|
|
1595
1377
|
return file.size >= MIN_COMPRESS_BYTES;
|
|
1596
1378
|
}
|
|
1597
|
-
|
|
1598
1379
|
// pkgs/@akanjs/devkit/frontendBuild/ssrBaseArtifactBuilder.ts
|
|
1599
|
-
import
|
|
1380
|
+
import path7 from "path";
|
|
1600
1381
|
import { optimize } from "@tailwindcss/node";
|
|
1601
1382
|
function prepareCssAsset(command, basePath, cssText) {
|
|
1602
1383
|
return optimize(cssText, { file: `${basePath || "root"}.css`, minify: command === "build" }).code;
|
|
@@ -1614,7 +1395,7 @@ class SsrBaseArtifactBuilder {
|
|
|
1614
1395
|
this.#command = command;
|
|
1615
1396
|
this.#serverEnv = serverEnv;
|
|
1616
1397
|
this.#artifactDir = `${command === "build" ? app.dist.cwdPath : app.cwdPath}/.akan/artifact`;
|
|
1617
|
-
this.#absArtifactDir =
|
|
1398
|
+
this.#absArtifactDir = path7.resolve(this.#artifactDir);
|
|
1618
1399
|
}
|
|
1619
1400
|
static servedWeb(command, web) {
|
|
1620
1401
|
return command === "start" ? { ssr: true, csr: true } : web;
|
|
@@ -1639,7 +1420,7 @@ class SsrBaseArtifactBuilder {
|
|
|
1639
1420
|
rscRuntimeSsrManifest,
|
|
1640
1421
|
vendorMap,
|
|
1641
1422
|
cssAssets,
|
|
1642
|
-
pagesBundlePath: this.#command === "build" ?
|
|
1423
|
+
pagesBundlePath: this.#command === "build" ? path7.relative(this.#absArtifactDir, pagesBundle.bundlePath) : pagesBundle.bundlePath,
|
|
1643
1424
|
pagesBundleBuildId: pagesBundle.buildId,
|
|
1644
1425
|
domains: [...akanConfig.domains],
|
|
1645
1426
|
subRoutes: Object.fromEntries(Array.from(akanConfig.subRoutes.entries()).map(([basePath, domains]) => [basePath, [...domains]])),
|
|
@@ -1657,18 +1438,18 @@ class SsrBaseArtifactBuilder {
|
|
|
1657
1438
|
androidSha256CertFingerprints: target.android?.sha256CertFingerprints
|
|
1658
1439
|
}))
|
|
1659
1440
|
};
|
|
1660
|
-
await Bun.write(
|
|
1441
|
+
await Bun.write(path7.join(this.#absArtifactDir, "base-artifact.json"), `${JSON.stringify(artifact, null, 2)}
|
|
1661
1442
|
`);
|
|
1662
1443
|
this.#app.verbose(`[base-artifact] complete in ${Date.now() - this.#started}ms`);
|
|
1663
1444
|
return { artifact, seedIndex, cssCompiler, optimizedFonts };
|
|
1664
1445
|
}
|
|
1665
1446
|
async#buildRuntimeClientEntries() {
|
|
1666
1447
|
const akanServerPath = await this.#resolveAkanServerPath();
|
|
1667
|
-
const rscClientEntry =
|
|
1668
|
-
const rscSegmentOutletEntry =
|
|
1448
|
+
const rscClientEntry = path7.resolve(akanServerPath, "rscClient.tsx");
|
|
1449
|
+
const rscSegmentOutletEntry = path7.resolve(akanServerPath, "rscSegmentOutlet.tsx");
|
|
1669
1450
|
const vendorEntries = VENDOR_SPECIFIERS.map((specifier) => ({
|
|
1670
1451
|
specifier,
|
|
1671
|
-
absPath:
|
|
1452
|
+
absPath: path7.resolve(akanServerPath, "vendor", `${specifier.replaceAll("/", "-").replaceAll(".", "-")}.ts`)
|
|
1672
1453
|
}));
|
|
1673
1454
|
const entries = [rscClientEntry, rscSegmentOutletEntry, ...vendorEntries.map((v) => v.absPath)];
|
|
1674
1455
|
const clientBundle = await new ClientEntriesBundler({ app: this.#app, entries, command: this.#command }).bundle();
|
|
@@ -1705,15 +1486,15 @@ class SsrBaseArtifactBuilder {
|
|
|
1705
1486
|
async#resolveAkanServerPath() {
|
|
1706
1487
|
const candidates = [];
|
|
1707
1488
|
try {
|
|
1708
|
-
candidates.push(
|
|
1489
|
+
candidates.push(path7.dirname(Bun.resolveSync("akanjs/server", this.#app.workspace.workspaceRoot)));
|
|
1709
1490
|
} catch {}
|
|
1710
|
-
candidates.push(
|
|
1491
|
+
candidates.push(path7.join(this.#app.workspace.workspaceRoot, "pkgs/akanjs/server"), path7.join(this.#app.workspace.workspaceRoot, "node_modules/akanjs/server"));
|
|
1711
1492
|
try {
|
|
1712
|
-
candidates.push(
|
|
1493
|
+
candidates.push(path7.dirname(Bun.resolveSync("akanjs/server", path7.dirname(Bun.main))));
|
|
1713
1494
|
} catch {}
|
|
1714
|
-
candidates.push(
|
|
1495
|
+
candidates.push(path7.join(path7.dirname(Bun.main), "node_modules/akanjs/server"), path7.join(path7.dirname(Bun.main), "../../akanjs/server"), path7.resolve(import.meta.dir, "../../server"), path7.resolve(import.meta.dir, "../server"));
|
|
1715
1496
|
for (const candidate of candidates) {
|
|
1716
|
-
if (await Bun.file(
|
|
1497
|
+
if (await Bun.file(path7.join(candidate, "rscClient.tsx")).exists())
|
|
1717
1498
|
return candidate;
|
|
1718
1499
|
}
|
|
1719
1500
|
throw new Error(`[base-artifact] failed to locate akanjs/server; looked in: ${candidates.join(", ")}`);
|
|
@@ -1742,7 +1523,7 @@ ${preparedCssText}`).toString(36);
|
|
|
1742
1523
|
`styles/${cssAssetName}-${cssHash}.css`,
|
|
1743
1524
|
`/_akan/styles/${cssAssetName}-${cssHash}.css`
|
|
1744
1525
|
];
|
|
1745
|
-
await Bun.write(
|
|
1526
|
+
await Bun.write(path7.join(this.#absArtifactDir, cssRelPath), preparedCssText);
|
|
1746
1527
|
SsrBaseArtifactBuilder.#warnDroppedImports(this.#app, cssRelPath, preparedCssText, imported);
|
|
1747
1528
|
this.#app.verbose(`[base-artifact] wrote ${preparedCssText.length} bytes of CSS for ${basePath} -> ${cssRelPath}`);
|
|
1748
1529
|
return [basePath, { cssUrl, cssRelPath }];
|
|
@@ -1758,4 +1539,4 @@ ${preparedCssText}`).toString(36);
|
|
|
1758
1539
|
}
|
|
1759
1540
|
}
|
|
1760
1541
|
}
|
|
1761
|
-
export { AllRoutesBuilder,
|
|
1542
|
+
export { AllRoutesBuilder, CssCompiler, FontOptimizer, FontPruner, precompressArtifacts, SsrBaseArtifactBuilder };
|