@akanjs/cli 2.4.1-rc.2 → 2.4.1-rc.4
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 -0
- package/{agent.command-h4afc69n.js → agent.command-t4346579.js} +6 -6
- package/{application.command-47mj9qsy.js → application.command-zkmeemdt.js} +5 -4
- package/{applicationBuildRunner-yz144508.js → applicationBuildRunner-5w8j9yp7.js} +2 -1
- package/buildBatch.proc.js +176 -0
- package/{capacitorApp-y0h6cgft.js → capacitorApp-wnxzrppx.js} +2 -2
- package/{cloud.command-hxcgsf80.js → cloud.command-5ztqxppm.js} +9 -8
- package/{context.command-nqbtak4f.js → context.command-smwxwgwf.js} +14 -14
- package/{guideline.command-ya0dh44f.js → guideline.command-1r0esdjs.js} +3 -3
- package/incrementalBuilder.proc.js +281 -160
- package/index-0fn1r7gg.js +466 -0
- package/{index-qhtr07v8.js → index-0jwvs8vp.js} +19 -19
- package/{index-a6sbyy0b.js → index-0t7pwff2.js} +3 -0
- package/{index-h6ca6qg0.js → index-2v10yr8g.js} +1 -1
- package/{index-73pr2cmy.js → index-2v6a4wd5.js} +4 -4
- package/{index-45aj5ry0.js → index-3sd2vape.js} +3 -3
- package/{index-y3hdhy4p.js → index-ahpcr9ss.js} +5 -5
- package/{index-8rc0bm04.js → index-eay4t1te.js} +2 -2
- package/{index-qaq13qk3.js → index-eqxmxan6.js} +1 -1
- package/{index-pmm9e2jf.js → index-fh990y67.js} +2 -2
- package/{index-sgmas1fc.js → index-gr4cjv99.js} +5 -5
- package/{index-1xdrsbry.js → index-hk58kr1m.js} +1 -1
- package/{index-85msc0wg.js → index-jbtn8h1y.js} +3 -3
- package/{index-a5rmdgy4.js → index-khzzttv1.js} +1362 -1447
- package/{index-77crfweb.js → index-n5y2gbf2.js} +478 -20
- package/{index-n6h3482q.js → index-nv7et4t3.js} +2 -2
- package/{index-hwzpw9c1.js → index-pya1h7wx.js} +3 -3
- package/{index-hdqztm58.js → index-qpz4csbs.js} +1 -1
- package/{index-mq6ns0f9.js → index-s9yajzkj.js} +9 -9
- package/{index-8pkbzj26.js → index-wh201a76.js} +7 -7
- package/{index-swf4bmbg.js → index-y4kg682s.js} +1 -1
- package/index.js +18 -18
- package/{library.command-r15zdqvp.js → library.command-jrew04m6.js} +3 -3
- package/{localRegistry.command-6z4s13mj.js → localRegistry.command-m08dkm1b.js} +8 -7
- package/{module.command-qrj3kmyz.js → module.command-p98t7522.js} +6 -6
- package/{package.command-5x5m0ej1.js → package.command-57dyrfmn.js} +3 -3
- package/package.json +2 -2
- package/{page.command-c6xdx0xm.js → page.command-tas6f3na.js} +3 -3
- package/{primitive.command-pv9ssmtf.js → primitive.command-bjrbsakw.js} +7 -7
- package/{quality.command-es67wvdp.js → quality.command-zavaafbw.js} +2 -2
- package/{repair.command-677675vw.js → repair.command-je57wx67.js} +5 -5
- package/{scalar.command-kabkd6wd.js → scalar.command-gqqyy1kr.js} +5 -5
- package/{workflow.command-64r6cw0w.js → workflow.command-d6mrc47s.js} +10 -10
- package/{workspace.command-875aj35r.js → workspace.command-bbg96z8k.js} +24 -23
|
@@ -2636,1555 +2636,1499 @@ ${CsrArtifactBuilder.escapeInlineScript(await loadScript(src))}
|
|
|
2636
2636
|
}
|
|
2637
2637
|
}
|
|
2638
2638
|
|
|
2639
|
-
// pkgs/@akanjs/devkit/frontendBuild/
|
|
2639
|
+
// pkgs/@akanjs/devkit/frontendBuild/cssCompiler.ts
|
|
2640
|
+
import path15 from "path";
|
|
2641
|
+
import { Logger as Logger2 } from "akanjs/common";
|
|
2642
|
+
import { compile } from "tailwindcss";
|
|
2643
|
+
|
|
2644
|
+
// pkgs/@akanjs/devkit/frontendBuild/cssImportResolver.ts
|
|
2640
2645
|
import path14 from "path";
|
|
2641
|
-
var
|
|
2642
|
-
var CONFIG_BASENAMES = new Set(["akan.config.ts", "bunfig.toml", "tsconfig.json", "package.json"]);
|
|
2643
|
-
var BARREL_FACETS = new Set(["common", "srvkit", "ui", "webkit", "plugin"]);
|
|
2644
|
-
var CLIENT_SUFFIXES = [".Template.tsx", ".Unit.tsx", ".Util.tsx", ".View.tsx", ".Zone.tsx", ".store.ts"];
|
|
2645
|
-
var SHARED_SUFFIXES = [".constant.ts", ".dictionary.ts", ".signal.ts"];
|
|
2646
|
-
var SERVER_SUFFIXES = [".service.ts", ".document.ts"];
|
|
2647
|
-
var RUNTIME_METADATA_BASENAMES = new Set(["dict.ts", "sig.ts", "useClient.ts", "useServer.ts"]);
|
|
2646
|
+
var CSS_IMPORT_EXTS = ["", ".css", "/styles.css", "/index.css"];
|
|
2648
2647
|
|
|
2649
|
-
class
|
|
2648
|
+
class CssImportResolver {
|
|
2650
2649
|
#workspaceRoot;
|
|
2651
|
-
|
|
2652
|
-
|
|
2650
|
+
#paths;
|
|
2651
|
+
#wildcardEntries;
|
|
2652
|
+
constructor(workspaceRoot, paths = {}) {
|
|
2653
|
+
this.#workspaceRoot = workspaceRoot;
|
|
2654
|
+
this.#paths = paths;
|
|
2655
|
+
this.#wildcardEntries = Object.entries(paths).filter(([key]) => key.endsWith("/*")).map(([key, replacements]) => ({ prefix: key.slice(0, -1), replacements })).sort((a, b) => b.prefix.length - a.prefix.length);
|
|
2653
2656
|
}
|
|
2654
|
-
|
|
2655
|
-
const
|
|
2656
|
-
|
|
2657
|
-
const kindSet = new Set(kinds);
|
|
2658
|
-
const roles = new Set;
|
|
2659
|
-
const actions = new Set;
|
|
2660
|
-
const reasonByFile = {};
|
|
2661
|
-
for (const kind of kindSet) {
|
|
2662
|
-
if (kind === "css") {
|
|
2663
|
-
roles.add("css");
|
|
2664
|
-
actions.add("rebuild-css");
|
|
2665
|
-
}
|
|
2666
|
-
if (kind === "config") {
|
|
2667
|
-
roles.add("config");
|
|
2668
|
-
actions.add("restart-dev-host");
|
|
2669
|
-
}
|
|
2670
|
-
}
|
|
2671
|
-
for (const file of fileList) {
|
|
2672
|
-
const reasons = new Set;
|
|
2673
|
-
const fileRoles = this.#rolesForFile(file, { isGenerated: generatedSet.has(path14.resolve(file)), reasons });
|
|
2674
|
-
for (const role of fileRoles)
|
|
2675
|
-
roles.add(role);
|
|
2676
|
-
if (reasons.has("runtime-metadata"))
|
|
2677
|
-
actions.add("restart-builder");
|
|
2678
|
-
if (reasons.size > 0)
|
|
2679
|
-
reasonByFile[path14.resolve(file)] = [...reasons].sort();
|
|
2680
|
-
}
|
|
2681
|
-
if (roles.has("barrel"))
|
|
2682
|
-
actions.add("sync-generated");
|
|
2683
|
-
if (roles.has("server") || roles.has("shared"))
|
|
2684
|
-
actions.add("restart-backend");
|
|
2685
|
-
if (roles.has("client") || roles.has("shared"))
|
|
2686
|
-
actions.add("rebuild-client");
|
|
2687
|
-
if (roles.has("css"))
|
|
2688
|
-
actions.add("rebuild-css");
|
|
2689
|
-
return {
|
|
2690
|
-
generation,
|
|
2691
|
-
files: fileList,
|
|
2692
|
-
generatedFiles: uniqueResolved(generatedFiles),
|
|
2693
|
-
roles: [...roles].sort(),
|
|
2694
|
-
actions: [...actions].sort(),
|
|
2695
|
-
reasonByFile
|
|
2696
|
-
};
|
|
2657
|
+
static async create(app) {
|
|
2658
|
+
const tsconfig = await app.getTsConfig();
|
|
2659
|
+
return new CssImportResolver(app.workspace.workspaceRoot, tsconfig.compilerOptions.paths ?? {});
|
|
2697
2660
|
}
|
|
2698
|
-
|
|
2699
|
-
const
|
|
2700
|
-
|
|
2701
|
-
|
|
2702
|
-
|
|
2703
|
-
|
|
2704
|
-
|
|
2705
|
-
|
|
2706
|
-
|
|
2707
|
-
|
|
2708
|
-
reasons.add("config-file");
|
|
2709
|
-
}
|
|
2710
|
-
if (ext === ".css") {
|
|
2711
|
-
roles.add("css");
|
|
2712
|
-
reasons.add("css-file");
|
|
2713
|
-
}
|
|
2714
|
-
if (isGenerated || isSource && this.#isBarrelFacetChild(parts)) {
|
|
2715
|
-
roles.add("barrel");
|
|
2716
|
-
reasons.add(isGenerated ? "generated-index" : "barrel-facet-child");
|
|
2661
|
+
async resolve(id, fromBase) {
|
|
2662
|
+
for (const resolve of [
|
|
2663
|
+
() => this.#resolveWithTsconfig(id),
|
|
2664
|
+
() => this.#resolveWithBun(id, fromBase),
|
|
2665
|
+
() => this.#resolveWithRequire(id, fromBase),
|
|
2666
|
+
() => this.#resolvePackageStyle(id, fromBase)
|
|
2667
|
+
]) {
|
|
2668
|
+
const resolved = await resolve();
|
|
2669
|
+
if (resolved)
|
|
2670
|
+
return resolved;
|
|
2717
2671
|
}
|
|
2718
|
-
|
|
2719
|
-
|
|
2720
|
-
|
|
2672
|
+
return null;
|
|
2673
|
+
}
|
|
2674
|
+
#resolveWithBun(id, fromBase) {
|
|
2675
|
+
for (const base of this.#resolutionBases(fromBase)) {
|
|
2676
|
+
try {
|
|
2677
|
+
const resolved = Bun.resolveSync(id, base);
|
|
2678
|
+
if (CssImportResolver.isCssFile(resolved))
|
|
2679
|
+
return resolved;
|
|
2680
|
+
} catch {}
|
|
2721
2681
|
}
|
|
2722
|
-
|
|
2723
|
-
|
|
2724
|
-
|
|
2682
|
+
return null;
|
|
2683
|
+
}
|
|
2684
|
+
#resolveWithRequire(id, fromBase) {
|
|
2685
|
+
for (const base of this.#resolutionBases(fromBase)) {
|
|
2686
|
+
try {
|
|
2687
|
+
const resolved = __require.resolve(id, { paths: [base] });
|
|
2688
|
+
if (CssImportResolver.isCssFile(resolved))
|
|
2689
|
+
return resolved;
|
|
2690
|
+
} catch {}
|
|
2725
2691
|
}
|
|
2726
|
-
|
|
2727
|
-
|
|
2728
|
-
|
|
2692
|
+
return null;
|
|
2693
|
+
}
|
|
2694
|
+
async#resolveWithTsconfig(id) {
|
|
2695
|
+
const exact = this.#paths[id];
|
|
2696
|
+
if (exact) {
|
|
2697
|
+
for (const repl of exact) {
|
|
2698
|
+
const resolved = await this.#firstExisting(path14.resolve(this.#workspaceRoot, repl));
|
|
2699
|
+
if (resolved)
|
|
2700
|
+
return resolved;
|
|
2701
|
+
}
|
|
2729
2702
|
}
|
|
2730
|
-
|
|
2731
|
-
|
|
2703
|
+
for (const { prefix, replacements } of this.#wildcardEntries) {
|
|
2704
|
+
if (!id.startsWith(prefix))
|
|
2705
|
+
continue;
|
|
2706
|
+
const suffix = id.slice(prefix.length);
|
|
2707
|
+
for (const repl of replacements) {
|
|
2708
|
+
const replPath = repl.endsWith("/*") ? repl.slice(0, -1) : repl;
|
|
2709
|
+
const resolved = await this.#firstExisting(path14.resolve(this.#workspaceRoot, replPath + suffix));
|
|
2710
|
+
if (resolved)
|
|
2711
|
+
return resolved;
|
|
2712
|
+
}
|
|
2732
2713
|
}
|
|
2733
|
-
|
|
2734
|
-
|
|
2735
|
-
|
|
2736
|
-
|
|
2737
|
-
|
|
2714
|
+
return null;
|
|
2715
|
+
}
|
|
2716
|
+
async#resolvePackageStyle(id, fromBase) {
|
|
2717
|
+
const pkgName = CssImportResolver.getPackageName(id);
|
|
2718
|
+
if (!pkgName)
|
|
2719
|
+
return null;
|
|
2720
|
+
for (const base of this.#resolutionBases(fromBase)) {
|
|
2721
|
+
try {
|
|
2722
|
+
const pkgPath = __require.resolve(`${pkgName}/package.json`, { paths: [base] });
|
|
2723
|
+
const resolved = await this.#resolvePackageStyleFromPackageJson(id, pkgName, pkgPath);
|
|
2724
|
+
if (resolved)
|
|
2725
|
+
return resolved;
|
|
2726
|
+
} catch {}
|
|
2738
2727
|
}
|
|
2739
|
-
|
|
2740
|
-
|
|
2741
|
-
|
|
2728
|
+
for (const pkgPath of this.#packageJsonCandidates(pkgName)) {
|
|
2729
|
+
const resolved = await this.#resolvePackageStyleFromPackageJson(id, pkgName, pkgPath);
|
|
2730
|
+
if (resolved)
|
|
2731
|
+
return resolved;
|
|
2742
2732
|
}
|
|
2743
|
-
return
|
|
2733
|
+
return null;
|
|
2744
2734
|
}
|
|
2745
|
-
#
|
|
2746
|
-
|
|
2747
|
-
|
|
2735
|
+
async#resolvePackageStyleFromPackageJson(id, pkgName, pkgPath) {
|
|
2736
|
+
try {
|
|
2737
|
+
if (!await Bun.file(pkgPath).exists())
|
|
2738
|
+
return null;
|
|
2739
|
+
const pkgDir = path14.dirname(pkgPath);
|
|
2740
|
+
const pkg = await Bun.file(pkgPath).json();
|
|
2741
|
+
const subpath = id === pkgName ? "." : `.${id.slice(pkgName.length)}`;
|
|
2742
|
+
const exportValue = pkg.exports?.[subpath];
|
|
2743
|
+
const styleEntry = (typeof exportValue === "string" ? exportValue : exportValue?.style || exportValue?.import || exportValue?.default) || pkg.exports?.["."]?.style || pkg.style || "index.css";
|
|
2744
|
+
return await this.#firstExisting(path14.resolve(pkgDir, styleEntry));
|
|
2745
|
+
} catch {
|
|
2746
|
+
return null;
|
|
2747
|
+
}
|
|
2748
2748
|
}
|
|
2749
|
-
#
|
|
2750
|
-
|
|
2751
|
-
return parts.includes("ui") || parts.includes("webkit") || parts.includes("page") || CLIENT_SUFFIXES.some((suffix) => base.endsWith(suffix));
|
|
2749
|
+
#resolutionBases(fromBase) {
|
|
2750
|
+
return [fromBase, this.#workspaceRoot, path14.dirname(Bun.main), path14.resolve(path14.dirname(Bun.main), "../..")];
|
|
2752
2751
|
}
|
|
2753
|
-
#
|
|
2754
|
-
|
|
2755
|
-
|
|
2752
|
+
#packageJsonCandidates(pkgName) {
|
|
2753
|
+
return [
|
|
2754
|
+
path14.join(this.#workspaceRoot, "pkgs", pkgName, "package.json"),
|
|
2755
|
+
path14.join(this.#workspaceRoot, "node_modules", pkgName, "package.json"),
|
|
2756
|
+
path14.join(path14.dirname(Bun.main), "node_modules", pkgName, "package.json"),
|
|
2757
|
+
path14.join(path14.dirname(Bun.main), "../../", pkgName, "package.json")
|
|
2758
|
+
];
|
|
2756
2759
|
}
|
|
2757
|
-
#
|
|
2758
|
-
const
|
|
2759
|
-
|
|
2760
|
-
|
|
2761
|
-
|
|
2762
|
-
|
|
2763
|
-
|
|
2764
|
-
return base.endsWith(".dictionary.ts") || base.endsWith(".signal.ts");
|
|
2760
|
+
async#firstExisting(basePath) {
|
|
2761
|
+
for (const suffix of CSS_IMPORT_EXTS) {
|
|
2762
|
+
const candidate = `${basePath}${suffix}`;
|
|
2763
|
+
if (await Bun.file(candidate).exists())
|
|
2764
|
+
return candidate;
|
|
2765
|
+
}
|
|
2766
|
+
return null;
|
|
2765
2767
|
}
|
|
2766
|
-
|
|
2767
|
-
|
|
2768
|
-
|
|
2769
|
-
|
|
2770
|
-
|
|
2771
|
-
return false;
|
|
2772
|
-
if (!facet || !BARREL_FACETS.has(facet))
|
|
2773
|
-
return false;
|
|
2774
|
-
if (!child || child.startsWith(".") || child === "index.ts" || child === "index.tsx")
|
|
2775
|
-
return false;
|
|
2776
|
-
return true;
|
|
2768
|
+
static getPackageName(id) {
|
|
2769
|
+
const parts = id.split("/");
|
|
2770
|
+
if (id.startsWith("@"))
|
|
2771
|
+
return parts.length >= 2 ? `${parts[0]}/${parts[1]}` : null;
|
|
2772
|
+
return parts[0] ?? null;
|
|
2777
2773
|
}
|
|
2778
|
-
|
|
2779
|
-
|
|
2780
|
-
return false;
|
|
2781
|
-
const [scope] = rel.split(path14.sep);
|
|
2782
|
-
return scope === "apps" || scope === "libs" || scope === "pkgs";
|
|
2774
|
+
static isCssFile(filePath) {
|
|
2775
|
+
return path14.extname(filePath) === ".css";
|
|
2783
2776
|
}
|
|
2784
2777
|
}
|
|
2785
|
-
var uniqueResolved = (files) => [...new Set(files.map((file) => path14.resolve(file)))].sort();
|
|
2786
2778
|
|
|
2787
|
-
// pkgs/@akanjs/devkit/frontendBuild/
|
|
2788
|
-
|
|
2789
|
-
|
|
2790
|
-
var
|
|
2791
|
-
var
|
|
2792
|
-
var FACET_EXCLUDED_FILE_RE = /(^index\.tsx?$|\.d\.ts$|\.(test|spec)\.(ts|tsx)$|\.css$|\.scss$|\.sass$)/;
|
|
2793
|
-
var FACET_PASCAL_CASE_RE = /^[A-Z][A-Za-z0-9]*$/;
|
|
2794
|
-
var FACET_CAMEL_CASE_RE = /^[a-z][A-Za-z0-9]*$/;
|
|
2795
|
-
var MODULE_UI_TYPES = ["Template", "Unit", "Util", "View", "Zone"];
|
|
2796
|
-
var SERVICE_UI_TYPES = ["Util", "Zone"];
|
|
2797
|
-
var SCALAR_UI_TYPES = ["Template", "Unit"];
|
|
2779
|
+
// pkgs/@akanjs/devkit/frontendBuild/cssCompiler.ts
|
|
2780
|
+
var SOURCE_EXTS2 = [".ts", ".tsx", ".js", ".jsx", ".mjs", ".cjs"];
|
|
2781
|
+
var NON_SOURCE_EXT_RE2 = /\.(json|svg|png|jpe?g|webp|gif|avif|ico|woff2?|ttf|otf|mp3|mp4|wav)$/i;
|
|
2782
|
+
var NODE_MODULES_RE3 = /[\\/]node_modules[\\/]/;
|
|
2783
|
+
var AKANJS_NODE_MODULE_RE3 = /[\\/]node_modules[\\/]akanjs[\\/]/;
|
|
2798
2784
|
|
|
2799
|
-
class
|
|
2800
|
-
#
|
|
2801
|
-
|
|
2802
|
-
|
|
2785
|
+
class CssCompiler {
|
|
2786
|
+
#logger = new Logger2("CssCompiler");
|
|
2787
|
+
#transpiler = new Bun.Transpiler({ loader: "tsx" });
|
|
2788
|
+
#app;
|
|
2789
|
+
#cssImportResolver = null;
|
|
2790
|
+
constructor(app) {
|
|
2791
|
+
this.#app = app;
|
|
2803
2792
|
}
|
|
2804
|
-
|
|
2805
|
-
|
|
2806
|
-
|
|
2807
|
-
|
|
2808
|
-
|
|
2809
|
-
|
|
2810
|
-
|
|
2811
|
-
|
|
2812
|
-
errors.push(`[generated-index] module detection failed for ${file}: ${formatError2(err)}`);
|
|
2813
|
-
return null;
|
|
2814
|
-
});
|
|
2815
|
-
if (moduleIndex)
|
|
2816
|
-
indexPaths.add(moduleIndex);
|
|
2817
|
-
}
|
|
2818
|
-
const changedFiles = [];
|
|
2819
|
-
for (const indexPath of [...indexPaths].sort()) {
|
|
2820
|
-
try {
|
|
2821
|
-
const changed = await this.#syncIndex(indexPath);
|
|
2822
|
-
if (changed)
|
|
2823
|
-
changedFiles.push(indexPath);
|
|
2824
|
-
} catch (err) {
|
|
2825
|
-
errors.push(`[generated-index] sync failed for ${indexPath}: ${formatError2(err)}`);
|
|
2826
|
-
}
|
|
2827
|
-
}
|
|
2828
|
-
return { changedFiles, errors };
|
|
2829
|
-
}
|
|
2830
|
-
#facetIndexFor(file) {
|
|
2831
|
-
const abs = path15.resolve(file);
|
|
2832
|
-
const rel = path15.relative(this.#workspaceRoot, abs);
|
|
2833
|
-
if (rel.startsWith("..") || path15.isAbsolute(rel))
|
|
2834
|
-
return null;
|
|
2835
|
-
const parts = rel.split(path15.sep).filter(Boolean);
|
|
2836
|
-
if (parts.length < 4)
|
|
2837
|
-
return null;
|
|
2838
|
-
const [scope, project, facet, child] = parts;
|
|
2839
|
-
if (scope !== "apps" && scope !== "libs" || !project || !facet || !BARREL_FACETS2.has(facet))
|
|
2840
|
-
return null;
|
|
2841
|
-
if (!child || child.startsWith(".") || child === "index.ts" || child === "index.tsx")
|
|
2842
|
-
return null;
|
|
2843
|
-
return path15.join(this.#workspaceRoot, scope, project, facet, "index.ts");
|
|
2844
|
-
}
|
|
2845
|
-
async#moduleIndexForDirectoryEvent(file) {
|
|
2846
|
-
const abs = path15.resolve(file);
|
|
2847
|
-
const rel = path15.relative(this.#workspaceRoot, abs);
|
|
2848
|
-
if (rel.startsWith("..") || path15.isAbsolute(rel))
|
|
2849
|
-
return null;
|
|
2850
|
-
const parts = rel.split(path15.sep).filter(Boolean);
|
|
2851
|
-
if (parts.length !== 4 && parts.length !== 5)
|
|
2852
|
-
return null;
|
|
2853
|
-
const [scope, project, libSegment, moduleSegment, scalarSegment] = parts;
|
|
2854
|
-
if (scope !== "apps" && scope !== "libs" || !project || libSegment !== "lib")
|
|
2855
|
-
return null;
|
|
2856
|
-
const isExistingDirectory = await stat2(abs).then((s) => s.isDirectory()).catch(() => false);
|
|
2857
|
-
const looksLikeDeletedDirectory = !path15.extname(abs);
|
|
2858
|
-
if (!isExistingDirectory && !looksLikeDeletedDirectory)
|
|
2859
|
-
return null;
|
|
2860
|
-
if (moduleSegment === "__scalar" && scalarSegment) {
|
|
2861
|
-
return path15.join(this.#workspaceRoot, scope, project, "lib", "__scalar", scalarSegment, "index.ts");
|
|
2862
|
-
}
|
|
2863
|
-
if (!moduleSegment || moduleSegment === "__scalar")
|
|
2864
|
-
return null;
|
|
2865
|
-
return path15.join(this.#workspaceRoot, scope, project, "lib", moduleSegment, "index.ts");
|
|
2866
|
-
}
|
|
2867
|
-
async#syncIndex(indexPath) {
|
|
2868
|
-
const dir = path15.dirname(indexPath);
|
|
2869
|
-
const content = await this.#contentForIndex(indexPath);
|
|
2870
|
-
if (content === null) {
|
|
2871
|
-
if (!await exists(indexPath))
|
|
2872
|
-
return false;
|
|
2873
|
-
await rm2(indexPath, { force: true });
|
|
2874
|
-
return true;
|
|
2875
|
-
}
|
|
2876
|
-
const current = await readFile2(indexPath, "utf8").catch(() => null);
|
|
2877
|
-
if (current === content)
|
|
2878
|
-
return false;
|
|
2879
|
-
await mkdir4(dir, { recursive: true });
|
|
2880
|
-
await writeFile2(indexPath, content);
|
|
2881
|
-
return true;
|
|
2882
|
-
}
|
|
2883
|
-
async#contentForIndex(indexPath) {
|
|
2884
|
-
const parts = path15.relative(this.#workspaceRoot, indexPath).split(path15.sep).filter(Boolean);
|
|
2885
|
-
const facet = parts.at(-2);
|
|
2886
|
-
if (facet && BARREL_FACETS2.has(facet))
|
|
2887
|
-
return this.#facetContent(path15.dirname(indexPath));
|
|
2888
|
-
return this.#moduleContent(path15.dirname(indexPath));
|
|
2889
|
-
}
|
|
2890
|
-
async#facetContent(dir) {
|
|
2891
|
-
const nameCasePattern = path15.basename(dir) === "ui" ? FACET_PASCAL_CASE_RE : FACET_CAMEL_CASE_RE;
|
|
2892
|
-
const entries = await readdir2(dir, { withFileTypes: true }).catch(() => []);
|
|
2893
|
-
const exportNames = entries.flatMap((entry) => {
|
|
2894
|
-
const name = entry.name;
|
|
2895
|
-
if (name.startsWith("."))
|
|
2896
|
-
return [];
|
|
2897
|
-
if (entry.isDirectory())
|
|
2898
|
-
return nameCasePattern.test(name) ? [name] : [];
|
|
2899
|
-
if (!entry.isFile())
|
|
2900
|
-
return [];
|
|
2901
|
-
if (!FACET_SOURCE_FILE_RE.test(name) || FACET_EXCLUDED_FILE_RE.test(name))
|
|
2902
|
-
return [];
|
|
2903
|
-
const exportName = name.replace(FACET_SOURCE_FILE_RE, "");
|
|
2904
|
-
return nameCasePattern.test(exportName) ? [exportName] : [];
|
|
2905
|
-
}).sort();
|
|
2906
|
-
if (exportNames.length === 0)
|
|
2907
|
-
return null;
|
|
2908
|
-
return `${exportNames.map((name) => `export * from "./${name}";`).join(`
|
|
2909
|
-
`)}
|
|
2910
|
-
`;
|
|
2793
|
+
#cssText = null;
|
|
2794
|
+
#cssTextByBasePath = null;
|
|
2795
|
+
async getCss({ refresh } = {}) {
|
|
2796
|
+
if (this.#cssText !== null && !refresh)
|
|
2797
|
+
return this.#cssText;
|
|
2798
|
+
const { cssPaths, sourcePaths } = await this.discoverCssAndSources({ refresh });
|
|
2799
|
+
this.#cssText = await this.compileCss(cssPaths, sourcePaths);
|
|
2800
|
+
return this.#cssText;
|
|
2911
2801
|
}
|
|
2912
|
-
async
|
|
2913
|
-
|
|
2914
|
-
|
|
2915
|
-
const
|
|
2916
|
-
|
|
2917
|
-
|
|
2918
|
-
const
|
|
2919
|
-
const
|
|
2920
|
-
|
|
2921
|
-
|
|
2922
|
-
|
|
2923
|
-
|
|
2924
|
-
|
|
2925
|
-
|
|
2926
|
-
|
|
2927
|
-
|
|
2928
|
-
|
|
2929
|
-
|
|
2930
|
-
|
|
2931
|
-
|
|
2932
|
-
|
|
2933
|
-
|
|
2934
|
-
|
|
2935
|
-
|
|
2802
|
+
async getCssByBasePath({ refresh } = {}) {
|
|
2803
|
+
if (this.#cssTextByBasePath !== null && !refresh)
|
|
2804
|
+
return this.#cssTextByBasePath;
|
|
2805
|
+
const akanConfig = await this.#app.getConfig({ refresh });
|
|
2806
|
+
const pageKeys = await this.#app.getPageKeys({ refresh });
|
|
2807
|
+
const basePaths = [...akanConfig.basePaths];
|
|
2808
|
+
const rootPageKeys = pageKeys.filter((pageKey) => getPageKeyBasePath(pageKey, basePaths) === null);
|
|
2809
|
+
const cssEntries = await Promise.all([
|
|
2810
|
+
(async () => {
|
|
2811
|
+
if (rootPageKeys.length === 0)
|
|
2812
|
+
return ["", ""];
|
|
2813
|
+
const started = Date.now();
|
|
2814
|
+
const { cssPaths, sourcePaths } = await this.discoverCssAndSources({ refresh, pageKeys: rootPageKeys });
|
|
2815
|
+
const css = await this.compileCss(cssPaths, sourcePaths);
|
|
2816
|
+
this.#logger.verbose(`css base=root paths=${cssPaths.length} sources=${sourcePaths.length} in ${Date.now() - started}ms`);
|
|
2817
|
+
return ["", css];
|
|
2818
|
+
})(),
|
|
2819
|
+
...basePaths.map(async (basePath) => {
|
|
2820
|
+
const basePathPageKeys = pageKeys.filter((pageKey) => getPageKeyBasePath(pageKey, basePaths) === basePath);
|
|
2821
|
+
if (basePathPageKeys.length === 0)
|
|
2822
|
+
return [basePath, ""];
|
|
2823
|
+
const started = Date.now();
|
|
2824
|
+
const { cssPaths, sourcePaths } = await this.discoverCssAndSources({ refresh, pageKeys: basePathPageKeys });
|
|
2825
|
+
const css = await this.compileCss(cssPaths, sourcePaths);
|
|
2826
|
+
this.#logger.verbose(`css base=${basePath} paths=${cssPaths.length} sources=${sourcePaths.length} in ${Date.now() - started}ms`);
|
|
2827
|
+
return [basePath, css];
|
|
2828
|
+
})
|
|
2829
|
+
]);
|
|
2830
|
+
this.#cssTextByBasePath = Object.fromEntries(cssEntries);
|
|
2831
|
+
return this.#cssTextByBasePath;
|
|
2936
2832
|
}
|
|
2937
|
-
}
|
|
2938
|
-
|
|
2939
|
-
|
|
2940
|
-
var formatError2 = (err) => err instanceof Error ? err.message : String(err);
|
|
2941
|
-
|
|
2942
|
-
// pkgs/@akanjs/devkit/frontendBuild/fontOptimizer.ts
|
|
2943
|
-
import { mkdir as mkdir5 } from "fs/promises";
|
|
2944
|
-
import path16 from "path";
|
|
2945
|
-
import {
|
|
2946
|
-
generateFontFace,
|
|
2947
|
-
getMetricsForFamily,
|
|
2948
|
-
readMetrics,
|
|
2949
|
-
resolveCategoryFallbacks
|
|
2950
|
-
} from "fontaine";
|
|
2951
|
-
import { createFont, woff2 } from "fonteditor-core";
|
|
2952
|
-
import subsetFont from "subset-font";
|
|
2953
|
-
import ts4 from "typescript";
|
|
2954
|
-
var FONT_URL_PREFIX = "/_akan/fonts";
|
|
2955
|
-
var DEFAULT_FONT_SUBSETS = ["latin"];
|
|
2956
|
-
|
|
2957
|
-
class FontOptimizer {
|
|
2958
|
-
#app;
|
|
2959
|
-
#command;
|
|
2960
|
-
#artifactRoot;
|
|
2961
|
-
#files = [];
|
|
2962
|
-
#cssParts = [];
|
|
2963
|
-
#woff2Ready = null;
|
|
2964
|
-
static #ksX1001Text = null;
|
|
2965
|
-
constructor(app, command = "start") {
|
|
2966
|
-
this.#app = app;
|
|
2967
|
-
this.#command = command;
|
|
2968
|
-
this.#artifactRoot = path16.join(command === "build" ? app.dist.cwdPath : app.cwdPath, ".akan/artifact");
|
|
2833
|
+
async discoverCss({ refresh } = {}) {
|
|
2834
|
+
const { cssPaths } = await this.discoverCssAndSources({ refresh });
|
|
2835
|
+
return cssPaths;
|
|
2969
2836
|
}
|
|
2970
|
-
async
|
|
2971
|
-
|
|
2972
|
-
|
|
2973
|
-
|
|
2837
|
+
async discoverCssAndSources({
|
|
2838
|
+
refresh,
|
|
2839
|
+
pageKeys
|
|
2840
|
+
} = {}) {
|
|
2841
|
+
pageKeys ??= await this.#app.getPageKeys({ refresh });
|
|
2842
|
+
const seeds = pageKeys.map((key) => path15.resolve(this.#app.cwdPath, "page", key));
|
|
2843
|
+
const cssFiles = new Set;
|
|
2844
|
+
const sourceFiles = new Set;
|
|
2845
|
+
const queue = [...seeds];
|
|
2846
|
+
const resolvePackage = await createTsconfigPackageResolver(this.#app);
|
|
2847
|
+
const analyzer = new BarrelAnalyzer({ resolvePackage });
|
|
2848
|
+
const akanConfig = await this.#app.getConfig({ refresh });
|
|
2849
|
+
while (queue.length > 0) {
|
|
2850
|
+
const filePath = queue.shift();
|
|
2851
|
+
if (!filePath || sourceFiles.has(filePath) || isIgnoredNodeModuleSource(filePath))
|
|
2974
2852
|
continue;
|
|
2975
|
-
|
|
2976
|
-
|
|
2977
|
-
|
|
2978
|
-
|
|
2979
|
-
|
|
2980
|
-
return { css: this.#cssParts.join(`
|
|
2981
|
-
`), fonts, files: this.#files };
|
|
2982
|
-
}
|
|
2983
|
-
async discoverFonts() {
|
|
2984
|
-
const pageKeys = await this.#app.getPageKeys();
|
|
2985
|
-
const fonts = [];
|
|
2986
|
-
await Promise.all(pageKeys.map(async (key) => {
|
|
2987
|
-
const filePath = path16.resolve(this.#app.cwdPath, "page", key);
|
|
2988
|
-
const file = Bun.file(filePath);
|
|
2989
|
-
if (!await file.exists())
|
|
2990
|
-
return;
|
|
2991
|
-
fonts.push(...this.#extractFontsExport(await file.text(), filePath));
|
|
2992
|
-
}));
|
|
2993
|
-
return this.#dedupeFonts(fonts);
|
|
2994
|
-
}
|
|
2995
|
-
async#optimizeFont(font) {
|
|
2996
|
-
const faceCss = [];
|
|
2997
|
-
for (const face of this.#getFontFaces(font)) {
|
|
2998
|
-
const sourcePath = await this.#resolveFontSourcePath(face.src);
|
|
2999
|
-
if (!sourcePath) {
|
|
3000
|
-
this.#app.logger.warn(`[font] source not found: ${face.src}`);
|
|
2853
|
+
sourceFiles.add(filePath);
|
|
2854
|
+
let content;
|
|
2855
|
+
try {
|
|
2856
|
+
content = await Bun.file(filePath).text();
|
|
2857
|
+
} catch {
|
|
3001
2858
|
continue;
|
|
3002
2859
|
}
|
|
3003
|
-
|
|
3004
|
-
|
|
3005
|
-
|
|
3006
|
-
|
|
3007
|
-
|
|
3008
|
-
|
|
3009
|
-
|
|
3010
|
-
|
|
3011
|
-
|
|
3012
|
-
|
|
3013
|
-
|
|
3014
|
-
|
|
3015
|
-
this.#cssParts.push(...faceCss, this.#buildRootVariableRule(font));
|
|
3016
|
-
}
|
|
3017
|
-
#extractFontsExport(source, filePath) {
|
|
3018
|
-
const sourceFile = ts4.createSourceFile(filePath, source, ts4.ScriptTarget.Latest, true, ts4.ScriptKind.TSX);
|
|
3019
|
-
const fonts = [];
|
|
3020
|
-
for (const statement of sourceFile.statements) {
|
|
3021
|
-
if (!ts4.isVariableStatement(statement))
|
|
3022
|
-
continue;
|
|
3023
|
-
const modifiers = ts4.canHaveModifiers(statement) ? ts4.getModifiers(statement) : undefined;
|
|
3024
|
-
const isExported = modifiers?.some((modifier) => modifier.kind === ts4.SyntaxKind.ExportKeyword) ?? false;
|
|
3025
|
-
if (!isExported)
|
|
2860
|
+
let source = content;
|
|
2861
|
+
if (akanConfig.barrelImports.length > 0) {
|
|
2862
|
+
try {
|
|
2863
|
+
const rewritten = await rewriteBarrelImports(content, akanConfig.barrelImports, analyzer);
|
|
2864
|
+
if (rewritten !== null)
|
|
2865
|
+
source = rewritten;
|
|
2866
|
+
} catch {}
|
|
2867
|
+
}
|
|
2868
|
+
let imports;
|
|
2869
|
+
try {
|
|
2870
|
+
imports = this.#transpiler.scanImports(source);
|
|
2871
|
+
} catch {
|
|
3026
2872
|
continue;
|
|
3027
|
-
|
|
3028
|
-
|
|
2873
|
+
}
|
|
2874
|
+
const importerDir = path15.dirname(filePath);
|
|
2875
|
+
for (const imp of imports) {
|
|
2876
|
+
const spec = imp.path;
|
|
2877
|
+
if (!spec)
|
|
2878
|
+
continue;
|
|
2879
|
+
if (spec.endsWith(".css")) {
|
|
2880
|
+
const cssPath = await this.#resolveCssImport(spec, importerDir);
|
|
2881
|
+
cssFiles.add(cssPath);
|
|
3029
2882
|
continue;
|
|
3030
|
-
const value = declaration.initializer ? this.#literalToValue(declaration.initializer) : null;
|
|
3031
|
-
if (Array.isArray(value)) {
|
|
3032
|
-
fonts.push(...value.map((font) => this.#withFontDefaults(font)));
|
|
3033
2883
|
}
|
|
3034
|
-
|
|
3035
|
-
}
|
|
3036
|
-
return fonts;
|
|
3037
|
-
}
|
|
3038
|
-
#literalToValue(node) {
|
|
3039
|
-
if (ts4.isStringLiteralLike(node))
|
|
3040
|
-
return node.text;
|
|
3041
|
-
if (ts4.isNumericLiteral(node))
|
|
3042
|
-
return Number(node.text);
|
|
3043
|
-
if (node.kind === ts4.SyntaxKind.TrueKeyword)
|
|
3044
|
-
return true;
|
|
3045
|
-
if (node.kind === ts4.SyntaxKind.FalseKeyword)
|
|
3046
|
-
return false;
|
|
3047
|
-
if (ts4.isArrayLiteralExpression(node))
|
|
3048
|
-
return node.elements.map((element) => this.#literalToValue(element));
|
|
3049
|
-
if (ts4.isObjectLiteralExpression(node)) {
|
|
3050
|
-
const obj = {};
|
|
3051
|
-
for (const prop of node.properties) {
|
|
3052
|
-
if (!ts4.isPropertyAssignment(prop))
|
|
2884
|
+
if (NON_SOURCE_EXT_RE2.test(spec))
|
|
3053
2885
|
continue;
|
|
3054
|
-
const
|
|
3055
|
-
if (!
|
|
2886
|
+
const resolved = await this.#resolveSourceImport(spec, importerDir, resolvePackage);
|
|
2887
|
+
if (!resolved || sourceFiles.has(resolved) || isIgnoredNodeModuleSource(resolved))
|
|
3056
2888
|
continue;
|
|
3057
|
-
|
|
2889
|
+
queue.push(resolved);
|
|
3058
2890
|
}
|
|
3059
|
-
return obj;
|
|
3060
|
-
}
|
|
3061
|
-
if (ts4.isAsExpression(node) || ts4.isSatisfiesExpression(node) || ts4.isParenthesizedExpression(node)) {
|
|
3062
|
-
return this.#literalToValue(node.expression);
|
|
3063
2891
|
}
|
|
3064
|
-
return;
|
|
3065
|
-
}
|
|
3066
|
-
#getPropertyName(name) {
|
|
3067
|
-
if (ts4.isIdentifier(name) || ts4.isStringLiteral(name) || ts4.isNumericLiteral(name))
|
|
3068
|
-
return name.text;
|
|
3069
|
-
return null;
|
|
3070
|
-
}
|
|
3071
|
-
#dedupeFonts(fonts) {
|
|
3072
|
-
const map = new Map;
|
|
3073
|
-
for (const font of fonts)
|
|
3074
|
-
map.set(JSON.stringify(font), font);
|
|
3075
|
-
return [...map.values()];
|
|
3076
|
-
}
|
|
3077
|
-
#withFontDefaults(font) {
|
|
3078
|
-
return { ...font, subsets: font.subsets ?? [...DEFAULT_FONT_SUBSETS] };
|
|
3079
|
-
}
|
|
3080
|
-
#getFontSubsets(font) {
|
|
3081
|
-
return font.subsets ?? DEFAULT_FONT_SUBSETS;
|
|
3082
|
-
}
|
|
3083
|
-
#getFontVariableName(font) {
|
|
3084
|
-
return font.variable ?? `--font-${font.name}`;
|
|
3085
|
-
}
|
|
3086
|
-
#getFontFallbackName(font) {
|
|
3087
|
-
return font.fallbackName ?? `${font.name} fallback`;
|
|
3088
|
-
}
|
|
3089
|
-
#isFontOptimizationEnabled(font) {
|
|
3090
|
-
return font.optimize !== false;
|
|
3091
|
-
}
|
|
3092
|
-
#getFontStyles(font) {
|
|
3093
|
-
return font.styles?.length ? font.styles : ["normal"];
|
|
3094
|
-
}
|
|
3095
|
-
#getFontFaces(font) {
|
|
3096
|
-
const enabledStyles = new Set(this.#getFontStyles(font));
|
|
3097
|
-
return font.paths.map((fontPath) => {
|
|
3098
|
-
const style = fontPath.style ?? "normal";
|
|
3099
|
-
return {
|
|
3100
|
-
font,
|
|
3101
|
-
path: fontPath,
|
|
3102
|
-
src: fontPath.src,
|
|
3103
|
-
weight: fontPath.weight,
|
|
3104
|
-
style,
|
|
3105
|
-
optimizedSrc: this.#getOptimizedFontSrc(font, fontPath)
|
|
3106
|
-
};
|
|
3107
|
-
}).filter((face) => enabledStyles.has(face.style));
|
|
3108
|
-
}
|
|
3109
|
-
#getOptimizedFontSrc(font, fontPath) {
|
|
3110
|
-
const style = fontPath.style ?? "normal";
|
|
3111
|
-
const hash = this.#hashFontConfig({
|
|
3112
|
-
name: font.name,
|
|
3113
|
-
src: fontPath.src,
|
|
3114
|
-
weight: fontPath.weight,
|
|
3115
|
-
style,
|
|
3116
|
-
display: font.display,
|
|
3117
|
-
subset: font.subset,
|
|
3118
|
-
subsets: this.#getFontSubsets(font),
|
|
3119
|
-
subsetText: font.subsetText,
|
|
3120
|
-
subsetFiles: font.subsetFiles,
|
|
3121
|
-
declarations: [...font.declarations ?? [], ...fontPath.declarations ?? []]
|
|
3122
|
-
});
|
|
3123
|
-
return `${FONT_URL_PREFIX}/${this.#slugFontPart(font.name)}-${this.#slugFontPart(String(fontPath.weight))}-${style}-${hash}.woff2`;
|
|
2892
|
+
return { cssPaths: [...cssFiles], sourcePaths: [...sourceFiles] };
|
|
3124
2893
|
}
|
|
3125
|
-
|
|
3126
|
-
|
|
3127
|
-
|
|
3128
|
-
|
|
3129
|
-
|
|
3130
|
-
|
|
2894
|
+
async compileCss(cssPaths, sourcePaths) {
|
|
2895
|
+
if (cssPaths.length === 0)
|
|
2896
|
+
return "";
|
|
2897
|
+
const compileStarted = Date.now();
|
|
2898
|
+
const compilers = await Promise.all(cssPaths.map(async (cssPath) => {
|
|
2899
|
+
const css = await Bun.file(cssPath).text();
|
|
2900
|
+
const base = path15.dirname(cssPath);
|
|
2901
|
+
const compiler = await compile(css, {
|
|
2902
|
+
base,
|
|
2903
|
+
loadStylesheet: (id, fromBase) => this.#loadStylesheet(id, fromBase),
|
|
2904
|
+
loadModule: (id, fromBase) => this.#loadModule(id, fromBase)
|
|
2905
|
+
});
|
|
2906
|
+
return { cssPath, compiler };
|
|
2907
|
+
}));
|
|
2908
|
+
const sourceDirs = new Set;
|
|
2909
|
+
for (const entry of compilers) {
|
|
2910
|
+
if (!entry)
|
|
2911
|
+
continue;
|
|
2912
|
+
for (const s of entry.compiler.sources)
|
|
2913
|
+
sourceDirs.add(s.base);
|
|
3131
2914
|
}
|
|
3132
|
-
|
|
3133
|
-
|
|
3134
|
-
|
|
3135
|
-
|
|
3136
|
-
|
|
3137
|
-
|
|
3138
|
-
|
|
3139
|
-
|
|
2915
|
+
const scanStarted = Date.now();
|
|
2916
|
+
const candidates = await this.#scanCandidates(sourcePaths, [...sourceDirs]);
|
|
2917
|
+
this.#logger.verbose(`css candidates scanned count=${candidates.length} sources=${sourcePaths.length} dirs=${sourceDirs.size} in ${Date.now() - scanStarted}ms`);
|
|
2918
|
+
const parts = [];
|
|
2919
|
+
for (const entry of compilers) {
|
|
2920
|
+
if (!entry)
|
|
2921
|
+
continue;
|
|
2922
|
+
parts.push(entry.compiler.build(candidates));
|
|
3140
2923
|
}
|
|
3141
|
-
|
|
2924
|
+
this.#logger.verbose(`css compiled paths=${cssPaths.length} candidates=${candidates.length} in ${Date.now() - compileStarted}ms`);
|
|
2925
|
+
return parts.join(`
|
|
2926
|
+
`);
|
|
3142
2927
|
}
|
|
3143
|
-
#
|
|
3144
|
-
|
|
2928
|
+
async#loadStylesheet(id, fromBase) {
|
|
2929
|
+
const p = await this.#resolveCssImport(id, fromBase);
|
|
2930
|
+
const content = await Bun.file(p).text();
|
|
2931
|
+
return { path: p, base: path15.dirname(p), content };
|
|
3145
2932
|
}
|
|
3146
|
-
async#
|
|
3147
|
-
if (
|
|
3148
|
-
return
|
|
3149
|
-
const
|
|
3150
|
-
const
|
|
3151
|
-
|
|
3152
|
-
|
|
3153
|
-
|
|
3154
|
-
|
|
3155
|
-
|
|
3156
|
-
|
|
3157
|
-
|
|
2933
|
+
async#resolveCssImport(id, fromBase) {
|
|
2934
|
+
if (id.startsWith(".") || id.startsWith("/"))
|
|
2935
|
+
return path15.resolve(fromBase, id);
|
|
2936
|
+
const resolver = await this.#getCssImportResolver();
|
|
2937
|
+
const resolved = await resolver.resolve(id, fromBase);
|
|
2938
|
+
if (resolved)
|
|
2939
|
+
return resolved;
|
|
2940
|
+
throw new Error(`[css] failed to resolve stylesheet import "${id}" from ${fromBase}`);
|
|
2941
|
+
}
|
|
2942
|
+
async#getCssImportResolver() {
|
|
2943
|
+
if (this.#cssImportResolver)
|
|
2944
|
+
return this.#cssImportResolver;
|
|
2945
|
+
this.#cssImportResolver = await CssImportResolver.create(this.#app);
|
|
2946
|
+
return this.#cssImportResolver;
|
|
2947
|
+
}
|
|
2948
|
+
async#loadModule(id, fromBase) {
|
|
2949
|
+
const p = __require.resolve(id, { paths: [fromBase] });
|
|
2950
|
+
const mod = await import(p);
|
|
2951
|
+
return { path: p, base: path15.dirname(p), module: mod.default ?? mod };
|
|
2952
|
+
}
|
|
2953
|
+
async#resolveSourceImport(id, fromBase, resolvePackage) {
|
|
2954
|
+
if (id.startsWith(".") || id.startsWith("/")) {
|
|
2955
|
+
const abs = id.startsWith("/") ? id : path15.resolve(fromBase, id);
|
|
2956
|
+
return resolveSourceFileCandidate(abs);
|
|
2957
|
+
}
|
|
2958
|
+
const pkg = await resolvePackage(id);
|
|
2959
|
+
if (pkg)
|
|
2960
|
+
return pkg.entryFile;
|
|
2961
|
+
for (const resolve of [() => resolveSourceWithBun(id, fromBase), () => resolveSourceWithRequire(id, fromBase)]) {
|
|
2962
|
+
const resolved = await resolve();
|
|
2963
|
+
if (resolved)
|
|
2964
|
+
return resolved;
|
|
3158
2965
|
}
|
|
3159
2966
|
return null;
|
|
3160
2967
|
}
|
|
3161
|
-
async#
|
|
3162
|
-
|
|
3163
|
-
const
|
|
3164
|
-
|
|
2968
|
+
async#scanCandidates(sourcePaths, dirs) {
|
|
2969
|
+
const CANDIDATE_RE = /-?[\w@][\w:/.-]*(?:\[[^\]]+\][\w:/.-]*)*/g;
|
|
2970
|
+
const candidates = new Set;
|
|
2971
|
+
const glob = new Bun.Glob("**/*.{tsx,ts,jsx,js,html}");
|
|
2972
|
+
const files = new Set(sourcePaths);
|
|
2973
|
+
await Promise.all(dirs.map(async (dir) => {
|
|
2974
|
+
for await (const file of glob.scan({ cwd: dir, absolute: true })) {
|
|
2975
|
+
if (isIgnoredNodeModuleSource(file))
|
|
2976
|
+
continue;
|
|
2977
|
+
files.add(file);
|
|
2978
|
+
}
|
|
2979
|
+
}));
|
|
2980
|
+
await Promise.all([...files].map(async (file) => {
|
|
2981
|
+
const content = await Bun.file(file).text();
|
|
2982
|
+
for (const m of content.matchAll(CANDIDATE_RE))
|
|
2983
|
+
candidates.add(m[0]);
|
|
2984
|
+
}));
|
|
2985
|
+
return [...candidates];
|
|
3165
2986
|
}
|
|
3166
|
-
|
|
3167
|
-
|
|
3168
|
-
|
|
3169
|
-
|
|
3170
|
-
|
|
2987
|
+
}
|
|
2988
|
+
async function resolveSourceFileCandidate(absPathNoExt) {
|
|
2989
|
+
if (await Bun.file(absPathNoExt).exists())
|
|
2990
|
+
return isSourceFile(absPathNoExt) ? absPathNoExt : null;
|
|
2991
|
+
for (const ext of SOURCE_EXTS2) {
|
|
2992
|
+
const filePath = `${absPathNoExt}${ext}`;
|
|
2993
|
+
if (await Bun.file(filePath).exists())
|
|
2994
|
+
return filePath;
|
|
3171
2995
|
}
|
|
3172
|
-
|
|
3173
|
-
const
|
|
3174
|
-
if (
|
|
3175
|
-
return
|
|
3176
|
-
if (signature === "wOF2")
|
|
3177
|
-
return "woff2";
|
|
3178
|
-
if (signature === "OTTO")
|
|
3179
|
-
return "otf";
|
|
3180
|
-
const ext = path16.extname(sourcePath).slice(1).toLowerCase();
|
|
3181
|
-
if (ext === "otf" || ext === "woff" || ext === "woff2")
|
|
3182
|
-
return ext;
|
|
3183
|
-
return "ttf";
|
|
2996
|
+
for (const ext of SOURCE_EXTS2) {
|
|
2997
|
+
const filePath = path15.join(absPathNoExt, `index${ext}`);
|
|
2998
|
+
if (await Bun.file(filePath).exists())
|
|
2999
|
+
return filePath;
|
|
3184
3000
|
}
|
|
3185
|
-
|
|
3186
|
-
|
|
3187
|
-
|
|
3188
|
-
|
|
3189
|
-
|
|
3001
|
+
return null;
|
|
3002
|
+
}
|
|
3003
|
+
function resolveSourceWithBun(id, fromBase) {
|
|
3004
|
+
try {
|
|
3005
|
+
const resolved = Bun.resolveSync(id, fromBase);
|
|
3006
|
+
return isSourceFile(resolved) ? resolved : null;
|
|
3007
|
+
} catch {
|
|
3008
|
+
return null;
|
|
3190
3009
|
}
|
|
3191
|
-
|
|
3192
|
-
|
|
3193
|
-
|
|
3194
|
-
|
|
3195
|
-
|
|
3196
|
-
|
|
3197
|
-
|
|
3198
|
-
for (const filePath of font.subsetFiles ?? []) {
|
|
3199
|
-
const abs = path16.isAbsolute(filePath) ? filePath : path16.join(this.#app.cwdPath, filePath);
|
|
3200
|
-
const file = Bun.file(abs);
|
|
3201
|
-
if (await file.exists())
|
|
3202
|
-
parts.add(await file.text());
|
|
3203
|
-
}
|
|
3204
|
-
return [...parts].join("");
|
|
3010
|
+
}
|
|
3011
|
+
function resolveSourceWithRequire(id, fromBase) {
|
|
3012
|
+
try {
|
|
3013
|
+
const resolved = __require.resolve(id, { paths: [fromBase] });
|
|
3014
|
+
return isSourceFile(resolved) ? resolved : null;
|
|
3015
|
+
} catch {
|
|
3016
|
+
return null;
|
|
3205
3017
|
}
|
|
3206
|
-
|
|
3207
|
-
|
|
3208
|
-
|
|
3209
|
-
|
|
3210
|
-
|
|
3211
|
-
|
|
3212
|
-
|
|
3213
|
-
|
|
3214
|
-
|
|
3215
|
-
|
|
3018
|
+
}
|
|
3019
|
+
function isSourceFile(filePath) {
|
|
3020
|
+
return SOURCE_EXTS2.includes(path15.extname(filePath));
|
|
3021
|
+
}
|
|
3022
|
+
function isIgnoredNodeModuleSource(filePath) {
|
|
3023
|
+
return NODE_MODULES_RE3.test(filePath) && !AKANJS_NODE_MODULE_RE3.test(filePath);
|
|
3024
|
+
}
|
|
3025
|
+
function getPageKeyBasePath(pageKey, basePaths) {
|
|
3026
|
+
const normalized = pageKey.split(path15.sep).join("/").replace(/^\.\//, "");
|
|
3027
|
+
const segments = normalized.split("/");
|
|
3028
|
+
const firstPublicSegment = segments.find((segment) => segment !== "[lang]" && !/^\(.+\)$/.test(segment));
|
|
3029
|
+
return firstPublicSegment && basePaths.includes(firstPublicSegment) ? firstPublicSegment : null;
|
|
3030
|
+
}
|
|
3031
|
+
|
|
3032
|
+
// pkgs/@akanjs/devkit/frontendBuild/devChangePlanner.ts
|
|
3033
|
+
import path16 from "path";
|
|
3034
|
+
var SOURCE_EXTS3 = new Set([".ts", ".tsx", ".js", ".jsx", ".mjs", ".cjs"]);
|
|
3035
|
+
var CONFIG_BASENAMES = new Set(["akan.config.ts", "bunfig.toml", "tsconfig.json", "package.json"]);
|
|
3036
|
+
var BARREL_FACETS = new Set(["common", "srvkit", "ui", "webkit", "plugin"]);
|
|
3037
|
+
var CLIENT_SUFFIXES = [".Template.tsx", ".Unit.tsx", ".Util.tsx", ".View.tsx", ".Zone.tsx", ".store.ts"];
|
|
3038
|
+
var SHARED_SUFFIXES = [".constant.ts", ".dictionary.ts", ".signal.ts"];
|
|
3039
|
+
var SERVER_SUFFIXES = [".service.ts", ".document.ts"];
|
|
3040
|
+
var RUNTIME_METADATA_BASENAMES = new Set(["dict.ts", "sig.ts", "useClient.ts", "useServer.ts"]);
|
|
3041
|
+
|
|
3042
|
+
class DevChangePlanner {
|
|
3043
|
+
#workspaceRoot;
|
|
3044
|
+
constructor({ workspaceRoot }) {
|
|
3045
|
+
this.#workspaceRoot = path16.resolve(workspaceRoot);
|
|
3216
3046
|
}
|
|
3217
|
-
|
|
3218
|
-
const
|
|
3219
|
-
const
|
|
3220
|
-
const
|
|
3221
|
-
|
|
3222
|
-
|
|
3223
|
-
|
|
3224
|
-
|
|
3225
|
-
|
|
3047
|
+
plan({ generation, files, kinds, generatedFiles = [] }) {
|
|
3048
|
+
const fileList = uniqueResolved([...files, ...generatedFiles]);
|
|
3049
|
+
const generatedSet = new Set(generatedFiles.map((file) => path16.resolve(file)));
|
|
3050
|
+
const kindSet = new Set(kinds);
|
|
3051
|
+
const roles = new Set;
|
|
3052
|
+
const actions = new Set;
|
|
3053
|
+
const reasonByFile = {};
|
|
3054
|
+
for (const kind of kindSet) {
|
|
3055
|
+
if (kind === "css") {
|
|
3056
|
+
roles.add("css");
|
|
3057
|
+
actions.add("rebuild-css");
|
|
3226
3058
|
}
|
|
3227
|
-
|
|
3228
|
-
|
|
3229
|
-
|
|
3230
|
-
#rangeText(start, end) {
|
|
3231
|
-
let text = "";
|
|
3232
|
-
for (let code = start;code <= end; code++)
|
|
3233
|
-
text += String.fromCodePoint(code);
|
|
3234
|
-
return text;
|
|
3235
|
-
}
|
|
3236
|
-
static #getKsX1001Text() {
|
|
3237
|
-
if (FontOptimizer.#ksX1001Text)
|
|
3238
|
-
return FontOptimizer.#ksX1001Text;
|
|
3239
|
-
try {
|
|
3240
|
-
const decoder = new TextDecoder("euc-kr");
|
|
3241
|
-
const chars = new Set;
|
|
3242
|
-
for (let lead = 161;lead <= 254; lead++) {
|
|
3243
|
-
for (let trail = 161;trail <= 254; trail++) {
|
|
3244
|
-
const char = decoder.decode(Uint8Array.of(lead, trail));
|
|
3245
|
-
if (char && char !== "\uFFFD")
|
|
3246
|
-
chars.add(char);
|
|
3247
|
-
}
|
|
3059
|
+
if (kind === "config") {
|
|
3060
|
+
roles.add("config");
|
|
3061
|
+
actions.add("restart-dev-host");
|
|
3248
3062
|
}
|
|
3249
|
-
FontOptimizer.#ksX1001Text = [...chars].join("");
|
|
3250
|
-
} catch {
|
|
3251
|
-
FontOptimizer.#ksX1001Text = FontOptimizer.#rangeTextStatic(44032, 55203);
|
|
3252
3063
|
}
|
|
3253
|
-
|
|
3254
|
-
|
|
3255
|
-
|
|
3256
|
-
|
|
3257
|
-
|
|
3258
|
-
|
|
3259
|
-
|
|
3260
|
-
|
|
3261
|
-
|
|
3262
|
-
|
|
3263
|
-
|
|
3264
|
-
|
|
3265
|
-
|
|
3266
|
-
|
|
3267
|
-
|
|
3268
|
-
|
|
3269
|
-
|
|
3270
|
-
|
|
3271
|
-
return
|
|
3272
|
-
|
|
3273
|
-
|
|
3274
|
-
|
|
3064
|
+
for (const file of fileList) {
|
|
3065
|
+
const reasons = new Set;
|
|
3066
|
+
const fileRoles = this.#rolesForFile(file, { isGenerated: generatedSet.has(path16.resolve(file)), reasons });
|
|
3067
|
+
for (const role of fileRoles)
|
|
3068
|
+
roles.add(role);
|
|
3069
|
+
if (reasons.has("runtime-metadata"))
|
|
3070
|
+
actions.add("restart-builder");
|
|
3071
|
+
if (reasons.size > 0)
|
|
3072
|
+
reasonByFile[path16.resolve(file)] = [...reasons].sort();
|
|
3073
|
+
}
|
|
3074
|
+
if (roles.has("barrel"))
|
|
3075
|
+
actions.add("sync-generated");
|
|
3076
|
+
if (roles.has("server") || roles.has("shared"))
|
|
3077
|
+
actions.add("restart-backend");
|
|
3078
|
+
if (roles.has("client") || roles.has("shared"))
|
|
3079
|
+
actions.add("rebuild-client");
|
|
3080
|
+
if (roles.has("css"))
|
|
3081
|
+
actions.add("rebuild-css");
|
|
3082
|
+
return {
|
|
3083
|
+
generation,
|
|
3084
|
+
files: fileList,
|
|
3085
|
+
generatedFiles: uniqueResolved(generatedFiles),
|
|
3086
|
+
roles: [...roles].sort(),
|
|
3087
|
+
actions: [...actions].sort(),
|
|
3088
|
+
reasonByFile
|
|
3089
|
+
};
|
|
3275
3090
|
}
|
|
3276
|
-
|
|
3277
|
-
|
|
3278
|
-
|
|
3279
|
-
const
|
|
3280
|
-
|
|
3281
|
-
|
|
3282
|
-
const
|
|
3283
|
-
|
|
3284
|
-
|
|
3285
|
-
|
|
3286
|
-
|
|
3287
|
-
|
|
3288
|
-
|
|
3289
|
-
|
|
3290
|
-
|
|
3291
|
-
|
|
3292
|
-
|
|
3293
|
-
|
|
3294
|
-
|
|
3295
|
-
|
|
3296
|
-
|
|
3297
|
-
|
|
3298
|
-
|
|
3299
|
-
}));
|
|
3091
|
+
#rolesForFile(file, { isGenerated, reasons }) {
|
|
3092
|
+
const roles = new Set;
|
|
3093
|
+
const abs = path16.resolve(file);
|
|
3094
|
+
const base = path16.basename(abs);
|
|
3095
|
+
const ext = path16.extname(abs).toLowerCase();
|
|
3096
|
+
const isSource = SOURCE_EXTS3.has(ext);
|
|
3097
|
+
const rel = path16.relative(this.#workspaceRoot, abs);
|
|
3098
|
+
const parts = rel.split(path16.sep).filter(Boolean);
|
|
3099
|
+
if (CONFIG_BASENAMES.has(base)) {
|
|
3100
|
+
roles.add("config");
|
|
3101
|
+
reasons.add("config-file");
|
|
3102
|
+
}
|
|
3103
|
+
if (ext === ".css") {
|
|
3104
|
+
roles.add("css");
|
|
3105
|
+
reasons.add("css-file");
|
|
3106
|
+
}
|
|
3107
|
+
if (isGenerated || isSource && this.#isBarrelFacetChild(parts)) {
|
|
3108
|
+
roles.add("barrel");
|
|
3109
|
+
reasons.add(isGenerated ? "generated-index" : "barrel-facet-child");
|
|
3110
|
+
}
|
|
3111
|
+
if (isSource && this.#isServerBiased(abs, parts)) {
|
|
3112
|
+
roles.add("server");
|
|
3113
|
+
reasons.add("server-path");
|
|
3300
3114
|
}
|
|
3301
|
-
|
|
3115
|
+
if (isSource && this.#isClientBiased(abs, parts)) {
|
|
3116
|
+
roles.add("client");
|
|
3117
|
+
reasons.add("client-path");
|
|
3118
|
+
}
|
|
3119
|
+
if (isSource && this.#isSharedBiased(abs, parts)) {
|
|
3120
|
+
roles.add("shared");
|
|
3121
|
+
reasons.add("shared-path");
|
|
3122
|
+
}
|
|
3123
|
+
if (isSource && this.#isRuntimeMetadataFile(parts, base)) {
|
|
3124
|
+
reasons.add("runtime-metadata");
|
|
3125
|
+
}
|
|
3126
|
+
if (roles.has("server") && roles.has("client")) {
|
|
3127
|
+
roles.delete("server");
|
|
3128
|
+
roles.delete("client");
|
|
3129
|
+
roles.add("shared");
|
|
3130
|
+
reasons.add("server-client-overlap");
|
|
3131
|
+
}
|
|
3132
|
+
if (roles.size === 0 && SOURCE_EXTS3.has(ext) && this.#isWorkspaceSource(rel)) {
|
|
3133
|
+
roles.add("shared");
|
|
3134
|
+
reasons.add("workspace-source-fallback");
|
|
3135
|
+
}
|
|
3136
|
+
return roles;
|
|
3302
3137
|
}
|
|
3303
|
-
#
|
|
3304
|
-
|
|
3138
|
+
#isServerBiased(abs, parts) {
|
|
3139
|
+
const base = path16.basename(abs);
|
|
3140
|
+
return parts.includes("srvkit") || SERVER_SUFFIXES.some((suffix) => base.endsWith(suffix)) || base === "main.ts" || base === "server.ts";
|
|
3305
3141
|
}
|
|
3306
|
-
#
|
|
3307
|
-
const
|
|
3308
|
-
|
|
3309
|
-
for (const font of fonts) {
|
|
3310
|
-
const className = font.className || `font-${font.name}`;
|
|
3311
|
-
const selector = `.${this.#escapeCssClassName(className)}`;
|
|
3312
|
-
const rule = `${selector} { font-family: var(${this.#getFontVariableName(font)}); }`;
|
|
3313
|
-
if (seen.has(rule))
|
|
3314
|
-
continue;
|
|
3315
|
-
seen.add(rule);
|
|
3316
|
-
rules.push(rule);
|
|
3317
|
-
}
|
|
3318
|
-
return rules.join(`
|
|
3319
|
-
`);
|
|
3142
|
+
#isClientBiased(abs, parts) {
|
|
3143
|
+
const base = path16.basename(abs);
|
|
3144
|
+
return parts.includes("ui") || parts.includes("webkit") || parts.includes("page") || CLIENT_SUFFIXES.some((suffix) => base.endsWith(suffix));
|
|
3320
3145
|
}
|
|
3321
|
-
#
|
|
3322
|
-
|
|
3146
|
+
#isSharedBiased(abs, parts) {
|
|
3147
|
+
const base = path16.basename(abs);
|
|
3148
|
+
return parts.includes("common") || SHARED_SUFFIXES.some((suffix) => base.endsWith(suffix)) || RUNTIME_METADATA_BASENAMES.has(base);
|
|
3323
3149
|
}
|
|
3324
|
-
#
|
|
3325
|
-
|
|
3150
|
+
#isRuntimeMetadataFile(parts, base) {
|
|
3151
|
+
const parent = parts.at(-2);
|
|
3152
|
+
if (parent === "lib" && RUNTIME_METADATA_BASENAMES.has(base))
|
|
3153
|
+
return true;
|
|
3154
|
+
const libIndex = parts.lastIndexOf("lib");
|
|
3155
|
+
if (libIndex < 0 || parts.length <= libIndex + 1)
|
|
3156
|
+
return false;
|
|
3157
|
+
return base.endsWith(".dictionary.ts") || base.endsWith(".signal.ts");
|
|
3326
3158
|
}
|
|
3327
|
-
#
|
|
3328
|
-
|
|
3159
|
+
#isBarrelFacetChild(parts) {
|
|
3160
|
+
if (parts.length < 4)
|
|
3161
|
+
return false;
|
|
3162
|
+
const [scope, , facet, child] = parts;
|
|
3163
|
+
if (scope !== "apps" && scope !== "libs")
|
|
3164
|
+
return false;
|
|
3165
|
+
if (!facet || !BARREL_FACETS.has(facet))
|
|
3166
|
+
return false;
|
|
3167
|
+
if (!child || child.startsWith(".") || child === "index.ts" || child === "index.tsx")
|
|
3168
|
+
return false;
|
|
3169
|
+
return true;
|
|
3170
|
+
}
|
|
3171
|
+
#isWorkspaceSource(rel) {
|
|
3172
|
+
if (!rel || rel.startsWith("..") || path16.isAbsolute(rel))
|
|
3173
|
+
return false;
|
|
3174
|
+
const [scope] = rel.split(path16.sep);
|
|
3175
|
+
return scope === "apps" || scope === "libs" || scope === "pkgs";
|
|
3329
3176
|
}
|
|
3330
3177
|
}
|
|
3178
|
+
var uniqueResolved = (files) => [...new Set(files.map((file) => path16.resolve(file)))].sort();
|
|
3331
3179
|
|
|
3332
|
-
// pkgs/@akanjs/devkit/frontendBuild/
|
|
3333
|
-
import
|
|
3334
|
-
import path18 from "path";
|
|
3335
|
-
|
|
3336
|
-
// pkgs/@akanjs/devkit/frontendBuild/hmrChangeClassifier.ts
|
|
3180
|
+
// pkgs/@akanjs/devkit/frontendBuild/devGeneratedIndexSync.ts
|
|
3181
|
+
import { mkdir as mkdir4, readdir as readdir2, readFile as readFile2, rm as rm2, stat as stat2, writeFile as writeFile2 } from "fs/promises";
|
|
3337
3182
|
import path17 from "path";
|
|
3338
|
-
var
|
|
3339
|
-
var
|
|
3340
|
-
var
|
|
3341
|
-
|
|
3342
|
-
|
|
3343
|
-
|
|
3344
|
-
|
|
3345
|
-
|
|
3346
|
-
const base = path17.basename(abs);
|
|
3347
|
-
if (CONFIG_BASENAMES2.has(base))
|
|
3348
|
-
return "config";
|
|
3349
|
-
const ext = path17.extname(abs).toLowerCase();
|
|
3350
|
-
if (CSS_EXTS.has(ext))
|
|
3351
|
-
return "css";
|
|
3352
|
-
if (SOURCE_EXTS3.has(ext))
|
|
3353
|
-
return "code";
|
|
3354
|
-
return "ignore";
|
|
3355
|
-
}
|
|
3356
|
-
#isUninteresting(abs) {
|
|
3357
|
-
const base = path17.basename(abs);
|
|
3358
|
-
if (!base)
|
|
3359
|
-
return true;
|
|
3360
|
-
if (base.startsWith("."))
|
|
3361
|
-
return true;
|
|
3362
|
-
if (base.endsWith("~") || base.endsWith(".swp") || base.endsWith(".swx") || base.endsWith(".tmp"))
|
|
3363
|
-
return true;
|
|
3364
|
-
if (abs.includes(`${path17.sep}node_modules${path17.sep}`))
|
|
3365
|
-
return true;
|
|
3366
|
-
if (abs.includes(`${path17.sep}.akan${path17.sep}`))
|
|
3367
|
-
return true;
|
|
3368
|
-
return false;
|
|
3369
|
-
}
|
|
3370
|
-
}
|
|
3183
|
+
var BARREL_FACETS2 = new Set(["common", "srvkit", "ui", "webkit", "plugin"]);
|
|
3184
|
+
var FACET_SOURCE_FILE_RE = /\.(ts|tsx)$/;
|
|
3185
|
+
var FACET_EXCLUDED_FILE_RE = /(^index\.tsx?$|\.d\.ts$|\.(test|spec)\.(ts|tsx)$|\.css$|\.scss$|\.sass$)/;
|
|
3186
|
+
var FACET_PASCAL_CASE_RE = /^[A-Z][A-Za-z0-9]*$/;
|
|
3187
|
+
var FACET_CAMEL_CASE_RE = /^[a-z][A-Za-z0-9]*$/;
|
|
3188
|
+
var MODULE_UI_TYPES = ["Template", "Unit", "Util", "View", "Zone"];
|
|
3189
|
+
var SERVICE_UI_TYPES = ["Util", "Zone"];
|
|
3190
|
+
var SCALAR_UI_TYPES = ["Template", "Unit"];
|
|
3371
3191
|
|
|
3372
|
-
|
|
3373
|
-
|
|
3374
|
-
|
|
3375
|
-
|
|
3376
|
-
#onBatch;
|
|
3377
|
-
#logger;
|
|
3378
|
-
#watchers = [];
|
|
3379
|
-
#pending = new Map;
|
|
3380
|
-
#classifier = new HmrChangeClassifier;
|
|
3381
|
-
#timer = null;
|
|
3382
|
-
#stopped = false;
|
|
3383
|
-
#flushing = false;
|
|
3384
|
-
constructor(opts) {
|
|
3385
|
-
this.#roots = [...new Set(opts.roots.map((r) => path18.resolve(r)))];
|
|
3386
|
-
this.#debounceMs = opts.debounceMs ?? 80;
|
|
3387
|
-
this.#onBatch = opts.onBatch;
|
|
3388
|
-
this.#logger = opts.logger;
|
|
3192
|
+
class DevGeneratedIndexSync {
|
|
3193
|
+
#workspaceRoot;
|
|
3194
|
+
constructor({ workspaceRoot }) {
|
|
3195
|
+
this.#workspaceRoot = path17.resolve(workspaceRoot);
|
|
3389
3196
|
}
|
|
3390
|
-
|
|
3391
|
-
|
|
3392
|
-
|
|
3393
|
-
|
|
3394
|
-
|
|
3395
|
-
|
|
3396
|
-
|
|
3397
|
-
|
|
3398
|
-
});
|
|
3399
|
-
|
|
3400
|
-
|
|
3401
|
-
|
|
3402
|
-
|
|
3403
|
-
}
|
|
3197
|
+
async syncForBatch(files) {
|
|
3198
|
+
const indexPaths = new Set;
|
|
3199
|
+
const errors = [];
|
|
3200
|
+
for (const file of files) {
|
|
3201
|
+
const facetIndex = this.#facetIndexFor(file);
|
|
3202
|
+
if (facetIndex)
|
|
3203
|
+
indexPaths.add(facetIndex);
|
|
3204
|
+
const moduleIndex = await this.#moduleIndexForDirectoryEvent(file).catch((err) => {
|
|
3205
|
+
errors.push(`[generated-index] module detection failed for ${file}: ${formatError2(err)}`);
|
|
3206
|
+
return null;
|
|
3207
|
+
});
|
|
3208
|
+
if (moduleIndex)
|
|
3209
|
+
indexPaths.add(moduleIndex);
|
|
3404
3210
|
}
|
|
3405
|
-
|
|
3406
|
-
|
|
3407
|
-
this.#stopped = true;
|
|
3408
|
-
if (this.#timer)
|
|
3409
|
-
clearTimeout(this.#timer);
|
|
3410
|
-
for (const w of this.#watchers) {
|
|
3211
|
+
const changedFiles = [];
|
|
3212
|
+
for (const indexPath of [...indexPaths].sort()) {
|
|
3411
3213
|
try {
|
|
3412
|
-
|
|
3413
|
-
|
|
3414
|
-
|
|
3415
|
-
|
|
3416
|
-
|
|
3417
|
-
const kind = this.#classifier.classify(abs);
|
|
3418
|
-
if (kind === "ignore")
|
|
3419
|
-
return;
|
|
3420
|
-
this.#pending.set(abs, kind);
|
|
3421
|
-
if (this.#flushing)
|
|
3422
|
-
return;
|
|
3423
|
-
if (this.#timer)
|
|
3424
|
-
clearTimeout(this.#timer);
|
|
3425
|
-
this.#timer = setTimeout(() => this.#flush(), this.#debounceMs);
|
|
3426
|
-
}
|
|
3427
|
-
#flush() {
|
|
3428
|
-
this.#timer = null;
|
|
3429
|
-
if (this.#stopped || this.#pending.size === 0 || this.#flushing)
|
|
3430
|
-
return;
|
|
3431
|
-
this.#drain();
|
|
3432
|
-
}
|
|
3433
|
-
async#drain() {
|
|
3434
|
-
this.#flushing = true;
|
|
3435
|
-
try {
|
|
3436
|
-
while (!this.#stopped && this.#pending.size > 0) {
|
|
3437
|
-
const files = Array.from(this.#pending.keys());
|
|
3438
|
-
const kinds = new Set(this.#pending.values());
|
|
3439
|
-
this.#pending.clear();
|
|
3440
|
-
try {
|
|
3441
|
-
await this.#onBatch({ files, kinds });
|
|
3442
|
-
} catch (e) {
|
|
3443
|
-
this.#logger.error(`[hmr] onBatch error: ${e.message}`);
|
|
3444
|
-
}
|
|
3214
|
+
const changed = await this.#syncIndex(indexPath);
|
|
3215
|
+
if (changed)
|
|
3216
|
+
changedFiles.push(indexPath);
|
|
3217
|
+
} catch (err) {
|
|
3218
|
+
errors.push(`[generated-index] sync failed for ${indexPath}: ${formatError2(err)}`);
|
|
3445
3219
|
}
|
|
3446
|
-
} finally {
|
|
3447
|
-
this.#flushing = false;
|
|
3448
|
-
if (!this.#stopped && this.#pending.size > 0)
|
|
3449
|
-
this.#timer = setTimeout(() => this.#flush(), this.#debounceMs);
|
|
3450
3220
|
}
|
|
3221
|
+
return { changedFiles, errors };
|
|
3451
3222
|
}
|
|
3452
|
-
|
|
3453
|
-
|
|
3454
|
-
|
|
3455
|
-
|
|
3456
|
-
|
|
3457
|
-
// pkgs/@akanjs/devkit/transforms/externalizeFrameworkPlugin.ts
|
|
3458
|
-
import path19 from "path";
|
|
3459
|
-
var DEFAULT_INCLUDE = ["akanjs/", "@apps/", "@libs/"];
|
|
3460
|
-
var DEFAULT_EXCLUDE_EXACT = new Set(["akanjs/webkit", "@akanjs/cli", "@akanjs/devkit"]);
|
|
3461
|
-
var DEFAULT_EXCLUDE_PREFIX = ["@akanjs/cli/", "@akanjs/devkit/"];
|
|
3462
|
-
var OPTIONAL_BACKEND_EXTERNAL_EXACT = new Set([
|
|
3463
|
-
"@libsql/client",
|
|
3464
|
-
"bullmq",
|
|
3465
|
-
"croner",
|
|
3466
|
-
"ioredis",
|
|
3467
|
-
"postgres",
|
|
3468
|
-
"protobufjs"
|
|
3469
|
-
]);
|
|
3470
|
-
var RUNTIME_EXTERNAL_EXACT = new Set([
|
|
3471
|
-
"react",
|
|
3472
|
-
"react-dom",
|
|
3473
|
-
"react/jsx-runtime",
|
|
3474
|
-
"react/jsx-dev-runtime",
|
|
3475
|
-
"react-server-dom-webpack",
|
|
3476
|
-
"react-server-dom-webpack/server.node",
|
|
3477
|
-
"react-server-dom-webpack/client.node",
|
|
3478
|
-
"react-server-dom-webpack/client.browser"
|
|
3479
|
-
]);
|
|
3480
|
-
var RUNTIME_EXTERNAL_PREFIX = ["react-dom/", "react-server-dom-webpack/"];
|
|
3481
|
-
var CANDIDATE_EXTS3 = [".tsx", ".ts", ".jsx", ".js", ".mjs", ".cjs"];
|
|
3482
|
-
async function createExternalizeFrameworkPlugin(options) {
|
|
3483
|
-
const tsconfig = await options.app.getTsConfig();
|
|
3484
|
-
const includePrefixes = options.include ?? DEFAULT_INCLUDE;
|
|
3485
|
-
const extraExact = new Set(options.extra ?? []);
|
|
3486
|
-
const workspaceRoot = options.app.workspace.workspaceRoot;
|
|
3487
|
-
const tsconfigPaths = tsconfig.compilerOptions.paths ?? {};
|
|
3488
|
-
const rootEntries = Object.entries(tsconfigPaths).filter(([k]) => !k.endsWith("/*")).map(([k, v]) => ({ pkg: k, entryFile: v[0] ?? null })).filter((e) => e.entryFile !== null);
|
|
3489
|
-
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);
|
|
3490
|
-
async function resolveWorkspaceSubpath(spec) {
|
|
3491
|
-
if (!workspaceRoot)
|
|
3223
|
+
#facetIndexFor(file) {
|
|
3224
|
+
const abs = path17.resolve(file);
|
|
3225
|
+
const rel = path17.relative(this.#workspaceRoot, abs);
|
|
3226
|
+
if (rel.startsWith("..") || path17.isAbsolute(rel))
|
|
3492
3227
|
return null;
|
|
3493
|
-
|
|
3494
|
-
|
|
3495
|
-
|
|
3496
|
-
|
|
3497
|
-
|
|
3498
|
-
|
|
3499
|
-
|
|
3500
|
-
|
|
3501
|
-
|
|
3502
|
-
const hit = await firstExisting(candidate);
|
|
3503
|
-
if (hit)
|
|
3504
|
-
return hit;
|
|
3505
|
-
}
|
|
3506
|
-
}
|
|
3507
|
-
for (const { pkg, entryFile } of rootEntries) {
|
|
3508
|
-
if (spec !== pkg && !spec.startsWith(`${pkg}/`))
|
|
3509
|
-
continue;
|
|
3510
|
-
if (spec === pkg)
|
|
3511
|
-
continue;
|
|
3512
|
-
const suffix = spec.slice(pkg.length + 1);
|
|
3513
|
-
const pkgDir = path19.dirname(path19.resolve(workspaceRoot, entryFile));
|
|
3514
|
-
const candidate = path19.join(pkgDir, suffix);
|
|
3515
|
-
const hit = await firstExisting(candidate);
|
|
3516
|
-
if (hit)
|
|
3517
|
-
return hit;
|
|
3518
|
-
}
|
|
3519
|
-
return null;
|
|
3228
|
+
const parts = rel.split(path17.sep).filter(Boolean);
|
|
3229
|
+
if (parts.length < 4)
|
|
3230
|
+
return null;
|
|
3231
|
+
const [scope, project, facet, child] = parts;
|
|
3232
|
+
if (scope !== "apps" && scope !== "libs" || !project || !facet || !BARREL_FACETS2.has(facet))
|
|
3233
|
+
return null;
|
|
3234
|
+
if (!child || child.startsWith(".") || child === "index.ts" || child === "index.tsx")
|
|
3235
|
+
return null;
|
|
3236
|
+
return path17.join(this.#workspaceRoot, scope, project, facet, "index.ts");
|
|
3520
3237
|
}
|
|
3521
|
-
|
|
3522
|
-
|
|
3523
|
-
|
|
3524
|
-
|
|
3525
|
-
|
|
3526
|
-
|
|
3527
|
-
|
|
3528
|
-
|
|
3529
|
-
|
|
3530
|
-
|
|
3531
|
-
|
|
3532
|
-
|
|
3533
|
-
|
|
3534
|
-
|
|
3535
|
-
|
|
3536
|
-
|
|
3537
|
-
|
|
3538
|
-
for (const prefix of RUNTIME_EXTERNAL_PREFIX) {
|
|
3539
|
-
if (spec.startsWith(prefix))
|
|
3540
|
-
return { path: spec, external: true };
|
|
3541
|
-
}
|
|
3542
|
-
for (const prefix of includePrefixes) {
|
|
3543
|
-
if (!spec.startsWith(prefix))
|
|
3544
|
-
continue;
|
|
3545
|
-
const resolved = await resolveWorkspaceSubpath(spec);
|
|
3546
|
-
if (resolved)
|
|
3547
|
-
return { path: resolved };
|
|
3548
|
-
return;
|
|
3549
|
-
}
|
|
3550
|
-
return;
|
|
3551
|
-
});
|
|
3238
|
+
async#moduleIndexForDirectoryEvent(file) {
|
|
3239
|
+
const abs = path17.resolve(file);
|
|
3240
|
+
const rel = path17.relative(this.#workspaceRoot, abs);
|
|
3241
|
+
if (rel.startsWith("..") || path17.isAbsolute(rel))
|
|
3242
|
+
return null;
|
|
3243
|
+
const parts = rel.split(path17.sep).filter(Boolean);
|
|
3244
|
+
if (parts.length !== 4 && parts.length !== 5)
|
|
3245
|
+
return null;
|
|
3246
|
+
const [scope, project, libSegment, moduleSegment, scalarSegment] = parts;
|
|
3247
|
+
if (scope !== "apps" && scope !== "libs" || !project || libSegment !== "lib")
|
|
3248
|
+
return null;
|
|
3249
|
+
const isExistingDirectory = await stat2(abs).then((s) => s.isDirectory()).catch(() => false);
|
|
3250
|
+
const looksLikeDeletedDirectory = !path17.extname(abs);
|
|
3251
|
+
if (!isExistingDirectory && !looksLikeDeletedDirectory)
|
|
3252
|
+
return null;
|
|
3253
|
+
if (moduleSegment === "__scalar" && scalarSegment) {
|
|
3254
|
+
return path17.join(this.#workspaceRoot, scope, project, "lib", "__scalar", scalarSegment, "index.ts");
|
|
3552
3255
|
}
|
|
3553
|
-
|
|
3554
|
-
|
|
3555
|
-
|
|
3556
|
-
if (await Bun.file(basePath).exists())
|
|
3557
|
-
return basePath;
|
|
3558
|
-
for (const ext of CANDIDATE_EXTS3) {
|
|
3559
|
-
const candidate = `${basePath}${ext}`;
|
|
3560
|
-
if (await Bun.file(candidate).exists())
|
|
3561
|
-
return candidate;
|
|
3256
|
+
if (!moduleSegment || moduleSegment === "__scalar")
|
|
3257
|
+
return null;
|
|
3258
|
+
return path17.join(this.#workspaceRoot, scope, project, "lib", moduleSegment, "index.ts");
|
|
3562
3259
|
}
|
|
3563
|
-
|
|
3564
|
-
const
|
|
3565
|
-
|
|
3566
|
-
|
|
3260
|
+
async#syncIndex(indexPath) {
|
|
3261
|
+
const dir = path17.dirname(indexPath);
|
|
3262
|
+
const content = await this.#contentForIndex(indexPath);
|
|
3263
|
+
if (content === null) {
|
|
3264
|
+
if (!await exists(indexPath))
|
|
3265
|
+
return false;
|
|
3266
|
+
await rm2(indexPath, { force: true });
|
|
3267
|
+
return true;
|
|
3268
|
+
}
|
|
3269
|
+
const current = await readFile2(indexPath, "utf8").catch(() => null);
|
|
3270
|
+
if (current === content)
|
|
3271
|
+
return false;
|
|
3272
|
+
await mkdir4(dir, { recursive: true });
|
|
3273
|
+
await writeFile2(indexPath, content);
|
|
3274
|
+
return true;
|
|
3567
3275
|
}
|
|
3568
|
-
|
|
3569
|
-
|
|
3570
|
-
|
|
3571
|
-
|
|
3572
|
-
|
|
3573
|
-
|
|
3574
|
-
|
|
3575
|
-
|
|
3576
|
-
|
|
3577
|
-
|
|
3578
|
-
|
|
3579
|
-
|
|
3580
|
-
|
|
3581
|
-
|
|
3582
|
-
|
|
3583
|
-
|
|
3584
|
-
|
|
3585
|
-
|
|
3586
|
-
|
|
3587
|
-
|
|
3588
|
-
|
|
3589
|
-
|
|
3590
|
-
|
|
3591
|
-
|
|
3592
|
-
|
|
3276
|
+
async#contentForIndex(indexPath) {
|
|
3277
|
+
const parts = path17.relative(this.#workspaceRoot, indexPath).split(path17.sep).filter(Boolean);
|
|
3278
|
+
const facet = parts.at(-2);
|
|
3279
|
+
if (facet && BARREL_FACETS2.has(facet))
|
|
3280
|
+
return this.#facetContent(path17.dirname(indexPath));
|
|
3281
|
+
return this.#moduleContent(path17.dirname(indexPath));
|
|
3282
|
+
}
|
|
3283
|
+
async#facetContent(dir) {
|
|
3284
|
+
const nameCasePattern = path17.basename(dir) === "ui" ? FACET_PASCAL_CASE_RE : FACET_CAMEL_CASE_RE;
|
|
3285
|
+
const entries = await readdir2(dir, { withFileTypes: true }).catch(() => []);
|
|
3286
|
+
const exportNames = entries.flatMap((entry) => {
|
|
3287
|
+
const name = entry.name;
|
|
3288
|
+
if (name.startsWith("."))
|
|
3289
|
+
return [];
|
|
3290
|
+
if (entry.isDirectory())
|
|
3291
|
+
return nameCasePattern.test(name) ? [name] : [];
|
|
3292
|
+
if (!entry.isFile())
|
|
3293
|
+
return [];
|
|
3294
|
+
if (!FACET_SOURCE_FILE_RE.test(name) || FACET_EXCLUDED_FILE_RE.test(name))
|
|
3295
|
+
return [];
|
|
3296
|
+
const exportName = name.replace(FACET_SOURCE_FILE_RE, "");
|
|
3297
|
+
return nameCasePattern.test(exportName) ? [exportName] : [];
|
|
3298
|
+
}).sort();
|
|
3299
|
+
if (exportNames.length === 0)
|
|
3300
|
+
return null;
|
|
3301
|
+
return `${exportNames.map((name) => `export * from "./${name}";`).join(`
|
|
3302
|
+
`)}
|
|
3303
|
+
`;
|
|
3304
|
+
}
|
|
3305
|
+
async#moduleContent(dir) {
|
|
3306
|
+
const rel = path17.relative(this.#workspaceRoot, dir);
|
|
3307
|
+
const parts = rel.split(path17.sep).filter(Boolean);
|
|
3308
|
+
const moduleSegment = parts.at(-1);
|
|
3309
|
+
if (!moduleSegment)
|
|
3310
|
+
return null;
|
|
3311
|
+
const isScalar = parts.at(-2) === "__scalar";
|
|
3312
|
+
const rawModel = moduleSegment.startsWith("_") ? moduleSegment.slice(1) : moduleSegment;
|
|
3313
|
+
if (!rawModel)
|
|
3314
|
+
return null;
|
|
3315
|
+
const modelName = capitalize(rawModel);
|
|
3316
|
+
const allowedTypes = isScalar ? SCALAR_UI_TYPES : moduleSegment.startsWith("_") ? SERVICE_UI_TYPES : MODULE_UI_TYPES;
|
|
3317
|
+
const fileTypes = [];
|
|
3318
|
+
for (const type of allowedTypes) {
|
|
3319
|
+
if (await exists(path17.join(dir, `${modelName}.${type}.tsx`)))
|
|
3320
|
+
fileTypes.push(type);
|
|
3593
3321
|
}
|
|
3594
|
-
|
|
3595
|
-
|
|
3596
|
-
|
|
3597
|
-
|
|
3598
|
-
|
|
3599
|
-
|
|
3600
|
-
|
|
3601
|
-
|
|
3602
|
-
return "ts";
|
|
3603
|
-
return "js";
|
|
3322
|
+
if (fileTypes.length === 0)
|
|
3323
|
+
return null;
|
|
3324
|
+
return `
|
|
3325
|
+
${fileTypes.map((type) => `import * as ${type} from "./${modelName}.${type}";`).join(`
|
|
3326
|
+
`)}
|
|
3327
|
+
|
|
3328
|
+
export const ${modelName} = { ${fileTypes.join(", ")} };`;
|
|
3329
|
+
}
|
|
3604
3330
|
}
|
|
3331
|
+
var exists = async (file) => stat2(file).then(() => true).catch(() => false);
|
|
3332
|
+
var capitalize = (value) => `${value.charAt(0).toUpperCase()}${value.slice(1)}`;
|
|
3333
|
+
var formatError2 = (err) => err instanceof Error ? err.message : String(err);
|
|
3605
3334
|
|
|
3606
|
-
// pkgs/@akanjs/devkit/frontendBuild/
|
|
3607
|
-
|
|
3335
|
+
// pkgs/@akanjs/devkit/frontendBuild/fontOptimizer.ts
|
|
3336
|
+
import { mkdir as mkdir5, stat as stat3 } from "fs/promises";
|
|
3337
|
+
import path18 from "path";
|
|
3338
|
+
import ts4 from "typescript";
|
|
3339
|
+
var FONT_URL_PREFIX = "/_akan/fonts";
|
|
3340
|
+
var DEFAULT_FONT_SUBSETS = ["latin"];
|
|
3608
3341
|
|
|
3609
|
-
class
|
|
3342
|
+
class FontOptimizer {
|
|
3610
3343
|
#app;
|
|
3611
3344
|
#command;
|
|
3612
|
-
#
|
|
3613
|
-
#
|
|
3614
|
-
|
|
3345
|
+
#artifactRoot;
|
|
3346
|
+
#files = [];
|
|
3347
|
+
#cssParts = [];
|
|
3348
|
+
#woff2Ready = null;
|
|
3349
|
+
static #ksX1001Text = null;
|
|
3350
|
+
static #cacheVersion = 1;
|
|
3351
|
+
constructor(app, command = "start") {
|
|
3615
3352
|
this.#app = app;
|
|
3616
3353
|
this.#command = command;
|
|
3617
|
-
this.#
|
|
3354
|
+
this.#artifactRoot = path18.join(command === "build" ? app.dist.cwdPath : app.cwdPath, ".akan/artifact");
|
|
3618
3355
|
}
|
|
3619
|
-
async
|
|
3620
|
-
const
|
|
3621
|
-
const
|
|
3622
|
-
const
|
|
3623
|
-
|
|
3624
|
-
|
|
3625
|
-
|
|
3626
|
-
|
|
3627
|
-
|
|
3628
|
-
|
|
3629
|
-
|
|
3630
|
-
|
|
3631
|
-
|
|
3632
|
-
|
|
3633
|
-
|
|
3634
|
-
|
|
3635
|
-
|
|
3636
|
-
|
|
3637
|
-
|
|
3638
|
-
|
|
3639
|
-
|
|
3640
|
-
|
|
3641
|
-
|
|
3642
|
-
|
|
3643
|
-
|
|
3644
|
-
|
|
3645
|
-
|
|
3646
|
-
|
|
3647
|
-
|
|
3648
|
-
|
|
3649
|
-
|
|
3650
|
-
|
|
3651
|
-
|
|
3652
|
-
|
|
3653
|
-
|
|
3654
|
-
|
|
3655
|
-
|
|
3656
|
-
|
|
3657
|
-
|
|
3658
|
-
|
|
3659
|
-
|
|
3660
|
-
|
|
3661
|
-
|
|
3662
|
-
|
|
3663
|
-
|
|
3664
|
-
|
|
3665
|
-
|
|
3666
|
-
|
|
3667
|
-
|
|
3356
|
+
async optimize() {
|
|
3357
|
+
const fonts = await this.discoverFonts();
|
|
3358
|
+
const cacheKey = await this.#buildCacheKey(fonts);
|
|
3359
|
+
const cached = cacheKey ? await this.#readCache(cacheKey) : null;
|
|
3360
|
+
if (cached) {
|
|
3361
|
+
this.#app.verbose(`[font] reused ${cached.files.length} cached file(s); skipped subsetting`);
|
|
3362
|
+
return { css: cached.css, fonts, files: cached.files };
|
|
3363
|
+
}
|
|
3364
|
+
for (const font of fonts) {
|
|
3365
|
+
if (!this.#isFontOptimizationEnabled(font))
|
|
3366
|
+
continue;
|
|
3367
|
+
await this.#optimizeFont(font);
|
|
3368
|
+
}
|
|
3369
|
+
const fontUtilityCss = this.#buildFontUtilityRules(fonts);
|
|
3370
|
+
if (fontUtilityCss)
|
|
3371
|
+
this.#cssParts.push(fontUtilityCss);
|
|
3372
|
+
const result = { css: this.#cssParts.join(`
|
|
3373
|
+
`), fonts, files: this.#files };
|
|
3374
|
+
if (cacheKey)
|
|
3375
|
+
await this.#writeCache(cacheKey, result);
|
|
3376
|
+
return result;
|
|
3377
|
+
}
|
|
3378
|
+
get #cachePath() {
|
|
3379
|
+
return path18.join(this.#artifactRoot, "fontCache.json");
|
|
3380
|
+
}
|
|
3381
|
+
async#buildCacheKey(fonts) {
|
|
3382
|
+
const sources = [];
|
|
3383
|
+
for (const font of fonts) {
|
|
3384
|
+
if (!this.#isFontOptimizationEnabled(font))
|
|
3385
|
+
continue;
|
|
3386
|
+
for (const face of this.#getFontFaces(font)) {
|
|
3387
|
+
const sourcePath = await this.#resolveFontSourcePath(face.src);
|
|
3388
|
+
const stamp = sourcePath ? await this.#fileStamp(sourcePath) : null;
|
|
3389
|
+
if (!stamp)
|
|
3390
|
+
return null;
|
|
3391
|
+
sources.push({ optimizedSrc: face.optimizedSrc, ...stamp });
|
|
3392
|
+
}
|
|
3393
|
+
for (const filePath of font.subsetFiles ?? []) {
|
|
3394
|
+
const abs = path18.isAbsolute(filePath) ? filePath : path18.join(this.#app.cwdPath, filePath);
|
|
3395
|
+
const stamp = await this.#fileStamp(abs);
|
|
3396
|
+
if (!stamp)
|
|
3397
|
+
return null;
|
|
3398
|
+
sources.push({ subsetFile: filePath, ...stamp });
|
|
3399
|
+
}
|
|
3400
|
+
if (this.#getFontSubsets(font).includes("auto"))
|
|
3401
|
+
sources.push({ autoSubsetText: this.#hashFontConfig(await this.#collectAutoSubsetText()) });
|
|
3402
|
+
}
|
|
3403
|
+
return this.#hashFontConfig({ version: FontOptimizer.#cacheVersion, fonts, sources });
|
|
3404
|
+
}
|
|
3405
|
+
async#fileStamp(filePath) {
|
|
3406
|
+
try {
|
|
3407
|
+
const stats = await stat3(filePath);
|
|
3408
|
+
return { mtimeMs: Math.round(stats.mtimeMs), size: stats.size };
|
|
3409
|
+
} catch {
|
|
3410
|
+
return null;
|
|
3411
|
+
}
|
|
3412
|
+
}
|
|
3413
|
+
async#readCache(key) {
|
|
3414
|
+
const cache = await Bun.file(this.#cachePath).json().catch(() => null);
|
|
3415
|
+
if (cache?.version !== FontOptimizer.#cacheVersion || cache.key !== key)
|
|
3416
|
+
return null;
|
|
3417
|
+
const files = cache.files.map((relativePath) => path18.join(this.#artifactRoot, relativePath));
|
|
3418
|
+
for (const filePath of files) {
|
|
3419
|
+
if (!await Bun.file(filePath).exists())
|
|
3420
|
+
return null;
|
|
3421
|
+
}
|
|
3422
|
+
return { css: cache.css, files };
|
|
3423
|
+
}
|
|
3424
|
+
async#writeCache(key, result) {
|
|
3425
|
+
const cache = {
|
|
3426
|
+
version: FontOptimizer.#cacheVersion,
|
|
3427
|
+
key,
|
|
3428
|
+
css: result.css,
|
|
3429
|
+
files: result.files.map((filePath) => path18.relative(this.#artifactRoot, filePath))
|
|
3668
3430
|
};
|
|
3431
|
+
await Bun.write(this.#cachePath, JSON.stringify(cache));
|
|
3432
|
+
}
|
|
3433
|
+
async discoverFonts() {
|
|
3434
|
+
const pageKeys = await this.#app.getPageKeys();
|
|
3435
|
+
const fonts = [];
|
|
3436
|
+
await Promise.all(pageKeys.map(async (key) => {
|
|
3437
|
+
const filePath = path18.resolve(this.#app.cwdPath, "page", key);
|
|
3438
|
+
const file = Bun.file(filePath);
|
|
3439
|
+
if (!await file.exists())
|
|
3440
|
+
return;
|
|
3441
|
+
const source = await file.text();
|
|
3442
|
+
if (!source.includes("fonts"))
|
|
3443
|
+
return;
|
|
3444
|
+
fonts.push(...this.#extractFontsExport(source, filePath));
|
|
3445
|
+
}));
|
|
3446
|
+
return this.#dedupeFonts(fonts);
|
|
3447
|
+
}
|
|
3448
|
+
async#optimizeFont(font) {
|
|
3449
|
+
const faceCss = [];
|
|
3450
|
+
for (const face of this.#getFontFaces(font)) {
|
|
3451
|
+
const sourcePath = await this.#resolveFontSourcePath(face.src);
|
|
3452
|
+
if (!sourcePath) {
|
|
3453
|
+
this.#app.logger.warn(`[font] source not found: ${face.src}`);
|
|
3454
|
+
continue;
|
|
3455
|
+
}
|
|
3456
|
+
const outputPath = path18.join(this.#artifactRoot, face.optimizedSrc.replace(/^\/_akan\//, ""));
|
|
3457
|
+
await mkdir5(path18.dirname(outputPath), { recursive: true });
|
|
3458
|
+
const sourceBuffer = Buffer.from(await Bun.file(sourcePath).arrayBuffer());
|
|
3459
|
+
const outputBuffer = await this.#buildFontBuffer(font, sourceBuffer, sourcePath);
|
|
3460
|
+
await Bun.write(outputPath, outputBuffer);
|
|
3461
|
+
this.#files.push(outputPath);
|
|
3462
|
+
faceCss.push(this.#buildOptimizedFontFaceRule(font, face));
|
|
3463
|
+
const fallbackCss = await this.#buildFontaineFallbackCss(font, face, outputPath);
|
|
3464
|
+
if (fallbackCss)
|
|
3465
|
+
faceCss.push(fallbackCss);
|
|
3466
|
+
}
|
|
3467
|
+
if (faceCss.length > 0)
|
|
3468
|
+
this.#cssParts.push(...faceCss, this.#buildRootVariableRule(font));
|
|
3469
|
+
}
|
|
3470
|
+
#extractFontsExport(source, filePath) {
|
|
3471
|
+
const sourceFile = ts4.createSourceFile(filePath, source, ts4.ScriptTarget.Latest, true, ts4.ScriptKind.TSX);
|
|
3472
|
+
const fonts = [];
|
|
3473
|
+
for (const statement of sourceFile.statements) {
|
|
3474
|
+
if (!ts4.isVariableStatement(statement))
|
|
3475
|
+
continue;
|
|
3476
|
+
const modifiers = ts4.canHaveModifiers(statement) ? ts4.getModifiers(statement) : undefined;
|
|
3477
|
+
const isExported = modifiers?.some((modifier) => modifier.kind === ts4.SyntaxKind.ExportKeyword) ?? false;
|
|
3478
|
+
if (!isExported)
|
|
3479
|
+
continue;
|
|
3480
|
+
for (const declaration of statement.declarationList.declarations) {
|
|
3481
|
+
if (!ts4.isIdentifier(declaration.name) || declaration.name.text !== "fonts")
|
|
3482
|
+
continue;
|
|
3483
|
+
const value = declaration.initializer ? this.#literalToValue(declaration.initializer) : null;
|
|
3484
|
+
if (Array.isArray(value)) {
|
|
3485
|
+
fonts.push(...value.map((font) => this.#withFontDefaults(font)));
|
|
3486
|
+
}
|
|
3487
|
+
}
|
|
3488
|
+
}
|
|
3489
|
+
return fonts;
|
|
3669
3490
|
}
|
|
3670
|
-
|
|
3671
|
-
|
|
3491
|
+
#literalToValue(node) {
|
|
3492
|
+
if (ts4.isStringLiteralLike(node))
|
|
3493
|
+
return node.text;
|
|
3494
|
+
if (ts4.isNumericLiteral(node))
|
|
3495
|
+
return Number(node.text);
|
|
3496
|
+
if (node.kind === ts4.SyntaxKind.TrueKeyword)
|
|
3497
|
+
return true;
|
|
3498
|
+
if (node.kind === ts4.SyntaxKind.FalseKeyword)
|
|
3499
|
+
return false;
|
|
3500
|
+
if (ts4.isArrayLiteralExpression(node))
|
|
3501
|
+
return node.elements.map((element) => this.#literalToValue(element));
|
|
3502
|
+
if (ts4.isObjectLiteralExpression(node)) {
|
|
3503
|
+
const obj = {};
|
|
3504
|
+
for (const prop of node.properties) {
|
|
3505
|
+
if (!ts4.isPropertyAssignment(prop))
|
|
3506
|
+
continue;
|
|
3507
|
+
const name = this.#getPropertyName(prop.name);
|
|
3508
|
+
if (!name)
|
|
3509
|
+
continue;
|
|
3510
|
+
obj[name] = this.#literalToValue(prop.initializer);
|
|
3511
|
+
}
|
|
3512
|
+
return obj;
|
|
3513
|
+
}
|
|
3514
|
+
if (ts4.isAsExpression(node) || ts4.isSatisfiesExpression(node) || ts4.isParenthesizedExpression(node)) {
|
|
3515
|
+
return this.#literalToValue(node.expression);
|
|
3516
|
+
}
|
|
3517
|
+
return;
|
|
3672
3518
|
}
|
|
3673
|
-
|
|
3674
|
-
|
|
3519
|
+
#getPropertyName(name) {
|
|
3520
|
+
if (ts4.isIdentifier(name) || ts4.isStringLiteral(name) || ts4.isNumericLiteral(name))
|
|
3521
|
+
return name.text;
|
|
3522
|
+
return null;
|
|
3675
3523
|
}
|
|
3676
|
-
#
|
|
3677
|
-
const
|
|
3678
|
-
|
|
3679
|
-
|
|
3680
|
-
|
|
3681
|
-
...Object.fromEntries(Object.entries(this.#app.getPublicEnv()).map(([key, value]) => [`process.env.${key}`, JSON.stringify(value)]))
|
|
3682
|
-
};
|
|
3524
|
+
#dedupeFonts(fonts) {
|
|
3525
|
+
const map = new Map;
|
|
3526
|
+
for (const font of fonts)
|
|
3527
|
+
map.set(JSON.stringify(font), font);
|
|
3528
|
+
return [...map.values()];
|
|
3683
3529
|
}
|
|
3684
|
-
|
|
3685
|
-
return {
|
|
3686
|
-
name: "akan-pages-entry",
|
|
3687
|
-
setup(build) {
|
|
3688
|
-
build.onResolve({ filter: /^akan-pages-entry$/ }, () => ({
|
|
3689
|
-
path: VIRTUAL_PAGES_ENTRY,
|
|
3690
|
-
namespace: "akan-virtual"
|
|
3691
|
-
}));
|
|
3692
|
-
build.onLoad({ filter: /^akan-pages-entry$/, namespace: "akan-virtual" }, () => ({
|
|
3693
|
-
contents: source,
|
|
3694
|
-
loader: "tsx"
|
|
3695
|
-
}));
|
|
3696
|
-
}
|
|
3697
|
-
};
|
|
3530
|
+
#withFontDefaults(font) {
|
|
3531
|
+
return { ...font, subsets: font.subsets ?? [...DEFAULT_FONT_SUBSETS] };
|
|
3698
3532
|
}
|
|
3699
|
-
|
|
3700
|
-
return
|
|
3701
|
-
name: "akan-server-css-stub",
|
|
3702
|
-
setup(build) {
|
|
3703
|
-
build.onLoad({ filter: /\.css$/ }, () => ({
|
|
3704
|
-
contents: "",
|
|
3705
|
-
loader: "js"
|
|
3706
|
-
}));
|
|
3707
|
-
}
|
|
3708
|
-
};
|
|
3533
|
+
#getFontSubsets(font) {
|
|
3534
|
+
return font.subsets ?? DEFAULT_FONT_SUBSETS;
|
|
3709
3535
|
}
|
|
3710
|
-
|
|
3711
|
-
return {
|
|
3712
|
-
name: "akan-server-use-client-fetch",
|
|
3713
|
-
setup(build) {
|
|
3714
|
-
build.onLoad({ filter: /[/\\]lib[/\\]useClient\.(ts|tsx|js|jsx)$/ }, async (args) => {
|
|
3715
|
-
const source = await Bun.file(args.path).text();
|
|
3716
|
-
const transformed = PagesBundleBuilder.transformServerUseClientFetchSource(source);
|
|
3717
|
-
if (transformed === source)
|
|
3718
|
-
return;
|
|
3719
|
-
return { contents: transformed, loader: loaderFor4(args.path) };
|
|
3720
|
-
});
|
|
3721
|
-
}
|
|
3722
|
-
};
|
|
3536
|
+
#getFontVariableName(font) {
|
|
3537
|
+
return font.variable ?? `--font-${font.name}`;
|
|
3723
3538
|
}
|
|
3724
|
-
|
|
3725
|
-
|
|
3726
|
-
return source;
|
|
3727
|
-
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 });");
|
|
3539
|
+
#getFontFallbackName(font) {
|
|
3540
|
+
return font.fallbackName ?? `${font.name} fallback`;
|
|
3728
3541
|
}
|
|
3729
|
-
|
|
3730
|
-
|
|
3731
|
-
if (absPath.endsWith(".tsx"))
|
|
3732
|
-
return "tsx";
|
|
3733
|
-
if (absPath.endsWith(".jsx"))
|
|
3734
|
-
return "jsx";
|
|
3735
|
-
if (absPath.endsWith(".ts"))
|
|
3736
|
-
return "ts";
|
|
3737
|
-
return "js";
|
|
3738
|
-
}
|
|
3739
|
-
|
|
3740
|
-
// pkgs/@akanjs/devkit/frontendBuild/precompressArtifacts.ts
|
|
3741
|
-
import fs4 from "fs";
|
|
3742
|
-
import path21 from "path";
|
|
3743
|
-
var COMPRESSIBLE_EXTS = new Set([".css", ".html", ".js", ".json", ".svg"]);
|
|
3744
|
-
var MIN_COMPRESS_BYTES = 1024;
|
|
3745
|
-
async function precompressArtifacts(app) {
|
|
3746
|
-
const roots = [path21.join(app.dist.cwdPath, ".akan/artifact/client")];
|
|
3747
|
-
const result = { files: 0, inputBytes: 0, outputBytes: 0 };
|
|
3748
|
-
await Promise.all(roots.map((root) => precompressRoot(root, result)));
|
|
3749
|
-
if (result.files > 0) {
|
|
3750
|
-
app.verbose(`[precompress] wrote ${result.files} gzip sidecars (${formatBytes(result.inputBytes)} -> ${formatBytes(result.outputBytes)})`);
|
|
3542
|
+
#isFontOptimizationEnabled(font) {
|
|
3543
|
+
return font.optimize !== false;
|
|
3751
3544
|
}
|
|
3752
|
-
|
|
3753
|
-
|
|
3754
|
-
async function precompressRoot(root, result) {
|
|
3755
|
-
if (!fs4.existsSync(root) || !fs4.statSync(root).isDirectory())
|
|
3756
|
-
return;
|
|
3757
|
-
const glob = new Bun.Glob("**/*");
|
|
3758
|
-
for await (const filePath of glob.scan({ cwd: root, absolute: true })) {
|
|
3759
|
-
if (!await shouldPrecompress(filePath))
|
|
3760
|
-
continue;
|
|
3761
|
-
const bytes = await Bun.file(filePath).bytes();
|
|
3762
|
-
const gz = Bun.gzipSync(toArrayBuffer(bytes));
|
|
3763
|
-
await Bun.write(`${filePath}.gz`, gz);
|
|
3764
|
-
result.files += 1;
|
|
3765
|
-
result.inputBytes += bytes.byteLength;
|
|
3766
|
-
result.outputBytes += gz.byteLength;
|
|
3545
|
+
#getFontStyles(font) {
|
|
3546
|
+
return font.styles?.length ? font.styles : ["normal"];
|
|
3767
3547
|
}
|
|
3768
|
-
|
|
3769
|
-
|
|
3770
|
-
|
|
3771
|
-
|
|
3772
|
-
|
|
3773
|
-
|
|
3774
|
-
|
|
3775
|
-
|
|
3776
|
-
|
|
3777
|
-
|
|
3778
|
-
|
|
3779
|
-
|
|
3780
|
-
|
|
3781
|
-
}
|
|
3782
|
-
function formatBytes(bytes) {
|
|
3783
|
-
if (bytes < 1024)
|
|
3784
|
-
return `${bytes}B`;
|
|
3785
|
-
if (bytes < 1024 * 1024)
|
|
3786
|
-
return `${(bytes / 1024).toFixed(1)}KB`;
|
|
3787
|
-
return `${(bytes / 1024 / 1024).toFixed(1)}MB`;
|
|
3788
|
-
}
|
|
3789
|
-
|
|
3790
|
-
// pkgs/@akanjs/devkit/frontendBuild/ssrBaseArtifactBuilder.ts
|
|
3791
|
-
import path24 from "path";
|
|
3792
|
-
import { optimize } from "@tailwindcss/node";
|
|
3793
|
-
|
|
3794
|
-
// pkgs/@akanjs/devkit/frontendBuild/cssCompiler.ts
|
|
3795
|
-
import path23 from "path";
|
|
3796
|
-
import { Logger as Logger2 } from "akanjs/common";
|
|
3797
|
-
import { compile } from "tailwindcss";
|
|
3798
|
-
|
|
3799
|
-
// pkgs/@akanjs/devkit/frontendBuild/cssImportResolver.ts
|
|
3800
|
-
import path22 from "path";
|
|
3801
|
-
var CSS_IMPORT_EXTS = ["", ".css", "/styles.css", "/index.css"];
|
|
3802
|
-
|
|
3803
|
-
class CssImportResolver {
|
|
3804
|
-
#workspaceRoot;
|
|
3805
|
-
#paths;
|
|
3806
|
-
#wildcardEntries;
|
|
3807
|
-
constructor(workspaceRoot, paths = {}) {
|
|
3808
|
-
this.#workspaceRoot = workspaceRoot;
|
|
3809
|
-
this.#paths = paths;
|
|
3810
|
-
this.#wildcardEntries = Object.entries(paths).filter(([key]) => key.endsWith("/*")).map(([key, replacements]) => ({ prefix: key.slice(0, -1), replacements })).sort((a, b) => b.prefix.length - a.prefix.length);
|
|
3548
|
+
#getFontFaces(font) {
|
|
3549
|
+
const enabledStyles = new Set(this.#getFontStyles(font));
|
|
3550
|
+
return font.paths.map((fontPath) => {
|
|
3551
|
+
const style = fontPath.style ?? "normal";
|
|
3552
|
+
return {
|
|
3553
|
+
font,
|
|
3554
|
+
path: fontPath,
|
|
3555
|
+
src: fontPath.src,
|
|
3556
|
+
weight: fontPath.weight,
|
|
3557
|
+
style,
|
|
3558
|
+
optimizedSrc: this.#getOptimizedFontSrc(font, fontPath)
|
|
3559
|
+
};
|
|
3560
|
+
}).filter((face) => enabledStyles.has(face.style));
|
|
3811
3561
|
}
|
|
3812
|
-
|
|
3813
|
-
const
|
|
3814
|
-
|
|
3562
|
+
#getOptimizedFontSrc(font, fontPath) {
|
|
3563
|
+
const style = fontPath.style ?? "normal";
|
|
3564
|
+
const hash = this.#hashFontConfig({
|
|
3565
|
+
name: font.name,
|
|
3566
|
+
src: fontPath.src,
|
|
3567
|
+
weight: fontPath.weight,
|
|
3568
|
+
style,
|
|
3569
|
+
display: font.display,
|
|
3570
|
+
subset: font.subset,
|
|
3571
|
+
subsets: this.#getFontSubsets(font),
|
|
3572
|
+
subsetText: font.subsetText,
|
|
3573
|
+
subsetFiles: font.subsetFiles,
|
|
3574
|
+
declarations: [...font.declarations ?? [], ...fontPath.declarations ?? []]
|
|
3575
|
+
});
|
|
3576
|
+
return `${FONT_URL_PREFIX}/${this.#slugFontPart(font.name)}-${this.#slugFontPart(String(fontPath.weight))}-${style}-${hash}.woff2`;
|
|
3815
3577
|
}
|
|
3816
|
-
|
|
3817
|
-
|
|
3818
|
-
|
|
3819
|
-
|
|
3820
|
-
|
|
3821
|
-
|
|
3822
|
-
|
|
3823
|
-
|
|
3824
|
-
|
|
3825
|
-
|
|
3578
|
+
#hashFontConfig(value) {
|
|
3579
|
+
const input = this.#stableStringify(value);
|
|
3580
|
+
let hash = 2166136261;
|
|
3581
|
+
for (let i = 0;i < input.length; i++) {
|
|
3582
|
+
hash ^= input.charCodeAt(i);
|
|
3583
|
+
hash = Math.imul(hash, 16777619);
|
|
3584
|
+
}
|
|
3585
|
+
return (hash >>> 0).toString(36);
|
|
3586
|
+
}
|
|
3587
|
+
#stableStringify(value) {
|
|
3588
|
+
if (Array.isArray(value))
|
|
3589
|
+
return `[${value.map((item) => this.#stableStringify(item)).join(",")}]`;
|
|
3590
|
+
if (value && typeof value === "object") {
|
|
3591
|
+
const entries = Object.entries(value).filter(([, v]) => v !== undefined).sort(([a], [b]) => a.localeCompare(b));
|
|
3592
|
+
return `{${entries.map(([k, v]) => `${JSON.stringify(k)}:${this.#stableStringify(v)}`).join(",")}}`;
|
|
3593
|
+
}
|
|
3594
|
+
return JSON.stringify(value);
|
|
3595
|
+
}
|
|
3596
|
+
#slugFontPart(value) {
|
|
3597
|
+
return value.trim().toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "") || "font";
|
|
3598
|
+
}
|
|
3599
|
+
async#resolveFontSourcePath(src) {
|
|
3600
|
+
if (!src.startsWith("/"))
|
|
3601
|
+
return null;
|
|
3602
|
+
const rel = src.replace(/^\//, "");
|
|
3603
|
+
const candidates = [
|
|
3604
|
+
this.#command === "build" ? path18.join(this.#app.dist.cwdPath, "public", rel) : null,
|
|
3605
|
+
path18.join(this.#app.cwdPath, "public", rel),
|
|
3606
|
+
this.#resolveWorkspacePublicPath(rel)
|
|
3607
|
+
].filter(Boolean);
|
|
3608
|
+
for (const candidate of candidates) {
|
|
3609
|
+
if (await Bun.file(candidate).exists())
|
|
3610
|
+
return candidate;
|
|
3826
3611
|
}
|
|
3827
3612
|
return null;
|
|
3828
3613
|
}
|
|
3829
|
-
#
|
|
3830
|
-
|
|
3831
|
-
|
|
3832
|
-
|
|
3833
|
-
|
|
3834
|
-
return resolved;
|
|
3835
|
-
} catch {}
|
|
3836
|
-
}
|
|
3837
|
-
return null;
|
|
3614
|
+
async#buildFontBuffer(font, sourceBuffer, sourcePath) {
|
|
3615
|
+
if (font.subset === false)
|
|
3616
|
+
return this.#convertToWoff2(sourceBuffer, sourcePath);
|
|
3617
|
+
const { default: subsetFont } = await import("subset-font");
|
|
3618
|
+
return subsetFont(sourceBuffer, await this.#getSubsetText(font), { targetFormat: "woff2" });
|
|
3838
3619
|
}
|
|
3839
|
-
#
|
|
3840
|
-
|
|
3841
|
-
|
|
3842
|
-
|
|
3843
|
-
|
|
3844
|
-
|
|
3845
|
-
|
|
3620
|
+
async#convertToWoff2(buffer, sourcePath) {
|
|
3621
|
+
const { createFont } = await import("fonteditor-core");
|
|
3622
|
+
await this.#initWoff2();
|
|
3623
|
+
const font = createFont(buffer, { type: this.#getFontType(sourcePath, buffer) });
|
|
3624
|
+
return font.write({ type: "woff2", toBuffer: true });
|
|
3625
|
+
}
|
|
3626
|
+
async#initWoff2() {
|
|
3627
|
+
this.#woff2Ready ??= import("fonteditor-core").then(({ woff2 }) => woff2.init()).then(() => {
|
|
3628
|
+
return;
|
|
3629
|
+
});
|
|
3630
|
+
return this.#woff2Ready;
|
|
3631
|
+
}
|
|
3632
|
+
#getFontType(sourcePath, buffer) {
|
|
3633
|
+
const signature = buffer.toString("ascii", 0, 4);
|
|
3634
|
+
if (signature === "wOFF")
|
|
3635
|
+
return "woff";
|
|
3636
|
+
if (signature === "wOF2")
|
|
3637
|
+
return "woff2";
|
|
3638
|
+
if (signature === "OTTO")
|
|
3639
|
+
return "otf";
|
|
3640
|
+
const ext = path18.extname(sourcePath).slice(1).toLowerCase();
|
|
3641
|
+
if (ext === "otf" || ext === "woff" || ext === "woff2")
|
|
3642
|
+
return ext;
|
|
3643
|
+
return "ttf";
|
|
3644
|
+
}
|
|
3645
|
+
#resolveWorkspacePublicPath(rel) {
|
|
3646
|
+
const [root, dep, ...rest] = rel.split("/");
|
|
3647
|
+
if (root !== "libs" || !dep || rest.length === 0)
|
|
3648
|
+
return null;
|
|
3649
|
+
return path18.join(this.#app.workspace.workspaceRoot, "libs", dep, "public", ...rest);
|
|
3650
|
+
}
|
|
3651
|
+
async#getSubsetText(font) {
|
|
3652
|
+
const parts = new Set;
|
|
3653
|
+
const subsets = this.#getFontSubsets(font);
|
|
3654
|
+
for (const subset of subsets)
|
|
3655
|
+
parts.add(await this.#getSubsetPresetText(subset));
|
|
3656
|
+
if (font.subsetText)
|
|
3657
|
+
parts.add(font.subsetText);
|
|
3658
|
+
for (const filePath of font.subsetFiles ?? []) {
|
|
3659
|
+
const abs = path18.isAbsolute(filePath) ? filePath : path18.join(this.#app.cwdPath, filePath);
|
|
3660
|
+
const file = Bun.file(abs);
|
|
3661
|
+
if (await file.exists())
|
|
3662
|
+
parts.add(await file.text());
|
|
3846
3663
|
}
|
|
3847
|
-
return
|
|
3664
|
+
return [...parts].join("");
|
|
3848
3665
|
}
|
|
3849
|
-
async#
|
|
3850
|
-
|
|
3851
|
-
|
|
3852
|
-
|
|
3853
|
-
|
|
3854
|
-
|
|
3855
|
-
|
|
3666
|
+
async#getSubsetPresetText(subset) {
|
|
3667
|
+
if (subset === "latin")
|
|
3668
|
+
return this.#rangeText(32, 126);
|
|
3669
|
+
if (subset === "latin-ext")
|
|
3670
|
+
return `${this.#rangeText(32, 126)}${this.#rangeText(160, 591)}`;
|
|
3671
|
+
if (subset === "ks-x-1001")
|
|
3672
|
+
return FontOptimizer.#getKsX1001Text();
|
|
3673
|
+
if (subset === "auto")
|
|
3674
|
+
return this.#collectAutoSubsetText();
|
|
3675
|
+
return "";
|
|
3676
|
+
}
|
|
3677
|
+
async#collectAutoSubsetText() {
|
|
3678
|
+
const roots = ["page", "ui"].map((dir) => path18.join(this.#app.cwdPath, dir));
|
|
3679
|
+
const glob = new Bun.Glob("**/*.{ts,tsx,js,jsx,html,md}");
|
|
3680
|
+
const parts = [];
|
|
3681
|
+
await Promise.all(roots.map(async (root) => {
|
|
3682
|
+
if (!await Bun.file(root).exists())
|
|
3683
|
+
return;
|
|
3684
|
+
for await (const filePath of glob.scan({ cwd: root, absolute: true })) {
|
|
3685
|
+
parts.push(await Bun.file(filePath).text());
|
|
3856
3686
|
}
|
|
3857
|
-
}
|
|
3858
|
-
|
|
3859
|
-
|
|
3860
|
-
|
|
3861
|
-
|
|
3862
|
-
|
|
3863
|
-
|
|
3864
|
-
|
|
3865
|
-
|
|
3866
|
-
|
|
3687
|
+
}));
|
|
3688
|
+
return parts.join("");
|
|
3689
|
+
}
|
|
3690
|
+
#rangeText(start, end) {
|
|
3691
|
+
let text = "";
|
|
3692
|
+
for (let code = start;code <= end; code++)
|
|
3693
|
+
text += String.fromCodePoint(code);
|
|
3694
|
+
return text;
|
|
3695
|
+
}
|
|
3696
|
+
static #getKsX1001Text() {
|
|
3697
|
+
if (FontOptimizer.#ksX1001Text)
|
|
3698
|
+
return FontOptimizer.#ksX1001Text;
|
|
3699
|
+
try {
|
|
3700
|
+
const decoder = new TextDecoder("euc-kr");
|
|
3701
|
+
const chars = new Set;
|
|
3702
|
+
for (let lead = 161;lead <= 254; lead++) {
|
|
3703
|
+
for (let trail = 161;trail <= 254; trail++) {
|
|
3704
|
+
const char = decoder.decode(Uint8Array.of(lead, trail));
|
|
3705
|
+
if (char && char !== "\uFFFD")
|
|
3706
|
+
chars.add(char);
|
|
3707
|
+
}
|
|
3867
3708
|
}
|
|
3709
|
+
FontOptimizer.#ksX1001Text = [...chars].join("");
|
|
3710
|
+
} catch {
|
|
3711
|
+
FontOptimizer.#ksX1001Text = FontOptimizer.#rangeTextStatic(44032, 55203);
|
|
3868
3712
|
}
|
|
3869
|
-
return
|
|
3713
|
+
return FontOptimizer.#ksX1001Text;
|
|
3870
3714
|
}
|
|
3871
|
-
|
|
3872
|
-
|
|
3873
|
-
|
|
3874
|
-
|
|
3875
|
-
|
|
3876
|
-
|
|
3877
|
-
|
|
3878
|
-
|
|
3879
|
-
|
|
3880
|
-
|
|
3881
|
-
|
|
3715
|
+
static #rangeTextStatic(start, end) {
|
|
3716
|
+
let text = "";
|
|
3717
|
+
for (let code = start;code <= end; code++)
|
|
3718
|
+
text += String.fromCodePoint(code);
|
|
3719
|
+
return text;
|
|
3720
|
+
}
|
|
3721
|
+
#buildOptimizedFontFaceRule(font, face) {
|
|
3722
|
+
const declarations = [
|
|
3723
|
+
["font-family", this.#quote(font.name)],
|
|
3724
|
+
["src", `url(${this.#quote(face.optimizedSrc)}) format("woff2")`],
|
|
3725
|
+
["font-weight", String(face.weight)],
|
|
3726
|
+
["font-style", face.style],
|
|
3727
|
+
["font-display", font.display ?? "swap"],
|
|
3728
|
+
...this.#toDeclarationEntries(font.declarations),
|
|
3729
|
+
...this.#toDeclarationEntries(face.path.declarations)
|
|
3730
|
+
];
|
|
3731
|
+
return `@font-face {
|
|
3732
|
+
${declarations.map(([prop, value]) => ` ${prop}: ${value};`).join(`
|
|
3733
|
+
`)}
|
|
3734
|
+
}`;
|
|
3735
|
+
}
|
|
3736
|
+
async#buildFontaineFallbackCss(font, face, outputPath) {
|
|
3737
|
+
if (font.adjustFontFallback === false)
|
|
3738
|
+
return "";
|
|
3739
|
+
const { generateFontFace, getMetricsForFamily, readMetrics, resolveCategoryFallbacks } = await import("fontaine");
|
|
3740
|
+
const metrics = await readMetrics(outputPath).catch(() => null);
|
|
3741
|
+
if (!metrics)
|
|
3742
|
+
return "";
|
|
3743
|
+
const fallbacks = resolveCategoryFallbacks({
|
|
3744
|
+
fontFamily: font.name,
|
|
3745
|
+
fallbacks: font.fallbacks ?? {},
|
|
3746
|
+
metrics: { ...metrics, category: font.category }
|
|
3747
|
+
});
|
|
3748
|
+
const css = [];
|
|
3749
|
+
for (let i = fallbacks.length - 1;i >= 0; i--) {
|
|
3750
|
+
const fallback = fallbacks[i];
|
|
3751
|
+
const fallbackMetrics = await getMetricsForFamily(fallback);
|
|
3752
|
+
if (!fallbackMetrics)
|
|
3753
|
+
continue;
|
|
3754
|
+
css.push(generateFontFace({ ...metrics, category: font.category }, {
|
|
3755
|
+
name: this.#getFontFallbackName(font),
|
|
3756
|
+
font: fallback,
|
|
3757
|
+
metrics: fallbackMetrics,
|
|
3758
|
+
"font-weight": String(face.weight),
|
|
3759
|
+
"font-style": face.style
|
|
3760
|
+
}));
|
|
3882
3761
|
}
|
|
3883
|
-
|
|
3884
|
-
|
|
3885
|
-
|
|
3886
|
-
|
|
3762
|
+
return css.join("");
|
|
3763
|
+
}
|
|
3764
|
+
#buildRootVariableRule(font) {
|
|
3765
|
+
return `:root { ${this.#getFontVariableName(font)}: ${this.#quote(font.name)}, ${this.#quote(this.#getFontFallbackName(font))}; }`;
|
|
3766
|
+
}
|
|
3767
|
+
#buildFontUtilityRules(fonts) {
|
|
3768
|
+
const rules = [];
|
|
3769
|
+
const seen = new Set;
|
|
3770
|
+
for (const font of fonts) {
|
|
3771
|
+
const className = font.className || `font-${font.name}`;
|
|
3772
|
+
const selector = `.${this.#escapeCssClassName(className)}`;
|
|
3773
|
+
const rule = `${selector} { font-family: var(${this.#getFontVariableName(font)}); }`;
|
|
3774
|
+
if (seen.has(rule))
|
|
3775
|
+
continue;
|
|
3776
|
+
seen.add(rule);
|
|
3777
|
+
rules.push(rule);
|
|
3887
3778
|
}
|
|
3888
|
-
return
|
|
3779
|
+
return rules.join(`
|
|
3780
|
+
`);
|
|
3889
3781
|
}
|
|
3890
|
-
|
|
3891
|
-
|
|
3892
|
-
|
|
3893
|
-
|
|
3894
|
-
|
|
3895
|
-
|
|
3896
|
-
|
|
3897
|
-
|
|
3898
|
-
|
|
3899
|
-
|
|
3900
|
-
|
|
3782
|
+
#escapeCssClassName(value) {
|
|
3783
|
+
return value.replace(/^-?\d|[^a-zA-Z0-9_-]/g, (part) => [...part].map((char) => `\\${char.codePointAt(0)?.toString(16)} `).join(""));
|
|
3784
|
+
}
|
|
3785
|
+
#toDeclarationEntries(declarations = []) {
|
|
3786
|
+
return declarations.map((declaration) => [declaration.prop, declaration.value]);
|
|
3787
|
+
}
|
|
3788
|
+
#quote(value) {
|
|
3789
|
+
return JSON.stringify(value);
|
|
3790
|
+
}
|
|
3791
|
+
}
|
|
3792
|
+
|
|
3793
|
+
// pkgs/@akanjs/devkit/frontendBuild/pagesBundleBuilder.ts
|
|
3794
|
+
import path20 from "path";
|
|
3795
|
+
|
|
3796
|
+
// pkgs/@akanjs/devkit/transforms/externalizeFrameworkPlugin.ts
|
|
3797
|
+
import path19 from "path";
|
|
3798
|
+
var DEFAULT_INCLUDE = ["akanjs/", "@apps/", "@libs/"];
|
|
3799
|
+
var DEFAULT_EXCLUDE_EXACT = new Set(["akanjs/webkit", "@akanjs/cli", "@akanjs/devkit"]);
|
|
3800
|
+
var DEFAULT_EXCLUDE_PREFIX = ["@akanjs/cli/", "@akanjs/devkit/"];
|
|
3801
|
+
var OPTIONAL_BACKEND_EXTERNAL_EXACT = new Set([
|
|
3802
|
+
"@libsql/client",
|
|
3803
|
+
"bullmq",
|
|
3804
|
+
"croner",
|
|
3805
|
+
"ioredis",
|
|
3806
|
+
"postgres",
|
|
3807
|
+
"protobufjs"
|
|
3808
|
+
]);
|
|
3809
|
+
var RUNTIME_EXTERNAL_EXACT = new Set([
|
|
3810
|
+
"react",
|
|
3811
|
+
"react-dom",
|
|
3812
|
+
"react/jsx-runtime",
|
|
3813
|
+
"react/jsx-dev-runtime",
|
|
3814
|
+
"react-server-dom-webpack",
|
|
3815
|
+
"react-server-dom-webpack/server.node",
|
|
3816
|
+
"react-server-dom-webpack/client.node",
|
|
3817
|
+
"react-server-dom-webpack/client.browser"
|
|
3818
|
+
]);
|
|
3819
|
+
var RUNTIME_EXTERNAL_PREFIX = ["react-dom/", "react-server-dom-webpack/"];
|
|
3820
|
+
var CANDIDATE_EXTS3 = [".tsx", ".ts", ".jsx", ".js", ".mjs", ".cjs"];
|
|
3821
|
+
async function createExternalizeFrameworkPlugin(options) {
|
|
3822
|
+
const tsconfig = await options.app.getTsConfig();
|
|
3823
|
+
const includePrefixes = options.include ?? DEFAULT_INCLUDE;
|
|
3824
|
+
const extraExact = new Set(options.extra ?? []);
|
|
3825
|
+
const workspaceRoot = options.app.workspace.workspaceRoot;
|
|
3826
|
+
const tsconfigPaths = tsconfig.compilerOptions.paths ?? {};
|
|
3827
|
+
const rootEntries = Object.entries(tsconfigPaths).filter(([k]) => !k.endsWith("/*")).map(([k, v]) => ({ pkg: k, entryFile: v[0] ?? null })).filter((e) => e.entryFile !== null);
|
|
3828
|
+
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);
|
|
3829
|
+
async function resolveWorkspaceSubpath(spec) {
|
|
3830
|
+
if (!workspaceRoot)
|
|
3901
3831
|
return null;
|
|
3832
|
+
for (const { prefix, replacements } of wildcardEntries) {
|
|
3833
|
+
if (!spec.startsWith(prefix))
|
|
3834
|
+
continue;
|
|
3835
|
+
const suffix = spec.slice(prefix.length);
|
|
3836
|
+
for (const repl of replacements) {
|
|
3837
|
+
const replPath = repl?.endsWith("/*") ? repl.slice(0, -1) : repl ?? "";
|
|
3838
|
+
if (!replPath)
|
|
3839
|
+
continue;
|
|
3840
|
+
const candidate = path19.resolve(workspaceRoot, replPath + suffix);
|
|
3841
|
+
const hit = await firstExisting(candidate);
|
|
3842
|
+
if (hit)
|
|
3843
|
+
return hit;
|
|
3844
|
+
}
|
|
3902
3845
|
}
|
|
3903
|
-
|
|
3904
|
-
|
|
3905
|
-
|
|
3906
|
-
|
|
3907
|
-
|
|
3908
|
-
|
|
3909
|
-
|
|
3910
|
-
|
|
3911
|
-
|
|
3912
|
-
|
|
3913
|
-
|
|
3914
|
-
}
|
|
3915
|
-
async#firstExisting(basePath) {
|
|
3916
|
-
for (const suffix of CSS_IMPORT_EXTS) {
|
|
3917
|
-
const candidate = `${basePath}${suffix}`;
|
|
3918
|
-
if (await Bun.file(candidate).exists())
|
|
3919
|
-
return candidate;
|
|
3846
|
+
for (const { pkg, entryFile } of rootEntries) {
|
|
3847
|
+
if (spec !== pkg && !spec.startsWith(`${pkg}/`))
|
|
3848
|
+
continue;
|
|
3849
|
+
if (spec === pkg)
|
|
3850
|
+
continue;
|
|
3851
|
+
const suffix = spec.slice(pkg.length + 1);
|
|
3852
|
+
const pkgDir = path19.dirname(path19.resolve(workspaceRoot, entryFile));
|
|
3853
|
+
const candidate = path19.join(pkgDir, suffix);
|
|
3854
|
+
const hit = await firstExisting(candidate);
|
|
3855
|
+
if (hit)
|
|
3856
|
+
return hit;
|
|
3920
3857
|
}
|
|
3921
3858
|
return null;
|
|
3922
3859
|
}
|
|
3923
|
-
|
|
3924
|
-
|
|
3925
|
-
|
|
3926
|
-
|
|
3927
|
-
|
|
3860
|
+
return {
|
|
3861
|
+
name: "akan-externalize-framework",
|
|
3862
|
+
setup(build) {
|
|
3863
|
+
build.onResolve({ filter: /.*/ }, async (args) => {
|
|
3864
|
+
const spec = args.path;
|
|
3865
|
+
if (spec === "." || spec === ".." || spec.startsWith("./") || spec.startsWith("../") || spec.startsWith("/"))
|
|
3866
|
+
return;
|
|
3867
|
+
if (extraExact.has(spec) || DEFAULT_EXCLUDE_EXACT.has(spec))
|
|
3868
|
+
return { path: spec, external: true };
|
|
3869
|
+
for (const prefix of DEFAULT_EXCLUDE_PREFIX) {
|
|
3870
|
+
if (spec.startsWith(prefix))
|
|
3871
|
+
return { path: spec, external: true };
|
|
3872
|
+
}
|
|
3873
|
+
if (OPTIONAL_BACKEND_EXTERNAL_EXACT.has(spec))
|
|
3874
|
+
return { path: spec, external: true };
|
|
3875
|
+
if (RUNTIME_EXTERNAL_EXACT.has(spec))
|
|
3876
|
+
return { path: spec, external: true };
|
|
3877
|
+
for (const prefix of RUNTIME_EXTERNAL_PREFIX) {
|
|
3878
|
+
if (spec.startsWith(prefix))
|
|
3879
|
+
return { path: spec, external: true };
|
|
3880
|
+
}
|
|
3881
|
+
for (const prefix of includePrefixes) {
|
|
3882
|
+
if (!spec.startsWith(prefix))
|
|
3883
|
+
continue;
|
|
3884
|
+
const resolved = await resolveWorkspaceSubpath(spec);
|
|
3885
|
+
if (resolved)
|
|
3886
|
+
return { path: resolved };
|
|
3887
|
+
return;
|
|
3888
|
+
}
|
|
3889
|
+
return;
|
|
3890
|
+
});
|
|
3891
|
+
}
|
|
3892
|
+
};
|
|
3893
|
+
}
|
|
3894
|
+
async function firstExisting(basePath) {
|
|
3895
|
+
if (await Bun.file(basePath).exists())
|
|
3896
|
+
return basePath;
|
|
3897
|
+
for (const ext of CANDIDATE_EXTS3) {
|
|
3898
|
+
const candidate = `${basePath}${ext}`;
|
|
3899
|
+
if (await Bun.file(candidate).exists())
|
|
3900
|
+
return candidate;
|
|
3928
3901
|
}
|
|
3929
|
-
|
|
3930
|
-
|
|
3902
|
+
for (const ext of CANDIDATE_EXTS3) {
|
|
3903
|
+
const candidate = path19.join(basePath, `index${ext}`);
|
|
3904
|
+
if (await Bun.file(candidate).exists())
|
|
3905
|
+
return candidate;
|
|
3931
3906
|
}
|
|
3907
|
+
return null;
|
|
3932
3908
|
}
|
|
3933
3909
|
|
|
3934
|
-
// pkgs/@akanjs/devkit/
|
|
3935
|
-
|
|
3936
|
-
|
|
3937
|
-
|
|
3938
|
-
|
|
3939
|
-
|
|
3940
|
-
|
|
3941
|
-
|
|
3942
|
-
|
|
3943
|
-
#app;
|
|
3944
|
-
#cssImportResolver = null;
|
|
3945
|
-
constructor(app) {
|
|
3946
|
-
this.#app = app;
|
|
3947
|
-
}
|
|
3948
|
-
#cssText = null;
|
|
3949
|
-
#cssTextByBasePath = null;
|
|
3950
|
-
async getCss({ refresh } = {}) {
|
|
3951
|
-
if (this.#cssText !== null && !refresh)
|
|
3952
|
-
return this.#cssText;
|
|
3953
|
-
const { cssPaths, sourcePaths } = await this.discoverCssAndSources({ refresh });
|
|
3954
|
-
this.#cssText = await this.compileCss(cssPaths, sourcePaths);
|
|
3955
|
-
return this.#cssText;
|
|
3956
|
-
}
|
|
3957
|
-
async getCssByBasePath({ refresh } = {}) {
|
|
3958
|
-
if (this.#cssTextByBasePath !== null && !refresh)
|
|
3959
|
-
return this.#cssTextByBasePath;
|
|
3960
|
-
const akanConfig = await this.#app.getConfig({ refresh });
|
|
3961
|
-
const pageKeys = await this.#app.getPageKeys({ refresh });
|
|
3962
|
-
const basePaths = [...akanConfig.basePaths];
|
|
3963
|
-
const rootPageKeys = pageKeys.filter((pageKey) => getPageKeyBasePath(pageKey, basePaths) === null);
|
|
3964
|
-
const cssEntries = await Promise.all([
|
|
3965
|
-
(async () => {
|
|
3966
|
-
if (rootPageKeys.length === 0)
|
|
3967
|
-
return ["", ""];
|
|
3968
|
-
const started = Date.now();
|
|
3969
|
-
const { cssPaths, sourcePaths } = await this.discoverCssAndSources({ refresh, pageKeys: rootPageKeys });
|
|
3970
|
-
const css = await this.compileCss(cssPaths, sourcePaths);
|
|
3971
|
-
this.#logger.verbose(`css base=root paths=${cssPaths.length} sources=${sourcePaths.length} in ${Date.now() - started}ms`);
|
|
3972
|
-
return ["", css];
|
|
3973
|
-
})(),
|
|
3974
|
-
...basePaths.map(async (basePath) => {
|
|
3975
|
-
const basePathPageKeys = pageKeys.filter((pageKey) => getPageKeyBasePath(pageKey, basePaths) === basePath);
|
|
3976
|
-
if (basePathPageKeys.length === 0)
|
|
3977
|
-
return [basePath, ""];
|
|
3978
|
-
const started = Date.now();
|
|
3979
|
-
const { cssPaths, sourcePaths } = await this.discoverCssAndSources({ refresh, pageKeys: basePathPageKeys });
|
|
3980
|
-
const css = await this.compileCss(cssPaths, sourcePaths);
|
|
3981
|
-
this.#logger.verbose(`css base=${basePath} paths=${cssPaths.length} sources=${sourcePaths.length} in ${Date.now() - started}ms`);
|
|
3982
|
-
return [basePath, css];
|
|
3983
|
-
})
|
|
3984
|
-
]);
|
|
3985
|
-
this.#cssTextByBasePath = Object.fromEntries(cssEntries);
|
|
3986
|
-
return this.#cssTextByBasePath;
|
|
3987
|
-
}
|
|
3988
|
-
async discoverCss({ refresh } = {}) {
|
|
3989
|
-
const { cssPaths } = await this.discoverCssAndSources({ refresh });
|
|
3990
|
-
return cssPaths;
|
|
3991
|
-
}
|
|
3992
|
-
async discoverCssAndSources({
|
|
3993
|
-
refresh,
|
|
3994
|
-
pageKeys
|
|
3995
|
-
} = {}) {
|
|
3996
|
-
pageKeys ??= await this.#app.getPageKeys({ refresh });
|
|
3997
|
-
const seeds = pageKeys.map((key) => path23.resolve(this.#app.cwdPath, "page", key));
|
|
3998
|
-
const cssFiles = new Set;
|
|
3999
|
-
const sourceFiles = new Set;
|
|
4000
|
-
const queue = [...seeds];
|
|
4001
|
-
const resolvePackage = await createTsconfigPackageResolver(this.#app);
|
|
4002
|
-
const analyzer = new BarrelAnalyzer({ resolvePackage });
|
|
4003
|
-
const akanConfig = await this.#app.getConfig({ refresh });
|
|
4004
|
-
while (queue.length > 0) {
|
|
4005
|
-
const filePath = queue.shift();
|
|
4006
|
-
if (!filePath || sourceFiles.has(filePath) || isIgnoredNodeModuleSource(filePath))
|
|
4007
|
-
continue;
|
|
4008
|
-
sourceFiles.add(filePath);
|
|
4009
|
-
let content;
|
|
4010
|
-
try {
|
|
4011
|
-
content = await Bun.file(filePath).text();
|
|
4012
|
-
} catch {
|
|
4013
|
-
continue;
|
|
4014
|
-
}
|
|
4015
|
-
let source = content;
|
|
4016
|
-
if (akanConfig.barrelImports.length > 0) {
|
|
3910
|
+
// pkgs/@akanjs/devkit/transforms/useClientBundlePlugin.ts
|
|
3911
|
+
function createUseClientBundlePlugin(options = {}) {
|
|
3912
|
+
return {
|
|
3913
|
+
name: "akan-use-client-bundle",
|
|
3914
|
+
setup(build) {
|
|
3915
|
+
build.onLoad({ filter: /\.(tsx|ts|jsx|js)$/ }, async (args) => {
|
|
3916
|
+
if (args.path.includes("/node_modules/"))
|
|
3917
|
+
return;
|
|
3918
|
+
let source;
|
|
4017
3919
|
try {
|
|
4018
|
-
|
|
4019
|
-
|
|
4020
|
-
|
|
4021
|
-
} catch {}
|
|
4022
|
-
}
|
|
4023
|
-
let imports;
|
|
4024
|
-
try {
|
|
4025
|
-
imports = this.#transpiler.scanImports(source);
|
|
4026
|
-
} catch {
|
|
4027
|
-
continue;
|
|
4028
|
-
}
|
|
4029
|
-
const importerDir = path23.dirname(filePath);
|
|
4030
|
-
for (const imp of imports) {
|
|
4031
|
-
const spec = imp.path;
|
|
4032
|
-
if (!spec)
|
|
4033
|
-
continue;
|
|
4034
|
-
if (spec.endsWith(".css")) {
|
|
4035
|
-
const cssPath = await this.#resolveCssImport(spec, importerDir);
|
|
4036
|
-
cssFiles.add(cssPath);
|
|
4037
|
-
continue;
|
|
3920
|
+
source = await Bun.file(args.path).text();
|
|
3921
|
+
} catch {
|
|
3922
|
+
return;
|
|
4038
3923
|
}
|
|
4039
|
-
|
|
4040
|
-
|
|
4041
|
-
|
|
4042
|
-
|
|
4043
|
-
|
|
4044
|
-
|
|
4045
|
-
|
|
4046
|
-
}
|
|
4047
|
-
return { cssPaths: [...cssFiles], sourcePaths: [...sourceFiles] };
|
|
4048
|
-
}
|
|
4049
|
-
async compileCss(cssPaths, sourcePaths) {
|
|
4050
|
-
if (cssPaths.length === 0)
|
|
4051
|
-
return "";
|
|
4052
|
-
const compileStarted = Date.now();
|
|
4053
|
-
const compilers = await Promise.all(cssPaths.map(async (cssPath) => {
|
|
4054
|
-
const css = await Bun.file(cssPath).text();
|
|
4055
|
-
const base = path23.dirname(cssPath);
|
|
4056
|
-
const compiler = await compile(css, {
|
|
4057
|
-
base,
|
|
4058
|
-
loadStylesheet: (id, fromBase) => this.#loadStylesheet(id, fromBase),
|
|
4059
|
-
loadModule: (id, fromBase) => this.#loadModule(id, fromBase)
|
|
3924
|
+
const stubbed = transformUseClient(source, {
|
|
3925
|
+
path: args.path,
|
|
3926
|
+
workspaceRoot: options.workspaceRoot
|
|
3927
|
+
});
|
|
3928
|
+
if (stubbed === null)
|
|
3929
|
+
return;
|
|
3930
|
+
return { contents: stubbed, loader: loaderFor3(args.path) };
|
|
4060
3931
|
});
|
|
4061
|
-
return { cssPath, compiler };
|
|
4062
|
-
}));
|
|
4063
|
-
const sourceDirs = new Set;
|
|
4064
|
-
for (const entry of compilers) {
|
|
4065
|
-
if (!entry)
|
|
4066
|
-
continue;
|
|
4067
|
-
for (const s of entry.compiler.sources)
|
|
4068
|
-
sourceDirs.add(s.base);
|
|
4069
|
-
}
|
|
4070
|
-
const scanStarted = Date.now();
|
|
4071
|
-
const candidates = await this.#scanCandidates(sourcePaths, [...sourceDirs]);
|
|
4072
|
-
this.#logger.verbose(`css candidates scanned count=${candidates.length} sources=${sourcePaths.length} dirs=${sourceDirs.size} in ${Date.now() - scanStarted}ms`);
|
|
4073
|
-
const parts = [];
|
|
4074
|
-
for (const entry of compilers) {
|
|
4075
|
-
if (!entry)
|
|
4076
|
-
continue;
|
|
4077
|
-
parts.push(entry.compiler.build(candidates));
|
|
4078
3932
|
}
|
|
4079
|
-
|
|
4080
|
-
|
|
4081
|
-
|
|
3933
|
+
};
|
|
3934
|
+
}
|
|
3935
|
+
function loaderFor3(absPath) {
|
|
3936
|
+
if (absPath.endsWith(".tsx"))
|
|
3937
|
+
return "tsx";
|
|
3938
|
+
if (absPath.endsWith(".jsx"))
|
|
3939
|
+
return "jsx";
|
|
3940
|
+
if (absPath.endsWith(".ts"))
|
|
3941
|
+
return "ts";
|
|
3942
|
+
return "js";
|
|
3943
|
+
}
|
|
3944
|
+
|
|
3945
|
+
// pkgs/@akanjs/devkit/frontendBuild/pagesBundleBuilder.ts
|
|
3946
|
+
var VIRTUAL_PAGES_ENTRY = "akan-pages-entry";
|
|
3947
|
+
|
|
3948
|
+
class PagesBundleBuilder {
|
|
3949
|
+
#app;
|
|
3950
|
+
#command;
|
|
3951
|
+
#pageEntries;
|
|
3952
|
+
#started = Date.now();
|
|
3953
|
+
constructor(app, command = "start", pageEntries) {
|
|
3954
|
+
this.#app = app;
|
|
3955
|
+
this.#command = command;
|
|
3956
|
+
this.#pageEntries = pageEntries;
|
|
4082
3957
|
}
|
|
4083
|
-
async
|
|
4084
|
-
const
|
|
4085
|
-
const
|
|
4086
|
-
|
|
3958
|
+
async build() {
|
|
3959
|
+
const akanConfig = await this.#app.getConfig();
|
|
3960
|
+
const resolvedEntries = this.#pageEntries ?? await resolveSsrPageEntriesForApp(this.#app, await this.#app.getPageKeys());
|
|
3961
|
+
const entrySource = PagesEntrySourceGenerator.generate(resolvedEntries);
|
|
3962
|
+
const workspaceRoot = this.#app.workspace.workspaceRoot;
|
|
3963
|
+
const result = await Bun.build({
|
|
3964
|
+
entrypoints: [VIRTUAL_PAGES_ENTRY],
|
|
3965
|
+
outdir: `${this.#artifactDir}/server`,
|
|
3966
|
+
target: "bun",
|
|
3967
|
+
format: "esm",
|
|
3968
|
+
splitting: this.#splitting,
|
|
3969
|
+
minify: this.#command === "build",
|
|
3970
|
+
naming: {
|
|
3971
|
+
entry: "pages-[hash].[ext]",
|
|
3972
|
+
chunk: "chunks/[name]-[hash].[ext]",
|
|
3973
|
+
asset: "assets/[name]-[hash].[ext]"
|
|
3974
|
+
},
|
|
3975
|
+
define: this.#define(),
|
|
3976
|
+
plugins: [
|
|
3977
|
+
PagesBundleBuilder.createPagesEntryPlugin(entrySource),
|
|
3978
|
+
PagesBundleBuilder.createServerCssStubPlugin(),
|
|
3979
|
+
PagesBundleBuilder.createServerUseClientFetchPlugin(),
|
|
3980
|
+
await createExternalizeFrameworkPlugin({ app: this.#app, extra: akanConfig.externalLibs }),
|
|
3981
|
+
akanConfig.barrelImports.length > 0 ? await createBarrelImportsPlugin(this.#app, {
|
|
3982
|
+
pipeAfter: (source, args) => transformUseClient(source, {
|
|
3983
|
+
path: args.path,
|
|
3984
|
+
workspaceRoot
|
|
3985
|
+
})
|
|
3986
|
+
}) : createUseClientBundlePlugin({ workspaceRoot })
|
|
3987
|
+
]
|
|
3988
|
+
});
|
|
3989
|
+
if (!result.success)
|
|
3990
|
+
throw new AggregateError(result.logs, "[PagesBundleBuilder] Bun.build failed");
|
|
3991
|
+
const entryArtifact = result.outputs.find((a) => a.kind === "entry-point");
|
|
3992
|
+
if (!entryArtifact)
|
|
3993
|
+
throw new Error("[PagesBundleBuilder] Bun.build emitted no entry-point artifact");
|
|
3994
|
+
const bundlePath = path20.resolve(entryArtifact.path);
|
|
3995
|
+
const buildId = Date.now();
|
|
3996
|
+
const outputBytes = result.outputs.reduce((sum, output) => sum + output.size, 0);
|
|
3997
|
+
const chunkCount = result.outputs.filter((output) => output.kind === "chunk").length;
|
|
3998
|
+
this.#app.verbose(`[PagesBundleBuilder] ${path20.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`);
|
|
3999
|
+
return {
|
|
4000
|
+
bundlePath,
|
|
4001
|
+
buildId,
|
|
4002
|
+
splitting: this.#splitting,
|
|
4003
|
+
entryBytes: entryArtifact.size,
|
|
4004
|
+
outputBytes,
|
|
4005
|
+
outputCount: result.outputs.length,
|
|
4006
|
+
chunkCount
|
|
4007
|
+
};
|
|
4087
4008
|
}
|
|
4088
|
-
|
|
4089
|
-
|
|
4090
|
-
return path23.resolve(fromBase, id);
|
|
4091
|
-
const resolver = await this.#getCssImportResolver();
|
|
4092
|
-
const resolved = await resolver.resolve(id, fromBase);
|
|
4093
|
-
if (resolved)
|
|
4094
|
-
return resolved;
|
|
4095
|
-
throw new Error(`[css] failed to resolve stylesheet import "${id}" from ${fromBase}`);
|
|
4009
|
+
get #artifactDir() {
|
|
4010
|
+
return `${this.#command === "build" ? this.#app.dist.cwdPath : this.#app.cwdPath}/.akan/artifact`;
|
|
4096
4011
|
}
|
|
4097
|
-
|
|
4098
|
-
|
|
4099
|
-
return this.#cssImportResolver;
|
|
4100
|
-
this.#cssImportResolver = await CssImportResolver.create(this.#app);
|
|
4101
|
-
return this.#cssImportResolver;
|
|
4012
|
+
get #splitting() {
|
|
4013
|
+
return process.env.AKAN_SERVER_PAGES_SPLITTING === "1";
|
|
4102
4014
|
}
|
|
4103
|
-
|
|
4104
|
-
const
|
|
4105
|
-
|
|
4106
|
-
|
|
4015
|
+
#define() {
|
|
4016
|
+
const nodeEnv = this.#command === "build" ? "production" : "development";
|
|
4017
|
+
return {
|
|
4018
|
+
"process.env.NODE_ENV": JSON.stringify(nodeEnv),
|
|
4019
|
+
"process.env.AKAN_PUBLIC_RENDER_ENV": JSON.stringify("ssr"),
|
|
4020
|
+
...Object.fromEntries(Object.entries(this.#app.getPublicEnv()).map(([key, value]) => [`process.env.${key}`, JSON.stringify(value)]))
|
|
4021
|
+
};
|
|
4107
4022
|
}
|
|
4108
|
-
|
|
4109
|
-
|
|
4110
|
-
|
|
4111
|
-
|
|
4112
|
-
|
|
4113
|
-
|
|
4114
|
-
|
|
4115
|
-
|
|
4116
|
-
|
|
4117
|
-
|
|
4118
|
-
|
|
4119
|
-
|
|
4120
|
-
|
|
4121
|
-
|
|
4023
|
+
static createPagesEntryPlugin(source) {
|
|
4024
|
+
return {
|
|
4025
|
+
name: "akan-pages-entry",
|
|
4026
|
+
setup(build) {
|
|
4027
|
+
build.onResolve({ filter: /^akan-pages-entry$/ }, () => ({
|
|
4028
|
+
path: VIRTUAL_PAGES_ENTRY,
|
|
4029
|
+
namespace: "akan-virtual"
|
|
4030
|
+
}));
|
|
4031
|
+
build.onLoad({ filter: /^akan-pages-entry$/, namespace: "akan-virtual" }, () => ({
|
|
4032
|
+
contents: source,
|
|
4033
|
+
loader: "tsx"
|
|
4034
|
+
}));
|
|
4035
|
+
}
|
|
4036
|
+
};
|
|
4122
4037
|
}
|
|
4123
|
-
|
|
4124
|
-
|
|
4125
|
-
|
|
4126
|
-
|
|
4127
|
-
|
|
4128
|
-
|
|
4129
|
-
|
|
4130
|
-
|
|
4131
|
-
continue;
|
|
4132
|
-
files.add(file);
|
|
4038
|
+
static createServerCssStubPlugin() {
|
|
4039
|
+
return {
|
|
4040
|
+
name: "akan-server-css-stub",
|
|
4041
|
+
setup(build) {
|
|
4042
|
+
build.onLoad({ filter: /\.css$/ }, () => ({
|
|
4043
|
+
contents: "",
|
|
4044
|
+
loader: "js"
|
|
4045
|
+
}));
|
|
4133
4046
|
}
|
|
4134
|
-
}
|
|
4135
|
-
await Promise.all([...files].map(async (file) => {
|
|
4136
|
-
const content = await Bun.file(file).text();
|
|
4137
|
-
for (const m of content.matchAll(CANDIDATE_RE))
|
|
4138
|
-
candidates.add(m[0]);
|
|
4139
|
-
}));
|
|
4140
|
-
return [...candidates];
|
|
4047
|
+
};
|
|
4141
4048
|
}
|
|
4142
|
-
|
|
4143
|
-
|
|
4144
|
-
|
|
4145
|
-
|
|
4146
|
-
|
|
4147
|
-
|
|
4148
|
-
|
|
4149
|
-
|
|
4049
|
+
static createServerUseClientFetchPlugin() {
|
|
4050
|
+
return {
|
|
4051
|
+
name: "akan-server-use-client-fetch",
|
|
4052
|
+
setup(build) {
|
|
4053
|
+
build.onLoad({ filter: /[/\\]lib[/\\]useClient\.(ts|tsx|js|jsx)$/ }, async (args) => {
|
|
4054
|
+
const source = await Bun.file(args.path).text();
|
|
4055
|
+
const transformed = PagesBundleBuilder.transformServerUseClientFetchSource(source);
|
|
4056
|
+
if (transformed === source)
|
|
4057
|
+
return;
|
|
4058
|
+
return { contents: transformed, loader: loaderFor4(args.path) };
|
|
4059
|
+
});
|
|
4060
|
+
}
|
|
4061
|
+
};
|
|
4150
4062
|
}
|
|
4151
|
-
|
|
4152
|
-
|
|
4153
|
-
|
|
4154
|
-
|
|
4063
|
+
static transformServerUseClientFetchSource(source) {
|
|
4064
|
+
if (!source.includes(`with { type: "macro" }`) || !source.includes("FetchClient.build"))
|
|
4065
|
+
return source;
|
|
4066
|
+
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 });");
|
|
4155
4067
|
}
|
|
4156
|
-
return null;
|
|
4157
4068
|
}
|
|
4158
|
-
function
|
|
4159
|
-
|
|
4160
|
-
|
|
4161
|
-
|
|
4162
|
-
|
|
4163
|
-
|
|
4069
|
+
function loaderFor4(absPath) {
|
|
4070
|
+
if (absPath.endsWith(".tsx"))
|
|
4071
|
+
return "tsx";
|
|
4072
|
+
if (absPath.endsWith(".jsx"))
|
|
4073
|
+
return "jsx";
|
|
4074
|
+
if (absPath.endsWith(".ts"))
|
|
4075
|
+
return "ts";
|
|
4076
|
+
return "js";
|
|
4077
|
+
}
|
|
4078
|
+
|
|
4079
|
+
// pkgs/@akanjs/devkit/frontendBuild/precompressArtifacts.ts
|
|
4080
|
+
import fs3 from "fs";
|
|
4081
|
+
import path21 from "path";
|
|
4082
|
+
var COMPRESSIBLE_EXTS = new Set([".css", ".html", ".js", ".json", ".svg"]);
|
|
4083
|
+
var MIN_COMPRESS_BYTES = 1024;
|
|
4084
|
+
async function precompressArtifacts(app) {
|
|
4085
|
+
const roots = [path21.join(app.dist.cwdPath, ".akan/artifact/client")];
|
|
4086
|
+
const result = { files: 0, inputBytes: 0, outputBytes: 0 };
|
|
4087
|
+
await Promise.all(roots.map((root) => precompressRoot(root, result)));
|
|
4088
|
+
if (result.files > 0) {
|
|
4089
|
+
app.verbose(`[precompress] wrote ${result.files} gzip sidecars (${formatBytes(result.inputBytes)} -> ${formatBytes(result.outputBytes)})`);
|
|
4164
4090
|
}
|
|
4091
|
+
return result;
|
|
4165
4092
|
}
|
|
4166
|
-
function
|
|
4167
|
-
|
|
4168
|
-
|
|
4169
|
-
|
|
4170
|
-
|
|
4171
|
-
|
|
4093
|
+
async function precompressRoot(root, result) {
|
|
4094
|
+
if (!fs3.existsSync(root) || !fs3.statSync(root).isDirectory())
|
|
4095
|
+
return;
|
|
4096
|
+
const glob = new Bun.Glob("**/*");
|
|
4097
|
+
for await (const filePath of glob.scan({ cwd: root, absolute: true })) {
|
|
4098
|
+
if (!await shouldPrecompress(filePath))
|
|
4099
|
+
continue;
|
|
4100
|
+
const bytes = await Bun.file(filePath).bytes();
|
|
4101
|
+
const gz = Bun.gzipSync(toArrayBuffer(bytes));
|
|
4102
|
+
await Bun.write(`${filePath}.gz`, gz);
|
|
4103
|
+
result.files += 1;
|
|
4104
|
+
result.inputBytes += bytes.byteLength;
|
|
4105
|
+
result.outputBytes += gz.byteLength;
|
|
4172
4106
|
}
|
|
4173
4107
|
}
|
|
4174
|
-
function
|
|
4175
|
-
|
|
4108
|
+
async function shouldPrecompress(filePath) {
|
|
4109
|
+
if (filePath.endsWith(".gz"))
|
|
4110
|
+
return false;
|
|
4111
|
+
if (!COMPRESSIBLE_EXTS.has(path21.extname(filePath).toLowerCase()))
|
|
4112
|
+
return false;
|
|
4113
|
+
const file = Bun.file(filePath);
|
|
4114
|
+
if (!await file.exists())
|
|
4115
|
+
return false;
|
|
4116
|
+
return file.size >= MIN_COMPRESS_BYTES;
|
|
4176
4117
|
}
|
|
4177
|
-
function
|
|
4178
|
-
return
|
|
4118
|
+
function toArrayBuffer(bytes) {
|
|
4119
|
+
return bytes.buffer.slice(bytes.byteOffset, bytes.byteOffset + bytes.byteLength);
|
|
4179
4120
|
}
|
|
4180
|
-
function
|
|
4181
|
-
|
|
4182
|
-
|
|
4183
|
-
|
|
4184
|
-
|
|
4121
|
+
function formatBytes(bytes) {
|
|
4122
|
+
if (bytes < 1024)
|
|
4123
|
+
return `${bytes}B`;
|
|
4124
|
+
if (bytes < 1024 * 1024)
|
|
4125
|
+
return `${(bytes / 1024).toFixed(1)}KB`;
|
|
4126
|
+
return `${(bytes / 1024 / 1024).toFixed(1)}MB`;
|
|
4185
4127
|
}
|
|
4186
4128
|
|
|
4187
4129
|
// pkgs/@akanjs/devkit/frontendBuild/ssrBaseArtifactBuilder.ts
|
|
4130
|
+
import path22 from "path";
|
|
4131
|
+
import { optimize } from "@tailwindcss/node";
|
|
4188
4132
|
function prepareCssAsset(command, basePath, cssText) {
|
|
4189
4133
|
return optimize(cssText, { file: `${basePath || "root"}.css`, minify: command === "build" }).code;
|
|
4190
4134
|
}
|
|
@@ -4199,7 +4143,7 @@ class SsrBaseArtifactBuilder {
|
|
|
4199
4143
|
this.#app = app;
|
|
4200
4144
|
this.#command = command;
|
|
4201
4145
|
this.#artifactDir = `${command === "build" ? app.dist.cwdPath : app.cwdPath}/.akan/artifact`;
|
|
4202
|
-
this.#absArtifactDir =
|
|
4146
|
+
this.#absArtifactDir = path22.resolve(this.#artifactDir);
|
|
4203
4147
|
}
|
|
4204
4148
|
async build() {
|
|
4205
4149
|
const akanConfig = await this.#app.getConfig();
|
|
@@ -4221,7 +4165,7 @@ class SsrBaseArtifactBuilder {
|
|
|
4221
4165
|
rscRuntimeSsrManifest,
|
|
4222
4166
|
vendorMap,
|
|
4223
4167
|
cssAssets,
|
|
4224
|
-
pagesBundlePath: this.#command === "build" ?
|
|
4168
|
+
pagesBundlePath: this.#command === "build" ? path22.relative(this.#absArtifactDir, pagesBundle.bundlePath) : pagesBundle.bundlePath,
|
|
4225
4169
|
pagesBundleBuildId: pagesBundle.buildId,
|
|
4226
4170
|
domains: [...akanConfig.domains],
|
|
4227
4171
|
subRoutes: Object.fromEntries(Array.from(akanConfig.subRoutes.entries()).map(([basePath, domains]) => [basePath, [...domains]])),
|
|
@@ -4237,18 +4181,18 @@ class SsrBaseArtifactBuilder {
|
|
|
4237
4181
|
androidSha256CertFingerprints: target.deepLinks?.android?.sha256CertFingerprints
|
|
4238
4182
|
}))
|
|
4239
4183
|
};
|
|
4240
|
-
await Bun.write(
|
|
4184
|
+
await Bun.write(path22.join(this.#absArtifactDir, "base-artifact.json"), `${JSON.stringify(artifact, null, 2)}
|
|
4241
4185
|
`);
|
|
4242
4186
|
this.#app.verbose(`[base-artifact] complete in ${Date.now() - this.#started}ms`);
|
|
4243
4187
|
return { artifact, seedIndex, cssCompiler, optimizedFonts };
|
|
4244
4188
|
}
|
|
4245
4189
|
async#buildRuntimeClientEntries() {
|
|
4246
4190
|
const akanServerPath = await this.#resolveAkanServerPath();
|
|
4247
|
-
const rscClientEntry =
|
|
4248
|
-
const rscSegmentOutletEntry =
|
|
4191
|
+
const rscClientEntry = path22.resolve(akanServerPath, "rscClient.tsx");
|
|
4192
|
+
const rscSegmentOutletEntry = path22.resolve(akanServerPath, "rscSegmentOutlet.tsx");
|
|
4249
4193
|
const vendorEntries = VENDOR_SPECIFIERS.map((specifier) => ({
|
|
4250
4194
|
specifier,
|
|
4251
|
-
absPath:
|
|
4195
|
+
absPath: path22.resolve(akanServerPath, "vendor", `${specifier.replaceAll("/", "-").replaceAll(".", "-")}.ts`)
|
|
4252
4196
|
}));
|
|
4253
4197
|
const entries = [rscClientEntry, rscSegmentOutletEntry, ...vendorEntries.map((v) => v.absPath)];
|
|
4254
4198
|
const clientBundle = await new ClientEntriesBundler({ app: this.#app, entries, command: this.#command }).bundle();
|
|
@@ -4285,15 +4229,15 @@ class SsrBaseArtifactBuilder {
|
|
|
4285
4229
|
async#resolveAkanServerPath() {
|
|
4286
4230
|
const candidates = [];
|
|
4287
4231
|
try {
|
|
4288
|
-
candidates.push(
|
|
4232
|
+
candidates.push(path22.dirname(Bun.resolveSync("akanjs/server", this.#app.workspace.workspaceRoot)));
|
|
4289
4233
|
} catch {}
|
|
4290
|
-
candidates.push(
|
|
4234
|
+
candidates.push(path22.join(this.#app.workspace.workspaceRoot, "pkgs/akanjs/server"), path22.join(this.#app.workspace.workspaceRoot, "node_modules/akanjs/server"));
|
|
4291
4235
|
try {
|
|
4292
|
-
candidates.push(
|
|
4236
|
+
candidates.push(path22.dirname(Bun.resolveSync("akanjs/server", path22.dirname(Bun.main))));
|
|
4293
4237
|
} catch {}
|
|
4294
|
-
candidates.push(
|
|
4238
|
+
candidates.push(path22.join(path22.dirname(Bun.main), "node_modules/akanjs/server"), path22.join(path22.dirname(Bun.main), "../../akanjs/server"), path22.resolve(import.meta.dir, "../../server"), path22.resolve(import.meta.dir, "../server"));
|
|
4295
4239
|
for (const candidate of candidates) {
|
|
4296
|
-
if (await Bun.file(
|
|
4240
|
+
if (await Bun.file(path22.join(candidate, "rscClient.tsx")).exists())
|
|
4297
4241
|
return candidate;
|
|
4298
4242
|
}
|
|
4299
4243
|
throw new Error(`[base-artifact] failed to locate akanjs/server; looked in: ${candidates.join(", ")}`);
|
|
@@ -4322,38 +4266,9 @@ ${preparedCssText}`).toString(36);
|
|
|
4322
4266
|
`styles/${cssAssetName}-${cssHash}.css`,
|
|
4323
4267
|
`/_akan/styles/${cssAssetName}-${cssHash}.css`
|
|
4324
4268
|
];
|
|
4325
|
-
await Bun.write(
|
|
4269
|
+
await Bun.write(path22.join(this.#absArtifactDir, cssRelPath), preparedCssText);
|
|
4326
4270
|
this.#app.verbose(`[base-artifact] wrote ${preparedCssText.length} bytes of CSS for ${basePath} -> ${cssRelPath}`);
|
|
4327
4271
|
return [basePath, { cssUrl, cssRelPath }];
|
|
4328
4272
|
}
|
|
4329
4273
|
}
|
|
4330
|
-
|
|
4331
|
-
// pkgs/@akanjs/devkit/frontendBuild/watchRootResolver.ts
|
|
4332
|
-
import fs5 from "fs";
|
|
4333
|
-
import path25 from "path";
|
|
4334
|
-
|
|
4335
|
-
class WatchRootResolver {
|
|
4336
|
-
#app;
|
|
4337
|
-
constructor(app) {
|
|
4338
|
-
this.#app = app;
|
|
4339
|
-
}
|
|
4340
|
-
async resolve() {
|
|
4341
|
-
const tsconfig = await this.#app.getTsConfig();
|
|
4342
|
-
const set = new Set;
|
|
4343
|
-
set.add(path25.resolve(`${this.#app.cwdPath}/page`));
|
|
4344
|
-
for (const targets of Object.values(tsconfig.compilerOptions.paths ?? {})) {
|
|
4345
|
-
for (const target of targets) {
|
|
4346
|
-
if (!target)
|
|
4347
|
-
continue;
|
|
4348
|
-
if (path25.isAbsolute(target))
|
|
4349
|
-
continue;
|
|
4350
|
-
const cleaned = target.replace(/\/?\*+.*$/, "").replace(/\/[^/]+\.[^/]+$/, "");
|
|
4351
|
-
const resolved = path25.resolve(this.#app.workspace.workspaceRoot, cleaned);
|
|
4352
|
-
if (fs5.existsSync(resolved))
|
|
4353
|
-
set.add(resolved);
|
|
4354
|
-
}
|
|
4355
|
-
}
|
|
4356
|
-
return [...set];
|
|
4357
|
-
}
|
|
4358
|
-
}
|
|
4359
|
-
export { GraphClientEntryDiscovery, RouteClientBuilder, AllRoutesBuilder, AutoImportSync, CsrArtifactBuilder, DevChangePlanner, DevGeneratedIndexSync, FontOptimizer, HmrWatcher, PagesBundleBuilder, precompressArtifacts, SsrBaseArtifactBuilder, WatchRootResolver };
|
|
4274
|
+
export { GraphClientEntryDiscovery, RouteClientBuilder, AllRoutesBuilder, AutoImportSync, CsrArtifactBuilder, CssCompiler, DevChangePlanner, DevGeneratedIndexSync, FontOptimizer, PagesBundleBuilder, precompressArtifacts, SsrBaseArtifactBuilder };
|