@akanjs/cli 3.0.0-beta.8 → 3.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.build-stamp +1 -1
- package/CodeAgent-2vmp9qe0.js +9 -0
- package/CodeAgentRpcHost-p7h6n2hp.js +7 -0
- package/CodeAgentRpcListener-7p7z5dd7.js +97 -0
- package/CodeTui-5ed6bg2a.js +1457 -0
- package/README.ko.md +83 -28
- package/README.md +70 -27
- package/TunnelShare-cwh9ezsm.js +9 -0
- package/{agent.command-0q17pnrs.js → agent.command-gn73rhe7.js} +8 -9
- package/akanCode.js +102 -0
- package/application.command-e968t0hw.js +246 -0
- package/{applicationBuildRunner-kxwbssgn.js → applicationBuildRunner-64taxseg.js} +121 -77
- package/buildBatch.proc.js +120 -32
- package/cloud.command-0jm301rr.js +95 -0
- package/cloud.script-nemnzjpv.js +20 -0
- package/code.command-18kcm7hq.js +154 -0
- package/commandManifest.json +1 -1
- package/consoleToStderr-h5bryj94.js +7 -0
- package/context.command-trd9bn18.js +48 -0
- package/context.runner-y5hwh673.js +21 -0
- package/{dependencyScanner-shs82ynp.js → dependencyScanner-06z2dmrr.js} +2 -2
- package/{devTui-m1atgkc9.js → devTui-q7k7bakd.js} +122 -40
- package/fileSys-wzzrntkw.js +9 -0
- package/guideline.command-bdmdn2de.js +42 -0
- package/guidelines/agentRule/agentRule.instruction.md +154 -26
- package/guidelines/conventions/conventions.instruction.md +134 -48
- package/guidelines/mcpRule/mcpRule.instruction.md +7 -5
- package/guidelines/queryRule/queryRule.instruction.md +21 -7
- package/guidelines/runtimeRule/runtimeRule.instruction.md +291 -6
- package/guidelines/ssrRule/ssrRule.instruction.md +4 -2
- package/guidelines/transportRule/transportRule.instruction.md +10 -8
- package/guidelines/workspaceOnboarding/workspaceOnboarding.instruction.md +20 -0
- package/guidelines/workspaceRecipes/workspaceRecipes.instruction.md +66 -3
- package/incrementalBuilder.proc.js +564 -109
- package/index-0wpybxtr.js +2729 -0
- package/{index-kcxmv48c.js → index-2ahj896a.js} +45 -79
- package/index-2wc41k5w.js +49 -0
- package/index-3hx1z96c.js +87 -0
- package/{index-2tv3fdx4.js → index-3w94e7n3.js} +1228 -1458
- package/{index-hwkygn9s.js → index-48k96p55.js} +325 -36
- package/index-4npz05nm.js +187 -0
- package/{index-q0y2jq9j.js → index-52w0fvh8.js} +21 -4
- package/index-6he6gvfg.js +4300 -0
- package/index-7md2g9vw.js +6 -0
- package/{index-04bk599g.js → index-93j60em0.js} +10 -17
- package/index-94s080qm.js +266 -0
- package/index-9dp031by.js +12 -0
- package/{index-ktb5njgy.js → index-9z771ptz.js} +49 -99
- package/{index-y6n8nmny.js → index-abxpehf8.js} +75 -203
- package/{index-3d9y066c.js → index-bjwxs904.js} +9 -8
- package/{index-9077wzd2.js → index-by9pxkr0.js} +43 -26
- package/index-c6eggbee.js +123 -0
- package/index-d2242ksy.js +99 -0
- package/index-drehwpdt.js +1119 -0
- package/{index-92zztqy1.js → index-dy0jx9ay.js} +2 -17
- package/{index-ffvk10zz.js → index-f3c78nd6.js} +107 -241
- package/index-gbggbqcp.js +1058 -0
- package/{index-by5gpp8x.js → index-hj6w4ka1.js} +2 -1
- package/index-hsmtysz4.js +53 -0
- package/{index-j9yhg404.js → index-jmrn54tt.js} +4 -7
- package/{index-a64esmtb.js → index-kcdj1z5v.js} +523 -1022
- package/{index-d43r08tf.js → index-kqabytj7.js} +77 -150
- package/{index-vyvre9en.js → index-mbxbhay5.js} +6 -2
- package/{index-vxtad5bd.js → index-mgxpx3f2.js} +5 -8
- package/index-p6pbz9n5.js +25 -0
- package/{index-sj5vxr8f.js → index-p993efkq.js} +981 -897
- package/{index-2xp0kfkt.js → index-pmhqttv3.js} +37 -25
- package/index-tqm7jjcn.js +700 -0
- package/{index-0g7r8xz3.js → index-vpj42xwt.js} +114 -232
- package/index-wa5jbxj1.js +760 -0
- package/{index-k2j1hc4r.js → index-wprnsjms.js} +12 -12
- package/{index-gyd220p2.js → index-y3mphr1m.js} +554 -577
- package/index-yg7jhz8x.js +69 -0
- package/{index-h7yw2z9n.js → index-zzvvc845.js} +8 -14
- package/index.js +23 -22
- package/{library.command-qzzbj6d8.js → library.command-9h749a1x.js} +4 -5
- package/{localRegistry.command-vyrgg4xw.js → localRegistry.command-2bqqhxyz.js} +23 -43
- package/{module.command-rq5hq3ax.js → module.command-nv8nzgft.js} +8 -12
- package/{package.command-d9hd6v9n.js → package.command-mq4bggp6.js} +6 -7
- package/package.json +15 -7
- package/{page.command-126d56te.js → page.command-j639n707.js} +4 -5
- package/{primitive.command-taxd1538.js → primitive.command-kz6849th.js} +9 -23
- package/prompter-76k56b37.js +8 -0
- package/{quality.command-6gsna0a3.js → quality.command-4hyabwkb.js} +205 -377
- package/{recipeScanner-fpagtgc1.js → recipeScanner-4b69ybe0.js} +1 -1
- package/{repair.command-tq2dgy72.js → repair.command-a9awpbqr.js} +11 -35
- package/{routeSourceValidator-apyqw7wk.js → routeSourceValidator-7ycnfz0e.js} +0 -6
- package/{scalar.command-3bcmrrkv.js → scalar.command-ma4fpm9m.js} +7 -11
- package/skills/akan-code/SKILL.md +130 -0
- package/skills/akan-debug/SKILL.md +42 -0
- package/skills/akan-deps/SKILL.md +50 -0
- package/skills/akan-err/SKILL.md +39 -0
- package/skills/akan-module/SKILL.md +45 -0
- package/skills/akan-scaffold/SKILL.md +76 -0
- package/skills/akan-scalar/SKILL.md +52 -0
- package/skills/akan-service/SKILL.md +34 -0
- package/skills/akan-store/SKILL.md +67 -0
- package/skills/akan-ui/SKILL.md +63 -0
- package/skills/akan-validate/SKILL.md +44 -0
- package/{subspace.command-eddzqpx3.js → subspace.command-3k2pmrs4.js} +53 -62
- package/templates/__scalar/__model__/__model__.abstract.md.template +8 -0
- package/templates/app/page/_index.tsx +257 -258
- package/templates/app/page/_layout.tsx +5 -1
- package/templates/app/page/_overrides.tsx +12 -0
- package/templates/app/page/styles.css.template +316 -14
- package/templates/app/public/apple-touch-icon.png +0 -0
- package/templates/app/public/favicon.ico +0 -0
- package/templates/app/public/jelly/cloud.webp +0 -0
- package/templates/app/public/jelly/comet.webp +0 -0
- package/templates/app/public/jelly/moon.webp +0 -0
- package/templates/app/public/jelly/planet.webp +0 -0
- package/templates/app/public/jelly/rocket.webp +0 -0
- package/templates/app/public/jelly/star.webp +0 -0
- package/templates/app/public/jelly/sugar.webp +0 -0
- package/templates/app/public/logo.png +0 -0
- package/templates/app/ui/Recipe/index.ts +9 -0
- package/templates/app/ui/Recipe/jellyButton.ts +63 -0
- package/templates/appSample/lib/__scalar/workHistory/workHistory.abstract.ts +8 -39
- package/templates/appSample/lib/_noti/noti.abstract.ts +6 -29
- package/templates/appSample/lib/task/Task.Unit.tsx +1 -1
- package/templates/appSample/lib/task/Task.Util.tsx +10 -13
- package/templates/appSample/lib/task/Task.View.tsx +2 -2
- package/templates/appSample/lib/task/Task.Zone.tsx +5 -2
- package/templates/appSample/lib/task/task.abstract.ts +12 -32
- package/templates/appSample/lib/task/task.signal.spec.ts +1 -15
- package/templates/appSample/lib/task/task.signal.test.ts +15 -1
- package/templates/appSample/lib/task/task.signal.ts +10 -8
- package/templates/appSample/page/task/[taskId]/_index.tsx +6 -6
- package/templates/appSample/page/task/[taskId]/edit.tsx +5 -4
- package/templates/appSample/page/task/_index.tsx +4 -3
- package/templates/appSample/page/task/_layout.tsx +1 -1
- package/templates/appSample/page/task/new.tsx +5 -4
- package/templates/appSample/srvkit/{AuthGuard.ts → guards.ts} +7 -4
- package/templates/appSample/srvkit/{SessionInternalArg.ts → internalArgs.ts} +6 -4
- package/templates/appSample/ui/GlobalLoading.tsx +2 -1
- package/templates/appSample/ui/QuantityControl.tsx +3 -3
- package/templates/client.ts +7 -30
- package/templates/crudPages/[__model__Id]/edit/page.tsx +2 -1
- package/templates/crudPages/[__model__Id]/page.tsx +2 -1
- package/templates/crudPages/new/page.tsx +2 -1
- package/templates/crudPages/page.tsx +2 -1
- package/templates/crudSinglePage/page.tsx +2 -1
- package/templates/lib/__lib/lib.dictionary.ts +0 -2
- package/templates/lib/__lib/lib.document.ts +0 -2
- package/templates/lib/sig.ts +8 -30
- package/templates/localDev/docker-compose.yaml.template +3 -14
- package/templates/module/__model__.signal.ts +12 -4
- package/templates/pkgRoot/tsconfig.json.template +2 -7
- package/templates/server.ts +1 -3
- package/templates/service/__model__.abstract.md.template +8 -0
- package/templates/workspaceRoot/.gitignore.template +0 -1
- package/templates/workspaceRoot/AGENTS.md.template +6 -0
- package/templates/workspaceRoot/package.json.template +1 -3
- package/tunnel.command-xwr46b66.js +129 -0
- package/{typeChecker-3xgrzc2v.js → typeChecker-px49mezc.js} +1 -1
- package/typecheck.proc.js +1 -1
- package/workflow.command-t7wqe4h7.js +79 -0
- package/{workspace.command-06wvstgr.js → workspace.command-4derhzrh.js} +79 -109
- package/abstractCompactor-gmkc0z8j.js +0 -102
- package/application.command-18y0s485.js +0 -204
- package/applicationReleasePackager-4yasxwv9.js +0 -244
- package/capacitorApp-36rbr858.js +0 -12
- package/cloud.command-av97ndh6.js +0 -94
- package/cloud.script-cm11xjdz.js +0 -20
- package/context.command-5v37ef3n.js +0 -81
- package/devTuiView-0hc2emhe.js +0 -9
- package/getDirname-dr3jz1xg.js +0 -7
- package/guideline.command-4pz9g1rt.js +0 -360
- package/guidelines/agentRule/agentRule.generate.json +0 -31
- package/guidelines/componentRule/componentRule.generate.json +0 -31
- package/guidelines/conventions/conventions.generate.json +0 -25
- package/guidelines/cssRule/cssRule.generate.json +0 -31
- package/guidelines/docPageRule/docPageRule.generate.json +0 -31
- package/guidelines/docPageRule/docPageRule.instruction.md +0 -25
- package/guidelines/docSyncRule/docSyncRule.generate.json +0 -31
- package/guidelines/docSyncRule/docSyncRule.instruction.md +0 -25
- package/guidelines/enumConstant/enumConstant.generate.json +0 -31
- package/guidelines/enumConstant/enumConstant.instruction.md +0 -28
- package/guidelines/fieldRule/fieldRule.generate.json +0 -31
- package/guidelines/framework/framework.generate.json +0 -32
- package/guidelines/mcpRule/mcpRule.generate.json +0 -35
- package/guidelines/modelConstant/modelConstant.generate.json +0 -32
- package/guidelines/modelDictionary/modelDictionary.generate.json +0 -32
- package/guidelines/modelDocument/modelDocument.generate.json +0 -31
- package/guidelines/modelService/modelService.generate.json +0 -31
- package/guidelines/modelSignal/modelSignal.generate.json +0 -31
- package/guidelines/modelStore/modelStore.generate.json +0 -31
- package/guidelines/modelTemplate/modelTemplate.generate.json +0 -31
- package/guidelines/modelUnit/modelUnit.generate.json +0 -31
- package/guidelines/modelUtil/modelUtil.generate.json +0 -31
- package/guidelines/modelView/modelView.generate.json +0 -31
- package/guidelines/modelZone/modelZone.generate.json +0 -31
- package/guidelines/moduleCodegen/moduleCodegen.generate.json +0 -31
- package/guidelines/moduleCodegen/moduleCodegen.instruction.md +0 -28
- package/guidelines/moduleOverview/moduleOverview.generate.json +0 -31
- package/guidelines/queryRule/queryRule.generate.json +0 -31
- package/guidelines/recipeRule/recipeRule.generate.json +0 -31
- package/guidelines/runtimeRule/runtimeRule.generate.json +0 -25
- package/guidelines/scalarConstant/scalarConstant.generate.json +0 -32
- package/guidelines/scalarDictionary/scalarDictionary.generate.json +0 -32
- package/guidelines/scalarModule/scalarModule.generate.json +0 -31
- package/guidelines/sharedUiUsage/sharedUiUsage.generate.json +0 -31
- package/guidelines/sharedUiUsage/sharedUiUsage.instruction.md +0 -25
- package/guidelines/ssrRule/ssrRule.generate.json +0 -31
- package/guidelines/transportRule/transportRule.generate.json +0 -31
- package/guidelines/utilUiUsage/utilUiUsage.generate.json +0 -31
- package/guidelines/utilUiUsage/utilUiUsage.instruction.md +0 -25
- package/guidelines/workspaceOnboarding/workspaceOnboarding.generate.json +0 -24
- package/guidelines/workspaceRecipes/workspaceRecipes.generate.json +0 -33
- package/index-47pj3aee.js +0 -76
- package/index-5ystnsd5.js +0 -841
- package/index-6wwzs1bc.js +0 -7
- package/index-a17x49vg.js +0 -229
- package/index-m0xccmmm.js +0 -228
- package/index-meh558jy.js +0 -65
- package/index-rtxxan88.js +0 -471
- package/index-te54x9zm.js +0 -338
- package/index-w8kmx1b6.js +0 -2176
- package/index-wt7qrtd4.js +0 -1516
- package/templates/__scalar/__model__/__model__.abstract.md +0 -28
- package/templates/module/__model__.abstract.md +0 -28
- package/templates/service/__model__.abstract.md +0 -28
- package/templates/workspaceRoot/patches/react-dom@19.2.7.patch.template +0 -62
- package/workflow.command-gtdnzvbz.js +0 -108
|
@@ -1,161 +1,474 @@
|
|
|
1
1
|
// @bun
|
|
2
2
|
import {
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
getDirname2
|
|
7
|
-
} from "./index-6wwzs1bc.js";
|
|
3
|
+
getDirname2,
|
|
4
|
+
FileSys2
|
|
5
|
+
} from "./index-52w0fvh8.js";
|
|
8
6
|
|
|
9
|
-
// pkgs/@akanjs/devkit/
|
|
10
|
-
import {
|
|
7
|
+
// pkgs/@akanjs/devkit/artifact/implicitRootLayout.ts
|
|
8
|
+
import { readdir, rm } from "fs/promises";
|
|
9
|
+
import path2 from "path";
|
|
10
|
+
|
|
11
|
+
// pkgs/@akanjs/devkit/transforms/asyncDefaultExportDetector.ts
|
|
12
|
+
import fs from "fs";
|
|
11
13
|
import path from "path";
|
|
12
|
-
import { Logger } from "akanjs/common";
|
|
13
14
|
|
|
14
|
-
class
|
|
15
|
-
static
|
|
16
|
-
static
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
static #logger = new Logger("CodegenLock");
|
|
20
|
-
static #queue = Promise.resolve();
|
|
21
|
-
static pathIn(workspaceRoot) {
|
|
22
|
-
return path.join(workspaceRoot, "local", ".akan", CodegenLock.fileName);
|
|
23
|
-
}
|
|
24
|
-
static async run(workspaceRoot, label, fn) {
|
|
25
|
-
const ahead = CodegenLock.#queue;
|
|
26
|
-
let done;
|
|
27
|
-
CodegenLock.#queue = new Promise((resolve) => {
|
|
28
|
-
done = resolve;
|
|
29
|
-
});
|
|
30
|
-
try {
|
|
31
|
-
await CodegenLock.#waitForQueue(ahead, label);
|
|
32
|
-
return await CodegenLock.#withFileLock(workspaceRoot, label, fn);
|
|
33
|
-
} finally {
|
|
34
|
-
done();
|
|
35
|
-
}
|
|
15
|
+
class AsyncDefaultExportDetector {
|
|
16
|
+
static #typescriptLoad;
|
|
17
|
+
static #loadTypescript() {
|
|
18
|
+
AsyncDefaultExportDetector.#typescriptLoad ??= import("typescript").then((mod) => mod.default ?? mod);
|
|
19
|
+
return AsyncDefaultExportDetector.#typescriptLoad;
|
|
36
20
|
}
|
|
37
|
-
static async
|
|
38
|
-
let timer = null;
|
|
39
|
-
const expired = new Promise((resolve) => {
|
|
40
|
-
timer = setTimeout(() => resolve("expired"), CodegenLock.waitTimeoutMs);
|
|
41
|
-
});
|
|
21
|
+
static async detect(moduleAbsPath) {
|
|
42
22
|
try {
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
23
|
+
const typescript = await AsyncDefaultExportDetector.#loadTypescript();
|
|
24
|
+
const source = fs.readFileSync(path.resolve(moduleAbsPath), "utf8");
|
|
25
|
+
const sourceFile = typescript.createSourceFile(moduleAbsPath, source, typescript.ScriptTarget.Latest, true, /\.[tj]sx$/.test(moduleAbsPath) ? typescript.ScriptKind.TSX : typescript.ScriptKind.TS);
|
|
26
|
+
return new AsyncDefaultExportDetector(typescript).detectInSourceFile(sourceFile);
|
|
27
|
+
} catch {
|
|
28
|
+
return false;
|
|
48
29
|
}
|
|
49
30
|
}
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
return;
|
|
54
|
-
});
|
|
55
|
-
const held = await CodegenLock.#acquire(lockPath, label);
|
|
56
|
-
try {
|
|
57
|
-
return await fn();
|
|
58
|
-
} finally {
|
|
59
|
-
if (held)
|
|
60
|
-
await rm(lockPath, { force: true }).catch(() => {
|
|
61
|
-
return;
|
|
62
|
-
});
|
|
63
|
-
}
|
|
31
|
+
#ts;
|
|
32
|
+
constructor(typescript) {
|
|
33
|
+
this.#ts = typescript;
|
|
64
34
|
}
|
|
65
|
-
|
|
66
|
-
const
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
35
|
+
detectInSourceFile(sourceFile) {
|
|
36
|
+
const ts = this.#ts;
|
|
37
|
+
const asyncBindings = new Map;
|
|
38
|
+
let defaultIdentifier = null;
|
|
39
|
+
for (const statement of sourceFile.statements) {
|
|
40
|
+
if (ts.isFunctionDeclaration(statement)) {
|
|
41
|
+
if (this.#hasModifier(statement, ts.SyntaxKind.DefaultKeyword)) {
|
|
42
|
+
return this.#hasModifier(statement, ts.SyntaxKind.AsyncKeyword);
|
|
43
|
+
}
|
|
44
|
+
if (statement.name) {
|
|
45
|
+
asyncBindings.set(statement.name.text, this.#hasModifier(statement, ts.SyntaxKind.AsyncKeyword));
|
|
46
|
+
}
|
|
47
|
+
continue;
|
|
77
48
|
}
|
|
78
|
-
if (
|
|
49
|
+
if (ts.isVariableStatement(statement)) {
|
|
50
|
+
for (const declaration of statement.declarationList.declarations) {
|
|
51
|
+
if (!ts.isIdentifier(declaration.name))
|
|
52
|
+
continue;
|
|
53
|
+
asyncBindings.set(declaration.name.text, this.#isAsyncFunctionExpression(declaration.initializer));
|
|
54
|
+
}
|
|
79
55
|
continue;
|
|
80
|
-
if (Date.now() >= deadline) {
|
|
81
|
-
CodegenLock.#logger.warn(`codegen lock at ${lockPath} held past ${CodegenLock.waitTimeoutMs}ms; continuing without it (${label})`);
|
|
82
|
-
return false;
|
|
83
56
|
}
|
|
84
|
-
|
|
57
|
+
if (ts.isExportAssignment(statement)) {
|
|
58
|
+
if (this.#isAsyncFunctionExpression(statement.expression))
|
|
59
|
+
return true;
|
|
60
|
+
if (ts.isCallExpression(statement.expression))
|
|
61
|
+
return true;
|
|
62
|
+
if (ts.isIdentifier(statement.expression))
|
|
63
|
+
defaultIdentifier = statement.expression.text;
|
|
64
|
+
continue;
|
|
65
|
+
}
|
|
66
|
+
if (ts.isExportDeclaration(statement) && statement.exportClause && ts.isNamedExports(statement.exportClause)) {
|
|
67
|
+
for (const specifier of statement.exportClause.elements) {
|
|
68
|
+
if (specifier.name.text !== "default")
|
|
69
|
+
continue;
|
|
70
|
+
defaultIdentifier = specifier.propertyName?.text ?? specifier.name.text;
|
|
71
|
+
}
|
|
72
|
+
}
|
|
85
73
|
}
|
|
74
|
+
return defaultIdentifier ? asyncBindings.get(defaultIdentifier) === true : false;
|
|
86
75
|
}
|
|
87
|
-
|
|
88
|
-
const
|
|
89
|
-
|
|
90
|
-
return true;
|
|
91
|
-
const holder = CodegenLock.#parseHolder(await readFile(lockPath, "utf8").catch(() => ""));
|
|
92
|
-
if (holder) {
|
|
93
|
-
if (CodegenLock.#isAlive(holder.pid))
|
|
94
|
-
return false;
|
|
95
|
-
} else if (Date.now() - info.mtimeMs < CodegenLock.unknownHolderStaleMs)
|
|
96
|
-
return false;
|
|
97
|
-
await rm(lockPath, { force: true }).catch(() => {
|
|
98
|
-
return;
|
|
99
|
-
});
|
|
100
|
-
return true;
|
|
101
|
-
}
|
|
102
|
-
static #parseHolder(raw) {
|
|
103
|
-
try {
|
|
104
|
-
const parsed = JSON.parse(raw);
|
|
105
|
-
if (typeof parsed.pid !== "number" || typeof parsed.at !== "number")
|
|
106
|
-
return null;
|
|
107
|
-
return { pid: parsed.pid, at: parsed.at, label: typeof parsed.label === "string" ? parsed.label : "" };
|
|
108
|
-
} catch {
|
|
109
|
-
return null;
|
|
110
|
-
}
|
|
76
|
+
#hasModifier(node, kind) {
|
|
77
|
+
const ts = this.#ts;
|
|
78
|
+
return ts.canHaveModifiers(node) && (ts.getModifiers(node)?.some((modifier) => modifier.kind === kind) ?? false);
|
|
111
79
|
}
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
try {
|
|
116
|
-
process.kill(pid, 0);
|
|
117
|
-
return true;
|
|
118
|
-
} catch (error) {
|
|
119
|
-
return error.code === "EPERM";
|
|
120
|
-
}
|
|
80
|
+
#isAsyncFunctionExpression(node) {
|
|
81
|
+
const ts = this.#ts;
|
|
82
|
+
return Boolean(node && (ts.isArrowFunction(node) || ts.isFunctionExpression(node)) && this.#hasModifier(node, ts.SyntaxKind.AsyncKeyword));
|
|
121
83
|
}
|
|
122
84
|
}
|
|
123
85
|
|
|
124
|
-
// pkgs/@akanjs/devkit/
|
|
125
|
-
|
|
86
|
+
// pkgs/@akanjs/devkit/artifact/implicitRootLayout.ts
|
|
87
|
+
var LAYOUT_KEY_RE = /^\.\/(.+\/)?_layout\.(tsx|ts|jsx|js)$/;
|
|
88
|
+
var IMPLICIT_LAYOUT_DIR = path2.join(".akan", "generated", "root-layouts");
|
|
89
|
+
var IMPLICIT_DICT_DIR = path2.join(".akan", "generated", "dict");
|
|
90
|
+
var IMPLICIT_OVERRIDES_DIR = path2.join(".akan", "generated", "overrides");
|
|
91
|
+
var OVERRIDES_KEY_RE = /^\.\/(.+\/)?_overrides\.(tsx|ts|jsx|js)$/;
|
|
92
|
+
var importSpecifier = (fromAbsPath, toAbsPath) => {
|
|
93
|
+
const rel = path2.relative(path2.dirname(fromAbsPath), toAbsPath).split(path2.sep).join("/");
|
|
94
|
+
return rel.startsWith(".") ? rel : `./${rel}`;
|
|
95
|
+
};
|
|
96
|
+
async function writeGeneratedOverridesLayoutFile(opts) {
|
|
97
|
+
const filename = `${opts.key.replace(/^\.\//, "").replace(/[^a-zA-Z0-9]+/g, "_")}.tsx`;
|
|
98
|
+
const absPath = path2.join(path2.resolve(opts.appCwdPath), IMPLICIT_OVERRIDES_DIR, filename);
|
|
99
|
+
const source = `"use client";
|
|
100
|
+
import { UiOverrideProvider } from "akanjs/ui";
|
|
101
|
+
import { createElement, type ReactNode } from "react";
|
|
102
|
+
import value from ${JSON.stringify(importSpecifier(absPath, opts.userAbsPath))};
|
|
126
103
|
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
104
|
+
export default function AkanUiOverridesLayout({ children }: { children?: ReactNode }) {
|
|
105
|
+
return createElement(UiOverrideProvider, { value }, children);
|
|
106
|
+
}
|
|
107
|
+
`;
|
|
108
|
+
await Bun.write(absPath, source);
|
|
109
|
+
return absPath;
|
|
110
|
+
}
|
|
111
|
+
async function pruneGeneratedFiles(dirAbsPath, keepAbsPaths) {
|
|
112
|
+
const keep = new Set(keepAbsPaths);
|
|
113
|
+
const names = await readdir(dirAbsPath).catch(() => []);
|
|
114
|
+
await Promise.all(names.map((name) => path2.join(dirAbsPath, name)).filter((absPath) => !keep.has(absPath)).map((absPath) => rm(absPath, { force: true })));
|
|
115
|
+
}
|
|
116
|
+
function getRootBoundarySegments(key) {
|
|
117
|
+
const match = LAYOUT_KEY_RE.exec(key);
|
|
118
|
+
if (!match)
|
|
119
|
+
return null;
|
|
120
|
+
const prefix = match[1]?.replace(/\/$/, "");
|
|
121
|
+
if (!prefix)
|
|
122
|
+
return [];
|
|
123
|
+
return prefix.split("/").filter(Boolean);
|
|
124
|
+
}
|
|
125
|
+
function implicitRootLayoutKey(segments) {
|
|
126
|
+
return `./${[...segments, "__root_layout"].join("/")}.tsx`;
|
|
127
|
+
}
|
|
128
|
+
function implicitRootLayoutAbsPath(appCwdPath, segments) {
|
|
129
|
+
const filename = segments.length ? `${segments.join("__")}__root_layout.tsx` : "__root_layout.tsx";
|
|
130
|
+
return path2.join(path2.resolve(appCwdPath), IMPLICIT_LAYOUT_DIR, filename);
|
|
131
|
+
}
|
|
132
|
+
function isRootBoundarySegments(segments, basePaths) {
|
|
133
|
+
const firstVisibleIndex = segments.findIndex((segment) => !/^\(.+\)$/.test(segment));
|
|
134
|
+
if (firstVisibleIndex === -1)
|
|
135
|
+
return segments.length <= 1;
|
|
136
|
+
if (segments.slice(firstVisibleIndex + 1).some((segment) => /^\(.+\)$/.test(segment)))
|
|
137
|
+
return false;
|
|
138
|
+
const visible = segments.slice(firstVisibleIndex);
|
|
139
|
+
const allowedBasePaths = new Set([...basePaths].map((basePath) => basePath.trim()).filter(Boolean));
|
|
140
|
+
return visible.length === 1 && (firstVisibleIndex > 0 || allowedBasePaths.has(visible[0] ?? ""));
|
|
141
|
+
}
|
|
142
|
+
function findRootBoundaries(pageKeys, appCwdPath, basePaths) {
|
|
143
|
+
const boundaries = new Map;
|
|
144
|
+
for (const key of pageKeys) {
|
|
145
|
+
const segments = getRootBoundarySegments(key);
|
|
146
|
+
if (!segments)
|
|
147
|
+
continue;
|
|
148
|
+
if (!isRootBoundarySegments(segments, basePaths))
|
|
149
|
+
continue;
|
|
150
|
+
const id = segments.join("/");
|
|
151
|
+
boundaries.set(id, {
|
|
152
|
+
sourceKey: key,
|
|
153
|
+
sourceAbsPath: path2.resolve(appCwdPath, "page", key.replace(/^\.\//, "")),
|
|
154
|
+
segments
|
|
155
|
+
});
|
|
132
156
|
}
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
157
|
+
if (boundaries.size === 0)
|
|
158
|
+
boundaries.set("", { sourceKey: null, sourceAbsPath: null, segments: [] });
|
|
159
|
+
return [...boundaries.values()].sort((a, b) => a.segments.join("/").localeCompare(b.segments.join("/")));
|
|
160
|
+
}
|
|
161
|
+
function hasAncestorRootBoundary(boundary, boundaries) {
|
|
162
|
+
return boundaries.some((candidate) => candidate !== boundary && candidate.segments.length < boundary.segments.length && candidate.segments.every((segment, index) => boundary.segments[index] === segment));
|
|
163
|
+
}
|
|
164
|
+
function findExplicitRootLayoutAbsPath(pageKeys, appCwdPath) {
|
|
165
|
+
const rootLayoutKey = pageKeys.find((key) => {
|
|
166
|
+
const segments = getRootBoundarySegments(key);
|
|
167
|
+
return segments !== null && segments.length === 0;
|
|
168
|
+
});
|
|
169
|
+
return rootLayoutKey ? path2.resolve(appCwdPath, "page", rootLayoutKey.replace(/^\.\//, "")) : null;
|
|
170
|
+
}
|
|
171
|
+
async function assertEnvClientConvention(appCwdPath, appName) {
|
|
172
|
+
const envPath = path2.join(appCwdPath, "env", "env.client.ts");
|
|
173
|
+
if (!await Bun.file(envPath).exists()) {
|
|
174
|
+
throw new Error(`[route-convention] app "${appName}" must provide env/env.client.ts exporting "env" for generated System.Provider`);
|
|
147
175
|
}
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
176
|
+
}
|
|
177
|
+
async function writeGeneratedDictionaryMacroFile(appCwdPath, appName) {
|
|
178
|
+
const absPath = path2.join(path2.resolve(appCwdPath), IMPLICIT_DICT_DIR, "useDict.ts");
|
|
179
|
+
await Bun.write(absPath, `import { getAllDictionary } from "@apps/${appName}/lib/dict" with { type: "macro" };
|
|
180
|
+
|
|
181
|
+
export const allDictionary = getAllDictionary();
|
|
182
|
+
`);
|
|
183
|
+
return absPath;
|
|
184
|
+
}
|
|
185
|
+
async function writeGeneratedRootLayoutFile(opts) {
|
|
186
|
+
await assertEnvClientConvention(opts.appCwdPath, opts.appName);
|
|
187
|
+
const dictMacroAbsPath = opts.includeSystemProvider ? await writeGeneratedDictionaryMacroFile(opts.appCwdPath, opts.appName) : null;
|
|
188
|
+
const absPath = implicitRootLayoutAbsPath(opts.appCwdPath, opts.boundary.segments);
|
|
189
|
+
const dictMacroSpecifier = dictMacroAbsPath ? importSpecifier(absPath, dictMacroAbsPath) : null;
|
|
190
|
+
const sourceSpecifier = opts.boundary.sourceAbsPath ? importSpecifier(absPath, opts.boundary.sourceAbsPath) : null;
|
|
191
|
+
const inheritedSourceAbsPath = opts.rootSourceAbsPath && opts.rootSourceAbsPath !== opts.boundary.sourceAbsPath ? opts.rootSourceAbsPath : null;
|
|
192
|
+
const inheritedSourceSpecifier = inheritedSourceAbsPath ? importSpecifier(absPath, inheritedSourceAbsPath) : null;
|
|
193
|
+
const clientImport = opts.includeStInit ? `import { st } from "@apps/${opts.appName}/client";
|
|
194
|
+
void st;
|
|
195
|
+
` : `import "@apps/${opts.appName}/client";
|
|
196
|
+
`;
|
|
197
|
+
const inheritedLabel = inheritedSourceAbsPath ? path2.relative(opts.appCwdPath, inheritedSourceAbsPath) : "";
|
|
198
|
+
const inheritedImport = inheritedSourceSpecifier ? `import * as inheritedModule from ${JSON.stringify(inheritedSourceSpecifier)};
|
|
199
|
+
const inheritedLayout = resolveRouteModule(inheritedModule as never, ${JSON.stringify(inheritedLabel)}).module as LayoutModule;
|
|
200
|
+
` : `const inheritedLayout: LayoutModule = {};
|
|
201
|
+
`;
|
|
202
|
+
const prefix = opts.boundary.segments.find((segment) => !/^\(.+\)$/.test(segment)) ?? null;
|
|
203
|
+
const userLabel = opts.boundary.sourceAbsPath ? path2.relative(opts.appCwdPath, opts.boundary.sourceAbsPath) : "";
|
|
204
|
+
const userImport = sourceSpecifier ? `import * as userModule from ${JSON.stringify(sourceSpecifier)};
|
|
205
|
+
const userLayout = resolveRouteModule(userModule as never, ${JSON.stringify(userLabel)}).module as LayoutModule;
|
|
206
|
+
const UserLayout = userLayout.default as (props: LayoutProps) => ReactNode | Promise<ReactNode>;
|
|
207
|
+
` : `const UserLayout = ({ children }: LayoutProps) => children;
|
|
208
|
+
const userLayout: LayoutModule = {};
|
|
209
|
+
`;
|
|
210
|
+
const isAsyncUserLayout = opts.boundary.sourceAbsPath ? await AsyncDefaultExportDetector.detect(opts.boundary.sourceAbsPath) : false;
|
|
211
|
+
const userLayoutElement = "<UserLayout params={params} searchParams={searchParams}>{children}</UserLayout>";
|
|
212
|
+
const layoutSignature = isAsyncUserLayout ? "export default async function GeneratedLayout" : "export default function GeneratedLayout";
|
|
213
|
+
const layoutBinding = isAsyncUserLayout ? ` const layout =
|
|
214
|
+
process.env.AKAN_PUBLIC_RENDER_ENV === "csr"
|
|
215
|
+
? await UserLayout({ params, searchParams, children })
|
|
216
|
+
: ${userLayoutElement};
|
|
217
|
+
` : "";
|
|
218
|
+
const layoutChild = isAsyncUserLayout ? "{layout}" : userLayoutElement;
|
|
219
|
+
const layoutReturn = isAsyncUserLayout ? "layout" : userLayoutElement;
|
|
220
|
+
const layoutExports = `export async function generateHead(props: PageProps) {
|
|
221
|
+
if (userLayout.generateHead) return userLayout.generateHead(props);
|
|
222
|
+
if (userLayout.head !== undefined) return userLayout.head;
|
|
223
|
+
if (inheritedLayout.generateHead) return inheritedLayout.generateHead(props);
|
|
224
|
+
return inheritedLayout.head;
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
export const NotFound = userLayout.NotFound ?? inheritedLayout.NotFound;
|
|
228
|
+
export const Error = userLayout.Error ?? inheritedLayout.Error;
|
|
229
|
+
export const pageConfig = userLayout.pageConfig ?? inheritedLayout.pageConfig;
|
|
230
|
+
|
|
231
|
+
${layoutSignature}({ children, params, searchParams }: LayoutProps) {
|
|
232
|
+
${layoutBinding} return `;
|
|
233
|
+
const source = opts.includeSystemProvider ? `import type { LayoutModule, LayoutProps, PageProps } from "akanjs/client";
|
|
234
|
+
import { loadFonts, resolveRouteModule } from "akanjs/client";
|
|
235
|
+
import type { ReactNode } from "react";
|
|
236
|
+
import { System } from "akanjs/ui";
|
|
237
|
+
import { env } from "@apps/${opts.appName}/env/env.client";
|
|
238
|
+
import { allDictionary } from ${JSON.stringify(dictMacroSpecifier)};
|
|
239
|
+
${clientImport}${inheritedImport}${userImport}
|
|
240
|
+
const userFonts = userLayout.fonts ?? inheritedLayout.fonts ?? [];
|
|
241
|
+
const defaultFonts = userFonts.filter((font) => font.default);
|
|
242
|
+
if (defaultFonts.length > 1) throw new Error("[route-convention] only one default font is allowed per root layout");
|
|
243
|
+
const defaultFont = defaultFonts[0];
|
|
244
|
+
const defaultFontClassName = defaultFont ? (defaultFont.className ?? \`font-\${defaultFont.name}\`) : undefined;
|
|
245
|
+
|
|
246
|
+
${layoutExports}(
|
|
247
|
+
<System.Provider
|
|
248
|
+
of={GeneratedLayout as never}
|
|
249
|
+
appName=${JSON.stringify(opts.appName)}
|
|
250
|
+
${prefix ? `prefix=${JSON.stringify(prefix)}
|
|
251
|
+
` : ""}params={params}
|
|
252
|
+
manifest={userLayout.manifest ?? inheritedLayout.manifest}
|
|
253
|
+
env={env}
|
|
254
|
+
theme={userLayout.theme ?? inheritedLayout.theme}
|
|
255
|
+
fonts={loadFonts(userFonts)}
|
|
256
|
+
className={defaultFontClassName}
|
|
257
|
+
layoutStyle={userLayout.layoutStyle ?? inheritedLayout.layoutStyle}
|
|
258
|
+
reconnect={userLayout.reconnect ?? inheritedLayout.reconnect ?? false}
|
|
259
|
+
wsConnect={userLayout.wsConnect ?? inheritedLayout.wsConnect ?? true}
|
|
260
|
+
allDictionary={process.env.AKAN_PUBLIC_RENDER_ENV === "ssr" ? allDictionary : undefined}
|
|
261
|
+
>
|
|
262
|
+
${layoutChild}
|
|
263
|
+
</System.Provider>
|
|
264
|
+
);
|
|
265
|
+
}
|
|
266
|
+
` : `import type { LayoutModule, LayoutProps, PageProps } from "akanjs/client";
|
|
267
|
+
import { resolveRouteModule } from "akanjs/client";
|
|
268
|
+
import type { ReactNode } from "react";
|
|
269
|
+
${inheritedImport}${userImport}
|
|
270
|
+
${layoutExports}${layoutReturn};
|
|
271
|
+
}
|
|
272
|
+
`;
|
|
273
|
+
await Bun.write(absPath, source);
|
|
274
|
+
return absPath;
|
|
275
|
+
}
|
|
276
|
+
async function resolveSsrPageEntries(opts) {
|
|
277
|
+
const absPageDir = path2.resolve(opts.appCwdPath, "page");
|
|
278
|
+
const hasSt = await Bun.file(path2.join(opts.appCwdPath, "lib", "st.ts")).exists();
|
|
279
|
+
const basePaths = opts.basePaths ?? [];
|
|
280
|
+
const rootSourceAbsPath = findExplicitRootLayoutAbsPath(opts.pageKeys, opts.appCwdPath);
|
|
281
|
+
const rootBoundaries = findRootBoundaries(opts.pageKeys, opts.appCwdPath, basePaths);
|
|
282
|
+
const rootLayoutKeys = new Set(opts.pageKeys.filter((key) => {
|
|
283
|
+
const segments = getRootBoundarySegments(key);
|
|
284
|
+
return segments !== null && isRootBoundarySegments(segments, basePaths);
|
|
285
|
+
}));
|
|
286
|
+
const base = await Promise.all(opts.pageKeys.filter((key) => !rootLayoutKeys.has(key)).map(async (key) => {
|
|
287
|
+
const userAbsPath = path2.resolve(absPageDir, key);
|
|
288
|
+
if (OVERRIDES_KEY_RE.test(key)) {
|
|
289
|
+
const moduleAbsPath = await writeGeneratedOverridesLayoutFile({
|
|
290
|
+
appCwdPath: opts.appCwdPath,
|
|
291
|
+
key,
|
|
292
|
+
userAbsPath
|
|
293
|
+
});
|
|
294
|
+
return { key, moduleAbsPath, seedAbsPaths: [userAbsPath] };
|
|
295
|
+
}
|
|
296
|
+
return { key, moduleAbsPath: userAbsPath };
|
|
297
|
+
}));
|
|
298
|
+
const generated = await Promise.all(rootBoundaries.map(async (boundary) => ({
|
|
299
|
+
key: implicitRootLayoutKey(boundary.segments),
|
|
300
|
+
moduleAbsPath: await writeGeneratedRootLayoutFile({
|
|
301
|
+
appCwdPath: opts.appCwdPath,
|
|
302
|
+
appName: opts.appName,
|
|
303
|
+
boundary,
|
|
304
|
+
rootSourceAbsPath,
|
|
305
|
+
includeStInit: hasSt && boundary.segments.length === 0,
|
|
306
|
+
includeSystemProvider: !hasAncestorRootBoundary(boundary, rootBoundaries)
|
|
307
|
+
}),
|
|
308
|
+
seedAbsPaths: [...new Set([boundary.sourceAbsPath, rootSourceAbsPath].filter((absPath) => absPath !== null))]
|
|
309
|
+
})));
|
|
310
|
+
const appCwdAbsPath = path2.resolve(opts.appCwdPath);
|
|
311
|
+
await Promise.all([
|
|
312
|
+
pruneGeneratedFiles(path2.join(appCwdAbsPath, IMPLICIT_OVERRIDES_DIR), base.filter(({ key }) => OVERRIDES_KEY_RE.test(key)).map(({ moduleAbsPath }) => moduleAbsPath)),
|
|
313
|
+
pruneGeneratedFiles(path2.join(appCwdAbsPath, IMPLICIT_LAYOUT_DIR), generated.map(({ moduleAbsPath }) => moduleAbsPath))
|
|
314
|
+
]);
|
|
315
|
+
return [...base, ...generated].sort((a, b) => a.key.localeCompare(b.key));
|
|
316
|
+
}
|
|
317
|
+
async function resolveSsrPageEntriesForApp(app, pageKeys) {
|
|
318
|
+
const config = await app.getConfig();
|
|
319
|
+
return resolveSsrPageEntries({ appCwdPath: app.cwdPath, appName: app.name, pageKeys, basePaths: config.basePaths });
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
// pkgs/@akanjs/devkit/codegenLock.ts
|
|
323
|
+
import { mkdir, open, readFile, rm as rm2, stat } from "fs/promises";
|
|
324
|
+
import path3 from "path";
|
|
325
|
+
import { Logger } from "akanjs/common";
|
|
326
|
+
|
|
327
|
+
class CodegenLock {
|
|
328
|
+
static fileName = "codegen.lock";
|
|
329
|
+
static waitTimeoutMs = 1e4;
|
|
330
|
+
static unknownHolderStaleMs = 60000;
|
|
331
|
+
static #pollMs = 25;
|
|
332
|
+
static #logger = new Logger("CodegenLock");
|
|
333
|
+
static #queue = Promise.resolve();
|
|
334
|
+
static pathIn(workspaceRoot) {
|
|
335
|
+
return path3.join(workspaceRoot, "local", ".akan", CodegenLock.fileName);
|
|
336
|
+
}
|
|
337
|
+
static async run(workspaceRoot, label, fn) {
|
|
338
|
+
const ahead = CodegenLock.#queue;
|
|
339
|
+
let done;
|
|
340
|
+
CodegenLock.#queue = new Promise((resolve) => {
|
|
341
|
+
done = resolve;
|
|
342
|
+
});
|
|
343
|
+
try {
|
|
344
|
+
await CodegenLock.#waitForQueue(ahead, label);
|
|
345
|
+
return await CodegenLock.#withFileLock(workspaceRoot, label, fn);
|
|
346
|
+
} finally {
|
|
347
|
+
done();
|
|
348
|
+
}
|
|
349
|
+
}
|
|
350
|
+
static async#waitForQueue(ahead, label) {
|
|
351
|
+
let timer = null;
|
|
352
|
+
const expired = new Promise((resolve) => {
|
|
353
|
+
timer = setTimeout(() => resolve("expired"), CodegenLock.waitTimeoutMs);
|
|
354
|
+
});
|
|
355
|
+
try {
|
|
356
|
+
if (await Promise.race([ahead.then(() => "done"), expired]) === "expired")
|
|
357
|
+
CodegenLock.#logger.warn(`codegen lock queued past ${CodegenLock.waitTimeoutMs}ms in this process; continuing without waiting (${label})`);
|
|
358
|
+
} finally {
|
|
359
|
+
if (timer)
|
|
360
|
+
clearTimeout(timer);
|
|
361
|
+
}
|
|
362
|
+
}
|
|
363
|
+
static async#withFileLock(workspaceRoot, label, fn) {
|
|
364
|
+
const lockPath = CodegenLock.pathIn(workspaceRoot);
|
|
365
|
+
await mkdir(path3.dirname(lockPath), { recursive: true }).catch(() => {
|
|
366
|
+
return;
|
|
367
|
+
});
|
|
368
|
+
const held = await CodegenLock.#acquire(lockPath, label);
|
|
369
|
+
try {
|
|
370
|
+
return await fn();
|
|
371
|
+
} finally {
|
|
372
|
+
if (held)
|
|
373
|
+
await rm2(lockPath, { force: true }).catch(() => {
|
|
374
|
+
return;
|
|
375
|
+
});
|
|
376
|
+
}
|
|
377
|
+
}
|
|
378
|
+
static async#acquire(lockPath, label) {
|
|
379
|
+
const deadline = Date.now() + CodegenLock.waitTimeoutMs;
|
|
380
|
+
for (;; ) {
|
|
381
|
+
const handle = await open(lockPath, "wx").catch(() => null);
|
|
382
|
+
if (handle) {
|
|
383
|
+
await handle.writeFile(JSON.stringify({ pid: process.pid, at: Date.now(), label })).catch(() => {
|
|
384
|
+
return;
|
|
385
|
+
});
|
|
386
|
+
await handle.close().catch(() => {
|
|
387
|
+
return;
|
|
388
|
+
});
|
|
389
|
+
return true;
|
|
390
|
+
}
|
|
391
|
+
if (await CodegenLock.#reclaimIfAbandoned(lockPath))
|
|
392
|
+
continue;
|
|
393
|
+
if (Date.now() >= deadline) {
|
|
394
|
+
CodegenLock.#logger.warn(`codegen lock at ${lockPath} held past ${CodegenLock.waitTimeoutMs}ms; continuing without it (${label})`);
|
|
395
|
+
return false;
|
|
396
|
+
}
|
|
397
|
+
await Bun.sleep(CodegenLock.#pollMs);
|
|
398
|
+
}
|
|
399
|
+
}
|
|
400
|
+
static async#reclaimIfAbandoned(lockPath) {
|
|
401
|
+
const info = await stat(lockPath).catch(() => null);
|
|
402
|
+
if (!info)
|
|
403
|
+
return true;
|
|
404
|
+
const holder = CodegenLock.#parseHolder(await readFile(lockPath, "utf8").catch(() => ""));
|
|
405
|
+
if (holder) {
|
|
406
|
+
if (CodegenLock.#isAlive(holder.pid))
|
|
407
|
+
return false;
|
|
408
|
+
} else if (Date.now() - info.mtimeMs < CodegenLock.unknownHolderStaleMs)
|
|
409
|
+
return false;
|
|
410
|
+
await rm2(lockPath, { force: true }).catch(() => {
|
|
411
|
+
return;
|
|
412
|
+
});
|
|
413
|
+
return true;
|
|
414
|
+
}
|
|
415
|
+
static #parseHolder(raw) {
|
|
416
|
+
try {
|
|
417
|
+
const parsed = JSON.parse(raw);
|
|
418
|
+
if (typeof parsed.pid !== "number" || typeof parsed.at !== "number")
|
|
419
|
+
return null;
|
|
420
|
+
return { pid: parsed.pid, at: parsed.at, label: typeof parsed.label === "string" ? parsed.label : "" };
|
|
421
|
+
} catch {
|
|
422
|
+
return null;
|
|
423
|
+
}
|
|
424
|
+
}
|
|
425
|
+
static #isAlive(pid) {
|
|
426
|
+
if (!Number.isInteger(pid) || pid <= 0)
|
|
427
|
+
return false;
|
|
428
|
+
try {
|
|
429
|
+
process.kill(pid, 0);
|
|
430
|
+
return true;
|
|
431
|
+
} catch (error) {
|
|
432
|
+
return error.code === "EPERM";
|
|
433
|
+
}
|
|
434
|
+
}
|
|
435
|
+
}
|
|
436
|
+
|
|
437
|
+
// pkgs/@akanjs/devkit/spinner.ts
|
|
438
|
+
import ora from "ora";
|
|
439
|
+
|
|
440
|
+
class Spinner {
|
|
441
|
+
static padding = 12;
|
|
442
|
+
static oraOptions = { discardStdin: false };
|
|
443
|
+
static canAnimate(stream = process.stderr) {
|
|
444
|
+
return !stream.isTTY || stream.columns > 0;
|
|
445
|
+
}
|
|
446
|
+
spinner;
|
|
447
|
+
stopWatch = null;
|
|
448
|
+
startAt = new Date;
|
|
449
|
+
prefix;
|
|
450
|
+
message;
|
|
451
|
+
enableSpin;
|
|
452
|
+
constructor(message, { prefix = "", indent = 0, enableSpin = true } = {}) {
|
|
453
|
+
Spinner.padding = Math.max(Spinner.padding, prefix.length);
|
|
454
|
+
this.prefix = prefix;
|
|
455
|
+
this.message = message;
|
|
456
|
+
this.spinner = ora({ ...Spinner.oraOptions, text: message });
|
|
457
|
+
this.spinner.prefixText = prefix.padStart(Spinner.padding, " ");
|
|
458
|
+
this.spinner.indent = indent;
|
|
459
|
+
this.enableSpin = enableSpin && Spinner.canAnimate();
|
|
460
|
+
}
|
|
461
|
+
start() {
|
|
462
|
+
this.startAt = new Date;
|
|
463
|
+
if (this.enableSpin) {
|
|
464
|
+
this.spinner.start();
|
|
465
|
+
this.stopWatch = setInterval(() => {
|
|
466
|
+
this.spinner.prefixText = this.prefix.padStart(Spinner.padding, " ");
|
|
467
|
+
this.spinner.text = `${this.message} (${this.#getElapsedTimeStr({ floor: true })})`;
|
|
468
|
+
}, 1000);
|
|
469
|
+
} else
|
|
470
|
+
this.spinner.info();
|
|
471
|
+
return this;
|
|
159
472
|
}
|
|
160
473
|
succeed(message) {
|
|
161
474
|
this.spinner.succeed(`${message} (${this.#getElapsedTimeStr()})`);
|
|
@@ -191,18 +504,19 @@ import {
|
|
|
191
504
|
fork,
|
|
192
505
|
spawn as spawn2
|
|
193
506
|
} from "child_process";
|
|
194
|
-
import { readFileSync as readFileSync2 } from "fs";
|
|
507
|
+
import { existsSync as existsSync2, readFileSync as readFileSync2 } from "fs";
|
|
195
508
|
import {
|
|
196
509
|
copyFile,
|
|
197
510
|
cp as cpEntry,
|
|
198
|
-
|
|
511
|
+
lstat,
|
|
512
|
+
mkdir as mkdir2,
|
|
199
513
|
readdir as readDirEntries,
|
|
200
514
|
realpath,
|
|
201
515
|
rm as rm3,
|
|
202
516
|
stat as stat2,
|
|
203
517
|
symlink
|
|
204
518
|
} from "fs/promises";
|
|
205
|
-
import
|
|
519
|
+
import path9 from "path";
|
|
206
520
|
import { pathToFileURL } from "url";
|
|
207
521
|
import {
|
|
208
522
|
capitalize,
|
|
@@ -218,7 +532,7 @@ import chalk2 from "chalk";
|
|
|
218
532
|
|
|
219
533
|
// pkgs/@akanjs/devkit/agentsIndex.ts
|
|
220
534
|
var recipeScannerLoad = null;
|
|
221
|
-
var loadRecipeScanner = () => recipeScannerLoad ??= import("./recipeScanner-
|
|
535
|
+
var loadRecipeScanner = () => recipeScannerLoad ??= import("./recipeScanner-4b69ybe0.js");
|
|
222
536
|
var AGENT_BLOCK_START = "<!-- akan:agent:start -->";
|
|
223
537
|
var AGENT_BLOCK_END = "<!-- akan:agent:end -->";
|
|
224
538
|
var AGENT_VERSION_PREFIX = "<!-- akan:agent:version ";
|
|
@@ -228,7 +542,7 @@ ${block}`;
|
|
|
228
542
|
var extractBlockVersion = (content) => content.match(/<!-- akan:agent:version ([^\s]+) -->/)?.[1] ?? null;
|
|
229
543
|
var readDevkitVersion = async () => {
|
|
230
544
|
const { readFile } = await import("fs/promises");
|
|
231
|
-
const { getDirname } = await import("./
|
|
545
|
+
const { getDirname } = await import("./fileSys-wzzrntkw.js");
|
|
232
546
|
try {
|
|
233
547
|
const raw = await readFile(`${getDirname(import.meta.url)}/package.json`, "utf-8");
|
|
234
548
|
return JSON.parse(raw).version ?? null;
|
|
@@ -354,79 +668,93 @@ var renderScopeClaudeMd = (scope) => `# ${scope.name} \u2014 Claude Code Guide
|
|
|
354
668
|
${CLAUDE_COMMENT_RULE}
|
|
355
669
|
`;
|
|
356
670
|
|
|
357
|
-
// pkgs/@akanjs/devkit/akanConfig/
|
|
358
|
-
import
|
|
359
|
-
import path2 from "path";
|
|
360
|
-
import { fileURLToPath } from "url";
|
|
361
|
-
import { normalizeRoutePrefix } from "akanjs/base";
|
|
362
|
-
import { resolveAkanI18nConfig } from "akanjs/common";
|
|
671
|
+
// pkgs/@akanjs/devkit/akanConfig/akanBin.ts
|
|
672
|
+
import path4 from "path";
|
|
363
673
|
|
|
364
674
|
// pkgs/@akanjs/devkit/akanConfig/types.ts
|
|
365
|
-
import { archs } from "akanjs";
|
|
675
|
+
import { archs, binPlatforms } from "akanjs";
|
|
366
676
|
|
|
677
|
+
// pkgs/@akanjs/devkit/akanConfig/akanBin.ts
|
|
678
|
+
class AkanBin {
|
|
679
|
+
static archive = /\.(zip|tar\.gz|tgz|tar\.xz|txz|tar\.bz2|tbz2|tar)$/i;
|
|
680
|
+
static fileName = /^[A-Za-z0-9][A-Za-z0-9._-]*$/;
|
|
681
|
+
static parse(declared, owner, baseDir) {
|
|
682
|
+
if (declared === undefined)
|
|
683
|
+
return {};
|
|
684
|
+
if (!declared || typeof declared !== "object" || Array.isArray(declared))
|
|
685
|
+
throw new Error(`${owner}: bin maps each executable's name to its platforms`);
|
|
686
|
+
return Object.fromEntries(Object.entries(declared).map(([name, platforms]) => [name, AkanBin.#platforms(name, platforms, owner, baseDir)]));
|
|
687
|
+
}
|
|
688
|
+
static isArchive(source) {
|
|
689
|
+
return AkanBin.archive.test("url" in source ? new URL(source.url).pathname : source.path);
|
|
690
|
+
}
|
|
691
|
+
static #platforms(name, platforms, owner, baseDir) {
|
|
692
|
+
if (!AkanBin.fileName.test(name))
|
|
693
|
+
throw new Error(`${owner}: bin.${name} is not a file name`);
|
|
694
|
+
if (!platforms || typeof platforms !== "object" || Array.isArray(platforms))
|
|
695
|
+
throw new Error(`${owner}: bin.${name} maps platforms (${binPlatforms.join(", ")}) to where the file comes from`);
|
|
696
|
+
const resolved = {};
|
|
697
|
+
for (const [platform, source] of Object.entries(platforms)) {
|
|
698
|
+
if (!binPlatforms.includes(platform))
|
|
699
|
+
throw new Error(`${owner}: bin.${name}.${platform} is not one of ${binPlatforms.join(", ")}`);
|
|
700
|
+
resolved[platform] = AkanBin.#source(`${owner}: bin.${name}.${platform}`, source, baseDir);
|
|
701
|
+
}
|
|
702
|
+
return resolved;
|
|
703
|
+
}
|
|
704
|
+
static #source(at, source, baseDir) {
|
|
705
|
+
if (!source || typeof source !== "object" || Array.isArray(source))
|
|
706
|
+
throw new Error(`${at} must be { url, sha256 } or { path }`);
|
|
707
|
+
const { url, sha256, path: localPath, file, ...rest } = source;
|
|
708
|
+
const extra = Object.keys(rest);
|
|
709
|
+
if (extra.length)
|
|
710
|
+
throw new Error(`${at}: unknown key ${extra.join(", ")}`);
|
|
711
|
+
if (url === undefined === (localPath === undefined))
|
|
712
|
+
throw new Error(`${at} takes either url and sha256, or path`);
|
|
713
|
+
const resolved = url !== undefined ? AkanBin.#urlSource(at, url, sha256) : AkanBin.#pathSource(at, localPath, sha256, baseDir);
|
|
714
|
+
const archive = AkanBin.isArchive(resolved);
|
|
715
|
+
if (file === undefined) {
|
|
716
|
+
if (archive)
|
|
717
|
+
throw new Error(`${at} names an archive: file picks the executable inside it`);
|
|
718
|
+
return resolved;
|
|
719
|
+
}
|
|
720
|
+
if (!archive)
|
|
721
|
+
throw new Error(`${at}: file picks the executable out of an archive, and this source is none`);
|
|
722
|
+
const parts = typeof file === "string" ? file.split(/[\\/]/).filter((part) => part && part !== ".") : [];
|
|
723
|
+
if (!parts.length || path4.isAbsolute(file) || parts.includes(".."))
|
|
724
|
+
throw new Error(`${at}: file must be a path inside the archive (got ${JSON.stringify(file)})`);
|
|
725
|
+
return { ...resolved, file: parts.join("/") };
|
|
726
|
+
}
|
|
727
|
+
static #urlSource(at, url, sha256) {
|
|
728
|
+
if (typeof url !== "string" || !URL.canParse(url) || !/^https?:$/.test(new URL(url).protocol))
|
|
729
|
+
throw new Error(`${at}: url must be an http(s) address`);
|
|
730
|
+
if (typeof sha256 !== "string" || !/^[0-9a-f]{64}$/i.test(sha256))
|
|
731
|
+
throw new Error(`${at}: sha256 must be the downloaded file's 64-digit hex digest`);
|
|
732
|
+
return { url, sha256: sha256.toLowerCase() };
|
|
733
|
+
}
|
|
734
|
+
static #pathSource(at, localPath, sha256, baseDir) {
|
|
735
|
+
if (typeof localPath !== "string" || !localPath)
|
|
736
|
+
throw new Error(`${at}: path must name a file`);
|
|
737
|
+
if (sha256 !== undefined)
|
|
738
|
+
throw new Error(`${at}: sha256 checks a download, and a path source downloads nothing`);
|
|
739
|
+
return { path: path4.resolve(baseDir, localPath) };
|
|
740
|
+
}
|
|
741
|
+
}
|
|
367
742
|
// pkgs/@akanjs/devkit/akanConfig/akanConfig.ts
|
|
743
|
+
import fs2 from "fs";
|
|
744
|
+
import path5 from "path";
|
|
745
|
+
import { fileURLToPath } from "url";
|
|
746
|
+
import { DatabaseModes, normalizeRoutePrefix } from "akanjs/base";
|
|
747
|
+
import { resolveAkanI18nConfig } from "akanjs/common";
|
|
368
748
|
var DEFAULT_BARREL_IMPORTS = ["akanjs/webkit", "akanjs/common", "akanjs/ui", "akanjs/server"];
|
|
369
749
|
var DEFAULT_OPTIMIZE_IMPORTS = [
|
|
370
|
-
"lucide-react",
|
|
371
|
-
"
|
|
372
|
-
"
|
|
373
|
-
"
|
|
374
|
-
|
|
375
|
-
"react-bootstrap",
|
|
376
|
-
"ahooks",
|
|
377
|
-
"@ant-design/icons",
|
|
378
|
-
"@headlessui/react",
|
|
379
|
-
"@headlessui-float/react",
|
|
380
|
-
"@heroicons/react/20/solid",
|
|
381
|
-
"@heroicons/react/24/solid",
|
|
382
|
-
"@heroicons/react/24/outline",
|
|
383
|
-
"@visx/visx",
|
|
384
|
-
"@tremor/react",
|
|
385
|
-
"rxjs",
|
|
386
|
-
"@mui/material",
|
|
387
|
-
"@mui/icons-material",
|
|
388
|
-
"recharts",
|
|
389
|
-
"react-use",
|
|
390
|
-
"@material-ui/core",
|
|
391
|
-
"@material-ui/icons",
|
|
392
|
-
"@tabler/icons-react",
|
|
393
|
-
"mui-core",
|
|
394
|
-
"react-icons/*"
|
|
395
|
-
];
|
|
750
|
+
"lucide-react date-fns lodash-es ramda antd react-bootstrap ahooks @ant-design/icons @headlessui/react",
|
|
751
|
+
"@headlessui-float/react @heroicons/react/20/solid @heroicons/react/24/solid @heroicons/react/24/outline",
|
|
752
|
+
"@visx/visx @tremor/react rxjs @mui/material @mui/icons-material recharts react-use @material-ui/core",
|
|
753
|
+
"@material-ui/icons @tabler/icons-react mui-core react-icons/*"
|
|
754
|
+
].flatMap((line) => line.split(" "));
|
|
396
755
|
var WORKSPACE_BARREL_FACETS = ["ui", "webkit", "common", "client", "server"];
|
|
397
756
|
var DEFAULT_DOCKER_IMAGE = "oven/bun:1-slim";
|
|
398
757
|
var SSR_RUNTIME_PACKAGES = ["react", "react-dom", "react-server-dom-webpack"];
|
|
399
|
-
var MOBILE_RUNTIME_PACKAGES = [
|
|
400
|
-
"@capacitor/cli",
|
|
401
|
-
"@capacitor/core",
|
|
402
|
-
"@capacitor/ios",
|
|
403
|
-
"@capacitor/android",
|
|
404
|
-
"@capacitor/assets"
|
|
405
|
-
];
|
|
406
|
-
var MOBILE_APP_CAPACITOR_PLUGINS = [
|
|
407
|
-
"@capacitor/app",
|
|
408
|
-
"@capacitor/browser",
|
|
409
|
-
"@capacitor/camera",
|
|
410
|
-
"@capacitor/core",
|
|
411
|
-
"@capacitor/device",
|
|
412
|
-
"@capacitor/geolocation",
|
|
413
|
-
"@capacitor/haptics",
|
|
414
|
-
"@capacitor/inappbrowser",
|
|
415
|
-
"@capacitor/keyboard",
|
|
416
|
-
"@capacitor/preferences",
|
|
417
|
-
"@capacitor/push-notifications",
|
|
418
|
-
"capacitor-plugin-safe-area"
|
|
419
|
-
];
|
|
420
|
-
var DATABASE_MODE_RUNTIME_PACKAGES = {
|
|
421
|
-
single: [],
|
|
422
|
-
multiple: ["@libsql/client", "bullmq", "ioredis", "protobufjs"],
|
|
423
|
-
cluster: ["bullmq", "ioredis", "postgres", "protobufjs"]
|
|
424
|
-
};
|
|
425
|
-
var AKAN_RUNTIME_PACKAGES = new Set([
|
|
426
|
-
...SSR_RUNTIME_PACKAGES,
|
|
427
|
-
...MOBILE_RUNTIME_PACKAGES,
|
|
428
|
-
...Object.values(DATABASE_MODE_RUNTIME_PACKAGES).flat()
|
|
429
|
-
]);
|
|
430
758
|
var DEFAULT_AKAN_IMAGE_CONFIG = {
|
|
431
759
|
deviceSizes: [640, 750, 828, 1080, 1200, 1920, 2048, 3840],
|
|
432
760
|
imageSizes: [32, 48, 64, 96, 128, 256, 384],
|
|
@@ -471,26 +799,26 @@ var normalizeDeepLinkDomain = (domain) => {
|
|
|
471
799
|
}
|
|
472
800
|
};
|
|
473
801
|
var normalizeDeepLinks = (deepLinks) => {
|
|
474
|
-
|
|
802
|
+
const schemes = normalizeStringList(deepLinks?.schemes);
|
|
803
|
+
const domains = normalizeStringList(deepLinks?.domains?.map(normalizeDeepLinkDomain));
|
|
804
|
+
if (!schemes && !domains)
|
|
475
805
|
return;
|
|
476
|
-
|
|
477
|
-
const domains = normalizeStringList(deepLinks.domains?.map(normalizeDeepLinkDomain));
|
|
478
|
-
const teamId = deepLinks.ios?.teamId?.trim();
|
|
479
|
-
const sha256CertFingerprints = normalizeStringList(deepLinks.android?.sha256CertFingerprints);
|
|
480
|
-
if (!schemes && !domains && !teamId && !sha256CertFingerprints)
|
|
481
|
-
return;
|
|
482
|
-
return {
|
|
483
|
-
...schemes ? { schemes } : {},
|
|
484
|
-
...domains ? { domains } : {},
|
|
485
|
-
...teamId ? { ios: { teamId } } : {},
|
|
486
|
-
...sha256CertFingerprints ? { android: { sha256CertFingerprints } } : {}
|
|
487
|
-
};
|
|
806
|
+
return { ...schemes ? { schemes } : {}, ...domains ? { domains } : {} };
|
|
488
807
|
};
|
|
489
808
|
var emptyLibContributions = () => ({
|
|
490
809
|
externalLibs: [],
|
|
810
|
+
trustedDependencies: [],
|
|
811
|
+
bin: [],
|
|
491
812
|
docker: { preRuns: [], postRuns: [] },
|
|
492
813
|
keepFonts: []
|
|
493
814
|
});
|
|
815
|
+
var normalizePackageNames = (names, owner) => {
|
|
816
|
+
if (names === undefined)
|
|
817
|
+
return [];
|
|
818
|
+
if (!Array.isArray(names) || names.some((name) => typeof name !== "string" || !name.trim()))
|
|
819
|
+
throw new Error(`${owner}: trustedDependencies lists package names`);
|
|
820
|
+
return [...new Set(names.map((name) => name.trim()))];
|
|
821
|
+
};
|
|
494
822
|
var normalizeKeepFonts = (keepFonts) => [
|
|
495
823
|
...new Set((keepFonts ?? []).map((glob) => glob.trim().replace(/^\/+/, "")).filter(Boolean))
|
|
496
824
|
];
|
|
@@ -506,17 +834,20 @@ class AkanAppConfig {
|
|
|
506
834
|
rootPackageJson;
|
|
507
835
|
docker;
|
|
508
836
|
dockerfile;
|
|
509
|
-
|
|
837
|
+
database;
|
|
510
838
|
web;
|
|
511
839
|
externalLibs;
|
|
840
|
+
trustedDependencies;
|
|
841
|
+
bin;
|
|
842
|
+
libBins;
|
|
512
843
|
barrelImports;
|
|
513
844
|
optimizeImports;
|
|
514
845
|
images;
|
|
515
846
|
i18n;
|
|
516
847
|
api;
|
|
517
848
|
publicEnv;
|
|
518
|
-
|
|
519
|
-
|
|
849
|
+
native;
|
|
850
|
+
hasNativeConfig;
|
|
520
851
|
secrets;
|
|
521
852
|
assets;
|
|
522
853
|
syncPageLibs;
|
|
@@ -534,8 +865,17 @@ class AkanAppConfig {
|
|
|
534
865
|
this.baseDevEnv = baseDevEnv;
|
|
535
866
|
this.plugins = plugins;
|
|
536
867
|
this.#applyRoutes(config?.routes);
|
|
537
|
-
this.
|
|
868
|
+
this.database = AkanAppConfig.#database(app, config);
|
|
538
869
|
this.externalLibs = [...new Set([...config?.externalLibs ?? [], ...libContributions.externalLibs])];
|
|
870
|
+
const owner = `apps/${app.name}/akan.config.ts`;
|
|
871
|
+
this.trustedDependencies = [
|
|
872
|
+
...new Set([
|
|
873
|
+
...normalizePackageNames(config?.trustedDependencies, owner),
|
|
874
|
+
...libContributions.trustedDependencies ?? []
|
|
875
|
+
])
|
|
876
|
+
];
|
|
877
|
+
this.bin = AkanBin.parse(config?.bin, owner, app.cwdPath);
|
|
878
|
+
this.libBins = libContributions.bin ?? [];
|
|
539
879
|
this.barrelImports = [
|
|
540
880
|
...DEFAULT_BARREL_IMPORTS,
|
|
541
881
|
...WORKSPACE_BARREL_FACETS.map((facet) => `@apps/${app.name}/${facet}`),
|
|
@@ -563,100 +903,202 @@ class AkanAppConfig {
|
|
|
563
903
|
]
|
|
564
904
|
};
|
|
565
905
|
this.syncPageLibs = config?.syncPageLibs ?? false;
|
|
566
|
-
|
|
567
|
-
|
|
906
|
+
if (config.mobile !== undefined)
|
|
907
|
+
throw new Error(`apps/${this.app.name}/akan.config.ts declares \`mobile\`, which is now \`native\`: the platform settings sit in native.ios, native.android and native.desktop, and a target overrides them under native.targets.<name>.`);
|
|
908
|
+
this.hasNativeConfig = Boolean(config.native);
|
|
909
|
+
this.native = this.#resolveNativeConfig(config.native);
|
|
568
910
|
this.web = this.#resolveWebConfig(config.web);
|
|
569
911
|
this.docker = AkanAppConfig.#resolveDocker(config.docker, libContributions.docker);
|
|
570
912
|
this.dockerfile = this.#makeDockerfile();
|
|
571
913
|
}
|
|
572
914
|
#resolveWebConfig(web) {
|
|
573
915
|
const resolved = typeof web === "object" ? { ssr: true, csr: web.csr } : { ssr: web ?? true, csr: web ?? true };
|
|
574
|
-
if (!resolved.csr && this.
|
|
575
|
-
throw new Error(`apps/${this.app.name}/akan.config.ts turns the CSR bundle off but declares
|
|
916
|
+
if (!resolved.csr && this.hasNativeConfig)
|
|
917
|
+
throw new Error(`apps/${this.app.name}/akan.config.ts turns the CSR bundle off but declares a native section; the native apps ship that bundle. Drop the native section or leave CSR on.`);
|
|
576
918
|
return resolved;
|
|
577
919
|
}
|
|
578
|
-
#
|
|
579
|
-
const {
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
const
|
|
585
|
-
const
|
|
586
|
-
const
|
|
587
|
-
const
|
|
588
|
-
const
|
|
589
|
-
const
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
const
|
|
594
|
-
const
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
const
|
|
598
|
-
if (
|
|
599
|
-
throw new Error(
|
|
600
|
-
}
|
|
601
|
-
const
|
|
602
|
-
|
|
603
|
-
|
|
920
|
+
#resolveNativeConfig(native) {
|
|
921
|
+
const configPath = `apps/${this.app.name}/akan.config.ts`;
|
|
922
|
+
const { targets: rawTargets, ...root } = native ?? {};
|
|
923
|
+
AkanAppConfig.#assertNativeKeys(root, "native", configPath);
|
|
924
|
+
AkanAppConfig.#assertFiles(root, "native", configPath);
|
|
925
|
+
const appName = root.appName ?? this.app.name;
|
|
926
|
+
const appId = root.appId ?? deriveDefaultAppId(this.baseDevEnv.repoName, this.app.name);
|
|
927
|
+
const version = root.version ?? "0.0.1";
|
|
928
|
+
const buildNum = root.buildNum ?? 1;
|
|
929
|
+
const named = rawTargets !== undefined && Object.keys(rawTargets).length > 0;
|
|
930
|
+
const entries = named ? Object.entries(rawTargets) : [[this.basePaths.has(this.app.name) ? this.app.name : "default", {}]];
|
|
931
|
+
const targets = Object.fromEntries(entries.map(([name, own]) => {
|
|
932
|
+
const where = named ? `native.targets.${name}` : "native";
|
|
933
|
+
AkanAppConfig.#assertNativeKeys(own ?? {}, where, configPath);
|
|
934
|
+
const merged = AkanAppConfig.#overlay(root, own ?? {});
|
|
935
|
+
const fallbackBasePath = !named && this.basePaths.has(name) ? name : undefined;
|
|
936
|
+
const basePath = (merged.basePath ?? fallbackBasePath)?.replace(/^\/+|\/+$/g, "") || undefined;
|
|
937
|
+
if (basePath && !this.basePaths.has(basePath))
|
|
938
|
+
throw new Error(`Native target '${name}' uses unknown basePath '${basePath}' in ${configPath}`);
|
|
939
|
+
const { updates } = merged;
|
|
940
|
+
if (updates && (!updates.url || !updates.publicKey))
|
|
941
|
+
throw new Error(`${where}.updates in ${configPath} has no url or publicKey${named ? "; give them in native.updates or the target's own" : ""}.`);
|
|
942
|
+
AkanAppConfig.#assertFiles(own ?? {}, where, configPath);
|
|
943
|
+
const {
|
|
944
|
+
basePath: _basePath,
|
|
945
|
+
indexPath: rawIndexPath,
|
|
946
|
+
deepLinks: rawDeepLinks,
|
|
947
|
+
updates: _updates,
|
|
948
|
+
...rest
|
|
949
|
+
} = merged;
|
|
950
|
+
const indexPath = normalizeIndexPath(rawIndexPath);
|
|
951
|
+
const deepLinks = normalizeDeepLinks(rawDeepLinks);
|
|
952
|
+
const { teamId: rawTeamId, ...iosRest } = AkanAppConfig.#platformIndexPath(merged.ios ?? {});
|
|
953
|
+
const teamId = rawTeamId?.trim();
|
|
954
|
+
const { sha256CertFingerprints: rawFingerprints, ...androidRest } = AkanAppConfig.#platformIndexPath(merged.android ?? {});
|
|
955
|
+
const fingerprints = normalizeStringList(rawFingerprints);
|
|
956
|
+
const target = {
|
|
957
|
+
...rest,
|
|
604
958
|
name,
|
|
605
|
-
basePath,
|
|
606
|
-
indexPath,
|
|
607
|
-
deepLinks,
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
...target.android
|
|
619
|
-
},
|
|
620
|
-
ios: {
|
|
621
|
-
...rawMobile.ios,
|
|
622
|
-
...target.ios
|
|
623
|
-
}
|
|
959
|
+
...basePath ? { basePath } : {},
|
|
960
|
+
...indexPath ? { indexPath } : {},
|
|
961
|
+
...deepLinks ? { deepLinks } : {},
|
|
962
|
+
...merged.ios ? { ios: { ...iosRest, ...teamId ? { teamId } : {} } } : {},
|
|
963
|
+
...merged.android ? { android: { ...androidRest, ...fingerprints ? { sha256CertFingerprints: fingerprints } : {} } } : {},
|
|
964
|
+
...merged.desktop ? {
|
|
965
|
+
desktop: AkanAppConfig.#desktopServer(AkanAppConfig.#platformIndexPath(merged.desktop), `${where}.desktop.server`, configPath)
|
|
966
|
+
} : {},
|
|
967
|
+
appName: merged.appName ?? this.app.name,
|
|
968
|
+
appId: merged.appId ?? appId,
|
|
969
|
+
version: merged.version ?? version,
|
|
970
|
+
buildNum: merged.buildNum ?? buildNum,
|
|
971
|
+
...updates ? { updates } : {}
|
|
624
972
|
};
|
|
625
|
-
return [name,
|
|
973
|
+
return [name, target];
|
|
626
974
|
}));
|
|
627
|
-
return {
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
975
|
+
return { appName, appId, ...root.fileName ? { fileName: root.fileName } : {}, version, buildNum, targets };
|
|
976
|
+
}
|
|
977
|
+
static #desktopServer(desktop, where, configPath) {
|
|
978
|
+
const { server } = desktop;
|
|
979
|
+
if (server === undefined || typeof server === "boolean")
|
|
980
|
+
return desktop;
|
|
981
|
+
if (!AkanAppConfig.#isPlainObject(server))
|
|
982
|
+
throw new Error(`${where} in ${configPath} must be true or { omit: string[] }.`);
|
|
983
|
+
const omit = server.omit;
|
|
984
|
+
if (omit !== undefined && (!Array.isArray(omit) || omit.some((name) => typeof name !== "string" || !name.trim())))
|
|
985
|
+
throw new Error(`${where}.omit in ${configPath} must be a list of package names.`);
|
|
986
|
+
const names = [...new Set((omit ?? []).map((name) => name.trim()))].sort();
|
|
987
|
+
return { ...desktop, server: { omit: names } };
|
|
988
|
+
}
|
|
989
|
+
static #platformIndexPath(section) {
|
|
990
|
+
const { indexPath: rawIndexPath, ...rest } = section;
|
|
991
|
+
const indexPath = normalizeIndexPath(rawIndexPath);
|
|
992
|
+
return { ...rest, ...indexPath ? { indexPath } : {} };
|
|
993
|
+
}
|
|
994
|
+
static #overlay(base, own) {
|
|
995
|
+
const merge = (kept, value, top, key) => {
|
|
996
|
+
if (!AkanAppConfig.#isPlainObject(kept) || !AkanAppConfig.#isPlainObject(value))
|
|
997
|
+
return value;
|
|
998
|
+
if (top && (key === "icon" || key === "splash"))
|
|
999
|
+
return value;
|
|
1000
|
+
const out = { ...kept };
|
|
1001
|
+
for (const [inner, next] of Object.entries(value))
|
|
1002
|
+
if (next !== undefined)
|
|
1003
|
+
out[inner] = merge(out[inner], next, false, inner);
|
|
1004
|
+
return out;
|
|
635
1005
|
};
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
return
|
|
1006
|
+
const out = { ...base };
|
|
1007
|
+
for (const [key, value] of Object.entries(own))
|
|
1008
|
+
if (value !== undefined)
|
|
1009
|
+
out[key] = merge(out[key], value, true, key);
|
|
1010
|
+
return out;
|
|
1011
|
+
}
|
|
1012
|
+
static #isPlainObject(value) {
|
|
1013
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
1014
|
+
}
|
|
1015
|
+
static #nativeKeys = {
|
|
1016
|
+
native: [
|
|
1017
|
+
"basePath",
|
|
1018
|
+
"indexPath",
|
|
1019
|
+
"appName",
|
|
1020
|
+
"appId",
|
|
1021
|
+
"fileName",
|
|
1022
|
+
"version",
|
|
1023
|
+
"buildNum",
|
|
1024
|
+
"icon",
|
|
1025
|
+
"splash",
|
|
1026
|
+
"permissions",
|
|
1027
|
+
"plugins",
|
|
1028
|
+
"deepLinks",
|
|
1029
|
+
"updates",
|
|
1030
|
+
"ios",
|
|
1031
|
+
"android",
|
|
1032
|
+
"desktop"
|
|
1033
|
+
],
|
|
1034
|
+
ios: ["indexPath", "teamId", "infoPlist", "entitlements", "privacy", "files"],
|
|
1035
|
+
android: [
|
|
1036
|
+
"indexPath",
|
|
1037
|
+
"sha256CertFingerprints",
|
|
1038
|
+
"googleServices",
|
|
1039
|
+
"push",
|
|
1040
|
+
"autoplay",
|
|
1041
|
+
"manifest",
|
|
1042
|
+
"application",
|
|
1043
|
+
"activity",
|
|
1044
|
+
"files"
|
|
1045
|
+
],
|
|
1046
|
+
desktop: ["indexPath", "server", "recovery", "window", "screenCapture", "entitlements"],
|
|
1047
|
+
desktopServer: ["omit"],
|
|
1048
|
+
deepLinks: ["schemes", "domains"]
|
|
1049
|
+
};
|
|
1050
|
+
static #movedKeys = {
|
|
1051
|
+
native: "the platform sections and plugins sit directly in the native section: ios, android, desktop, plugins",
|
|
1052
|
+
assets: "icon and splash sit directly in the native section",
|
|
1053
|
+
files: "ios.files (keyed by the path in the app bundle) or android.files (keyed res/<type>/<file> or assets/<path>)",
|
|
1054
|
+
push: "android.push",
|
|
1055
|
+
privacy: "ios.privacy",
|
|
1056
|
+
"deepLinks.ios": "ios.teamId",
|
|
1057
|
+
"deepLinks.android": "android.sha256CertFingerprints"
|
|
1058
|
+
};
|
|
1059
|
+
static #assertNativeKeys(section, where, configPath) {
|
|
1060
|
+
const check = (value, known, at, kind) => {
|
|
1061
|
+
if (!AkanAppConfig.#isPlainObject(value))
|
|
1062
|
+
return;
|
|
1063
|
+
for (const key of Object.keys(value)) {
|
|
1064
|
+
if (known.includes(key))
|
|
1065
|
+
continue;
|
|
1066
|
+
const moved = AkanAppConfig.#movedKeys[kind === "native" ? key : `${kind}.${key}`];
|
|
1067
|
+
throw new Error(moved ? `${at}.${key} in ${configPath} has moved: ${moved}.` : `${at}.${key} in ${configPath} is not a native setting. Known: ${known.join(", ")}.`);
|
|
1068
|
+
}
|
|
1069
|
+
};
|
|
1070
|
+
check(section, AkanAppConfig.#nativeKeys.native, where, "native");
|
|
1071
|
+
const settings = section;
|
|
1072
|
+
check(settings.ios, AkanAppConfig.#nativeKeys.ios, `${where}.ios`, "ios");
|
|
1073
|
+
check(settings.android, AkanAppConfig.#nativeKeys.android, `${where}.android`, "android");
|
|
1074
|
+
check(settings.desktop, AkanAppConfig.#nativeKeys.desktop, `${where}.desktop`, "desktop");
|
|
1075
|
+
check(settings.desktop?.server, AkanAppConfig.#nativeKeys.desktopServer, `${where}.desktop.server`, "desktopServer");
|
|
1076
|
+
check(settings.deepLinks, AkanAppConfig.#nativeKeys.deepLinks, `${where}.deepLinks`, "deepLinks");
|
|
1077
|
+
}
|
|
1078
|
+
static #androidFilePattern = /^(?:res\/[^/]+\/[^/]+|assets\/.+)$/;
|
|
1079
|
+
static #assertFiles(settings, where, configPath) {
|
|
1080
|
+
const check = (files, at, valid, shape) => {
|
|
1081
|
+
for (const [to, from] of Object.entries(files ?? {})) {
|
|
1082
|
+
if (typeof from !== "string")
|
|
1083
|
+
throw new Error(`${at}["${to}"] in ${configPath} must name its source file, relative to the app folder, e.g. { "${shape}": "assets/chime.mp3" }.`);
|
|
1084
|
+
if (!valid(to))
|
|
1085
|
+
throw new Error(`${at}["${to}"] in ${configPath} must land at ${shape}.`);
|
|
1086
|
+
}
|
|
1087
|
+
};
|
|
1088
|
+
check(settings.ios?.files, `${where}.ios.files`, (to) => to.length > 0 && !to.startsWith("/") && !to.split("/").includes(".."), "<path in the app bundle>");
|
|
1089
|
+
check(settings.android?.files, `${where}.android.files`, (to) => AkanAppConfig.#androidFilePattern.test(to), "res/<type>/<file> or assets/<path>");
|
|
641
1090
|
}
|
|
642
1091
|
#applyRoutes(routes = []) {
|
|
643
1092
|
for (const route of routes) {
|
|
644
|
-
|
|
645
|
-
|
|
1093
|
+
const basePath = route.basePath ? route.basePath.replace(/^\/+|\/+$/g, "") : null;
|
|
1094
|
+
if (basePath !== null)
|
|
646
1095
|
this.basePaths.add(basePath);
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
} else {
|
|
654
|
-
Object.keys(route.domains).forEach((branch) => void this.branches.add(branch));
|
|
655
|
-
Object.values(route.domains).flat().forEach((domain) => {
|
|
656
|
-
if (domain)
|
|
657
|
-
this.domains.add(domain.toLowerCase().replace(/:\d+$/, ""));
|
|
658
|
-
});
|
|
659
|
-
}
|
|
1096
|
+
const domains = basePath !== null ? this.subRoutes.getOrInsert(basePath, new Set) : this.domains;
|
|
1097
|
+
for (const branch of Object.keys(route.domains))
|
|
1098
|
+
this.branches.add(branch);
|
|
1099
|
+
for (const domain of Object.values(route.domains).flat())
|
|
1100
|
+
if (domain)
|
|
1101
|
+
domains.add(domain.toLowerCase().replace(/:\d+$/, ""));
|
|
660
1102
|
}
|
|
661
1103
|
const appName = this.app.name.toLowerCase();
|
|
662
1104
|
const serveDomain = this.baseDevEnv.serveDomain.toLowerCase();
|
|
@@ -693,18 +1135,37 @@ class AkanAppConfig {
|
|
|
693
1135
|
command: docker?.command ?? ["bun", "main.js"]
|
|
694
1136
|
};
|
|
695
1137
|
}
|
|
696
|
-
|
|
1138
|
+
getProductionEnv(environment = this.baseDevEnv.env) {
|
|
1139
|
+
return {
|
|
1140
|
+
PORT: "8282",
|
|
1141
|
+
NODE_ENV: "production",
|
|
1142
|
+
AKAN_PUBLIC_REPO_NAME: this.baseDevEnv.repoName,
|
|
1143
|
+
AKAN_PUBLIC_SERVE_DOMAIN: this.baseDevEnv.serveDomain,
|
|
1144
|
+
AKAN_PUBLIC_APP_NAME: this.app.name,
|
|
1145
|
+
AKAN_PUBLIC_ENV: environment,
|
|
1146
|
+
...this.basePaths.size ? { AKAN_PUBLIC_BASE_PATHS: [...this.basePaths].join(",") } : {},
|
|
1147
|
+
AKAN_PUBLIC_DEFAULT_LOCALE: this.i18n.defaultLocale,
|
|
1148
|
+
AKAN_PUBLIC_LOCALES: this.i18n.locales.join(","),
|
|
1149
|
+
AKAN_PUBLIC_API_PREFIX: this.api.prefix,
|
|
1150
|
+
AKAN_PUBLIC_WS_PREFIX: this.api.websocketPrefix,
|
|
1151
|
+
AKAN_PUBLIC_OPERATION_MODE: "cloud",
|
|
1152
|
+
AKAN_DATABASE_MODES: this.database.modes.join(","),
|
|
1153
|
+
AKAN_LOG_TO_FILE: "0",
|
|
1154
|
+
...this.web.ssr ? {} : { AKAN_SSR: "false" },
|
|
1155
|
+
...this.web.csr ? {} : { AKAN_CSR: "false" }
|
|
1156
|
+
};
|
|
1157
|
+
}
|
|
1158
|
+
dockerfileFor(environment) {
|
|
1159
|
+
return this.#makeDockerfile(environment);
|
|
1160
|
+
}
|
|
1161
|
+
#makeDockerfile(environment) {
|
|
697
1162
|
if (typeof this.docker === "string")
|
|
698
1163
|
return this.docker;
|
|
699
1164
|
const { image, preRuns, postRuns, command } = this.docker;
|
|
700
1165
|
const preRunScripts = this.#getDockerRunScripts(preRuns);
|
|
701
1166
|
const postRunScripts = this.#getDockerRunScripts(postRuns);
|
|
702
1167
|
const imageScript = this.#getDockerImageScript(image, DEFAULT_DOCKER_IMAGE);
|
|
703
|
-
const
|
|
704
|
-
...this.web.ssr ? [] : ["ENV AKAN_SSR=false"],
|
|
705
|
-
...this.web.csr ? [] : ["ENV AKAN_CSR=false"]
|
|
706
|
-
].join(`
|
|
707
|
-
`);
|
|
1168
|
+
const envLines = Object.entries(this.getProductionEnv(environment)).map(([key, value]) => `ENV ${key}=${value}`);
|
|
708
1169
|
return `${imageScript}
|
|
709
1170
|
RUN apt-get update && apt-get upgrade -y && apt-get install -y --no-install-recommends ca-certificates tzdata && rm -rf /var/lib/apt/lists/*
|
|
710
1171
|
RUN ln -sf /usr/share/zoneinfo/Asia/Seoul /etc/localtime
|
|
@@ -718,20 +1179,8 @@ RUN bun install --production
|
|
|
718
1179
|
${postRunScripts.join(`
|
|
719
1180
|
`)}
|
|
720
1181
|
COPY . .
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
ENV AKAN_PUBLIC_REPO_NAME=${this.baseDevEnv.repoName}
|
|
724
|
-
ENV AKAN_PUBLIC_SERVE_DOMAIN=${this.baseDevEnv.serveDomain}
|
|
725
|
-
ENV AKAN_PUBLIC_APP_NAME=${this.app.name}
|
|
726
|
-
ENV AKAN_PUBLIC_ENV=${this.baseDevEnv.env}
|
|
727
|
-
${this.basePaths.size ? `ENV AKAN_PUBLIC_BASE_PATHS=${[...this.basePaths].join(",")}` : ""}
|
|
728
|
-
ENV AKAN_PUBLIC_DEFAULT_LOCALE=${this.i18n.defaultLocale}
|
|
729
|
-
ENV AKAN_PUBLIC_LOCALES=${this.i18n.locales.join(",")}
|
|
730
|
-
ENV AKAN_PUBLIC_API_PREFIX=${this.api.prefix}
|
|
731
|
-
ENV AKAN_PUBLIC_WS_PREFIX=${this.api.websocketPrefix}
|
|
732
|
-
ENV AKAN_PUBLIC_OPERATION_MODE=cloud
|
|
733
|
-
ENV AKAN_LOG_TO_FILE=0
|
|
734
|
-
${webEnvLines}
|
|
1182
|
+
${envLines.join(`
|
|
1183
|
+
`)}
|
|
735
1184
|
CMD [${command.map((c) => `"${c}"`).join(",")}]`;
|
|
736
1185
|
}
|
|
737
1186
|
static #importGeneration = 0;
|
|
@@ -743,7 +1192,7 @@ CMD [${command.map((c) => `"${c}"`).join(",")}]`;
|
|
|
743
1192
|
static async from(app, { bustImportCache = false } = {}) {
|
|
744
1193
|
const [configImp, baseDevEnv, libs, rootPackageJson] = await Promise.all([
|
|
745
1194
|
AkanAppConfig.importConfigModule(app.cwdPath, { bustImportCache }),
|
|
746
|
-
WorkspaceExecutor.getBaseDevEnv(
|
|
1195
|
+
WorkspaceExecutor.getBaseDevEnv(path5.join(app.workspace.workspaceRoot, ".env")),
|
|
747
1196
|
app.workspace.getLibs(),
|
|
748
1197
|
app.workspace.getPackageJson()
|
|
749
1198
|
]);
|
|
@@ -753,503 +1202,159 @@ CMD [${command.map((c) => `"${c}"`).join(",")}]`;
|
|
|
753
1202
|
return new AkanAppConfig(app, libs, rootPackageJson, config, baseDevEnv, plugins ?? [], libContributions);
|
|
754
1203
|
}
|
|
755
1204
|
static async#collectLibContributions(app, libs, bustImportCache) {
|
|
756
|
-
const libConfigs = await Promise.all(libs.map(async (libName) => LibExecutor.from(app, libName).getConfig({ refresh: bustImportCache }).catch((error) => {
|
|
757
|
-
app.logger.warn(`Skipped libs/${libName}/akan.config.ts contributions: ${String(error)}`);
|
|
758
|
-
return null;
|
|
759
|
-
})));
|
|
760
|
-
return {
|
|
761
|
-
externalLibs: libConfigs.flatMap((libConfig) => libConfig?.externalLibs ?? []),
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
const rootVersion = this.rootPackageJson.dependencies?.[lib] ?? this.rootPackageJson.devDependencies?.[lib];
|
|
771
|
-
if (rootVersion)
|
|
772
|
-
return rootVersion;
|
|
773
|
-
const akanPackageJson = getAkanPackageJson();
|
|
774
|
-
return akanPackageJson.dependencies?.[lib] ?? akanPackageJson.peerDependencies?.[lib];
|
|
775
|
-
}
|
|
776
|
-
#getProductionRuntimePackages() {
|
|
777
|
-
return [...this.externalLibs, ...SSR_RUNTIME_PACKAGES, ...this.getDatabaseModeRuntimePackages()];
|
|
778
|
-
}
|
|
779
|
-
getDatabaseModeRuntimePackages(databaseMode = this.defaultDatabaseMode) {
|
|
780
|
-
return [...DATABASE_MODE_RUNTIME_PACKAGES[databaseMode]];
|
|
781
|
-
}
|
|
782
|
-
getMissingDatabaseModeDependencySpecs(databaseMode = this.defaultDatabaseMode) {
|
|
783
|
-
return this.#getMissingDependencySpecs(this.getDatabaseModeRuntimePackages(databaseMode));
|
|
784
|
-
}
|
|
785
|
-
getMobileRuntimePackages() {
|
|
786
|
-
return [...new Set([...MOBILE_RUNTIME_PACKAGES, ...MOBILE_APP_CAPACITOR_PLUGINS])];
|
|
787
|
-
}
|
|
788
|
-
getMissingMobileDependencySpecs() {
|
|
789
|
-
return this.#getMissingDependencySpecs(this.getMobileRuntimePackages());
|
|
790
|
-
}
|
|
791
|
-
getMobileAppCapacitorPlugins() {
|
|
792
|
-
return [...MOBILE_APP_CAPACITOR_PLUGINS];
|
|
793
|
-
}
|
|
794
|
-
#getMissingDependencySpecs(libs) {
|
|
795
|
-
const rootDependencies = {
|
|
796
|
-
...this.rootPackageJson.dependencies,
|
|
797
|
-
...this.rootPackageJson.devDependencies
|
|
798
|
-
};
|
|
799
|
-
return libs.filter((lib) => !rootDependencies[lib]).map((lib) => {
|
|
800
|
-
const version = this.#resolveProductionDependencyVersion(lib);
|
|
801
|
-
if (!version)
|
|
802
|
-
throw new Error(`Dependency ${lib} not found in package.json`);
|
|
803
|
-
return `${lib}@${version}`;
|
|
804
|
-
});
|
|
805
|
-
}
|
|
806
|
-
getProductionPackageJson(data = {}) {
|
|
807
|
-
return {
|
|
808
|
-
name: this.app.name,
|
|
809
|
-
description: this.app.name,
|
|
810
|
-
version: "1.0.0",
|
|
811
|
-
main: "./main.js",
|
|
812
|
-
dependencies: Object.fromEntries([...new Set(this.#getProductionRuntimePackages())].map((lib) => {
|
|
813
|
-
const version = this.#resolveProductionDependencyVersion(lib);
|
|
814
|
-
if (!version)
|
|
815
|
-
throw new Error(`Dependency ${lib} not found in package.json`);
|
|
816
|
-
return [lib, version];
|
|
817
|
-
})),
|
|
818
|
-
...data
|
|
819
|
-
};
|
|
820
|
-
}
|
|
821
|
-
}
|
|
822
|
-
var akanPackageJson = null;
|
|
823
|
-
function getAkanPackageJson() {
|
|
824
|
-
if (akanPackageJson)
|
|
825
|
-
return akanPackageJson;
|
|
826
|
-
const sourceDir = path2.dirname(fileURLToPath(import.meta.url));
|
|
827
|
-
const packageJsonPaths = [
|
|
828
|
-
path2.join(sourceDir, "../../../akanjs/package.json"),
|
|
829
|
-
path2.join(process.cwd(), "pkgs/akanjs/package.json"),
|
|
830
|
-
path2.join(path2.dirname(Bun.main), "node_modules/akanjs/package.json")
|
|
831
|
-
];
|
|
832
|
-
try {
|
|
833
|
-
packageJsonPaths.unshift(Bun.resolveSync("akanjs/package.json", sourceDir));
|
|
834
|
-
} catch {}
|
|
835
|
-
for (const packageJsonPath of packageJsonPaths) {
|
|
836
|
-
try {
|
|
837
|
-
akanPackageJson = JSON.parse(fs.readFileSync(packageJsonPath, "utf8"));
|
|
838
|
-
return akanPackageJson;
|
|
839
|
-
} catch {}
|
|
840
|
-
}
|
|
841
|
-
akanPackageJson = {
|
|
842
|
-
name: "akanjs",
|
|
843
|
-
version: "0.0.0",
|
|
844
|
-
description: "akanjs",
|
|
845
|
-
dependencies: {}
|
|
846
|
-
};
|
|
847
|
-
return akanPackageJson;
|
|
848
|
-
}
|
|
849
|
-
function mergeImageConfig(config = {}) {
|
|
850
|
-
return {
|
|
851
|
-
...DEFAULT_AKAN_IMAGE_CONFIG,
|
|
852
|
-
...config,
|
|
853
|
-
deviceSizes: config.deviceSizes ?? DEFAULT_AKAN_IMAGE_CONFIG.deviceSizes,
|
|
854
|
-
imageSizes: config.imageSizes ?? DEFAULT_AKAN_IMAGE_CONFIG.imageSizes,
|
|
855
|
-
formats: config.formats ?? DEFAULT_AKAN_IMAGE_CONFIG.formats,
|
|
856
|
-
qualities: config.qualities ?? DEFAULT_AKAN_IMAGE_CONFIG.qualities,
|
|
857
|
-
remotePatterns: config.remotePatterns ?? DEFAULT_AKAN_IMAGE_CONFIG.remotePatterns,
|
|
858
|
-
localPatterns: config.localPatterns ?? DEFAULT_AKAN_IMAGE_CONFIG.localPatterns
|
|
859
|
-
};
|
|
860
|
-
}
|
|
861
|
-
|
|
862
|
-
class AkanLibConfig {
|
|
863
|
-
lib;
|
|
864
|
-
externalLibs;
|
|
865
|
-
docker;
|
|
866
|
-
assets;
|
|
867
|
-
plugins;
|
|
868
|
-
constructor(lib, config, plugins = []) {
|
|
869
|
-
this.lib = lib;
|
|
870
|
-
this.externalLibs = config?.externalLibs ?? [];
|
|
871
|
-
this.docker = { preRuns: config?.docker?.preRuns ?? [], postRuns: config?.docker?.postRuns ?? [] };
|
|
872
|
-
this.assets = { keepFonts: normalizeKeepFonts(config?.assets?.keepFonts) };
|
|
873
|
-
this.plugins = plugins;
|
|
874
|
-
}
|
|
875
|
-
static async from(lib, { bustImportCache = false } = {}) {
|
|
876
|
-
const configImp = await AkanAppConfig.importConfigModule(lib.cwdPath, { bustImportCache });
|
|
877
|
-
const resolved = typeof configImp === "function" ? configImp(lib) : configImp;
|
|
878
|
-
const { plugins, ...config } = resolved ?? {};
|
|
879
|
-
return new AkanLibConfig(lib, config, plugins ?? []);
|
|
880
|
-
}
|
|
881
|
-
}
|
|
882
|
-
//! need to refactor
|
|
883
|
-
var increaseBuildNum = async (app) => {
|
|
884
|
-
const appConfig = await AkanAppConfig.from(app);
|
|
885
|
-
const akanConfigPath = path2.join(app.cwdPath, "akan.config.ts");
|
|
886
|
-
const akanConfig = fs.readFileSync(akanConfigPath, "utf8");
|
|
887
|
-
const akanConfigContent = akanConfig.replace(`buildNum: ${appConfig.mobile.buildNum}`, `buildNum: ${appConfig.mobile.buildNum + 1}`);
|
|
888
|
-
fs.writeFileSync(akanConfigPath, akanConfigContent);
|
|
889
|
-
};
|
|
890
|
-
var decreaseBuildNum = async (app) => {
|
|
891
|
-
const appConfig = await AkanAppConfig.from(app);
|
|
892
|
-
const akanConfigPath = path2.join(app.cwdPath, "akan.config.ts");
|
|
893
|
-
const akanConfig = fs.readFileSync(akanConfigPath, "utf8");
|
|
894
|
-
const akanConfigContent = akanConfig.replace(`buildNum: ${appConfig.mobile.buildNum}`, `buildNum: ${appConfig.mobile.buildNum - 1}`);
|
|
895
|
-
fs.writeFileSync(akanConfigPath, akanConfigContent);
|
|
896
|
-
};
|
|
897
|
-
// pkgs/@akanjs/devkit/artifact/implicitRootLayout.ts
|
|
898
|
-
import { mkdir as mkdir2 } from "fs/promises";
|
|
899
|
-
import path4 from "path";
|
|
900
|
-
|
|
901
|
-
// pkgs/@akanjs/devkit/transforms/asyncDefaultExportDetector.ts
|
|
902
|
-
import fs2 from "fs";
|
|
903
|
-
import path3 from "path";
|
|
904
|
-
|
|
905
|
-
class AsyncDefaultExportDetector {
|
|
906
|
-
static #typescriptLoad;
|
|
907
|
-
static #loadTypescript() {
|
|
908
|
-
AsyncDefaultExportDetector.#typescriptLoad ??= import("typescript").then((mod) => mod.default ?? mod);
|
|
909
|
-
return AsyncDefaultExportDetector.#typescriptLoad;
|
|
910
|
-
}
|
|
911
|
-
static async detect(moduleAbsPath) {
|
|
912
|
-
try {
|
|
913
|
-
const typescript = await AsyncDefaultExportDetector.#loadTypescript();
|
|
914
|
-
const source = fs2.readFileSync(path3.resolve(moduleAbsPath), "utf8");
|
|
915
|
-
const sourceFile = typescript.createSourceFile(moduleAbsPath, source, typescript.ScriptTarget.Latest, true, AsyncDefaultExportDetector.#scriptKind(typescript, moduleAbsPath));
|
|
916
|
-
return new AsyncDefaultExportDetector(typescript).detectInSourceFile(sourceFile);
|
|
917
|
-
} catch {
|
|
918
|
-
return false;
|
|
919
|
-
}
|
|
920
|
-
}
|
|
921
|
-
#ts;
|
|
922
|
-
constructor(typescript) {
|
|
923
|
-
this.#ts = typescript;
|
|
924
|
-
}
|
|
925
|
-
detectInSourceFile(sourceFile) {
|
|
926
|
-
const ts = this.#ts;
|
|
927
|
-
const asyncBindings = new Map;
|
|
928
|
-
let defaultIdentifier = null;
|
|
929
|
-
for (const statement of sourceFile.statements) {
|
|
930
|
-
if (ts.isFunctionDeclaration(statement)) {
|
|
931
|
-
if (this.#hasModifier(statement, ts.SyntaxKind.DefaultKeyword)) {
|
|
932
|
-
return this.#hasModifier(statement, ts.SyntaxKind.AsyncKeyword);
|
|
933
|
-
}
|
|
934
|
-
if (statement.name) {
|
|
935
|
-
asyncBindings.set(statement.name.text, this.#hasModifier(statement, ts.SyntaxKind.AsyncKeyword));
|
|
936
|
-
}
|
|
937
|
-
continue;
|
|
938
|
-
}
|
|
939
|
-
if (ts.isVariableStatement(statement)) {
|
|
940
|
-
for (const declaration of statement.declarationList.declarations) {
|
|
941
|
-
if (!ts.isIdentifier(declaration.name))
|
|
942
|
-
continue;
|
|
943
|
-
asyncBindings.set(declaration.name.text, this.#isAsyncFunctionExpression(declaration.initializer));
|
|
944
|
-
}
|
|
945
|
-
continue;
|
|
946
|
-
}
|
|
947
|
-
if (ts.isExportAssignment(statement)) {
|
|
948
|
-
if (this.#isAsyncFunctionExpression(statement.expression))
|
|
949
|
-
return true;
|
|
950
|
-
if (ts.isCallExpression(statement.expression))
|
|
951
|
-
return true;
|
|
952
|
-
if (ts.isIdentifier(statement.expression))
|
|
953
|
-
defaultIdentifier = statement.expression.text;
|
|
954
|
-
continue;
|
|
955
|
-
}
|
|
956
|
-
if (ts.isExportDeclaration(statement) && statement.exportClause && ts.isNamedExports(statement.exportClause)) {
|
|
957
|
-
const exportClause = statement.exportClause;
|
|
958
|
-
for (const specifier of exportClause.elements) {
|
|
959
|
-
if (specifier.name.text !== "default")
|
|
960
|
-
continue;
|
|
961
|
-
defaultIdentifier = specifier.propertyName?.text ?? specifier.name.text;
|
|
962
|
-
}
|
|
963
|
-
}
|
|
964
|
-
}
|
|
965
|
-
return defaultIdentifier ? asyncBindings.get(defaultIdentifier) === true : false;
|
|
1205
|
+
const libConfigs = await Promise.all(libs.map(async (libName) => LibExecutor.from(app, libName).getConfig({ refresh: bustImportCache }).catch((error) => {
|
|
1206
|
+
app.logger.warn(`Skipped libs/${libName}/akan.config.ts contributions: ${String(error)}`);
|
|
1207
|
+
return null;
|
|
1208
|
+
})));
|
|
1209
|
+
return {
|
|
1210
|
+
externalLibs: libConfigs.flatMap((libConfig) => libConfig?.externalLibs ?? []),
|
|
1211
|
+
trustedDependencies: libConfigs.flatMap((libConfig) => libConfig?.trustedDependencies ?? []),
|
|
1212
|
+
bin: libConfigs.flatMap((libConfig) => libConfig && Object.keys(libConfig.bin).length ? [{ lib: libConfig.lib.name, bin: libConfig.bin }] : []),
|
|
1213
|
+
docker: {
|
|
1214
|
+
preRuns: libConfigs.flatMap((libConfig) => libConfig?.docker.preRuns ?? []),
|
|
1215
|
+
postRuns: libConfigs.flatMap((libConfig) => libConfig?.docker.postRuns ?? [])
|
|
1216
|
+
},
|
|
1217
|
+
keepFonts: libConfigs.flatMap((libConfig) => (libConfig?.assets.keepFonts ?? []).map((glob) => `libs/${libConfig?.lib.name}/${glob}`))
|
|
1218
|
+
};
|
|
966
1219
|
}
|
|
967
|
-
#
|
|
968
|
-
const
|
|
969
|
-
|
|
1220
|
+
#resolveProductionDependencyVersion(lib) {
|
|
1221
|
+
const rootVersion = this.rootPackageJson.dependencies?.[lib] ?? this.rootPackageJson.devDependencies?.[lib];
|
|
1222
|
+
if (rootVersion)
|
|
1223
|
+
return rootVersion;
|
|
1224
|
+
const akanPackageJson = getAkanPackageJson();
|
|
1225
|
+
const version = akanPackageJson.dependencies?.[lib] ?? akanPackageJson.peerDependencies?.[lib];
|
|
1226
|
+
if (!version)
|
|
1227
|
+
throw new Error(`Dependency ${lib} not found in package.json`);
|
|
1228
|
+
return version;
|
|
970
1229
|
}
|
|
971
|
-
#
|
|
972
|
-
|
|
973
|
-
|
|
1230
|
+
#getProductionRuntimePackages() {
|
|
1231
|
+
return [
|
|
1232
|
+
...this.externalLibs,
|
|
1233
|
+
...SSR_RUNTIME_PACKAGES,
|
|
1234
|
+
...this.database.modes.flatMap((mode) => this.getDatabaseModeRuntimePackages(mode))
|
|
1235
|
+
];
|
|
974
1236
|
}
|
|
975
|
-
|
|
976
|
-
return
|
|
1237
|
+
getDatabaseModeRuntimePackages(databaseMode) {
|
|
1238
|
+
return [...DatabaseModes.drivers[databaseMode]];
|
|
977
1239
|
}
|
|
978
|
-
|
|
979
|
-
|
|
980
|
-
|
|
981
|
-
|
|
982
|
-
|
|
983
|
-
|
|
984
|
-
|
|
985
|
-
|
|
986
|
-
var IMPLICIT_DICT_DIR = path4.join(".akan", "generated", "dict");
|
|
987
|
-
var IMPLICIT_OVERRIDES_DIR = path4.join(".akan", "generated", "overrides");
|
|
988
|
-
var OVERRIDES_KEY_RE = /^\.\/(.+\/)?_overrides\.(tsx|ts|jsx|js)$/;
|
|
989
|
-
async function writeGeneratedOverridesLayoutFile(opts) {
|
|
990
|
-
const filename = `${opts.key.replace(/^\.\//, "").replace(/[^a-zA-Z0-9]+/g, "_")}.tsx`;
|
|
991
|
-
const absPath = path4.join(path4.resolve(opts.appCwdPath), IMPLICIT_OVERRIDES_DIR, filename);
|
|
992
|
-
const userRel = path4.relative(path4.dirname(absPath), opts.userAbsPath).split(path4.sep).join("/");
|
|
993
|
-
const userSpecifier = userRel.startsWith(".") ? userRel : `./${userRel}`;
|
|
994
|
-
const source = `"use client";
|
|
995
|
-
import { UiOverrideProvider } from "akanjs/ui";
|
|
996
|
-
import { createElement, type ReactNode } from "react";
|
|
997
|
-
import value from ${JSON.stringify(userSpecifier)};
|
|
998
|
-
|
|
999
|
-
export default function AkanUiOverridesLayout({ children }: { children?: ReactNode }) {
|
|
1000
|
-
return createElement(UiOverrideProvider, { value }, children);
|
|
1001
|
-
}
|
|
1002
|
-
`;
|
|
1003
|
-
await Bun.write(absPath, source);
|
|
1004
|
-
return absPath;
|
|
1005
|
-
}
|
|
1006
|
-
function getRootBoundarySegments(key) {
|
|
1007
|
-
const match = LAYOUT_KEY_RE.exec(key);
|
|
1008
|
-
if (!match)
|
|
1009
|
-
return null;
|
|
1010
|
-
const prefix = match[1]?.replace(/\/$/, "");
|
|
1011
|
-
if (!prefix)
|
|
1012
|
-
return [];
|
|
1013
|
-
return prefix.split("/").filter(Boolean);
|
|
1014
|
-
}
|
|
1015
|
-
function implicitRootLayoutKey(segments) {
|
|
1016
|
-
return `./${[...segments, "__root_layout"].join("/")}.tsx`;
|
|
1017
|
-
}
|
|
1018
|
-
function implicitRootLayoutAbsPath(appCwdPath, segments) {
|
|
1019
|
-
const filename = segments.length ? `${segments.join("__")}__root_layout.tsx` : "__root_layout.tsx";
|
|
1020
|
-
return path4.join(path4.resolve(appCwdPath), IMPLICIT_LAYOUT_DIR, filename);
|
|
1021
|
-
}
|
|
1022
|
-
function implicitDictionaryMacroAbsPath(appCwdPath) {
|
|
1023
|
-
return path4.join(path4.resolve(appCwdPath), IMPLICIT_DICT_DIR, "useDict.ts");
|
|
1024
|
-
}
|
|
1025
|
-
function isRootBoundarySegments(segments, basePaths) {
|
|
1026
|
-
const firstVisibleIndex = segments.findIndex((segment) => !/^\(.+\)$/.test(segment));
|
|
1027
|
-
if (firstVisibleIndex === -1)
|
|
1028
|
-
return segments.length <= 1;
|
|
1029
|
-
if (segments.slice(firstVisibleIndex + 1).some((segment) => /^\(.+\)$/.test(segment)))
|
|
1030
|
-
return false;
|
|
1031
|
-
const visible = segments.slice(firstVisibleIndex);
|
|
1032
|
-
const allowedBasePaths = new Set([...basePaths].map((basePath) => basePath.trim()).filter(Boolean));
|
|
1033
|
-
return visible.length === 1 && (firstVisibleIndex > 0 || allowedBasePaths.has(visible[0] ?? ""));
|
|
1034
|
-
}
|
|
1035
|
-
function findRootBoundaries(pageKeys, appCwdPath, basePaths) {
|
|
1036
|
-
const boundaries = new Map;
|
|
1037
|
-
for (const key of pageKeys) {
|
|
1038
|
-
const segments = getRootBoundarySegments(key);
|
|
1039
|
-
if (!segments)
|
|
1040
|
-
continue;
|
|
1041
|
-
if (!isRootBoundarySegments(segments, basePaths))
|
|
1042
|
-
continue;
|
|
1043
|
-
const id = segments.join("/");
|
|
1044
|
-
boundaries.set(id, {
|
|
1045
|
-
sourceKey: key,
|
|
1046
|
-
sourceAbsPath: path4.resolve(appCwdPath, "page", key.replace(/^\.\//, "")),
|
|
1047
|
-
segments
|
|
1240
|
+
getMissingDatabaseModeDependencySpecs(databaseMode) {
|
|
1241
|
+
return this.#getMissingDependencySpecs(this.getDatabaseModeRuntimePackages(databaseMode));
|
|
1242
|
+
}
|
|
1243
|
+
resolveDatabaseMode() {
|
|
1244
|
+
return DatabaseModes.resolve({
|
|
1245
|
+
requested: process.env.AKAN_DATABASE_MODE,
|
|
1246
|
+
declared: this.database.modes.join(","),
|
|
1247
|
+
local: true
|
|
1048
1248
|
});
|
|
1049
1249
|
}
|
|
1050
|
-
|
|
1051
|
-
|
|
1052
|
-
|
|
1250
|
+
static #database(app, config) {
|
|
1251
|
+
const declared = config?.database?.modes?.filter((mode) => !!mode);
|
|
1252
|
+
const modes = declared?.length ? declared : ["single"];
|
|
1253
|
+
return { modes: DatabaseModes.parseList(modes.join(","), `database.modes in apps/${app.name}/akan.config.ts`) };
|
|
1053
1254
|
}
|
|
1054
|
-
|
|
1055
|
-
|
|
1056
|
-
|
|
1057
|
-
|
|
1058
|
-
}
|
|
1059
|
-
|
|
1060
|
-
|
|
1061
|
-
|
|
1062
|
-
return
|
|
1063
|
-
}
|
|
1064
|
-
|
|
1065
|
-
|
|
1066
|
-
|
|
1067
|
-
|
|
1068
|
-
|
|
1069
|
-
|
|
1070
|
-
|
|
1071
|
-
|
|
1072
|
-
|
|
1073
|
-
|
|
1255
|
+
#getMissingDependencySpecs(libs) {
|
|
1256
|
+
const rootDependencies = {
|
|
1257
|
+
...this.rootPackageJson.dependencies,
|
|
1258
|
+
...this.rootPackageJson.devDependencies
|
|
1259
|
+
};
|
|
1260
|
+
return libs.filter((lib) => !rootDependencies[lib]).map((lib) => `${lib}@${this.#resolveProductionDependencyVersion(lib)}`);
|
|
1261
|
+
}
|
|
1262
|
+
get akanVersion() {
|
|
1263
|
+
return getAkanPackageJson().version;
|
|
1264
|
+
}
|
|
1265
|
+
getProductionPackageJson(data = {}) {
|
|
1266
|
+
return {
|
|
1267
|
+
name: this.app.name,
|
|
1268
|
+
description: this.app.name,
|
|
1269
|
+
version: "1.0.0",
|
|
1270
|
+
main: "./main.js",
|
|
1271
|
+
dependencies: Object.fromEntries([...new Set(this.#getProductionRuntimePackages())].map((lib) => [
|
|
1272
|
+
lib,
|
|
1273
|
+
this.#resolveProductionDependencyVersion(lib)
|
|
1274
|
+
])),
|
|
1275
|
+
...this.trustedDependencies.length ? { trustedDependencies: this.trustedDependencies } : {},
|
|
1276
|
+
...data
|
|
1277
|
+
};
|
|
1074
1278
|
}
|
|
1075
1279
|
}
|
|
1076
|
-
|
|
1077
|
-
|
|
1078
|
-
|
|
1079
|
-
|
|
1080
|
-
|
|
1081
|
-
|
|
1082
|
-
|
|
1083
|
-
|
|
1084
|
-
|
|
1085
|
-
|
|
1086
|
-
|
|
1087
|
-
|
|
1088
|
-
|
|
1089
|
-
|
|
1090
|
-
|
|
1091
|
-
|
|
1092
|
-
|
|
1093
|
-
|
|
1094
|
-
|
|
1095
|
-
|
|
1096
|
-
|
|
1097
|
-
|
|
1098
|
-
|
|
1099
|
-
|
|
1100
|
-
|
|
1101
|
-
|
|
1102
|
-
const inheritedImport = inheritedSourceSpecifier ? `import * as inheritedModule from ${JSON.stringify(inheritedSourceSpecifier)};
|
|
1103
|
-
const inheritedLayout = resolveRouteModule(inheritedModule as never, ${JSON.stringify(inheritedLabel)}).module as LayoutModule;
|
|
1104
|
-
` : `const inheritedLayout: LayoutModule = {};
|
|
1105
|
-
`;
|
|
1106
|
-
const prefix = routePrefixForSegments(opts.boundary.segments);
|
|
1107
|
-
const userLabel = opts.boundary.sourceAbsPath ? path4.relative(opts.appCwdPath, opts.boundary.sourceAbsPath) : "";
|
|
1108
|
-
const userImport = sourceSpecifier ? `import * as userModule from ${JSON.stringify(sourceSpecifier)};
|
|
1109
|
-
const userLayout = resolveRouteModule(userModule as never, ${JSON.stringify(userLabel)}).module as LayoutModule;
|
|
1110
|
-
const UserLayout = userLayout.default as (props: LayoutProps) => ReactNode | Promise<ReactNode>;
|
|
1111
|
-
` : `const UserLayout = ({ children }: LayoutProps) => children;
|
|
1112
|
-
const userLayout: LayoutModule = {};
|
|
1113
|
-
`;
|
|
1114
|
-
const isAsyncUserLayout = opts.boundary.sourceAbsPath ? await AsyncDefaultExportDetector.detect(opts.boundary.sourceAbsPath) : false;
|
|
1115
|
-
const userLayoutElement = "<UserLayout params={params} searchParams={searchParams}>{children}</UserLayout>";
|
|
1116
|
-
const layoutSignature = isAsyncUserLayout ? "export default async function GeneratedLayout" : "export default function GeneratedLayout";
|
|
1117
|
-
const layoutBinding = isAsyncUserLayout ? ` const layout =
|
|
1118
|
-
process.env.AKAN_PUBLIC_RENDER_ENV === "csr"
|
|
1119
|
-
? await UserLayout({ params, searchParams, children })
|
|
1120
|
-
: ${userLayoutElement};
|
|
1121
|
-
` : "";
|
|
1122
|
-
const layoutChild = isAsyncUserLayout ? "{layout}" : userLayoutElement;
|
|
1123
|
-
const layoutReturn = isAsyncUserLayout ? "layout" : userLayoutElement;
|
|
1124
|
-
const source = opts.includeSystemProvider ? `import type { LayoutModule, LayoutProps, PageProps } from "akanjs/client";
|
|
1125
|
-
import { loadFonts, resolveRouteModule } from "akanjs/client";
|
|
1126
|
-
import type { ReactNode } from "react";
|
|
1127
|
-
import { System } from "akanjs/ui";
|
|
1128
|
-
import { env } from "@apps/${opts.appName}/env/env.client";
|
|
1129
|
-
import { allDictionary } from ${JSON.stringify(dictMacroSpecifier)};
|
|
1130
|
-
${clientImport}${inheritedImport}${userImport}
|
|
1131
|
-
const userFonts = userLayout.fonts ?? inheritedLayout.fonts ?? [];
|
|
1132
|
-
const defaultFonts = userFonts.filter((font) => font.default);
|
|
1133
|
-
if (defaultFonts.length > 1) throw new Error("[route-convention] only one default font is allowed per root layout");
|
|
1134
|
-
const defaultFont = defaultFonts[0];
|
|
1135
|
-
const defaultFontClassName = defaultFont ? (defaultFont.className ?? \`font-\${defaultFont.name}\`) : undefined;
|
|
1136
|
-
|
|
1137
|
-
export async function generateHead(props: PageProps) {
|
|
1138
|
-
if (userLayout.generateHead) return userLayout.generateHead(props);
|
|
1139
|
-
if (userLayout.head !== undefined) return userLayout.head;
|
|
1140
|
-
if (inheritedLayout.generateHead) return inheritedLayout.generateHead(props);
|
|
1141
|
-
return inheritedLayout.head;
|
|
1142
|
-
}
|
|
1143
|
-
|
|
1144
|
-
export async function generateMetadata(props: PageProps) {
|
|
1145
|
-
if (userLayout.generateMetadata) return userLayout.generateMetadata(props);
|
|
1146
|
-
if (userLayout.metadata !== undefined) return userLayout.metadata;
|
|
1147
|
-
if (inheritedLayout.generateMetadata) return inheritedLayout.generateMetadata(props);
|
|
1148
|
-
return inheritedLayout.metadata;
|
|
1149
|
-
}
|
|
1150
|
-
|
|
1151
|
-
export const NotFound = userLayout.NotFound ?? inheritedLayout.NotFound;
|
|
1152
|
-
export const Error = userLayout.Error ?? inheritedLayout.Error;
|
|
1153
|
-
export const pageConfig = userLayout.pageConfig ?? inheritedLayout.pageConfig;
|
|
1154
|
-
|
|
1155
|
-
${layoutSignature}({ children, params, searchParams }: LayoutProps) {
|
|
1156
|
-
${layoutBinding} return (
|
|
1157
|
-
<System.Provider
|
|
1158
|
-
of={GeneratedLayout as never}
|
|
1159
|
-
appName=${JSON.stringify(opts.appName)}
|
|
1160
|
-
${prefix ? `prefix=${JSON.stringify(prefix)}
|
|
1161
|
-
` : ""}params={params}
|
|
1162
|
-
manifest={userLayout.manifest ?? inheritedLayout.manifest}
|
|
1163
|
-
env={env}
|
|
1164
|
-
theme={userLayout.theme ?? inheritedLayout.theme}
|
|
1165
|
-
fonts={loadFonts(userFonts)}
|
|
1166
|
-
className={defaultFontClassName}
|
|
1167
|
-
gaTrackingId={userLayout.gaTrackingId ?? inheritedLayout.gaTrackingId}
|
|
1168
|
-
layoutStyle={userLayout.layoutStyle ?? inheritedLayout.layoutStyle}
|
|
1169
|
-
reconnect={userLayout.reconnect ?? inheritedLayout.reconnect ?? false}
|
|
1170
|
-
wsConnect={userLayout.wsConnect ?? inheritedLayout.wsConnect ?? true}
|
|
1171
|
-
allDictionary={process.env.AKAN_PUBLIC_RENDER_ENV === "ssr" ? allDictionary : undefined}
|
|
1172
|
-
>
|
|
1173
|
-
${layoutChild}
|
|
1174
|
-
</System.Provider>
|
|
1175
|
-
);
|
|
1176
|
-
}
|
|
1177
|
-
` : `import type { LayoutModule, LayoutProps, PageProps } from "akanjs/client";
|
|
1178
|
-
import { resolveRouteModule } from "akanjs/client";
|
|
1179
|
-
import type { ReactNode } from "react";
|
|
1180
|
-
${inheritedImport}${userImport}
|
|
1181
|
-
export async function generateHead(props: PageProps) {
|
|
1182
|
-
if (userLayout.generateHead) return userLayout.generateHead(props);
|
|
1183
|
-
if (userLayout.head !== undefined) return userLayout.head;
|
|
1184
|
-
if (inheritedLayout.generateHead) return inheritedLayout.generateHead(props);
|
|
1185
|
-
return inheritedLayout.head;
|
|
1186
|
-
}
|
|
1187
|
-
|
|
1188
|
-
export async function generateMetadata(props: PageProps) {
|
|
1189
|
-
if (userLayout.generateMetadata) return userLayout.generateMetadata(props);
|
|
1190
|
-
if (userLayout.metadata !== undefined) return userLayout.metadata;
|
|
1191
|
-
if (inheritedLayout.generateMetadata) return inheritedLayout.generateMetadata(props);
|
|
1192
|
-
return inheritedLayout.metadata;
|
|
1193
|
-
}
|
|
1194
|
-
|
|
1195
|
-
export const NotFound = userLayout.NotFound ?? inheritedLayout.NotFound;
|
|
1196
|
-
export const Error = userLayout.Error ?? inheritedLayout.Error;
|
|
1197
|
-
export const pageConfig = userLayout.pageConfig ?? inheritedLayout.pageConfig;
|
|
1198
|
-
|
|
1199
|
-
${layoutSignature}({ children, params, searchParams }: LayoutProps) {
|
|
1200
|
-
${layoutBinding} return ${layoutReturn};
|
|
1201
|
-
}
|
|
1202
|
-
`;
|
|
1203
|
-
await Bun.write(absPath, source);
|
|
1204
|
-
return absPath;
|
|
1205
|
-
}
|
|
1206
|
-
async function resolveSsrPageEntries(opts) {
|
|
1207
|
-
const absPageDir = path4.resolve(opts.appCwdPath, "page");
|
|
1208
|
-
const hasSt = await appHasStModule(opts.appCwdPath);
|
|
1209
|
-
const basePaths = opts.basePaths ?? [];
|
|
1210
|
-
const rootSourceAbsPath = findExplicitRootLayoutAbsPath(opts.pageKeys, opts.appCwdPath);
|
|
1211
|
-
const rootBoundaries = findRootBoundaries(opts.pageKeys, opts.appCwdPath, basePaths);
|
|
1212
|
-
const rootLayoutKeys = new Set(opts.pageKeys.filter((key) => {
|
|
1213
|
-
const segments = getRootBoundarySegments(key);
|
|
1214
|
-
return segments !== null && isRootBoundarySegments(segments, basePaths);
|
|
1215
|
-
}));
|
|
1216
|
-
const base = await Promise.all(opts.pageKeys.filter((key) => !rootLayoutKeys.has(key)).map(async (key) => {
|
|
1217
|
-
const userAbsPath = path4.resolve(absPageDir, key);
|
|
1218
|
-
if (OVERRIDES_KEY_RE.test(key)) {
|
|
1219
|
-
const moduleAbsPath = await writeGeneratedOverridesLayoutFile({
|
|
1220
|
-
appCwdPath: opts.appCwdPath,
|
|
1221
|
-
key,
|
|
1222
|
-
userAbsPath
|
|
1223
|
-
});
|
|
1224
|
-
return { key, moduleAbsPath, seedAbsPaths: [userAbsPath] };
|
|
1225
|
-
}
|
|
1226
|
-
return { key, moduleAbsPath: userAbsPath };
|
|
1227
|
-
}));
|
|
1228
|
-
const generated = await Promise.all(rootBoundaries.map(async (boundary) => ({
|
|
1229
|
-
key: implicitRootLayoutKey(boundary.segments),
|
|
1230
|
-
moduleAbsPath: await writeGeneratedRootLayoutFile({
|
|
1231
|
-
appCwdPath: opts.appCwdPath,
|
|
1232
|
-
appName: opts.appName,
|
|
1233
|
-
boundary,
|
|
1234
|
-
rootSourceAbsPath,
|
|
1235
|
-
includeStInit: hasSt && boundary.segments.length === 0,
|
|
1236
|
-
includeSystemProvider: !hasAncestorRootBoundary(boundary, rootBoundaries)
|
|
1237
|
-
}),
|
|
1238
|
-
seedAbsPaths: [...new Set([boundary.sourceAbsPath, rootSourceAbsPath].filter((absPath) => absPath !== null))]
|
|
1239
|
-
})));
|
|
1240
|
-
const entries = [...base, ...generated];
|
|
1241
|
-
entries.sort((a, b) => a.key.localeCompare(b.key));
|
|
1242
|
-
return entries;
|
|
1280
|
+
var akanPackageJson = null;
|
|
1281
|
+
function getAkanPackageJson() {
|
|
1282
|
+
if (akanPackageJson)
|
|
1283
|
+
return akanPackageJson;
|
|
1284
|
+
const sourceDir = path5.dirname(fileURLToPath(import.meta.url));
|
|
1285
|
+
const packageJsonPaths = [
|
|
1286
|
+
path5.join(sourceDir, "../../../akanjs/package.json"),
|
|
1287
|
+
path5.join(process.cwd(), "pkgs/akanjs/package.json"),
|
|
1288
|
+
path5.join(path5.dirname(Bun.main), "node_modules/akanjs/package.json")
|
|
1289
|
+
];
|
|
1290
|
+
try {
|
|
1291
|
+
packageJsonPaths.unshift(Bun.resolveSync("akanjs/package.json", sourceDir));
|
|
1292
|
+
} catch {}
|
|
1293
|
+
for (const packageJsonPath of packageJsonPaths) {
|
|
1294
|
+
try {
|
|
1295
|
+
akanPackageJson = JSON.parse(fs2.readFileSync(packageJsonPath, "utf8"));
|
|
1296
|
+
return akanPackageJson;
|
|
1297
|
+
} catch {}
|
|
1298
|
+
}
|
|
1299
|
+
akanPackageJson = {
|
|
1300
|
+
name: "akanjs",
|
|
1301
|
+
version: "0.0.0",
|
|
1302
|
+
description: "akanjs",
|
|
1303
|
+
dependencies: {}
|
|
1304
|
+
};
|
|
1305
|
+
return akanPackageJson;
|
|
1243
1306
|
}
|
|
1244
|
-
|
|
1245
|
-
|
|
1246
|
-
|
|
1307
|
+
function mergeImageConfig(config = {}) {
|
|
1308
|
+
return {
|
|
1309
|
+
...DEFAULT_AKAN_IMAGE_CONFIG,
|
|
1310
|
+
...config,
|
|
1311
|
+
deviceSizes: config.deviceSizes ?? DEFAULT_AKAN_IMAGE_CONFIG.deviceSizes,
|
|
1312
|
+
imageSizes: config.imageSizes ?? DEFAULT_AKAN_IMAGE_CONFIG.imageSizes,
|
|
1313
|
+
formats: config.formats ?? DEFAULT_AKAN_IMAGE_CONFIG.formats,
|
|
1314
|
+
qualities: config.qualities ?? DEFAULT_AKAN_IMAGE_CONFIG.qualities,
|
|
1315
|
+
remotePatterns: config.remotePatterns ?? DEFAULT_AKAN_IMAGE_CONFIG.remotePatterns,
|
|
1316
|
+
localPatterns: config.localPatterns ?? DEFAULT_AKAN_IMAGE_CONFIG.localPatterns
|
|
1317
|
+
};
|
|
1247
1318
|
}
|
|
1248
1319
|
|
|
1320
|
+
class AkanLibConfig {
|
|
1321
|
+
lib;
|
|
1322
|
+
externalLibs;
|
|
1323
|
+
trustedDependencies;
|
|
1324
|
+
bin;
|
|
1325
|
+
docker;
|
|
1326
|
+
assets;
|
|
1327
|
+
plugins;
|
|
1328
|
+
constructor(lib, config, plugins = []) {
|
|
1329
|
+
this.lib = lib;
|
|
1330
|
+
this.externalLibs = config?.externalLibs ?? [];
|
|
1331
|
+
const owner = `libs/${lib.name}/akan.config.ts`;
|
|
1332
|
+
this.trustedDependencies = normalizePackageNames(config?.trustedDependencies, owner);
|
|
1333
|
+
this.bin = AkanBin.parse(config?.bin, owner, lib.cwdPath);
|
|
1334
|
+
this.docker = { preRuns: config?.docker?.preRuns ?? [], postRuns: config?.docker?.postRuns ?? [] };
|
|
1335
|
+
this.assets = { keepFonts: normalizeKeepFonts(config?.assets?.keepFonts) };
|
|
1336
|
+
this.plugins = plugins;
|
|
1337
|
+
}
|
|
1338
|
+
static async from(lib, { bustImportCache = false } = {}) {
|
|
1339
|
+
const configImp = await AkanAppConfig.importConfigModule(lib.cwdPath, { bustImportCache });
|
|
1340
|
+
const resolved = typeof configImp === "function" ? configImp(lib) : configImp;
|
|
1341
|
+
const { plugins, ...config } = resolved ?? {};
|
|
1342
|
+
return new AkanLibConfig(lib, config, plugins ?? []);
|
|
1343
|
+
}
|
|
1344
|
+
}
|
|
1345
|
+
//! need to refactor
|
|
1346
|
+
var shiftBuildNum = async (app, delta) => {
|
|
1347
|
+
const { buildNum } = (await AkanAppConfig.from(app)).native;
|
|
1348
|
+
const akanConfigPath = path5.join(app.cwdPath, "akan.config.ts");
|
|
1349
|
+
const akanConfig = fs2.readFileSync(akanConfigPath, "utf8");
|
|
1350
|
+
fs2.writeFileSync(akanConfigPath, akanConfig.replace(`buildNum: ${buildNum}`, `buildNum: ${buildNum + delta}`));
|
|
1351
|
+
};
|
|
1352
|
+
var increaseBuildNum = async (app) => await shiftBuildNum(app, 1);
|
|
1353
|
+
var decreaseBuildNum = async (app) => await shiftBuildNum(app, -1);
|
|
1249
1354
|
// pkgs/@akanjs/devkit/linter.ts
|
|
1250
1355
|
import { spawn } from "child_process";
|
|
1251
1356
|
import { existsSync, readFileSync } from "fs";
|
|
1252
|
-
import
|
|
1357
|
+
import path6 from "path";
|
|
1253
1358
|
import chalk from "chalk";
|
|
1254
1359
|
var parseBiomeReport = (output) => {
|
|
1255
1360
|
for (let start = output.indexOf("{");start !== -1; start = output.indexOf("{", start + 1)) {
|
|
@@ -1299,20 +1404,29 @@ var asBiomeReport = (candidate) => {
|
|
|
1299
1404
|
}
|
|
1300
1405
|
};
|
|
1301
1406
|
var BIOME_CONFIG_FILES = ["biome.json", "biome.jsonc"];
|
|
1407
|
+
var emptyResult = (filePath) => ({
|
|
1408
|
+
filePath,
|
|
1409
|
+
messages: [],
|
|
1410
|
+
errorCount: 0,
|
|
1411
|
+
warningCount: 0,
|
|
1412
|
+
fixableErrorCount: 0,
|
|
1413
|
+
fixableWarningCount: 0
|
|
1414
|
+
});
|
|
1302
1415
|
|
|
1303
1416
|
class Linter {
|
|
1417
|
+
static biomeBinName = process.platform === "win32" ? "biome.exe" : "biome";
|
|
1304
1418
|
lintRoot;
|
|
1305
1419
|
configPath;
|
|
1306
1420
|
#biomeBin;
|
|
1307
1421
|
constructor(cwdPath) {
|
|
1308
1422
|
this.lintRoot = this.#findBiomeRootPath(cwdPath);
|
|
1309
|
-
this.configPath = Linter.#configPathIn(this.lintRoot) ??
|
|
1310
|
-
const localBiomeBin =
|
|
1423
|
+
this.configPath = Linter.#configPathIn(this.lintRoot) ?? path6.join(this.lintRoot, "biome.json");
|
|
1424
|
+
const localBiomeBin = path6.join(this.lintRoot, "node_modules", ".bin", Linter.biomeBinName);
|
|
1311
1425
|
this.#biomeBin = existsSync(localBiomeBin) ? localBiomeBin : "biome";
|
|
1312
1426
|
}
|
|
1313
1427
|
static #configPathIn(dir) {
|
|
1314
1428
|
for (const fileName of BIOME_CONFIG_FILES) {
|
|
1315
|
-
const configPath =
|
|
1429
|
+
const configPath = path6.join(dir, fileName);
|
|
1316
1430
|
if (existsSync(configPath))
|
|
1317
1431
|
return configPath;
|
|
1318
1432
|
}
|
|
@@ -1321,19 +1435,19 @@ class Linter {
|
|
|
1321
1435
|
#findBiomeRootPath(dir) {
|
|
1322
1436
|
if (Linter.#configPathIn(dir))
|
|
1323
1437
|
return dir;
|
|
1324
|
-
const parentDir =
|
|
1438
|
+
const parentDir = path6.dirname(dir);
|
|
1325
1439
|
if (parentDir === dir)
|
|
1326
1440
|
throw new Error(`biome.json not found from ${dir}`);
|
|
1327
1441
|
return this.#findBiomeRootPath(parentDir);
|
|
1328
1442
|
}
|
|
1329
1443
|
#toBiomePath(filePath) {
|
|
1330
|
-
const relativePath =
|
|
1331
|
-
if (!relativePath.startsWith("..") && !
|
|
1444
|
+
const relativePath = path6.relative(this.lintRoot, filePath);
|
|
1445
|
+
if (!relativePath.startsWith("..") && !path6.isAbsolute(relativePath))
|
|
1332
1446
|
return relativePath;
|
|
1333
1447
|
return filePath;
|
|
1334
1448
|
}
|
|
1335
1449
|
#resolveFilePath(filePath) {
|
|
1336
|
-
return
|
|
1450
|
+
return path6.isAbsolute(filePath) ? filePath : path6.join(this.lintRoot, filePath);
|
|
1337
1451
|
}
|
|
1338
1452
|
async#runBiome(args, input) {
|
|
1339
1453
|
return await new Promise((resolve, reject) => {
|
|
@@ -1358,7 +1472,7 @@ class Linter {
|
|
|
1358
1472
|
const diagnosticPath = diagnostic.location?.path;
|
|
1359
1473
|
if (!diagnosticPath)
|
|
1360
1474
|
return fallbackFilePath;
|
|
1361
|
-
return
|
|
1475
|
+
return path6.isAbsolute(diagnosticPath) ? diagnosticPath : path6.join(this.lintRoot, diagnosticPath);
|
|
1362
1476
|
}
|
|
1363
1477
|
#createLintMessage(diagnostic) {
|
|
1364
1478
|
const start = diagnostic.location?.start;
|
|
@@ -1379,14 +1493,7 @@ class Linter {
|
|
|
1379
1493
|
if (diagnostic.severity !== "error" && diagnostic.severity !== "warning")
|
|
1380
1494
|
continue;
|
|
1381
1495
|
const diagnosticFilePath = this.#diagnosticFilePath(diagnostic, filePath);
|
|
1382
|
-
const result = resultsByPath.get(diagnosticFilePath) ??
|
|
1383
|
-
filePath: diagnosticFilePath,
|
|
1384
|
-
messages: [],
|
|
1385
|
-
errorCount: 0,
|
|
1386
|
-
warningCount: 0,
|
|
1387
|
-
fixableErrorCount: 0,
|
|
1388
|
-
fixableWarningCount: 0
|
|
1389
|
-
};
|
|
1496
|
+
const result = resultsByPath.get(diagnosticFilePath) ?? emptyResult(diagnosticFilePath);
|
|
1390
1497
|
const message = this.#createLintMessage(diagnostic);
|
|
1391
1498
|
result.messages.push(message);
|
|
1392
1499
|
if (message.severity === 2)
|
|
@@ -1396,14 +1503,7 @@ class Linter {
|
|
|
1396
1503
|
resultsByPath.set(diagnosticFilePath, result);
|
|
1397
1504
|
}
|
|
1398
1505
|
return [
|
|
1399
|
-
resultsByPath.get(filePath) ??
|
|
1400
|
-
filePath,
|
|
1401
|
-
messages: [],
|
|
1402
|
-
errorCount: 0,
|
|
1403
|
-
warningCount: 0,
|
|
1404
|
-
fixableErrorCount: 0,
|
|
1405
|
-
fixableWarningCount: 0
|
|
1406
|
-
},
|
|
1506
|
+
resultsByPath.get(filePath) ?? emptyResult(filePath),
|
|
1407
1507
|
...[...resultsByPath.entries()].filter(([resultPath]) => resultPath !== filePath).map(([, result]) => result)
|
|
1408
1508
|
];
|
|
1409
1509
|
}
|
|
@@ -1513,12 +1613,7 @@ ${errorText}, ${warningText} found`];
|
|
|
1513
1613
|
warningCount: acc.warningCount + result.warningCount,
|
|
1514
1614
|
fixableErrorCount: acc.fixableErrorCount + result.fixableErrorCount,
|
|
1515
1615
|
fixableWarningCount: acc.fixableWarningCount + result.fixableWarningCount
|
|
1516
|
-
}), {
|
|
1517
|
-
errorCount: 0,
|
|
1518
|
-
warningCount: 0,
|
|
1519
|
-
fixableErrorCount: 0,
|
|
1520
|
-
fixableWarningCount: 0
|
|
1521
|
-
});
|
|
1616
|
+
}), { errorCount: 0, warningCount: 0, fixableErrorCount: 0, fixableWarningCount: 0 });
|
|
1522
1617
|
return { results, details, stats };
|
|
1523
1618
|
}
|
|
1524
1619
|
async hasNoLintErrors(filePath) {
|
|
@@ -1576,19 +1671,16 @@ ${errorText}, ${warningText} found`];
|
|
|
1576
1671
|
async getProblematicRules(filePath) {
|
|
1577
1672
|
const { results } = await this.lintFile(filePath);
|
|
1578
1673
|
const ruleCounts = {};
|
|
1579
|
-
results.
|
|
1580
|
-
|
|
1581
|
-
|
|
1582
|
-
ruleCounts[message.ruleId] = (ruleCounts[message.ruleId] || 0) + 1;
|
|
1583
|
-
});
|
|
1584
|
-
});
|
|
1674
|
+
for (const message of results.flatMap((result) => result.messages))
|
|
1675
|
+
if (message.ruleId)
|
|
1676
|
+
ruleCounts[message.ruleId] = (ruleCounts[message.ruleId] || 0) + 1;
|
|
1585
1677
|
return ruleCounts;
|
|
1586
1678
|
}
|
|
1587
1679
|
}
|
|
1588
1680
|
|
|
1589
1681
|
// pkgs/@akanjs/devkit/repoIdentity.ts
|
|
1590
1682
|
import { execFileSync } from "child_process";
|
|
1591
|
-
import
|
|
1683
|
+
import path7 from "path";
|
|
1592
1684
|
var resolved = new Map;
|
|
1593
1685
|
var readRemoteName = (workspaceRoot) => {
|
|
1594
1686
|
try {
|
|
@@ -1606,14 +1698,13 @@ var resolveRepoName = (workspaceRoot) => {
|
|
|
1606
1698
|
const cached = resolved.get(workspaceRoot);
|
|
1607
1699
|
if (cached)
|
|
1608
1700
|
return cached;
|
|
1609
|
-
const repoName = readRemoteName(workspaceRoot) ??
|
|
1701
|
+
const repoName = readRemoteName(workspaceRoot) ?? path7.basename(workspaceRoot);
|
|
1610
1702
|
resolved.set(workspaceRoot, repoName);
|
|
1611
1703
|
return repoName;
|
|
1612
1704
|
};
|
|
1613
1705
|
|
|
1614
1706
|
// pkgs/@akanjs/devkit/scanInfo.ts
|
|
1615
|
-
import
|
|
1616
|
-
import path7 from "path";
|
|
1707
|
+
import path8 from "path";
|
|
1617
1708
|
|
|
1618
1709
|
// pkgs/@akanjs/devkit/workspaceLayout.ts
|
|
1619
1710
|
var appRootAllowedFiles = new Set([
|
|
@@ -1621,7 +1712,6 @@ var appRootAllowedFiles = new Set([
|
|
|
1621
1712
|
"CLAUDE.md",
|
|
1622
1713
|
"akan.app.json",
|
|
1623
1714
|
"akan.config.ts",
|
|
1624
|
-
"capacitor.config.ts",
|
|
1625
1715
|
"client.ts",
|
|
1626
1716
|
"main.ts",
|
|
1627
1717
|
"package.json",
|
|
@@ -1631,12 +1721,10 @@ var appRootAllowedFiles = new Set([
|
|
|
1631
1721
|
]);
|
|
1632
1722
|
var appRootAllowedDirs = new Set([
|
|
1633
1723
|
".akan",
|
|
1634
|
-
"android",
|
|
1635
1724
|
"common",
|
|
1636
1725
|
"env",
|
|
1637
|
-
"ios",
|
|
1638
1726
|
"lib",
|
|
1639
|
-
"
|
|
1727
|
+
"native",
|
|
1640
1728
|
"page",
|
|
1641
1729
|
"plugin",
|
|
1642
1730
|
"private",
|
|
@@ -1665,6 +1753,7 @@ var libRootAllowedDirs = new Set([
|
|
|
1665
1753
|
"common",
|
|
1666
1754
|
"env",
|
|
1667
1755
|
"lib",
|
|
1756
|
+
"native",
|
|
1668
1757
|
"page",
|
|
1669
1758
|
"plugin",
|
|
1670
1759
|
"private",
|
|
@@ -1688,13 +1777,20 @@ var libFacetRootAllowedFiles = new Set([
|
|
|
1688
1777
|
]);
|
|
1689
1778
|
var libFacetRootTestPattern = /^[A-Za-z][A-Za-z0-9_-]*\.signal\.(test|spec)\.(ts|tsx)$/;
|
|
1690
1779
|
var isAllowedLibFacetRootFile = (filename) => libFacetRootAllowedFiles.has(filename) || libFacetRootTestPattern.test(filename);
|
|
1780
|
+
var retiredAppRootEntries = new Set([
|
|
1781
|
+
"android",
|
|
1782
|
+
"ios",
|
|
1783
|
+
"mobile",
|
|
1784
|
+
"capacitor.config.ts",
|
|
1785
|
+
"capacitor.config.js",
|
|
1786
|
+
"capacitor.config.json"
|
|
1787
|
+
]);
|
|
1788
|
+
var rootEntryHintOf = (type, name) => type === "app" && retiredAppRootEntries.has(name) ? "a Capacitor-era entry; the native runtime generates its projects under .akan/native/<target>, so delete it" : null;
|
|
1691
1789
|
var isScannedRootEntry = (type, name) => !name.startsWith(".") || rootAllowedDirs[type].has(name);
|
|
1692
1790
|
|
|
1693
1791
|
// pkgs/@akanjs/devkit/scanInfo.ts
|
|
1694
|
-
var
|
|
1695
|
-
"constant",
|
|
1792
|
+
var serviceFileTypes = [
|
|
1696
1793
|
"dictionary",
|
|
1697
|
-
"document",
|
|
1698
1794
|
"service",
|
|
1699
1795
|
"signal",
|
|
1700
1796
|
"store",
|
|
@@ -1704,7 +1800,9 @@ var databaseFileTypes = [
|
|
|
1704
1800
|
"view",
|
|
1705
1801
|
"zone"
|
|
1706
1802
|
];
|
|
1707
|
-
var
|
|
1803
|
+
var databaseFileTypes = ["constant", "dictionary", "document", ...serviceFileTypes.slice(1)];
|
|
1804
|
+
var uiFileTypes = new Set(["template", "unit", "util", "view", "zone"]);
|
|
1805
|
+
var fileTypeOf = (filename) => databaseFileTypes.find((type) => filename.endsWith(uiFileTypes.has(type) ? `.${type[0].toUpperCase()}${type.slice(1)}.tsx` : `.${type}.ts`));
|
|
1708
1806
|
var internalLibDirs = new Set(["__lib", "__scalar"]);
|
|
1709
1807
|
var moduleNonUiFileTypes = {
|
|
1710
1808
|
database: new Set(["constant", "dictionary", "document", "service", "signal", "store"]),
|
|
@@ -1716,21 +1814,13 @@ var moduleUiFileTypes = {
|
|
|
1716
1814
|
service: new Set(["Util", "Zone"]),
|
|
1717
1815
|
scalar: new Set(["Template", "Unit"])
|
|
1718
1816
|
};
|
|
1719
|
-
var
|
|
1720
|
-
var
|
|
1721
|
-
var isAllowedTestFile = (filename) => testFilePattern.test(filename);
|
|
1722
|
-
var getScanPath = (exec, relativePath) => path7.posix.join(`${exec.type}s`, exec.name, relativePath.split(path7.sep).join("/"));
|
|
1723
|
-
async function clearGeneratedRootCapacitorConfigs(exec) {
|
|
1724
|
-
if (exec.type !== "app")
|
|
1725
|
-
return;
|
|
1726
|
-
await Promise.all(generatedRootCapacitorConfigFiles.map((filename) => rm2(exec.getPath(filename), { force: true })));
|
|
1727
|
-
}
|
|
1817
|
+
var createDependencyScanner = async (exec) => (await import("./dependencyScanner-06z2dmrr.js")).TypeScriptDependencyScanner.from(exec);
|
|
1818
|
+
var getScanPath = (exec, relativePath) => path8.posix.join(`${exec.type}s`, exec.name, relativePath.split(path8.sep).join("/"));
|
|
1728
1819
|
var getModuleNameFromPath = (kind, modulePath) => {
|
|
1729
|
-
const dirname =
|
|
1820
|
+
const dirname = path8.basename(modulePath);
|
|
1730
1821
|
return kind === "service" ? dirname.replace(/^_+/, "") : dirname;
|
|
1731
1822
|
};
|
|
1732
1823
|
async function assertScanConvention(exec, libRoot) {
|
|
1733
|
-
await clearGeneratedRootCapacitorConfigs(exec);
|
|
1734
1824
|
const violations = [];
|
|
1735
1825
|
const addViolation = (relativePath, reason) => {
|
|
1736
1826
|
violations.push(`${getScanPath(exec, relativePath)}: ${reason}`);
|
|
@@ -1738,28 +1828,28 @@ async function assertScanConvention(exec, libRoot) {
|
|
|
1738
1828
|
const allowedRootFiles = rootAllowedFiles[exec.type];
|
|
1739
1829
|
const allowedRootDirs = rootAllowedDirs[exec.type];
|
|
1740
1830
|
const { files, dirs } = await exec.getFilesAndDirs(".");
|
|
1741
|
-
|
|
1742
|
-
|
|
1743
|
-
|
|
1744
|
-
|
|
1745
|
-
|
|
1746
|
-
|
|
1831
|
+
for (const filename of files)
|
|
1832
|
+
if (!allowedRootFiles.has(filename))
|
|
1833
|
+
addViolation(filename, rootEntryHintOf(exec.type, filename) ?? `unsupported ${exec.type} root file`);
|
|
1834
|
+
for (const dirname of dirs)
|
|
1835
|
+
if (!allowedRootDirs.has(dirname))
|
|
1836
|
+
addViolation(dirname, rootEntryHintOf(exec.type, dirname) ?? `unsupported ${exec.type} root folder`);
|
|
1747
1837
|
if (exec.type === "lib")
|
|
1748
1838
|
for (const { relativePath, reason } of await exec.getPageConventionViolations())
|
|
1749
1839
|
addViolation(relativePath, reason);
|
|
1750
|
-
|
|
1751
|
-
|
|
1752
|
-
|
|
1753
|
-
|
|
1754
|
-
|
|
1755
|
-
|
|
1840
|
+
for (const filename of libRoot.files)
|
|
1841
|
+
if (!isAllowedLibFacetRootFile(filename))
|
|
1842
|
+
addViolation(path8.join("lib", filename), "unsupported lib facet root file");
|
|
1843
|
+
for (const dirname of libRoot.dirs)
|
|
1844
|
+
if (dirname.startsWith("__") && !internalLibDirs.has(dirname))
|
|
1845
|
+
addViolation(path8.join("lib", dirname), "unsupported internal lib folder");
|
|
1756
1846
|
const databaseDirs = libRoot.dirs.filter((dirname) => !dirname.startsWith("_"));
|
|
1757
1847
|
const serviceDirs = libRoot.dirs.filter((dirname) => dirname.startsWith("_") && !dirname.startsWith("__"));
|
|
1758
1848
|
const scalarDirs = await exec.readdir("lib/__scalar");
|
|
1759
1849
|
await Promise.all([
|
|
1760
|
-
...databaseDirs.map((dirname) => validateModuleFiles(exec, violations, "database",
|
|
1761
|
-
...serviceDirs.map((dirname) => validateModuleFiles(exec, violations, "service",
|
|
1762
|
-
...scalarDirs.map((dirname) => validateModuleFiles(exec, violations, "scalar",
|
|
1850
|
+
...databaseDirs.map((dirname) => validateModuleFiles(exec, violations, "database", path8.join("lib", dirname))),
|
|
1851
|
+
...serviceDirs.map((dirname) => validateModuleFiles(exec, violations, "service", path8.join("lib", dirname))),
|
|
1852
|
+
...scalarDirs.map((dirname) => validateModuleFiles(exec, violations, "scalar", path8.join("lib/__scalar", dirname)))
|
|
1763
1853
|
]);
|
|
1764
1854
|
if (violations.length > 0) {
|
|
1765
1855
|
throw new Error(`[scan-convention]
|
|
@@ -1771,41 +1861,28 @@ async function validateModuleFiles(exec, violations, kind, modulePath) {
|
|
|
1771
1861
|
const { files, dirs } = await exec.getFilesAndDirs(modulePath);
|
|
1772
1862
|
const moduleName = getModuleNameFromPath(kind, modulePath);
|
|
1773
1863
|
dirs.forEach((dirname) => {
|
|
1774
|
-
violations.push(`${getScanPath(exec,
|
|
1864
|
+
violations.push(`${getScanPath(exec, path8.join(modulePath, dirname))}: unsupported module folder`);
|
|
1775
1865
|
});
|
|
1776
1866
|
const uiModuleName = moduleName[0].toUpperCase() + moduleName.slice(1);
|
|
1777
|
-
|
|
1778
|
-
const
|
|
1779
|
-
if (filename === "index.ts" || filename === "index.tsx" ||
|
|
1780
|
-
|
|
1867
|
+
for (const filename of files) {
|
|
1868
|
+
const scanPath = getScanPath(exec, path8.join(modulePath, filename));
|
|
1869
|
+
if (filename === "index.ts" || filename === "index.tsx" || /\.(test|spec)\.(ts|tsx)$/.test(filename))
|
|
1870
|
+
continue;
|
|
1781
1871
|
if (filename === `${moduleName}.abstract.md`)
|
|
1782
|
-
|
|
1872
|
+
continue;
|
|
1783
1873
|
const uiMatch = filename.match(/^([A-Z][A-Za-z0-9]+)\.([A-Z][A-Za-z0-9]*)\.tsx$/);
|
|
1784
|
-
|
|
1785
|
-
|
|
1786
|
-
|
|
1787
|
-
|
|
1788
|
-
violations.push(`${getScanPath(exec, filePath)}: module name mismatch: expected '${uiModuleName}', got '${fileModuleName}'`);
|
|
1789
|
-
}
|
|
1790
|
-
if (!moduleUiFileTypes[kind].has(fileType)) {
|
|
1791
|
-
violations.push(`${getScanPath(exec, filePath)}: unsupported ${kind} UI file`);
|
|
1792
|
-
}
|
|
1793
|
-
return;
|
|
1794
|
-
}
|
|
1795
|
-
const nonUiMatch = filename.match(/^([a-z][a-zA-Z0-9]*)\.([a-z][a-z0-9]*)\.ts$/);
|
|
1796
|
-
if (nonUiMatch) {
|
|
1797
|
-
const fileModuleName = nonUiMatch[1];
|
|
1798
|
-
const fileType = nonUiMatch[2];
|
|
1799
|
-
if (fileModuleName !== moduleName) {
|
|
1800
|
-
violations.push(`${getScanPath(exec, filePath)}: module name mismatch: expected '${moduleName}', got '${fileModuleName}'`);
|
|
1801
|
-
}
|
|
1802
|
-
if (!moduleNonUiFileTypes[kind].has(fileType)) {
|
|
1803
|
-
violations.push(`${getScanPath(exec, filePath)}: unsupported ${kind} file`);
|
|
1804
|
-
}
|
|
1805
|
-
return;
|
|
1874
|
+
const match = uiMatch ?? filename.match(/^([a-z][a-zA-Z0-9]*)\.([a-z][a-z0-9]*)\.ts$/);
|
|
1875
|
+
if (!match) {
|
|
1876
|
+
violations.push(`${scanPath}: unsupported module file`);
|
|
1877
|
+
continue;
|
|
1806
1878
|
}
|
|
1807
|
-
|
|
1808
|
-
|
|
1879
|
+
const [, fileModuleName, fileType] = match;
|
|
1880
|
+
const expectedName = uiMatch ? uiModuleName : moduleName;
|
|
1881
|
+
if (fileModuleName !== expectedName)
|
|
1882
|
+
violations.push(`${scanPath}: module name mismatch: expected '${expectedName}', got '${fileModuleName}'`);
|
|
1883
|
+
if (!(uiMatch ? moduleUiFileTypes : moduleNonUiFileTypes)[kind].has(fileType))
|
|
1884
|
+
violations.push(`${scanPath}: unsupported ${kind}${uiMatch ? " UI" : ""} file`);
|
|
1885
|
+
}
|
|
1809
1886
|
}
|
|
1810
1887
|
|
|
1811
1888
|
class ScanInfo {
|
|
@@ -1853,86 +1930,24 @@ class ScanInfo {
|
|
|
1853
1930
|
} else
|
|
1854
1931
|
databaseDirs.push(name);
|
|
1855
1932
|
});
|
|
1933
|
+
const collect = async (dir, name, kind) => {
|
|
1934
|
+
for (const filename of await exec.readdir(dir)) {
|
|
1935
|
+
const type = fileTypeOf(filename);
|
|
1936
|
+
if (type)
|
|
1937
|
+
files[type][kind]?.push(name);
|
|
1938
|
+
}
|
|
1939
|
+
};
|
|
1856
1940
|
await Promise.all([
|
|
1857
|
-
...databaseDirs.map(
|
|
1858
|
-
|
|
1859
|
-
|
|
1860
|
-
if (filename.endsWith(".constant.ts"))
|
|
1861
|
-
files.constant.databases.push(name);
|
|
1862
|
-
else if (filename.endsWith(".dictionary.ts"))
|
|
1863
|
-
files.dictionary.databases.push(name);
|
|
1864
|
-
else if (filename.endsWith(".document.ts"))
|
|
1865
|
-
files.document.databases.push(name);
|
|
1866
|
-
else if (filename.endsWith(".service.ts"))
|
|
1867
|
-
files.service.databases.push(name);
|
|
1868
|
-
else if (filename.endsWith(".signal.ts"))
|
|
1869
|
-
files.signal.databases.push(name);
|
|
1870
|
-
else if (filename.endsWith(".store.ts"))
|
|
1871
|
-
files.store.databases.push(name);
|
|
1872
|
-
else if (filename.endsWith(".Template.tsx"))
|
|
1873
|
-
files.template.databases.push(name);
|
|
1874
|
-
else if (filename.endsWith(".Unit.tsx"))
|
|
1875
|
-
files.unit.databases.push(name);
|
|
1876
|
-
else if (filename.endsWith(".Util.tsx"))
|
|
1877
|
-
files.util.databases.push(name);
|
|
1878
|
-
else if (filename.endsWith(".View.tsx"))
|
|
1879
|
-
files.view.databases.push(name);
|
|
1880
|
-
else if (filename.endsWith(".Zone.tsx"))
|
|
1881
|
-
files.zone.databases.push(name);
|
|
1882
|
-
});
|
|
1883
|
-
}),
|
|
1884
|
-
...serviceDirs.map(async (dirname) => {
|
|
1885
|
-
const name = dirname.slice(1);
|
|
1886
|
-
const filenames = await exec.readdir(path7.join("lib", dirname));
|
|
1887
|
-
filenames.forEach((filename) => {
|
|
1888
|
-
if (filename.endsWith(".dictionary.ts"))
|
|
1889
|
-
files.dictionary.services.push(name);
|
|
1890
|
-
else if (filename.endsWith(".service.ts"))
|
|
1891
|
-
files.service.services.push(name);
|
|
1892
|
-
else if (filename.endsWith(".signal.ts"))
|
|
1893
|
-
files.signal.services.push(name);
|
|
1894
|
-
else if (filename.endsWith(".store.ts"))
|
|
1895
|
-
files.store.services.push(name);
|
|
1896
|
-
else if (filename.endsWith(".Template.tsx"))
|
|
1897
|
-
files.template.services.push(name);
|
|
1898
|
-
else if (filename.endsWith(".Unit.tsx"))
|
|
1899
|
-
files.unit.services.push(name);
|
|
1900
|
-
else if (filename.endsWith(".Util.tsx"))
|
|
1901
|
-
files.util.services.push(name);
|
|
1902
|
-
else if (filename.endsWith(".View.tsx"))
|
|
1903
|
-
files.view.services.push(name);
|
|
1904
|
-
else if (filename.endsWith(".Zone.tsx"))
|
|
1905
|
-
files.zone.services.push(name);
|
|
1906
|
-
});
|
|
1907
|
-
}),
|
|
1908
|
-
...scalarDirs.map(async (name) => {
|
|
1909
|
-
const filenames = await exec.readdir(path7.join("lib/__scalar", name));
|
|
1910
|
-
filenames.forEach((filename) => {
|
|
1911
|
-
if (filename.endsWith(".constant.ts"))
|
|
1912
|
-
files.constant.scalars.push(name);
|
|
1913
|
-
else if (filename.endsWith(".dictionary.ts"))
|
|
1914
|
-
files.dictionary.scalars.push(name);
|
|
1915
|
-
else if (filename.endsWith(".document.ts"))
|
|
1916
|
-
files.document.scalars.push(name);
|
|
1917
|
-
else if (filename.endsWith(".Template.tsx"))
|
|
1918
|
-
files.template.scalars.push(name);
|
|
1919
|
-
else if (filename.endsWith(".Unit.tsx"))
|
|
1920
|
-
files.unit.scalars.push(name);
|
|
1921
|
-
else if (filename.endsWith(".Util.tsx"))
|
|
1922
|
-
files.util.scalars.push(name);
|
|
1923
|
-
else if (filename.endsWith(".View.tsx"))
|
|
1924
|
-
files.view.scalars.push(name);
|
|
1925
|
-
else if (filename.endsWith(".Zone.tsx"))
|
|
1926
|
-
files.zone.scalars.push(name);
|
|
1927
|
-
});
|
|
1928
|
-
})
|
|
1941
|
+
...databaseDirs.map((name) => collect(path8.join("lib", name), name, "databases")),
|
|
1942
|
+
...serviceDirs.map((dirname) => collect(path8.join("lib", dirname), dirname.slice(1), "services")),
|
|
1943
|
+
...scalarDirs.map((name) => collect(path8.join("lib/__scalar", name), name, "scalars"))
|
|
1929
1944
|
]);
|
|
1930
1945
|
const routes = exec.type === "lib" ? [] : await exec.getPageKeys();
|
|
1931
1946
|
const common = {
|
|
1932
1947
|
name: exec.name,
|
|
1933
1948
|
type: exec.type,
|
|
1934
1949
|
repoName: exec.workspace.repoName,
|
|
1935
|
-
serveDomain: WorkspaceExecutor.getBaseDevEnv(
|
|
1950
|
+
serveDomain: WorkspaceExecutor.getBaseDevEnv(path8.join(exec.workspace.workspaceRoot, ".env")).serveDomain,
|
|
1936
1951
|
files,
|
|
1937
1952
|
libDeps,
|
|
1938
1953
|
pkgDeps,
|
|
@@ -1946,31 +1961,14 @@ class ScanInfo {
|
|
|
1946
1961
|
constructor(scanResult) {
|
|
1947
1962
|
this.name = scanResult.name;
|
|
1948
1963
|
this.scanResult = scanResult;
|
|
1949
|
-
|
|
1950
|
-
|
|
1951
|
-
const
|
|
1952
|
-
|
|
1953
|
-
|
|
1954
|
-
|
|
1955
|
-
|
|
1956
|
-
|
|
1957
|
-
this.file[key].databases.add(modelName);
|
|
1958
|
-
});
|
|
1959
|
-
services?.forEach((serviceName) => {
|
|
1960
|
-
const service = this.service.get(serviceName) ?? new Set;
|
|
1961
|
-
service.add(key);
|
|
1962
|
-
this.service.set(serviceName, service);
|
|
1963
|
-
this.file[key].all.add(serviceName);
|
|
1964
|
-
this.file[key].services.add(serviceName);
|
|
1965
|
-
});
|
|
1966
|
-
scalars?.forEach((scalarName) => {
|
|
1967
|
-
const scalar = this.scalar.get(scalarName) ?? new Set;
|
|
1968
|
-
scalar.add(key);
|
|
1969
|
-
this.scalar.set(scalarName, scalar);
|
|
1970
|
-
this.file[key].all.add(scalarName);
|
|
1971
|
-
this.file[key].scalars.add(scalarName);
|
|
1972
|
-
});
|
|
1973
|
-
});
|
|
1964
|
+
const modulesOf = { databases: this.database, services: this.service, scalars: this.scalar };
|
|
1965
|
+
for (const [key, groups] of Object.entries(scanResult.files))
|
|
1966
|
+
for (const kind of ["databases", "services", "scalars"])
|
|
1967
|
+
for (const name of groups[kind] ?? []) {
|
|
1968
|
+
modulesOf[kind].set(name, (modulesOf[kind].get(name) ?? new Set).add(key));
|
|
1969
|
+
this.file[key].all.add(name);
|
|
1970
|
+
this.file[key][kind].add(name);
|
|
1971
|
+
}
|
|
1974
1972
|
}
|
|
1975
1973
|
getScanResult() {
|
|
1976
1974
|
return this.scanResult;
|
|
@@ -1984,6 +1982,28 @@ class ScanInfo {
|
|
|
1984
1982
|
getScalarModules() {
|
|
1985
1983
|
return [...this.scalar.keys()];
|
|
1986
1984
|
}
|
|
1985
|
+
#sortedLibs = null;
|
|
1986
|
+
sortedLibs(libDeps) {
|
|
1987
|
+
if (this.#sortedLibs)
|
|
1988
|
+
return this.#sortedLibs;
|
|
1989
|
+
const libIndices = LibInfo.getSortedLibIndices();
|
|
1990
|
+
this.#sortedLibs = libDeps.sort((libNameA, libNameB) => {
|
|
1991
|
+
const indexA = libIndices.get(libNameA);
|
|
1992
|
+
const indexB = libIndices.get(libNameB);
|
|
1993
|
+
if (indexA === undefined || indexB === undefined)
|
|
1994
|
+
throw new Error(`LibInfo not found: ${libNameA} or ${libNameB}`);
|
|
1995
|
+
return indexA - indexB;
|
|
1996
|
+
});
|
|
1997
|
+
return this.#sortedLibs;
|
|
1998
|
+
}
|
|
1999
|
+
sortedLibInfos(libDeps) {
|
|
2000
|
+
return new Map(this.sortedLibs(libDeps).map((libName) => {
|
|
2001
|
+
const libInfo = LibInfo.libInfos.get(libName);
|
|
2002
|
+
if (!libInfo)
|
|
2003
|
+
throw new Error(`LibInfo not found: ${libName}`);
|
|
2004
|
+
return [libName, libInfo];
|
|
2005
|
+
}));
|
|
2006
|
+
}
|
|
1987
2007
|
}
|
|
1988
2008
|
var isAkanFrameworkDependency = (dep) => dep === "akanjs" || dep.startsWith("akanjs/");
|
|
1989
2009
|
|
|
@@ -2033,30 +2053,11 @@ class AppInfo extends ScanInfo {
|
|
|
2033
2053
|
}));
|
|
2034
2054
|
return [...libSet];
|
|
2035
2055
|
}
|
|
2036
|
-
#sortedLibs = null;
|
|
2037
|
-
#getSortedLibs() {
|
|
2038
|
-
if (this.#sortedLibs)
|
|
2039
|
-
return this.#sortedLibs;
|
|
2040
|
-
const libIndices = LibInfo.getSortedLibIndices();
|
|
2041
|
-
this.#sortedLibs = this.libDeps.sort((libNameA, libNameB) => {
|
|
2042
|
-
const indexA = libIndices.get(libNameA);
|
|
2043
|
-
const indexB = libIndices.get(libNameB);
|
|
2044
|
-
if (indexA === undefined || indexB === undefined)
|
|
2045
|
-
throw new Error(`LibInfo not found: ${libNameA} or ${libNameB}`);
|
|
2046
|
-
return indexA - indexB;
|
|
2047
|
-
});
|
|
2048
|
-
return this.#sortedLibs;
|
|
2049
|
-
}
|
|
2050
2056
|
getLibs() {
|
|
2051
|
-
return this
|
|
2057
|
+
return this.sortedLibs(this.libDeps);
|
|
2052
2058
|
}
|
|
2053
2059
|
getLibInfos() {
|
|
2054
|
-
return
|
|
2055
|
-
const libInfo = LibInfo.libInfos.get(libName);
|
|
2056
|
-
if (!libInfo)
|
|
2057
|
-
throw new Error(`LibInfo not found: ${libName}`);
|
|
2058
|
-
return [libName, libInfo];
|
|
2059
|
-
}));
|
|
2060
|
+
return this.sortedLibInfos(this.libDeps);
|
|
2060
2061
|
}
|
|
2061
2062
|
}
|
|
2062
2063
|
|
|
@@ -2096,38 +2097,18 @@ class LibInfo extends ScanInfo {
|
|
|
2096
2097
|
getScanResult() {
|
|
2097
2098
|
return this.scanResult;
|
|
2098
2099
|
}
|
|
2099
|
-
#sortedLibs = null;
|
|
2100
|
-
#getSortedLibs() {
|
|
2101
|
-
if (this.#sortedLibs)
|
|
2102
|
-
return this.#sortedLibs;
|
|
2103
|
-
const libs = LibInfo.getSortedLibIndices();
|
|
2104
|
-
this.#sortedLibs = this.scanResult.libDeps.sort((libNameA, libNameB) => {
|
|
2105
|
-
const indexA = libs.get(libNameA);
|
|
2106
|
-
const indexB = libs.get(libNameB);
|
|
2107
|
-
if (indexA === undefined || indexB === undefined)
|
|
2108
|
-
throw new Error(`LibInfo not found: ${libNameA} or ${libNameB}`);
|
|
2109
|
-
return indexA - indexB;
|
|
2110
|
-
});
|
|
2111
|
-
return this.#sortedLibs;
|
|
2112
|
-
}
|
|
2113
2100
|
getLibs() {
|
|
2114
|
-
return this
|
|
2101
|
+
return this.sortedLibs(this.scanResult.libDeps);
|
|
2115
2102
|
}
|
|
2116
2103
|
getLibInfo(libName) {
|
|
2117
2104
|
if (!this.getScanResult().libDeps.includes(libName))
|
|
2118
2105
|
return;
|
|
2119
|
-
|
|
2120
|
-
if (!libSet.has(libName))
|
|
2106
|
+
if (!this.getLibs().includes(libName))
|
|
2121
2107
|
throw new Error(`LibInfo is invalid: ${libName}`);
|
|
2122
2108
|
return LibInfo.libInfos.get(libName);
|
|
2123
2109
|
}
|
|
2124
2110
|
getLibInfos() {
|
|
2125
|
-
return
|
|
2126
|
-
const libInfo = LibInfo.libInfos.get(libName);
|
|
2127
|
-
if (!libInfo)
|
|
2128
|
-
throw new Error(`LibInfo not found: ${libName}`);
|
|
2129
|
-
return [libName, libInfo];
|
|
2130
|
-
}));
|
|
2111
|
+
return this.sortedLibInfos(this.scanResult.libDeps);
|
|
2131
2112
|
}
|
|
2132
2113
|
}
|
|
2133
2114
|
|
|
@@ -2139,18 +2120,14 @@ class PkgInfo {
|
|
|
2139
2120
|
const [tsconfig, rootPackageJson] = await Promise.all([exec.getTsConfig(), exec.workspace.getPackageJson()]);
|
|
2140
2121
|
const scanner = await createDependencyScanner(exec);
|
|
2141
2122
|
const npmSet = new Set(Object.keys({ ...rootPackageJson.dependencies, ...rootPackageJson.devDependencies }));
|
|
2142
|
-
const
|
|
2123
|
+
const workspacePathOf = (resolve) => resolve.replace(/^\.\//, "");
|
|
2124
|
+
const pkgPathSet = new Set(Object.keys(tsconfig.compilerOptions.paths ?? {}).filter((path) => tsconfig.compilerOptions.paths?.[path]?.some((resolve) => workspacePathOf(resolve).startsWith("pkgs/"))).map((path) => path.replace("/*", "")));
|
|
2143
2125
|
const [npmDepSet, pkgPathDepSet] = await scanner.getImportSets([npmSet, pkgPathSet]);
|
|
2144
2126
|
const pkgDeps = [...pkgPathDepSet].map((path) => {
|
|
2145
2127
|
const pathSplitLength = path.split("/").length;
|
|
2146
|
-
return (tsconfig.compilerOptions.paths?.[path]?.[0] ?? "*").split("/").slice(1, 1 + pathSplitLength).join("/");
|
|
2128
|
+
return workspacePathOf(tsconfig.compilerOptions.paths?.[path]?.[0] ?? "*").split("/").slice(1, 1 + pathSplitLength).join("/");
|
|
2147
2129
|
}).filter((pkg) => pkg !== exec.name);
|
|
2148
|
-
|
|
2149
|
-
name: exec.name,
|
|
2150
|
-
pkgDeps,
|
|
2151
|
-
dependencies: [...npmDepSet]
|
|
2152
|
-
};
|
|
2153
|
-
return pkgScanResult;
|
|
2130
|
+
return { name: exec.name, pkgDeps, dependencies: [...npmDepSet] };
|
|
2154
2131
|
}
|
|
2155
2132
|
static #pkgInfos = new Map;
|
|
2156
2133
|
static async fromExecutor(exec, options = {}) {
|
|
@@ -2188,17 +2165,9 @@ class WorkspaceInfo {
|
|
|
2188
2165
|
return existingWorkspaceInfo;
|
|
2189
2166
|
const [appNames, libNames, pkgNames] = await Promise.all([exec.getApps(), exec.getLibs(), exec.getPkgs()]);
|
|
2190
2167
|
const [appInfos, libInfos, pkgInfos] = await Promise.all([
|
|
2191
|
-
Promise.all(appNames.map(async (appName) =>
|
|
2192
|
-
|
|
2193
|
-
|
|
2194
|
-
})),
|
|
2195
|
-
Promise.all(libNames.map(async (libName) => {
|
|
2196
|
-
const lib = LibExecutor.from(exec, libName);
|
|
2197
|
-
return await lib.scan();
|
|
2198
|
-
})),
|
|
2199
|
-
Promise.all(pkgNames.map(async (pkgName) => {
|
|
2200
|
-
return await PkgExecutor.from(exec, pkgName).scan();
|
|
2201
|
-
}))
|
|
2168
|
+
Promise.all(appNames.map(async (appName) => await AppExecutor.from(exec, appName).scan())),
|
|
2169
|
+
Promise.all(libNames.map(async (libName) => await LibExecutor.from(exec, libName).scan())),
|
|
2170
|
+
Promise.all(pkgNames.map(async (pkgName) => await PkgExecutor.from(exec, pkgName).scan()))
|
|
2202
2171
|
]);
|
|
2203
2172
|
const workspaceInfo = new WorkspaceInfo(new Map(appInfos.map((app) => [app.exec.name, app])), new Map(libInfos.map((lib) => [lib.exec.name, lib])), new Map(pkgInfos.map((pkg) => [pkg.exec.name, pkg])));
|
|
2204
2173
|
WorkspaceInfo.#workspaceInfos.set(exec.name, workspaceInfo);
|
|
@@ -2207,40 +2176,9 @@ class WorkspaceInfo {
|
|
|
2207
2176
|
}
|
|
2208
2177
|
|
|
2209
2178
|
// pkgs/@akanjs/devkit/executors.ts
|
|
2210
|
-
var staticTemplateFileExtensions = new Set(
|
|
2211
|
-
|
|
2212
|
-
|
|
2213
|
-
".cjs",
|
|
2214
|
-
".css",
|
|
2215
|
-
".eot",
|
|
2216
|
-
".gif",
|
|
2217
|
-
".html",
|
|
2218
|
-
".ico",
|
|
2219
|
-
".jpeg",
|
|
2220
|
-
".jpg",
|
|
2221
|
-
".js",
|
|
2222
|
-
".json",
|
|
2223
|
-
".map",
|
|
2224
|
-
".md",
|
|
2225
|
-
".mjs",
|
|
2226
|
-
".mp3",
|
|
2227
|
-
".mp4",
|
|
2228
|
-
".ogg",
|
|
2229
|
-
".otf",
|
|
2230
|
-
".pdf",
|
|
2231
|
-
".png",
|
|
2232
|
-
".svg",
|
|
2233
|
-
".ttf",
|
|
2234
|
-
".txt",
|
|
2235
|
-
".wasm",
|
|
2236
|
-
".wav",
|
|
2237
|
-
".webm",
|
|
2238
|
-
".webp",
|
|
2239
|
-
".woff",
|
|
2240
|
-
".woff2",
|
|
2241
|
-
".xml"
|
|
2242
|
-
]);
|
|
2243
|
-
var formatCommandArg = (value) => /^[\w@%+=:,./-]+$/.test(value) ? value : JSON.stringify(value);
|
|
2179
|
+
var staticTemplateFileExtensions = new Set(".avif .bmp .cjs .css .eot .gif .html .ico .jpeg .jpg .js .json .map .md .mjs .mp3 .mp4 .ogg .otf .pdf .png .svg".split(" ").concat(".ttf .txt .wasm .wav .webm .webp .woff .woff2 .xml".split(" ")));
|
|
2180
|
+
var plainCommandArgPattern = process.platform === "win32" ? /^[\w@%+=:,./\\-]+$/ : /^[\w@%+=:,./-]+$/;
|
|
2181
|
+
var formatCommandArg = (value) => plainCommandArgPattern.test(value) ? value : JSON.stringify(value);
|
|
2244
2182
|
var formatCommandForDisplay = (command, args = []) => [command, ...args].map(formatCommandArg).join(" ");
|
|
2245
2183
|
|
|
2246
2184
|
class CommandExecutionError extends Error {
|
|
@@ -2314,6 +2252,10 @@ var parseEnvFile = (envPath) => {
|
|
|
2314
2252
|
}
|
|
2315
2253
|
return env;
|
|
2316
2254
|
};
|
|
2255
|
+
var withCapitalizedDict = (dict = {}) => ({
|
|
2256
|
+
...dict,
|
|
2257
|
+
...Object.fromEntries(Object.entries(dict).map(([key, value]) => [capitalize(key), capitalize(value)]))
|
|
2258
|
+
});
|
|
2317
2259
|
|
|
2318
2260
|
class Executor {
|
|
2319
2261
|
static verbose = false;
|
|
@@ -2340,150 +2282,57 @@ class Executor {
|
|
|
2340
2282
|
#stderr(data) {
|
|
2341
2283
|
Logger2.raw(chalk2.red(data.toString()));
|
|
2342
2284
|
}
|
|
2343
|
-
|
|
2344
|
-
const cwd = options.cwd?.toString() ?? this.cwdPath;
|
|
2345
|
-
const proc = exec(command, { cwd: this.cwdPath, ...options });
|
|
2285
|
+
#settle(proc, target, { onClose = false, keepLogs = false, redStderr = false } = {}) {
|
|
2346
2286
|
let stdout = "";
|
|
2347
2287
|
let stderr = "";
|
|
2348
2288
|
proc.stdout?.on("data", (data) => {
|
|
2349
2289
|
stdout += data.toString();
|
|
2290
|
+
if (keepLogs)
|
|
2291
|
+
this.logs.push(data.toString());
|
|
2350
2292
|
this.#stdout(data);
|
|
2351
2293
|
});
|
|
2352
2294
|
proc.stderr?.on("data", (data) => {
|
|
2353
2295
|
stderr += data.toString();
|
|
2354
|
-
|
|
2296
|
+
if (keepLogs)
|
|
2297
|
+
this.logs.push(data.toString());
|
|
2298
|
+
if (redStderr)
|
|
2299
|
+
this.#stderr(data);
|
|
2300
|
+
else
|
|
2301
|
+
this.#stdout(data);
|
|
2355
2302
|
});
|
|
2356
2303
|
return new Promise((resolve, reject) => {
|
|
2357
|
-
proc.on("error", (
|
|
2358
|
-
|
|
2359
|
-
|
|
2360
|
-
|
|
2361
|
-
code: null,
|
|
2362
|
-
signal: null,
|
|
2363
|
-
stdout,
|
|
2364
|
-
stderr,
|
|
2365
|
-
cause: error
|
|
2366
|
-
}));
|
|
2367
|
-
});
|
|
2368
|
-
proc.on("exit", (code, signal) => {
|
|
2369
|
-
if (code || signal)
|
|
2370
|
-
reject(new CommandExecutionError({
|
|
2371
|
-
command,
|
|
2372
|
-
cwd,
|
|
2373
|
-
code,
|
|
2374
|
-
signal,
|
|
2375
|
-
stdout,
|
|
2376
|
-
stderr
|
|
2377
|
-
}));
|
|
2304
|
+
proc.on("error", (cause) => reject(new CommandExecutionError({ ...target, code: null, signal: null, stdout, stderr, cause })));
|
|
2305
|
+
proc.on(onClose ? "close" : "exit", (code, signal) => {
|
|
2306
|
+
if ((onClose ? code !== 0 : code) || signal)
|
|
2307
|
+
reject(new CommandExecutionError({ ...target, code, signal, stdout, stderr }));
|
|
2378
2308
|
else
|
|
2379
|
-
resolve({ code, signal });
|
|
2309
|
+
resolve({ code, signal, stdout });
|
|
2380
2310
|
});
|
|
2381
2311
|
});
|
|
2382
2312
|
}
|
|
2313
|
+
exec(command, options = {}) {
|
|
2314
|
+
const target = { command, cwd: options.cwd?.toString() ?? this.cwdPath };
|
|
2315
|
+
const proc = exec(command, { cwd: this.cwdPath, ...options });
|
|
2316
|
+
return this.#settle(proc, target).then(({ code, signal }) => ({ code, signal }));
|
|
2317
|
+
}
|
|
2383
2318
|
spawn(command, args = [], options = {}) {
|
|
2384
|
-
const
|
|
2385
|
-
const proc = spawn2(command, args, {
|
|
2386
|
-
|
|
2387
|
-
...options
|
|
2388
|
-
});
|
|
2389
|
-
let stdout = "";
|
|
2390
|
-
let stderr = "";
|
|
2391
|
-
proc.stdout?.on("data", (data) => {
|
|
2392
|
-
stdout += data.toString();
|
|
2393
|
-
this.logs.push(data.toString());
|
|
2394
|
-
this.#stdout(data);
|
|
2395
|
-
});
|
|
2396
|
-
proc.stderr?.on("data", (data) => {
|
|
2397
|
-
stderr += data.toString();
|
|
2398
|
-
this.logs.push(data.toString());
|
|
2399
|
-
this.#stdout(data);
|
|
2400
|
-
});
|
|
2401
|
-
return new Promise((resolve, reject) => {
|
|
2402
|
-
proc.on("error", (error) => {
|
|
2403
|
-
reject(new CommandExecutionError({
|
|
2404
|
-
command,
|
|
2405
|
-
args,
|
|
2406
|
-
cwd,
|
|
2407
|
-
code: null,
|
|
2408
|
-
signal: null,
|
|
2409
|
-
stdout,
|
|
2410
|
-
stderr,
|
|
2411
|
-
cause: error
|
|
2412
|
-
}));
|
|
2413
|
-
});
|
|
2414
|
-
proc.on("close", (code, signal) => {
|
|
2415
|
-
if (code !== 0 || signal)
|
|
2416
|
-
reject(new CommandExecutionError({
|
|
2417
|
-
command,
|
|
2418
|
-
args,
|
|
2419
|
-
cwd,
|
|
2420
|
-
code,
|
|
2421
|
-
signal,
|
|
2422
|
-
stdout,
|
|
2423
|
-
stderr
|
|
2424
|
-
}));
|
|
2425
|
-
else
|
|
2426
|
-
resolve(stdout);
|
|
2427
|
-
});
|
|
2428
|
-
});
|
|
2319
|
+
const target = { command, args, cwd: options.cwd?.toString() ?? this.cwdPath };
|
|
2320
|
+
const proc = spawn2(command, args, { cwd: this.cwdPath, ...options });
|
|
2321
|
+
return this.#settle(proc, target, { onClose: true, keepLogs: true }).then(({ stdout }) => stdout);
|
|
2429
2322
|
}
|
|
2430
2323
|
spawnSync(command, args = [], options = {}) {
|
|
2431
|
-
|
|
2432
|
-
cwd: this.cwdPath,
|
|
2433
|
-
...options
|
|
2434
|
-
});
|
|
2435
|
-
return proc;
|
|
2324
|
+
return spawn2(command, args, { cwd: this.cwdPath, ...options });
|
|
2436
2325
|
}
|
|
2437
2326
|
fork(modulePath, args = [], options = {}) {
|
|
2438
|
-
const
|
|
2439
|
-
const proc = fork(modulePath, args, {
|
|
2440
|
-
|
|
2441
|
-
...options
|
|
2442
|
-
});
|
|
2443
|
-
let stdout = "";
|
|
2444
|
-
let stderr = "";
|
|
2445
|
-
proc.stdout?.on("data", (data) => {
|
|
2446
|
-
stdout += data.toString();
|
|
2447
|
-
this.#stdout(data);
|
|
2448
|
-
});
|
|
2449
|
-
proc.stderr?.on("data", (data) => {
|
|
2450
|
-
stderr += data.toString();
|
|
2451
|
-
this.#stderr(data);
|
|
2452
|
-
});
|
|
2453
|
-
return new Promise((resolve, reject) => {
|
|
2454
|
-
proc.on("error", (error) => {
|
|
2455
|
-
reject(new CommandExecutionError({
|
|
2456
|
-
command: modulePath,
|
|
2457
|
-
args,
|
|
2458
|
-
cwd,
|
|
2459
|
-
code: null,
|
|
2460
|
-
signal: null,
|
|
2461
|
-
stdout,
|
|
2462
|
-
stderr,
|
|
2463
|
-
cause: error
|
|
2464
|
-
}));
|
|
2465
|
-
});
|
|
2466
|
-
proc.on("exit", (code, signal) => {
|
|
2467
|
-
if (code || signal)
|
|
2468
|
-
reject(new CommandExecutionError({
|
|
2469
|
-
command: modulePath,
|
|
2470
|
-
args,
|
|
2471
|
-
cwd,
|
|
2472
|
-
code,
|
|
2473
|
-
signal,
|
|
2474
|
-
stdout,
|
|
2475
|
-
stderr
|
|
2476
|
-
}));
|
|
2477
|
-
else
|
|
2478
|
-
resolve({ code, signal });
|
|
2479
|
-
});
|
|
2480
|
-
});
|
|
2327
|
+
const target = { command: modulePath, args, cwd: options.cwd?.toString() ?? this.cwdPath };
|
|
2328
|
+
const proc = fork(modulePath, args, { cwd: this.cwdPath, ...options });
|
|
2329
|
+
return this.#settle(proc, target, { redStderr: true }).then(({ code, signal }) => ({ code, signal }));
|
|
2481
2330
|
}
|
|
2482
2331
|
getPath(filePath) {
|
|
2483
|
-
if (
|
|
2332
|
+
if (path9.isAbsolute(filePath))
|
|
2484
2333
|
return filePath;
|
|
2485
2334
|
if (filePath.startsWith("."))
|
|
2486
|
-
return
|
|
2335
|
+
return path9.join(this.cwdPath, filePath);
|
|
2487
2336
|
const baseParts = this.cwdPath.split(/[\\/]/).filter(Boolean);
|
|
2488
2337
|
const targetParts = filePath.split(/[\\/]/).filter(Boolean);
|
|
2489
2338
|
let overlapLength = 0;
|
|
@@ -2497,12 +2346,12 @@ class Executor {
|
|
|
2497
2346
|
if (isOverlap)
|
|
2498
2347
|
overlapLength = i;
|
|
2499
2348
|
}
|
|
2500
|
-
return
|
|
2349
|
+
return path9.join(this.cwdPath, ...targetParts.slice(overlapLength));
|
|
2501
2350
|
}
|
|
2502
2351
|
async mkdir(dirPath) {
|
|
2503
2352
|
const writePath = this.getPath(dirPath);
|
|
2504
|
-
if (!await
|
|
2505
|
-
await
|
|
2353
|
+
if (!await FileSys2.dirExists(writePath))
|
|
2354
|
+
await mkdir2(writePath, { recursive: true });
|
|
2506
2355
|
this.logger.verbose(`Make directory ${writePath}`);
|
|
2507
2356
|
return this;
|
|
2508
2357
|
}
|
|
@@ -2521,50 +2370,48 @@ class Executor {
|
|
|
2521
2370
|
}
|
|
2522
2371
|
async getFilesAndDirs(dirPath) {
|
|
2523
2372
|
const fullDirPath = this.getPath(dirPath);
|
|
2524
|
-
const
|
|
2525
|
-
const files = Array.from(
|
|
2526
|
-
const
|
|
2527
|
-
const allEntries = Array.from(dirGlob.scanSync({ cwd: fullDirPath, onlyFiles: false }));
|
|
2373
|
+
const glob = new Bun.Glob("*");
|
|
2374
|
+
const files = Array.from(glob.scanSync({ cwd: fullDirPath, onlyFiles: true }));
|
|
2375
|
+
const allEntries = Array.from(glob.scanSync({ cwd: fullDirPath, onlyFiles: false }));
|
|
2528
2376
|
const dirs = allEntries.filter((entry) => !files.includes(entry));
|
|
2529
2377
|
return { files, dirs };
|
|
2530
2378
|
}
|
|
2531
2379
|
async exists(filePath) {
|
|
2532
|
-
|
|
2533
|
-
return await FileSys.exists(readPath);
|
|
2380
|
+
return await FileSys2.exists(this.getPath(filePath));
|
|
2534
2381
|
}
|
|
2535
2382
|
async remove(filePath) {
|
|
2536
2383
|
const readPath = this.getPath(filePath);
|
|
2537
|
-
if (await
|
|
2538
|
-
await
|
|
2384
|
+
if (await FileSys2.fileExists(readPath))
|
|
2385
|
+
await FileSys2.delete(readPath);
|
|
2539
2386
|
this.logger.verbose(`Remove file ${readPath}`);
|
|
2540
2387
|
return this;
|
|
2541
2388
|
}
|
|
2542
2389
|
async removeDir(dirPath) {
|
|
2543
|
-
const readPath =
|
|
2544
|
-
if (await
|
|
2390
|
+
const readPath = path9.join(this.getPath(dirPath), ".");
|
|
2391
|
+
if (await FileSys2.entryExists(readPath))
|
|
2545
2392
|
await rm3(readPath, { recursive: true, force: true });
|
|
2546
2393
|
this.logger.verbose(`Remove directory ${readPath}`);
|
|
2547
2394
|
return this;
|
|
2548
2395
|
}
|
|
2549
2396
|
async writeFile(filePath, content, { overwrite = true, silent = false } = {}) {
|
|
2550
2397
|
const writePath = this.getPath(filePath);
|
|
2551
|
-
const dir =
|
|
2552
|
-
if (!await
|
|
2553
|
-
await
|
|
2398
|
+
const dir = path9.dirname(writePath);
|
|
2399
|
+
if (!await FileSys2.dirExists(dir))
|
|
2400
|
+
await mkdir2(dir, { recursive: true });
|
|
2554
2401
|
let contentStr = typeof content === "string" ? content : `${JSON.stringify(content, null, 2)}
|
|
2555
2402
|
`;
|
|
2556
|
-
if (await
|
|
2557
|
-
const currentContent = await
|
|
2403
|
+
if (await FileSys2.fileExists(writePath)) {
|
|
2404
|
+
const currentContent = await FileSys2.readText(writePath);
|
|
2558
2405
|
if (currentContent === contentStr || !overwrite) {
|
|
2559
2406
|
this.logger.verbose(`File ${writePath} is unchanged`);
|
|
2560
2407
|
contentStr = currentContent;
|
|
2561
2408
|
} else {
|
|
2562
|
-
await
|
|
2409
|
+
await FileSys2.writeText(writePath, contentStr);
|
|
2563
2410
|
if (Logger2.isVerbose())
|
|
2564
2411
|
this.logger.rawLog(chalk2.yellow(`File Update: ${filePath}`));
|
|
2565
2412
|
}
|
|
2566
2413
|
} else {
|
|
2567
|
-
await
|
|
2414
|
+
await FileSys2.writeText(writePath, contentStr);
|
|
2568
2415
|
if (!silent)
|
|
2569
2416
|
this.logger.rawLog(chalk2.green(`File Create: ${filePath}`));
|
|
2570
2417
|
}
|
|
@@ -2574,27 +2421,25 @@ class Executor {
|
|
|
2574
2421
|
await this.writeFile(filePath, content);
|
|
2575
2422
|
}
|
|
2576
2423
|
async getLocalFile(targetPath) {
|
|
2577
|
-
const filePath =
|
|
2424
|
+
const filePath = path9.isAbsolute(targetPath) ? targetPath : targetPath.replace(this.cwdPath, "");
|
|
2578
2425
|
const content = await this.readFile(filePath);
|
|
2579
2426
|
return { filePath, content };
|
|
2580
2427
|
}
|
|
2581
2428
|
async readFile(filePath) {
|
|
2582
|
-
|
|
2583
|
-
return await FileSys.readText(readPath);
|
|
2429
|
+
return await FileSys2.readText(this.getPath(filePath));
|
|
2584
2430
|
}
|
|
2585
2431
|
async readJson(filePath) {
|
|
2586
|
-
|
|
2587
|
-
return await FileSys.readJson(readPath);
|
|
2432
|
+
return await FileSys2.readJson(this.getPath(filePath));
|
|
2588
2433
|
}
|
|
2589
2434
|
async cp(srcPath, destPath, { dereference = false } = {}) {
|
|
2590
2435
|
const src = this.getPath(srcPath);
|
|
2591
2436
|
const dest = this.getPath(destPath);
|
|
2592
|
-
if (!await
|
|
2437
|
+
if (!await FileSys2.exists(src))
|
|
2593
2438
|
return;
|
|
2594
2439
|
const isDirectory = (await stat2(src)).isDirectory();
|
|
2595
|
-
if (!await
|
|
2596
|
-
await
|
|
2597
|
-
if (dereference)
|
|
2440
|
+
if (!await FileSys2.exists(dest) && isDirectory)
|
|
2441
|
+
await mkdir2(dest, { recursive: true });
|
|
2442
|
+
if (dereference || process.platform === "win32")
|
|
2598
2443
|
await cpEntry(src, dest, { recursive: isDirectory, dereference: true, force: true });
|
|
2599
2444
|
else
|
|
2600
2445
|
await $`cp -r ${src}${isDirectory ? "/." : ""} ${dest}`;
|
|
@@ -2618,19 +2463,10 @@ class Executor {
|
|
|
2618
2463
|
async getTsConfig(pathname = "tsconfig.json", { refresh } = {}) {
|
|
2619
2464
|
if (this.#tsconfig && !refresh)
|
|
2620
2465
|
return this.#tsconfig;
|
|
2621
|
-
|
|
2466
|
+
let tsconfig = await this.readJson(pathname);
|
|
2622
2467
|
if (tsconfig.extends) {
|
|
2623
|
-
const
|
|
2624
|
-
|
|
2625
|
-
...extendsTsconfig,
|
|
2626
|
-
...tsconfig,
|
|
2627
|
-
compilerOptions: {
|
|
2628
|
-
...extendsTsconfig.compilerOptions,
|
|
2629
|
-
...tsconfig.compilerOptions
|
|
2630
|
-
}
|
|
2631
|
-
};
|
|
2632
|
-
this.#tsconfig = result;
|
|
2633
|
-
return result;
|
|
2468
|
+
const base = await this.getTsConfig(tsconfig.extends, { refresh });
|
|
2469
|
+
tsconfig = { ...base, ...tsconfig, compilerOptions: { ...base.compilerOptions, ...tsconfig.compilerOptions } };
|
|
2634
2470
|
}
|
|
2635
2471
|
this.#tsconfig = tsconfig;
|
|
2636
2472
|
return tsconfig;
|
|
@@ -2666,31 +2502,29 @@ class Executor {
|
|
|
2666
2502
|
scanInfo,
|
|
2667
2503
|
overwrite = true
|
|
2668
2504
|
}, dict = {}, options = {}) {
|
|
2505
|
+
const convertPath = (target) => Object.entries(dict).reduce((converted, [key, value]) => converted.replace(new RegExp(`__${key}__`, "g"), value), target);
|
|
2669
2506
|
if (targetPath.endsWith(".ts") || targetPath.endsWith(".tsx")) {
|
|
2670
2507
|
const getContent = await import(templatePath);
|
|
2671
2508
|
const result = await getContent.default(scanInfo ?? null, dict, options);
|
|
2672
2509
|
if (result === null)
|
|
2673
2510
|
return null;
|
|
2674
|
-
const filename = typeof result === "object" ? result.filename :
|
|
2511
|
+
const filename = typeof result === "object" ? result.filename : path9.basename(targetPath).replace(".js", ".ts");
|
|
2675
2512
|
const content = typeof result === "object" ? result.content : result;
|
|
2676
|
-
const
|
|
2677
|
-
const convertedTargetPath = Object.entries(dict).reduce((path, [key, value]) => path.replace(new RegExp(`__${key}__`, "g"), value), `${dirname}/${filename}`);
|
|
2513
|
+
const convertedTargetPath = convertPath(`${path9.dirname(targetPath)}/${filename}`);
|
|
2678
2514
|
this.logger.verbose(`Apply template ${templatePath} to ${convertedTargetPath}`);
|
|
2679
2515
|
return this.writeFile(convertedTargetPath, content, { overwrite });
|
|
2680
2516
|
} else if (targetPath.endsWith(".template")) {
|
|
2681
|
-
const content = await
|
|
2682
|
-
const convertedTargetPath =
|
|
2517
|
+
const content = await FileSys2.readText(templatePath);
|
|
2518
|
+
const convertedTargetPath = convertPath(targetPath.slice(0, -9));
|
|
2683
2519
|
const convertedContent = Object.entries(dict).reduce((data, [key, value]) => data.replace(new RegExp(`<%= ${key} %>`, "g"), value), content);
|
|
2684
2520
|
this.logger.verbose(`Apply template ${templatePath} to ${convertedTargetPath}`);
|
|
2685
|
-
return this.writeFile(convertedTargetPath, convertedContent, {
|
|
2686
|
-
|
|
2687
|
-
|
|
2688
|
-
} else if (staticTemplateFileExtensions.has(path8.extname(targetPath).toLowerCase())) {
|
|
2689
|
-
const convertedTargetPath = Object.entries(dict).reduce((path, [key, value]) => path.replace(new RegExp(`__${key}__`, "g"), value), targetPath);
|
|
2521
|
+
return this.writeFile(convertedTargetPath, convertedContent, { overwrite });
|
|
2522
|
+
} else if (staticTemplateFileExtensions.has(path9.extname(targetPath).toLowerCase())) {
|
|
2523
|
+
const convertedTargetPath = convertPath(targetPath);
|
|
2690
2524
|
const writePath = this.getPath(convertedTargetPath);
|
|
2691
|
-
const dirname =
|
|
2692
|
-
if (!await
|
|
2693
|
-
await
|
|
2525
|
+
const dirname = path9.dirname(writePath);
|
|
2526
|
+
if (!await FileSys2.dirExists(dirname))
|
|
2527
|
+
await mkdir2(dirname, { recursive: true });
|
|
2694
2528
|
await copyFile(templatePath, writePath);
|
|
2695
2529
|
this.logger.verbose(`Apply template ${templatePath} to ${convertedTargetPath}`);
|
|
2696
2530
|
return { filePath: writePath, content: "" };
|
|
@@ -2707,12 +2541,10 @@ class Executor {
|
|
|
2707
2541
|
}) {
|
|
2708
2542
|
const templateRoot = await this.#resolveTemplateRoot();
|
|
2709
2543
|
const templatePath = `${templateRoot}${template ? `/${template}` : ""}`;
|
|
2710
|
-
|
|
2711
|
-
if ((await stat2(prefixTemplatePath)).isFile()) {
|
|
2712
|
-
const filename = path8.basename(prefixTemplatePath);
|
|
2544
|
+
if ((await stat2(templatePath)).isFile()) {
|
|
2713
2545
|
const fileContent = await this.#applyTemplateFile({
|
|
2714
|
-
templatePath
|
|
2715
|
-
targetPath:
|
|
2546
|
+
templatePath,
|
|
2547
|
+
targetPath: path9.join(basePath, path9.basename(templatePath)),
|
|
2716
2548
|
scanInfo,
|
|
2717
2549
|
overwrite
|
|
2718
2550
|
}, dict, options);
|
|
@@ -2720,19 +2552,19 @@ class Executor {
|
|
|
2720
2552
|
} else {
|
|
2721
2553
|
const subdirs = await readDirEntries(templatePath);
|
|
2722
2554
|
const fileContents = (await Promise.all(subdirs.map(async (subdir) => {
|
|
2723
|
-
const subpath =
|
|
2555
|
+
const subpath = path9.join(templatePath, subdir);
|
|
2724
2556
|
if ((await stat2(subpath)).isFile()) {
|
|
2725
2557
|
const fileContent = await this.#applyTemplateFile({
|
|
2726
2558
|
templatePath: subpath,
|
|
2727
|
-
targetPath:
|
|
2559
|
+
targetPath: path9.join(basePath, subdir),
|
|
2728
2560
|
scanInfo,
|
|
2729
2561
|
overwrite
|
|
2730
2562
|
}, dict, options);
|
|
2731
2563
|
return fileContent ? [fileContent] : [];
|
|
2732
2564
|
} else
|
|
2733
2565
|
return await this._applyTemplate({
|
|
2734
|
-
basePath:
|
|
2735
|
-
template:
|
|
2566
|
+
basePath: path9.join(basePath, subdir),
|
|
2567
|
+
template: path9.join(template, subdir),
|
|
2736
2568
|
scanInfo,
|
|
2737
2569
|
dict,
|
|
2738
2570
|
overwrite,
|
|
@@ -2744,21 +2576,17 @@ class Executor {
|
|
|
2744
2576
|
}
|
|
2745
2577
|
async#resolveTemplateRoot() {
|
|
2746
2578
|
const candidates = [
|
|
2747
|
-
|
|
2748
|
-
|
|
2579
|
+
path9.resolve(getDirname2(import.meta.url), "templates"),
|
|
2580
|
+
path9.resolve(getDirname2(import.meta.url), "../cli/templates")
|
|
2749
2581
|
];
|
|
2750
2582
|
for (const candidate of candidates) {
|
|
2751
|
-
if (await
|
|
2583
|
+
if (await FileSys2.dirExists(candidate))
|
|
2752
2584
|
return candidate;
|
|
2753
2585
|
}
|
|
2754
2586
|
return candidates[0];
|
|
2755
2587
|
}
|
|
2756
2588
|
async applyTemplate(options) {
|
|
2757
|
-
const
|
|
2758
|
-
...options.dict ?? {},
|
|
2759
|
-
...Object.fromEntries(Object.entries(options.dict ?? {}).map(([key, value]) => [capitalize(key), capitalize(value)]))
|
|
2760
|
-
};
|
|
2761
|
-
const fileContents = await this._applyTemplate({ ...options, dict });
|
|
2589
|
+
const fileContents = await this._applyTemplate({ ...options, dict: withCapitalizedDict(options.dict) });
|
|
2762
2590
|
await this.#formatAppliedTemplate(fileContents);
|
|
2763
2591
|
return fileContents;
|
|
2764
2592
|
}
|
|
@@ -2775,7 +2603,7 @@ class Executor {
|
|
|
2775
2603
|
}
|
|
2776
2604
|
}
|
|
2777
2605
|
async getTypeChecker() {
|
|
2778
|
-
const { TypeChecker } = await import("./typeChecker-
|
|
2606
|
+
const { TypeChecker } = await import("./typeChecker-px49mezc.js");
|
|
2779
2607
|
this.typeChecker ??= new TypeChecker(this);
|
|
2780
2608
|
return this.typeChecker;
|
|
2781
2609
|
}
|
|
@@ -2817,11 +2645,11 @@ class Executor {
|
|
|
2817
2645
|
async#resolveTypecheckWorkerEntry() {
|
|
2818
2646
|
const dirname = getDirname2(import.meta.url);
|
|
2819
2647
|
const candidates = [
|
|
2820
|
-
|
|
2821
|
-
|
|
2822
|
-
|
|
2823
|
-
|
|
2824
|
-
|
|
2648
|
+
path9.join(process.cwd(), "pkgs/@akanjs/devkit/typecheck/typecheck.proc.ts"),
|
|
2649
|
+
path9.join(process.cwd(), "node_modules/@akanjs/devkit/typecheck/typecheck.proc.ts"),
|
|
2650
|
+
path9.join(dirname, "typecheck/typecheck.proc.ts"),
|
|
2651
|
+
path9.join(dirname, "typecheck.proc.js"),
|
|
2652
|
+
path9.join(dirname, "typecheck.proc.ts")
|
|
2825
2653
|
];
|
|
2826
2654
|
for (const candidate of candidates)
|
|
2827
2655
|
if (await Bun.file(candidate).exists())
|
|
@@ -2853,12 +2681,11 @@ class WorkspaceExecutor extends Executor {
|
|
|
2853
2681
|
this.workspaceRoot = workspaceRoot;
|
|
2854
2682
|
this.repoName = repoName;
|
|
2855
2683
|
}
|
|
2856
|
-
static #execs = new Map;
|
|
2857
2684
|
static fromRoot({
|
|
2858
2685
|
workspaceRoot = process.cwd(),
|
|
2859
2686
|
repoName = resolveRepoName(workspaceRoot)
|
|
2860
2687
|
} = {}) {
|
|
2861
|
-
return
|
|
2688
|
+
return new WorkspaceExecutor({ workspaceRoot, repoName });
|
|
2862
2689
|
}
|
|
2863
2690
|
static getBaseDevEnv(envPath) {
|
|
2864
2691
|
const sourceEnv = envPath ? { ...process.env, ...parseEnvFile(envPath) } : process.env;
|
|
@@ -2897,13 +2724,9 @@ class WorkspaceExecutor extends Executor {
|
|
|
2897
2724
|
return await WorkspaceInfo.fromExecutor(this);
|
|
2898
2725
|
}
|
|
2899
2726
|
async getApps() {
|
|
2900
|
-
if (!await FileSys.dirExists(`${this.workspaceRoot}/apps`))
|
|
2901
|
-
return [];
|
|
2902
2727
|
return await this.#getDirHasFile(`${this.workspaceRoot}/apps`, "akan.config.ts");
|
|
2903
2728
|
}
|
|
2904
2729
|
async getLibs() {
|
|
2905
|
-
if (!await FileSys.dirExists(`${this.workspaceRoot}/libs`))
|
|
2906
|
-
return [];
|
|
2907
2730
|
return await this.#getDirHasFile(`${this.workspaceRoot}/libs`, "akan.config.ts");
|
|
2908
2731
|
}
|
|
2909
2732
|
async getSyss() {
|
|
@@ -2911,8 +2734,6 @@ class WorkspaceExecutor extends Executor {
|
|
|
2911
2734
|
return [appNames, libNames];
|
|
2912
2735
|
}
|
|
2913
2736
|
async getPkgs() {
|
|
2914
|
-
if (!await FileSys.dirExists(`${this.workspaceRoot}/pkgs`))
|
|
2915
|
-
return [];
|
|
2916
2737
|
return await this.#getDirHasFile(`${this.workspaceRoot}/pkgs`, "package.json");
|
|
2917
2738
|
}
|
|
2918
2739
|
async getExecs() {
|
|
@@ -2948,7 +2769,7 @@ class WorkspaceExecutor extends Executor {
|
|
|
2948
2769
|
await Promise.all(dirs.map(async (dir) => {
|
|
2949
2770
|
if (dir.includes("_") || AVOID_DIRS.includes(dir))
|
|
2950
2771
|
return;
|
|
2951
|
-
const dirPath =
|
|
2772
|
+
const dirPath = path9.join(dirname, dir);
|
|
2952
2773
|
if ((await stat2(dirPath)).isDirectory()) {
|
|
2953
2774
|
results.push(`${prefix}${dir}`);
|
|
2954
2775
|
if (maxDepth > 0)
|
|
@@ -2977,15 +2798,17 @@ class WorkspaceExecutor extends Executor {
|
|
|
2977
2798
|
return stdout.split("\x00").filter((file) => !!file).sort();
|
|
2978
2799
|
}
|
|
2979
2800
|
async#getDirHasFile(basePath, targetFilename) {
|
|
2980
|
-
|
|
2801
|
+
if (!await FileSys2.dirExists(basePath))
|
|
2802
|
+
return [];
|
|
2803
|
+
const AVOID_DIRS = ["node_modules", "dist", "public", "webkit", "local"];
|
|
2981
2804
|
const getDirs = async (dirname, maxDepth = 3, results = [], prefix = "") => {
|
|
2982
2805
|
const dirs = await this.readdir(dirname);
|
|
2983
2806
|
await Promise.all(dirs.map(async (dir) => {
|
|
2984
2807
|
if (AVOID_DIRS.includes(dir))
|
|
2985
2808
|
return;
|
|
2986
|
-
const dirPath =
|
|
2987
|
-
if (await
|
|
2988
|
-
const hasTargetFile = await
|
|
2809
|
+
const dirPath = path9.join(dirname, dir);
|
|
2810
|
+
if (await FileSys2.dirExists(dirPath)) {
|
|
2811
|
+
const hasTargetFile = await FileSys2.fileExists(path9.join(dirPath, targetFilename));
|
|
2989
2812
|
if (hasTargetFile)
|
|
2990
2813
|
results.push(`${prefix}${dir}`);
|
|
2991
2814
|
if (maxDepth > 0)
|
|
@@ -2994,39 +2817,25 @@ class WorkspaceExecutor extends Executor {
|
|
|
2994
2817
|
}));
|
|
2995
2818
|
return results;
|
|
2996
2819
|
};
|
|
2997
|
-
return await getDirs(basePath);
|
|
2820
|
+
return (await getDirs(basePath)).sort((a, b) => a.localeCompare(b));
|
|
2998
2821
|
}
|
|
2999
|
-
async
|
|
2822
|
+
async#fromEverySys(read) {
|
|
3000
2823
|
const [appNames, libNames] = await this.getSyss();
|
|
3001
|
-
const
|
|
3002
|
-
|
|
3003
|
-
|
|
3004
|
-
|
|
3005
|
-
|
|
2824
|
+
const fromApps = await Promise.all(appNames.map((appName) => read(AppExecutor.from(this, appName))));
|
|
2825
|
+
const fromLibs = await Promise.all(libNames.map((libName) => read(LibExecutor.from(this, libName))));
|
|
2826
|
+
return [...fromApps.flat(), ...fromLibs.flat()];
|
|
2827
|
+
}
|
|
2828
|
+
async getScalarConstantFiles() {
|
|
2829
|
+
return await this.#fromEverySys((sys) => sys.getScalarConstantFiles());
|
|
3006
2830
|
}
|
|
3007
2831
|
async getConstantFiles() {
|
|
3008
|
-
|
|
3009
|
-
const moduleConstantExampleFiles = [
|
|
3010
|
-
...(await Promise.all(appNames.map((appName) => AppExecutor.from(this, appName).getConstantFiles()))).flat(),
|
|
3011
|
-
...(await Promise.all(libNames.map((libName) => LibExecutor.from(this, libName).getConstantFiles()))).flat()
|
|
3012
|
-
];
|
|
3013
|
-
return moduleConstantExampleFiles;
|
|
2832
|
+
return await this.#fromEverySys((sys) => sys.getConstantFiles());
|
|
3014
2833
|
}
|
|
3015
2834
|
async getDictionaryFiles() {
|
|
3016
|
-
|
|
3017
|
-
const moduleDictionaryExampleFiles = [
|
|
3018
|
-
...(await Promise.all(appNames.map((appName) => AppExecutor.from(this, appName).getDictionaryFiles()))).flat(),
|
|
3019
|
-
...(await Promise.all(libNames.map((libName) => LibExecutor.from(this, libName).getDictionaryFiles()))).flat()
|
|
3020
|
-
];
|
|
3021
|
-
return moduleDictionaryExampleFiles;
|
|
2835
|
+
return await this.#fromEverySys((sys) => sys.getDictionaryFiles());
|
|
3022
2836
|
}
|
|
3023
2837
|
async getViewFiles() {
|
|
3024
|
-
|
|
3025
|
-
const viewExampleFiles = [
|
|
3026
|
-
...(await Promise.all(appNames.map((appName) => AppExecutor.from(this, appName).getViewsSourceCode()))).flat(),
|
|
3027
|
-
...(await Promise.all(libNames.map((libName) => LibExecutor.from(this, libName).getViewsSourceCode()))).flat()
|
|
3028
|
-
];
|
|
3029
|
-
return viewExampleFiles;
|
|
2838
|
+
return await this.#fromEverySys((sys) => sys.getViewsSourceCode());
|
|
3030
2839
|
}
|
|
3031
2840
|
}
|
|
3032
2841
|
var scanFacetDirs = ["ui", "webkit", "srvkit", "common", "plugin"];
|
|
@@ -3059,7 +2868,7 @@ class SysExecutor extends Executor {
|
|
|
3059
2868
|
return [];
|
|
3060
2869
|
const violations = [];
|
|
3061
2870
|
for (const entry of await this.getAllFiles("**/*", { cwd: this.getPath("page") })) {
|
|
3062
|
-
const relativePath = entry.split(
|
|
2871
|
+
const relativePath = entry.split(path9.sep).join("/");
|
|
3063
2872
|
const reason = getPageSourceFileViolation(relativePath);
|
|
3064
2873
|
if (reason)
|
|
3065
2874
|
violations.push({ relativePath: `page/${relativePath}`, reason });
|
|
@@ -3090,24 +2899,11 @@ class SysExecutor extends Executor {
|
|
|
3090
2899
|
scanInfo,
|
|
3091
2900
|
options: { exec: this, facet }
|
|
3092
2901
|
})),
|
|
3093
|
-
...
|
|
3094
|
-
|
|
3095
|
-
|
|
3096
|
-
scanInfo,
|
|
3097
|
-
|
|
3098
|
-
})),
|
|
3099
|
-
...scanInfo.getServiceModules().map((model) => this._applyTemplate({
|
|
3100
|
-
basePath: `lib/_${model}`,
|
|
3101
|
-
template: "moduleRoot",
|
|
3102
|
-
scanInfo,
|
|
3103
|
-
dict: { model, Model: capitalize(model) }
|
|
3104
|
-
})),
|
|
3105
|
-
...scanInfo.getScalarModules().map((model) => this._applyTemplate({
|
|
3106
|
-
basePath: `lib/__scalar/${model}`,
|
|
3107
|
-
template: "moduleRoot",
|
|
3108
|
-
scanInfo,
|
|
3109
|
-
dict: { model, Model: capitalize(model) }
|
|
3110
|
-
}))
|
|
2902
|
+
...[
|
|
2903
|
+
...scanInfo.getDatabaseModules().map((model) => [model, `lib/${model}`]),
|
|
2904
|
+
...scanInfo.getServiceModules().map((model) => [model, `lib/_${model}`]),
|
|
2905
|
+
...scanInfo.getScalarModules().map((model) => [model, `lib/__scalar/${model}`])
|
|
2906
|
+
].map(([model, basePath]) => this._applyTemplate({ basePath, template: "moduleRoot", scanInfo, dict: { model, Model: capitalize(model) } }))
|
|
3111
2907
|
];
|
|
3112
2908
|
}
|
|
3113
2909
|
async scan({
|
|
@@ -3169,45 +2965,43 @@ class SysExecutor extends Executor {
|
|
|
3169
2965
|
await this.setPackageJson(libPkgJsonWithDeps);
|
|
3170
2966
|
}
|
|
3171
2967
|
async getLocalFile(targetPath) {
|
|
3172
|
-
const filePath =
|
|
2968
|
+
const filePath = path9.isAbsolute(targetPath) ? targetPath : `${this.type}s/${this.name}/${targetPath}`;
|
|
3173
2969
|
const content = await this.workspace.readFile(filePath);
|
|
3174
2970
|
return { filePath, content };
|
|
3175
2971
|
}
|
|
3176
2972
|
async getDatabaseModules() {
|
|
3177
|
-
|
|
3178
|
-
return databaseModules;
|
|
2973
|
+
return (await this.readdir("lib")).filter((name) => !name.startsWith("_") && !name.endsWith(".ts")).filter((name) => existsSync2(`${this.cwdPath}/lib/${name}/${name}.constant.ts`));
|
|
3179
2974
|
}
|
|
3180
2975
|
async getServiceModules() {
|
|
3181
|
-
|
|
3182
|
-
return serviceModules;
|
|
2976
|
+
return (await this.readdir("lib")).filter((name) => name.startsWith("_") && !name.startsWith("__")).filter((name) => existsSync2(`${this.cwdPath}/lib/${name}/${name.slice(1)}.service.ts`));
|
|
3183
2977
|
}
|
|
3184
2978
|
async getScalarModules() {
|
|
3185
|
-
|
|
3186
|
-
|
|
2979
|
+
return (await this.readdir("lib/__scalar")).filter((name) => !name.startsWith("_")).filter((name) => existsSync2(`${this.cwdPath}/lib/__scalar/${name}/${name}.constant.ts`));
|
|
2980
|
+
}
|
|
2981
|
+
async#getComponentModules(role) {
|
|
2982
|
+
return (await this.readdir("lib")).filter((name) => !name.startsWith("_") && !name.endsWith(".ts")).filter((name) => existsSync2(`${this.cwdPath}/lib/${name}/${capitalize(name)}.${role}.tsx`));
|
|
2983
|
+
}
|
|
2984
|
+
async#getComponentSources(role) {
|
|
2985
|
+
const modules = await this.#getComponentModules(role);
|
|
2986
|
+
return Promise.all(modules.map((name) => this.getLocalFile(`lib/${name}/${capitalize(name)}.${role}.tsx`)));
|
|
3187
2987
|
}
|
|
3188
2988
|
async getViewComponents() {
|
|
3189
|
-
|
|
3190
|
-
return viewComponents;
|
|
2989
|
+
return await this.#getComponentModules("View");
|
|
3191
2990
|
}
|
|
3192
2991
|
async getUnitComponents() {
|
|
3193
|
-
|
|
3194
|
-
return unitComponents;
|
|
2992
|
+
return await this.#getComponentModules("Unit");
|
|
3195
2993
|
}
|
|
3196
2994
|
async getTemplateComponents() {
|
|
3197
|
-
|
|
3198
|
-
return templateComponents;
|
|
2995
|
+
return await this.#getComponentModules("Template");
|
|
3199
2996
|
}
|
|
3200
2997
|
async getViewsSourceCode() {
|
|
3201
|
-
|
|
3202
|
-
return Promise.all(viewComponents.map((viewComponent) => this.getLocalFile(`lib/${viewComponent}/${capitalize(viewComponent)}.View.tsx`)));
|
|
2998
|
+
return await this.#getComponentSources("View");
|
|
3203
2999
|
}
|
|
3204
3000
|
async getUnitsSourceCode() {
|
|
3205
|
-
|
|
3206
|
-
return Promise.all(unitComponents.map((unitComponent) => this.getLocalFile(`lib/${unitComponent}/${capitalize(unitComponent)}.Unit.tsx`)));
|
|
3001
|
+
return await this.#getComponentSources("Unit");
|
|
3207
3002
|
}
|
|
3208
3003
|
async getTemplatesSourceCode() {
|
|
3209
|
-
|
|
3210
|
-
return Promise.all(templateComponents.map((templateComponent) => this.getLocalFile(`lib/${templateComponent}/${capitalize(templateComponent)}.Template.tsx`)));
|
|
3004
|
+
return await this.#getComponentSources("Template");
|
|
3211
3005
|
}
|
|
3212
3006
|
async getScalarConstantFiles() {
|
|
3213
3007
|
const scalarModules = await this.getScalarModules();
|
|
@@ -3215,7 +3009,7 @@ class SysExecutor extends Executor {
|
|
|
3215
3009
|
}
|
|
3216
3010
|
async getScalarDictionaryFiles() {
|
|
3217
3011
|
const scalarModules = await this.getScalarModules();
|
|
3218
|
-
return Promise.all(scalarModules.map((scalarModule) => this.getLocalFile(`lib/${scalarModule}/${scalarModule}.dictionary.ts`)));
|
|
3012
|
+
return Promise.all(scalarModules.map((scalarModule) => this.getLocalFile(`lib/__scalar/${scalarModule}/${scalarModule}.dictionary.ts`)));
|
|
3219
3013
|
}
|
|
3220
3014
|
async getConstantFiles() {
|
|
3221
3015
|
const modules = await this.getModules();
|
|
@@ -3237,16 +3031,8 @@ class SysExecutor extends Executor {
|
|
|
3237
3031
|
return Promise.all(modules.map((module) => this.getLocalFile(`lib/${module}/${module}.dictionary.ts`)));
|
|
3238
3032
|
}
|
|
3239
3033
|
async applyTemplate(options) {
|
|
3240
|
-
const dict = {
|
|
3241
|
-
...options.dict ?? {},
|
|
3242
|
-
...Object.fromEntries(Object.entries(options.dict ?? {}).map(([key, value]) => [capitalize(key), capitalize(value)]))
|
|
3243
|
-
};
|
|
3244
3034
|
const scanInfo = await this.scan();
|
|
3245
|
-
const fileContents = await this._applyTemplate({
|
|
3246
|
-
...options,
|
|
3247
|
-
scanInfo,
|
|
3248
|
-
dict
|
|
3249
|
-
});
|
|
3035
|
+
const fileContents = await this._applyTemplate({ ...options, scanInfo, dict: withCapitalizedDict(options.dict) });
|
|
3250
3036
|
await this.scan();
|
|
3251
3037
|
return fileContents;
|
|
3252
3038
|
}
|
|
@@ -3255,7 +3041,7 @@ class SysExecutor extends Executor {
|
|
|
3255
3041
|
class AppExecutor extends SysExecutor {
|
|
3256
3042
|
static #routeSourceValidator = null;
|
|
3257
3043
|
static async#getRouteSourceValidator() {
|
|
3258
|
-
AppExecutor.#routeSourceValidator ??= (await import("./routeSourceValidator-
|
|
3044
|
+
AppExecutor.#routeSourceValidator ??= (await import("./routeSourceValidator-7ycnfz0e.js")).RouteSourceValidator;
|
|
3259
3045
|
return AppExecutor.#routeSourceValidator;
|
|
3260
3046
|
}
|
|
3261
3047
|
dist;
|
|
@@ -3264,15 +3050,20 @@ class AppExecutor extends SysExecutor {
|
|
|
3264
3050
|
super({ workspace, name, type: "app" });
|
|
3265
3051
|
this.dist = new Executor(`dist/${name}`, `${this.workspace.workspaceRoot}/dist/apps/${name}`);
|
|
3266
3052
|
}
|
|
3267
|
-
static #execs = new Map;
|
|
3268
3053
|
static from(executor, name) {
|
|
3269
|
-
|
|
3270
|
-
|
|
3271
|
-
|
|
3272
|
-
|
|
3273
|
-
|
|
3274
|
-
|
|
3275
|
-
|
|
3054
|
+
return new AppExecutor({ workspace: executor instanceof WorkspaceExecutor ? executor : executor.workspace, name });
|
|
3055
|
+
}
|
|
3056
|
+
static #keptOnStart = [path9.join(".akan", "native"), path9.join(".akan", "cache", "bin")];
|
|
3057
|
+
async#removeDevOutput(dir = ".akan") {
|
|
3058
|
+
const entries = await readDirEntries(this.getPath(dir)).catch(() => []);
|
|
3059
|
+
await Promise.all(entries.map(async (name) => {
|
|
3060
|
+
const entry = path9.join(dir, name);
|
|
3061
|
+
if (AppExecutor.#keptOnStart.includes(entry))
|
|
3062
|
+
return;
|
|
3063
|
+
if (AppExecutor.#keptOnStart.some((kept) => kept.startsWith(`${entry}${path9.sep}`)))
|
|
3064
|
+
return await this.#removeDevOutput(entry);
|
|
3065
|
+
await this.removeDir(entry);
|
|
3066
|
+
}));
|
|
3276
3067
|
}
|
|
3277
3068
|
getEnv() {
|
|
3278
3069
|
return WorkspaceExecutor.getBaseDevEnv().env;
|
|
@@ -3288,10 +3079,8 @@ class AppExecutor extends SysExecutor {
|
|
|
3288
3079
|
return basePort + appIndex + portOffset;
|
|
3289
3080
|
}
|
|
3290
3081
|
getCommandEnv(env = {}) {
|
|
3291
|
-
const
|
|
3292
|
-
const
|
|
3293
|
-
const PORT = (basePort + portOffset).toString();
|
|
3294
|
-
const AKAN_PUBLIC_SERVER_PORT = portOffset ? (8282 + portOffset).toString() : undefined;
|
|
3082
|
+
const { portOffset } = WorkspaceExecutor.getBaseDevEnv();
|
|
3083
|
+
const PORT = (8282 + portOffset).toString();
|
|
3295
3084
|
return {
|
|
3296
3085
|
...process.env,
|
|
3297
3086
|
AKAN_PUBLIC_APP_NAME: this.name,
|
|
@@ -3299,18 +3088,24 @@ class AppExecutor extends SysExecutor {
|
|
|
3299
3088
|
NODE_NO_WARNINGS: "1",
|
|
3300
3089
|
PORT,
|
|
3301
3090
|
AKAN_PUBLIC_CLIENT_PORT: PORT,
|
|
3302
|
-
...
|
|
3091
|
+
...portOffset ? { AKAN_PUBLIC_SERVER_PORT: PORT } : {},
|
|
3303
3092
|
...env
|
|
3304
3093
|
};
|
|
3305
3094
|
}
|
|
3095
|
+
async getDatabaseModeEnv() {
|
|
3096
|
+
const akanConfig = await this.getConfig();
|
|
3097
|
+
return {
|
|
3098
|
+
AKAN_DATABASE_MODE: akanConfig.resolveDatabaseMode(),
|
|
3099
|
+
AKAN_DATABASE_MODES: akanConfig.database.modes.join(",")
|
|
3100
|
+
};
|
|
3101
|
+
}
|
|
3306
3102
|
async prepareCommand(type) {
|
|
3307
3103
|
const akanConfig = await this.getConfig();
|
|
3308
|
-
const databaseMode = process.env.AKAN_DATABASE_MODE ?? akanConfig.defaultDatabaseMode ?? "single";
|
|
3309
3104
|
const routeEnv = {
|
|
3310
3105
|
AKAN_PUBLIC_BASE_PATHS: [...akanConfig.basePaths].join(","),
|
|
3311
3106
|
AKAN_PUBLIC_API_PREFIX: akanConfig.api.prefix,
|
|
3312
3107
|
AKAN_PUBLIC_WS_PREFIX: akanConfig.api.websocketPrefix,
|
|
3313
|
-
|
|
3108
|
+
...await this.getDatabaseModeEnv()
|
|
3314
3109
|
};
|
|
3315
3110
|
Object.assign(process.env, routeEnv);
|
|
3316
3111
|
if (type === "build") {
|
|
@@ -3323,7 +3118,7 @@ class AppExecutor extends SysExecutor {
|
|
|
3323
3118
|
...akanConfig.web.ssr ? [this.cp("public", `${this.dist.cwdPath}/public`, { dereference: true })] : []
|
|
3324
3119
|
]);
|
|
3325
3120
|
} else {
|
|
3326
|
-
await this
|
|
3121
|
+
await this.#removeDevOutput();
|
|
3327
3122
|
if (!akanConfig.web.ssr || !akanConfig.web.csr)
|
|
3328
3123
|
this.logger.verbose(`akan.config.ts disables web.${!akanConfig.web.ssr ? "ssr" : "csr"}; \`akan start\` still serves it, \`akan build\` will not`);
|
|
3329
3124
|
}
|
|
@@ -3339,6 +3134,8 @@ class AppExecutor extends SysExecutor {
|
|
|
3339
3134
|
const buildEnv = { ...env };
|
|
3340
3135
|
delete buildEnv.AKAN_PUBLIC_CLIENT_PORT;
|
|
3341
3136
|
delete buildEnv.AKAN_PUBLIC_SERVER_PORT;
|
|
3137
|
+
delete buildEnv.AKAN_PUBLIC_OPERATION_MODE;
|
|
3138
|
+
delete process.env.AKAN_PUBLIC_OPERATION_MODE;
|
|
3342
3139
|
Object.assign(process.env, buildEnv);
|
|
3343
3140
|
}
|
|
3344
3141
|
return { env };
|
|
@@ -3348,16 +3145,7 @@ class AppExecutor extends SysExecutor {
|
|
|
3348
3145
|
if (this.#publicEnv)
|
|
3349
3146
|
return this.#publicEnv;
|
|
3350
3147
|
const searchPatterns = [...patterns, "AKAN_PUBLIC_*"];
|
|
3351
|
-
const regexes = searchPatterns.map((pattern) => {
|
|
3352
|
-
let body = "";
|
|
3353
|
-
for (const ch of pattern) {
|
|
3354
|
-
if (ch === "*")
|
|
3355
|
-
body += ".*";
|
|
3356
|
-
else
|
|
3357
|
-
body += ch.replace(/[.+^${}()|[\]\\?]/g, "\\$&");
|
|
3358
|
-
}
|
|
3359
|
-
return new RegExp(`^${body}$`);
|
|
3360
|
-
});
|
|
3148
|
+
const regexes = searchPatterns.map((pattern) => new RegExp(`^${pattern.split("*").map((part) => part.replace(/[.+^${}()|[\]\\?]/g, "\\$&")).join(".*")}$`));
|
|
3361
3149
|
const publicEnv = {};
|
|
3362
3150
|
for (const [k, v] of Object.entries(process.env)) {
|
|
3363
3151
|
if (typeof v !== "string")
|
|
@@ -3386,19 +3174,23 @@ class AppExecutor extends SysExecutor {
|
|
|
3386
3174
|
const owners = new Map;
|
|
3387
3175
|
const devOnlyKeys = new Set;
|
|
3388
3176
|
const devOnlyDirs = [];
|
|
3389
|
-
|
|
3390
|
-
|
|
3177
|
+
const pageRoots = await this.getPageRoots();
|
|
3178
|
+
const libKeyPrefixes = pageRoots.map((root) => root.keyPrefix).filter(Boolean);
|
|
3179
|
+
for (const root of pageRoots) {
|
|
3180
|
+
if (!await FileSys2.dirExists(root.dir))
|
|
3391
3181
|
continue;
|
|
3392
3182
|
for await (const rel of glob.scan({
|
|
3393
3183
|
cwd: root.dir,
|
|
3394
3184
|
absolute: false,
|
|
3395
3185
|
onlyFiles: true
|
|
3396
3186
|
})) {
|
|
3397
|
-
const segments = rel.split(
|
|
3187
|
+
const segments = rel.split(path9.sep);
|
|
3398
3188
|
if (segments.some((s) => s === "node_modules"))
|
|
3399
3189
|
continue;
|
|
3400
3190
|
const posix = `${root.keyPrefix}${segments.join("/")}`;
|
|
3401
|
-
|
|
3191
|
+
if (!root.keyPrefix && libKeyPrefixes.some((prefix) => posix.startsWith(prefix)))
|
|
3192
|
+
continue;
|
|
3193
|
+
const absPath = path9.join(root.dir, ...segments);
|
|
3402
3194
|
validatePageSourceFile(posix, { filePath: absPath });
|
|
3403
3195
|
if (!isRouteSourceFile(posix))
|
|
3404
3196
|
continue;
|
|
@@ -3462,7 +3254,7 @@ class AppExecutor extends SysExecutor {
|
|
|
3462
3254
|
const entries = await readDirEntries(libsDir).catch(() => []);
|
|
3463
3255
|
for (const entry of entries.sort()) {
|
|
3464
3256
|
const dir = `${libsDir}/${entry}`;
|
|
3465
|
-
if (!await
|
|
3257
|
+
if (!await FileSys2.dirExists(dir))
|
|
3466
3258
|
continue;
|
|
3467
3259
|
roots.push({ dir, realDir: await realpath(dir), keyPrefix: `${parent}${AppExecutor.#pageLibsDir}/${entry}/` });
|
|
3468
3260
|
}
|
|
@@ -3487,7 +3279,8 @@ class AppExecutor extends SysExecutor {
|
|
|
3487
3279
|
return entries.map((entry) => `${parent}${AppExecutor.#pageLibsDir}/${entry}/`);
|
|
3488
3280
|
}))).flat();
|
|
3489
3281
|
const wanted = parents.flatMap((parent) => libs.map((lib) => `${parent}${AppExecutor.#pageLibsDir}/(${lib})/`));
|
|
3490
|
-
|
|
3282
|
+
const isLink = async (key) => (await lstat(`${this.cwdPath}/page/${key.slice(0, -1)}`).catch(() => null))?.isSymbolicLink() ?? false;
|
|
3283
|
+
if (linked.sort().join(",") === wanted.sort().join(",") && (await Promise.all(linked.map(isLink))).every(Boolean))
|
|
3491
3284
|
return false;
|
|
3492
3285
|
await Promise.all(parents.map((parent) => this.removeDir(`${this.cwdPath}/page/${parent}${AppExecutor.#pageLibsDir}`)));
|
|
3493
3286
|
for (const parent of parents) {
|
|
@@ -3503,7 +3296,7 @@ class AppExecutor extends SysExecutor {
|
|
|
3503
3296
|
async#resolvePageLibs(syncPageLibs, libDeps) {
|
|
3504
3297
|
if (!syncPageLibs)
|
|
3505
3298
|
return [];
|
|
3506
|
-
const hasPageDir = async (lib) => await
|
|
3299
|
+
const hasPageDir = async (lib) => await FileSys2.dirExists(`${this.workspace.workspaceRoot}/libs/${lib}/page`);
|
|
3507
3300
|
if (syncPageLibs === true) {
|
|
3508
3301
|
const libs = [];
|
|
3509
3302
|
for (const lib of libDeps)
|
|
@@ -3538,12 +3331,12 @@ class AppExecutor extends SysExecutor {
|
|
|
3538
3331
|
static async#linkLibAsset(targetPath, linkPath) {
|
|
3539
3332
|
const isWindows = process.platform === "win32";
|
|
3540
3333
|
try {
|
|
3541
|
-
const target = isWindows ? targetPath :
|
|
3334
|
+
const target = isWindows ? targetPath : path9.relative(path9.dirname(linkPath), targetPath);
|
|
3542
3335
|
await symlink(target, linkPath, isWindows ? "junction" : "dir");
|
|
3543
3336
|
} catch (error) {
|
|
3544
3337
|
if (!isWindows)
|
|
3545
3338
|
throw error;
|
|
3546
|
-
await
|
|
3339
|
+
await mkdir2(linkPath, { recursive: true });
|
|
3547
3340
|
await cpEntry(targetPath, linkPath, { recursive: true, dereference: true, force: true });
|
|
3548
3341
|
}
|
|
3549
3342
|
}
|
|
@@ -3575,13 +3368,14 @@ class AppExecutor extends SysExecutor {
|
|
|
3575
3368
|
appPath: this.cwdPath,
|
|
3576
3369
|
executor: this,
|
|
3577
3370
|
getPath: (rel) => this.getPath(rel),
|
|
3578
|
-
fileExists: (rel) =>
|
|
3371
|
+
fileExists: (rel) => FileSys2.fileExists(this.getPath(rel)),
|
|
3372
|
+
readFile: (rel) => this.readFile(rel),
|
|
3579
3373
|
writeFile: async (rel, content, opts) => {
|
|
3580
3374
|
await this.writeFile(rel, content, opts);
|
|
3581
3375
|
},
|
|
3582
3376
|
readEnvClient: async () => {
|
|
3583
|
-
const envClientPath =
|
|
3584
|
-
if (!await
|
|
3377
|
+
const envClientPath = path9.join(this.cwdPath, "env", "env.client.ts");
|
|
3378
|
+
if (!await FileSys2.fileExists(envClientPath))
|
|
3585
3379
|
return null;
|
|
3586
3380
|
try {
|
|
3587
3381
|
const envUrl = pathToFileURL(envClientPath);
|
|
@@ -3612,16 +3406,6 @@ class AppExecutor extends SysExecutor {
|
|
|
3612
3406
|
return true;
|
|
3613
3407
|
});
|
|
3614
3408
|
}
|
|
3615
|
-
async getPluginRuntimePackages() {
|
|
3616
|
-
const plugins = await this.collectPlugins();
|
|
3617
|
-
const appConfig = await this.getConfig();
|
|
3618
|
-
const ctx = {
|
|
3619
|
-
appName: this.name,
|
|
3620
|
-
mobile: appConfig.mobile,
|
|
3621
|
-
hasMobilePermission: (permission) => Object.values(appConfig.mobile.targets).some((target) => target.permissions?.includes(permission) ?? false)
|
|
3622
|
-
};
|
|
3623
|
-
return [...new Set(plugins.flatMap((plugin) => plugin.runtimePackages?.(ctx) ?? []))];
|
|
3624
|
-
}
|
|
3625
3409
|
async increaseBuildNum() {
|
|
3626
3410
|
await increaseBuildNum(this);
|
|
3627
3411
|
}
|
|
@@ -3637,15 +3421,8 @@ class LibExecutor extends SysExecutor {
|
|
|
3637
3421
|
super({ workspace, name, type: "lib" });
|
|
3638
3422
|
this.dist = new Executor(`dist/${name}`, `${this.workspace.workspaceRoot}/dist/libs/${name}`);
|
|
3639
3423
|
}
|
|
3640
|
-
static #execs = new Map;
|
|
3641
3424
|
static from(executor, name) {
|
|
3642
|
-
|
|
3643
|
-
if (exec)
|
|
3644
|
-
return exec;
|
|
3645
|
-
else if (executor instanceof WorkspaceExecutor)
|
|
3646
|
-
return new LibExecutor({ workspace: executor, name });
|
|
3647
|
-
else
|
|
3648
|
-
return new LibExecutor({ workspace: executor.workspace, name });
|
|
3425
|
+
return new LibExecutor({ workspace: executor instanceof WorkspaceExecutor ? executor : executor.workspace, name });
|
|
3649
3426
|
}
|
|
3650
3427
|
#akanConfig = null;
|
|
3651
3428
|
async getConfig({ refresh } = {}) {
|
|
@@ -3668,9 +3445,7 @@ class PkgExecutor extends Executor {
|
|
|
3668
3445
|
this.dist = new Executor(`dist/${name}`, `${this.workspace.workspaceRoot}/dist/pkgs/${name}`);
|
|
3669
3446
|
}
|
|
3670
3447
|
static from(executor, name) {
|
|
3671
|
-
|
|
3672
|
-
return new PkgExecutor({ workspace: executor, name });
|
|
3673
|
-
return new PkgExecutor({ workspace: executor.workspace, name });
|
|
3448
|
+
return new PkgExecutor({ workspace: executor instanceof WorkspaceExecutor ? executor : executor.workspace, name });
|
|
3674
3449
|
}
|
|
3675
3450
|
#scanInfo = null;
|
|
3676
3451
|
async scan({ refresh } = {}) {
|
|
@@ -3688,9 +3463,12 @@ class PkgExecutor extends Executor {
|
|
|
3688
3463
|
const rootVersion = rootDeps[dep];
|
|
3689
3464
|
if (rootVersion)
|
|
3690
3465
|
return rootVersion;
|
|
3466
|
+
const overrideVersion = rootPackageJson.overrides?.[dep];
|
|
3467
|
+
if (typeof overrideVersion === "string")
|
|
3468
|
+
return overrideVersion;
|
|
3691
3469
|
try {
|
|
3692
3470
|
const packageJsonPath = `pkgs/${dep}/package.json`;
|
|
3693
|
-
if (!await Bun.file(
|
|
3471
|
+
if (!await Bun.file(path9.join(this.workspace.workspaceRoot, packageJsonPath)).exists())
|
|
3694
3472
|
return;
|
|
3695
3473
|
const packageJson = await this.workspace.readJson(packageJsonPath);
|
|
3696
3474
|
if (packageJson.name === dep)
|
|
@@ -3713,16 +3491,8 @@ class PkgExecutor extends Executor {
|
|
|
3713
3491
|
}
|
|
3714
3492
|
if (missingDeps.length > 0)
|
|
3715
3493
|
throw new Error(`Missing dependency versions in root package.json: ${missingDeps.join(", ")}`);
|
|
3716
|
-
const
|
|
3717
|
-
|
|
3718
|
-
if (!version)
|
|
3719
|
-
throw new Error(`Missing dependency versions in root package.json: ${dep}`);
|
|
3720
|
-
return [dep, version];
|
|
3721
|
-
});
|
|
3722
|
-
return {
|
|
3723
|
-
dependencies: Object.fromEntries(toDependencyEntries(dependencyNames)),
|
|
3724
|
-
devDependencies: Object.fromEntries(toDependencyEntries(devDependencyNames))
|
|
3725
|
-
};
|
|
3494
|
+
const toDependencyMap = (names) => Object.fromEntries(names.map((dep) => [dep, dependencyVersions.get(dep)]));
|
|
3495
|
+
return { dependencies: toDependencyMap(dependencyNames), devDependencies: toDependencyMap(devDependencyNames) };
|
|
3726
3496
|
}
|
|
3727
3497
|
async updatePackageJsonDependencies(dependencies = [], devDependencies = []) {
|
|
3728
3498
|
const [rootPackageJson, pkgJson] = await Promise.all([this.workspace.getPackageJson(), this.getPackageJson()]);
|
|
@@ -3791,4 +3561,4 @@ class ModuleExecutor extends Executor {
|
|
|
3791
3561
|
}
|
|
3792
3562
|
}
|
|
3793
3563
|
|
|
3794
|
-
export { AGENT_BLOCK_START, AGENT_BLOCK_END, stampBlockVersion, extractBlockVersion, readDevkitVersion, upsertAgentBlock, extractAgentBlock, renderRecipeEntries, collectScopeRecipeSources, renderScopeAgentBlock, CLAUDE_COMMENT_RULE, AsyncDefaultExportDetector, resolveSsrPageEntriesForApp, CodegenLock, rootAllowedFiles, rootAllowedDirs, isAllowedLibFacetRootFile, isScannedRootEntry, AppInfo, Spinner,
|
|
3564
|
+
export { AGENT_BLOCK_START, AGENT_BLOCK_END, stampBlockVersion, extractBlockVersion, readDevkitVersion, upsertAgentBlock, extractAgentBlock, renderRecipeEntries, collectScopeRecipeSources, renderScopeAgentBlock, CLAUDE_COMMENT_RULE, binPlatforms, AsyncDefaultExportDetector, resolveSsrPageEntriesForApp, CodegenLock, Linter, rootAllowedFiles, rootAllowedDirs, isAllowedLibFacetRootFile, rootEntryHintOf, isScannedRootEntry, AppInfo, Spinner, Executor, WorkspaceExecutor, SysExecutor, AppExecutor, LibExecutor, PkgExecutor, ModuleExecutor };
|