@akanjs/cli 3.0.0-beta.2 → 3.0.0-beta.21
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-q2m47rqp.js +9 -0
- package/CodeAgentRpcHost-p7h6n2hp.js +7 -0
- package/CodeAgentRpcListener-7p7z5dd7.js +97 -0
- package/CodeTui-pdqhc3sn.js +1457 -0
- package/README.md +3 -0
- package/TunnelShare-scf4e4pd.js +9 -0
- package/{agent.command-d2gfxfck.js → agent.command-ttn09rx3.js} +8 -9
- package/akanCode.js +102 -0
- package/application.command-7gweenv7.js +179 -0
- package/{applicationBuildRunner-kxwbssgn.js → applicationBuildRunner-38exmejd.js} +63 -64
- package/buildBatch.proc.js +26 -25
- package/cloud.command-bhztw7c2.js +95 -0
- package/cloud.script-aabyhgba.js +20 -0
- package/code.command-6s8c0k91.js +154 -0
- package/commandManifest.json +1 -1
- package/consoleToStderr-h5bryj94.js +7 -0
- package/context.command-phmgywt2.js +48 -0
- package/context.runner-yvyw5g2d.js +21 -0
- package/{dependencyScanner-shs82ynp.js → dependencyScanner-ye2e6pdz.js} +2 -2
- package/{devTui-40ggmd99.js → devTui-q7k7bakd.js} +122 -40
- package/fileSys-5ngz93ga.js +9 -0
- package/guideline.command-7jk47fgq.js +42 -0
- package/guidelines/agentRule/agentRule.instruction.md +209 -28
- package/guidelines/conventions/conventions.instruction.md +96 -29
- package/guidelines/fieldRule/fieldRule.instruction.md +1 -0
- package/guidelines/mcpRule/mcpRule.instruction.md +7 -5
- package/guidelines/queryRule/queryRule.instruction.md +17 -7
- package/guidelines/runtimeRule/runtimeRule.instruction.md +77 -3
- package/guidelines/ssrRule/ssrRule.instruction.md +4 -2
- package/guidelines/transportRule/transportRule.instruction.md +51 -0
- package/guidelines/workspaceRecipes/workspaceRecipes.instruction.md +66 -3
- package/incrementalBuilder.proc.js +31 -48
- package/{index-h7yw2z9n.js → index-0bq14vr1.js} +8 -14
- package/{index-259dsnz3.js → index-1db2cw0y.js} +5 -8
- package/index-22d57v4c.js +266 -0
- package/index-3hx1z96c.js +87 -0
- package/{index-kcxmv48c.js → index-3t7g9e4r.js} +45 -79
- package/{index-ne2qy0nk.js → index-4dn1p115.js} +12 -12
- package/{index-q0y2jq9j.js → index-5b6tavv3.js} +6 -3
- package/index-7cqjvraf.js +69 -0
- package/index-7md2g9vw.js +6 -0
- package/{index-zry5nz81.js → index-8kb5p54p.js} +51 -30
- package/{index-ktb5njgy.js → index-9629w7ns.js} +49 -99
- package/index-9dp031by.js +12 -0
- package/{index-a64esmtb.js → index-aadpbnt2.js} +172 -308
- package/{index-3d9y066c.js → index-bjwxs904.js} +9 -8
- package/index-c6eggbee.js +123 -0
- package/index-d0arnrm8.js +700 -0
- package/index-d2242ksy.js +99 -0
- package/{index-600zhnve.js → index-dg97qp24.js} +70 -200
- package/{index-92zztqy1.js → index-dy0jx9ay.js} +2 -17
- package/{index-w8kmx1b6.js → index-fgq75haa.js} +198 -290
- package/{index-2tv3fdx4.js → index-ga6hygxr.js} +944 -1333
- package/{index-by5gpp8x.js → index-hj6w4ka1.js} +2 -1
- package/index-hr5n7t4x.js +465 -0
- package/{index-hwkygn9s.js → index-khpfpcsd.js} +302 -33
- package/{index-7737gxcp.js → index-m0ms3n1c.js} +107 -241
- package/{index-ws5e0j51.js → index-mgfx3mfs.js} +43 -26
- package/index-p6pbz9n5.js +25 -0
- package/index-pp54p5zk.js +53 -0
- package/{index-j9yhg404.js → index-q44jbw48.js} +4 -7
- package/{index-mak3cd28.js → index-rmcw8yc8.js} +137 -255
- package/{index-pnqbkf5p.js → index-rnt66vym.js} +6 -2
- package/index-smme4r47.js +49 -0
- package/index-tgjy8xf3.js +187 -0
- package/index-v5eayqqw.js +2729 -0
- package/index-wa5jbxj1.js +760 -0
- package/{index-4edzf1gr.js → index-wp43xrjv.js} +77 -150
- package/{index-fz5q3gd7.js → index-ya5qbmyg.js} +10 -17
- package/index-yjkte9aj.js +1119 -0
- package/{index-gyd220p2.js → index-yy4s1j72.js} +554 -577
- package/{index-v0qh106m.js → index-z50k041j.js} +580 -862
- package/index.js +23 -22
- package/{library.command-nbsky672.js → library.command-ccb1cp92.js} +4 -5
- package/{localRegistry.command-yeq57wjr.js → localRegistry.command-ajb9n87j.js} +23 -42
- package/{module.command-29vqzrh8.js → module.command-5k6h407d.js} +8 -11
- package/{package.command-axv0abmn.js → package.command-e6ntvpez.js} +6 -7
- package/package.json +14 -6
- package/{page.command-vs9s3zrh.js → page.command-v4f90wb8.js} +4 -5
- package/{primitive.command-cbxmqbtc.js → primitive.command-g6km57q5.js} +9 -22
- package/prompter-ptaa3nk0.js +8 -0
- package/{quality.command-gmbs7nws.js → quality.command-de3p36pz.js} +205 -377
- package/{recipeScanner-fpagtgc1.js → recipeScanner-4b69ybe0.js} +1 -1
- package/{repair.command-bqsjptsj.js → repair.command-d3x1javq.js} +11 -35
- package/{routeSourceValidator-apyqw7wk.js → routeSourceValidator-7ycnfz0e.js} +0 -6
- package/{scalar.command-6k1vhe9s.js → scalar.command-7ezrac2b.js} +7 -10
- 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-fwjfcts7.js → subspace.command-ps71ynp6.js} +138 -67
- package/templates/__scalar/__model__/__model__.abstract.md.template +8 -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.abstract.ts +12 -32
- package/templates/appSample/lib/task/task.signal.spec.ts +15 -18
- package/templates/appSample/lib/task/task.signal.test.ts +15 -1
- package/templates/appSample/lib/task/task.signal.ts +1 -1
- package/templates/appSample/srvkit/{AuthGuard.ts → guards.ts} +7 -4
- package/templates/appSample/srvkit/{SessionInternalArg.ts → internalArgs.ts} +6 -4
- 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 +0 -2
- package/templates/service/__model__.abstract.md.template +8 -0
- package/templates/workspaceRoot/.gitignore.template +0 -1
- package/tunnel.command-hsrv2mny.js +129 -0
- package/{typeChecker-3xgrzc2v.js → typeChecker-px49mezc.js} +1 -1
- package/typecheck.proc.js +1 -1
- package/workflow.command-z2p32nmf.js +79 -0
- package/{workspace.command-8s050m0h.js → workspace.command-tmdqkrh0.js} +77 -109
- package/abstractCompactor-mjh5ppsq.js +0 -101
- package/application.command-pj2m7584.js +0 -204
- package/applicationReleasePackager-4yasxwv9.js +0 -244
- package/capacitorApp-36rbr858.js +0 -12
- package/cloud.command-jshwdq22.js +0 -93
- package/context.command-ez8a0zyz.js +0 -80
- package/devTuiView-c08ccbat.js +0 -9
- package/getDirname-dr3jz1xg.js +0 -7
- package/guideline.command-xmqwx8zm.js +0 -359
- 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-2hq52jq3.js +0 -465
- package/index-47pj3aee.js +0 -76
- package/index-6wwzs1bc.js +0 -7
- package/index-8pm30m73.js +0 -841
- package/index-kn1h7p1x.js +0 -560
- package/index-meh558jy.js +0 -65
- package/index-rwg2aa4f.js +0 -229
- 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/workflow.command-nwpcdc8p.js +0 -107
|
@@ -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-5b6tavv3.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,11 +504,12 @@ 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,
|
|
@@ -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-5ngz93ga.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;
|
|
@@ -355,10 +669,10 @@ ${CLAUDE_COMMENT_RULE}
|
|
|
355
669
|
`;
|
|
356
670
|
|
|
357
671
|
// pkgs/@akanjs/devkit/akanConfig/akanConfig.ts
|
|
358
|
-
import
|
|
359
|
-
import
|
|
672
|
+
import fs2 from "fs";
|
|
673
|
+
import path4 from "path";
|
|
360
674
|
import { fileURLToPath } from "url";
|
|
361
|
-
import { normalizeRoutePrefix } from "akanjs/base";
|
|
675
|
+
import { DatabaseModes, normalizeRoutePrefix } from "akanjs/base";
|
|
362
676
|
import { resolveAkanI18nConfig } from "akanjs/common";
|
|
363
677
|
|
|
364
678
|
// pkgs/@akanjs/devkit/akanConfig/types.ts
|
|
@@ -367,66 +681,14 @@ import { archs } from "akanjs";
|
|
|
367
681
|
// pkgs/@akanjs/devkit/akanConfig/akanConfig.ts
|
|
368
682
|
var DEFAULT_BARREL_IMPORTS = ["akanjs/webkit", "akanjs/common", "akanjs/ui", "akanjs/server"];
|
|
369
683
|
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
|
-
];
|
|
684
|
+
"lucide-react date-fns lodash-es ramda antd react-bootstrap ahooks @ant-design/icons @headlessui/react",
|
|
685
|
+
"@headlessui-float/react @heroicons/react/20/solid @heroicons/react/24/solid @heroicons/react/24/outline",
|
|
686
|
+
"@visx/visx @tremor/react rxjs @mui/material @mui/icons-material recharts react-use @material-ui/core",
|
|
687
|
+
"@material-ui/icons @tabler/icons-react mui-core react-icons/*"
|
|
688
|
+
].flatMap((line) => line.split(" "));
|
|
396
689
|
var WORKSPACE_BARREL_FACETS = ["ui", "webkit", "common", "client", "server"];
|
|
397
690
|
var DEFAULT_DOCKER_IMAGE = "oven/bun:1-slim";
|
|
398
691
|
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
692
|
var DEFAULT_AKAN_IMAGE_CONFIG = {
|
|
431
693
|
deviceSizes: [640, 750, 828, 1080, 1200, 1920, 2048, 3840],
|
|
432
694
|
imageSizes: [32, 48, 64, 96, 128, 256, 384],
|
|
@@ -506,7 +768,7 @@ class AkanAppConfig {
|
|
|
506
768
|
rootPackageJson;
|
|
507
769
|
docker;
|
|
508
770
|
dockerfile;
|
|
509
|
-
|
|
771
|
+
database;
|
|
510
772
|
web;
|
|
511
773
|
externalLibs;
|
|
512
774
|
barrelImports;
|
|
@@ -534,7 +796,7 @@ class AkanAppConfig {
|
|
|
534
796
|
this.baseDevEnv = baseDevEnv;
|
|
535
797
|
this.plugins = plugins;
|
|
536
798
|
this.#applyRoutes(config?.routes);
|
|
537
|
-
this.
|
|
799
|
+
this.database = AkanAppConfig.#database(app, config);
|
|
538
800
|
this.externalLibs = [...new Set([...config?.externalLibs ?? [], ...libContributions.externalLibs])];
|
|
539
801
|
this.barrelImports = [
|
|
540
802
|
...DEFAULT_BARREL_IMPORTS,
|
|
@@ -572,7 +834,7 @@ class AkanAppConfig {
|
|
|
572
834
|
#resolveWebConfig(web) {
|
|
573
835
|
const resolved = typeof web === "object" ? { ssr: true, csr: web.csr } : { ssr: web ?? true, csr: web ?? true };
|
|
574
836
|
if (!resolved.csr && this.hasMobileConfig)
|
|
575
|
-
throw new Error(`apps/${this.app.name}/akan.config.ts turns the CSR bundle off but declares mobile targets; the
|
|
837
|
+
throw new Error(`apps/${this.app.name}/akan.config.ts turns the CSR bundle off but declares mobile targets; the mobile apps ship that bundle. Drop the mobile section or leave CSR on.`);
|
|
576
838
|
return resolved;
|
|
577
839
|
}
|
|
578
840
|
#resolveMobileConfig(mobile) {
|
|
@@ -581,57 +843,113 @@ class AkanAppConfig {
|
|
|
581
843
|
indexPath: _indexPath,
|
|
582
844
|
...rawMobile
|
|
583
845
|
} = mobile ?? {};
|
|
846
|
+
const configPath = `apps/${this.app.name}/akan.config.ts`;
|
|
847
|
+
AkanAppConfig.#assertMobileKeys(rawMobile, AkanAppConfig.#mobileKeys, "mobile", configPath);
|
|
584
848
|
const appName = rawMobile.appName ?? this.app.name;
|
|
585
849
|
const appId = rawMobile.appId ?? deriveDefaultAppId(this.baseDevEnv.repoName, this.app.name);
|
|
586
850
|
const version = rawMobile.version ?? "0.0.1";
|
|
587
851
|
const buildNum = rawMobile.buildNum ?? 1;
|
|
852
|
+
const files = AkanAppConfig.#resolveMobileFiles(rawMobile.files, "mobile", configPath);
|
|
853
|
+
const native = rawMobile.native;
|
|
588
854
|
const defaultTargetName = this.#defaultMobileTargetName(rawTargets);
|
|
589
855
|
const targetEntries = Object.entries(rawTargets ?? {
|
|
590
856
|
[defaultTargetName]: {}
|
|
591
857
|
});
|
|
592
858
|
const targets = Object.fromEntries(targetEntries.map(([name, rawTarget]) => {
|
|
593
|
-
const target = rawTarget;
|
|
859
|
+
const target = rawTarget ?? {};
|
|
860
|
+
const where = `mobile.targets.${name}`;
|
|
861
|
+
AkanAppConfig.#assertMobileKeys(target, AkanAppConfig.#targetKeys, where, configPath);
|
|
594
862
|
const fallbackBasePath = !rawTargets && this.basePaths.has(name) ? name : undefined;
|
|
595
863
|
const basePath = (target.basePath ?? fallbackBasePath)?.replace(/^\/+|\/+$/g, "") || undefined;
|
|
596
864
|
const indexPath = normalizeIndexPath(target.indexPath);
|
|
597
865
|
const deepLinks = normalizeDeepLinks(target.deepLinks);
|
|
598
866
|
if (basePath && !this.basePaths.has(basePath)) {
|
|
599
|
-
throw new Error(`Mobile target '${name}' uses unknown basePath '${basePath}' in
|
|
867
|
+
throw new Error(`Mobile target '${name}' uses unknown basePath '${basePath}' in ${configPath}`);
|
|
600
868
|
}
|
|
869
|
+
const targetFiles = AkanAppConfig.#resolveMobileFiles(target.files, where, configPath);
|
|
870
|
+
const fileName = target.fileName ?? rawMobile.fileName;
|
|
601
871
|
const resolved = {
|
|
602
|
-
...rawMobile,
|
|
603
|
-
...target,
|
|
604
872
|
name,
|
|
605
873
|
basePath,
|
|
606
874
|
indexPath,
|
|
607
875
|
deepLinks,
|
|
608
876
|
appName: target.appName ?? appName,
|
|
609
877
|
appId: target.appId ?? appId,
|
|
878
|
+
...fileName ? { fileName } : {},
|
|
610
879
|
version: target.version ?? version,
|
|
611
880
|
buildNum: target.buildNum ?? buildNum,
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
}
|
|
616
|
-
android: {
|
|
617
|
-
...rawMobile.android,
|
|
618
|
-
...target.android
|
|
619
|
-
},
|
|
620
|
-
ios: {
|
|
621
|
-
...rawMobile.ios,
|
|
622
|
-
...target.ios
|
|
623
|
-
}
|
|
881
|
+
...target.assets ? { assets: target.assets } : {},
|
|
882
|
+
...target.permissions ? { permissions: target.permissions } : {},
|
|
883
|
+
...files || targetFiles ? { files: { ...files, ...targetFiles } } : {},
|
|
884
|
+
...native || target.native ? { native: AkanAppConfig.#mergeNative(native, target.native) } : {}
|
|
624
885
|
};
|
|
625
886
|
return [name, resolved];
|
|
626
887
|
}));
|
|
627
888
|
return {
|
|
628
|
-
...rawMobile,
|
|
629
889
|
appName,
|
|
630
890
|
appId,
|
|
891
|
+
...rawMobile.fileName ? { fileName: rawMobile.fileName } : {},
|
|
631
892
|
version,
|
|
632
893
|
buildNum,
|
|
633
|
-
|
|
634
|
-
|
|
894
|
+
...files ? { files } : {},
|
|
895
|
+
...native ? { native } : {},
|
|
896
|
+
targets
|
|
897
|
+
};
|
|
898
|
+
}
|
|
899
|
+
static #mobileKeys = ["appName", "appId", "fileName", "version", "buildNum", "files", "native"];
|
|
900
|
+
static #targetKeys = [
|
|
901
|
+
"basePath",
|
|
902
|
+
"indexPath",
|
|
903
|
+
"appName",
|
|
904
|
+
"appId",
|
|
905
|
+
"fileName",
|
|
906
|
+
"version",
|
|
907
|
+
"buildNum",
|
|
908
|
+
"assets",
|
|
909
|
+
"permissions",
|
|
910
|
+
"deepLinks",
|
|
911
|
+
"files",
|
|
912
|
+
"native"
|
|
913
|
+
];
|
|
914
|
+
static #assertMobileKeys(section, known, where, configPath) {
|
|
915
|
+
for (const key of Object.keys(section)) {
|
|
916
|
+
if (known.includes(key))
|
|
917
|
+
continue;
|
|
918
|
+
if (key === "plugins" || key === "ios" || key === "android")
|
|
919
|
+
throw new Error(`${where}.${key} in ${configPath} is a Capacitor setting. The native runtime reads native.plugins, native.ios.{infoPlist,entitlements} and native.android.{manifest,application,activity,googleServices} instead.`);
|
|
920
|
+
throw new Error(`${where}.${key} in ${configPath} is not a mobile setting. Known: ${known.join(", ")}.`);
|
|
921
|
+
}
|
|
922
|
+
}
|
|
923
|
+
static #fileTargetPattern = /^(?:ios\/.+|android\/res\/[^/]+\/[^/]+|android\/assets\/.+)$/;
|
|
924
|
+
static #resolveMobileFiles(files, where, configPath) {
|
|
925
|
+
if (!files)
|
|
926
|
+
return;
|
|
927
|
+
for (const [to, from] of Object.entries(files)) {
|
|
928
|
+
if (typeof from !== "string")
|
|
929
|
+
throw new Error(`${where}.files in ${configPath} maps where a file lands to its source now, e.g. { "android/res/raw/chime.mp3": "assets/chime.mp3" }. google-services.json goes to native.android.googleServices, and GoogleService-Info.plist is no longer needed.`);
|
|
930
|
+
if (!AkanAppConfig.#fileTargetPattern.test(to))
|
|
931
|
+
throw new Error(`${where}.files["${to}"] in ${configPath} must land under ios/<path>, android/res/<type>/<file> or android/assets/<path>.`);
|
|
932
|
+
}
|
|
933
|
+
return files;
|
|
934
|
+
}
|
|
935
|
+
static #mergeNative(base, override) {
|
|
936
|
+
const plugins = [...new Set([...base?.plugins ?? [], ...override?.plugins ?? []])];
|
|
937
|
+
const ios = { ...base?.ios, ...override?.ios };
|
|
938
|
+
const android = {
|
|
939
|
+
...base?.android,
|
|
940
|
+
...override?.android
|
|
941
|
+
};
|
|
942
|
+
for (const key of ["manifest", "application", "activity"]) {
|
|
943
|
+
const merged = [...base?.android?.[key] ?? [], ...override?.android?.[key] ?? []];
|
|
944
|
+
if (merged.length)
|
|
945
|
+
android[key] = merged;
|
|
946
|
+
else
|
|
947
|
+
delete android[key];
|
|
948
|
+
}
|
|
949
|
+
return {
|
|
950
|
+
...plugins.length ? { plugins } : {},
|
|
951
|
+
...Object.keys(ios).length ? { ios } : {},
|
|
952
|
+
...Object.keys(android).length ? { android } : {}
|
|
635
953
|
};
|
|
636
954
|
}
|
|
637
955
|
#defaultMobileTargetName(rawTargets) {
|
|
@@ -641,22 +959,15 @@ class AkanAppConfig {
|
|
|
641
959
|
}
|
|
642
960
|
#applyRoutes(routes = []) {
|
|
643
961
|
for (const route of routes) {
|
|
644
|
-
|
|
645
|
-
|
|
962
|
+
const basePath = route.basePath ? route.basePath.replace(/^\/+|\/+$/g, "") : null;
|
|
963
|
+
if (basePath !== null)
|
|
646
964
|
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
|
-
}
|
|
965
|
+
const domains = basePath !== null ? this.subRoutes.getOrInsert(basePath, new Set) : this.domains;
|
|
966
|
+
for (const branch of Object.keys(route.domains))
|
|
967
|
+
this.branches.add(branch);
|
|
968
|
+
for (const domain of Object.values(route.domains).flat())
|
|
969
|
+
if (domain)
|
|
970
|
+
domains.add(domain.toLowerCase().replace(/:\d+$/, ""));
|
|
660
971
|
}
|
|
661
972
|
const appName = this.app.name.toLowerCase();
|
|
662
973
|
const serveDomain = this.baseDevEnv.serveDomain.toLowerCase();
|
|
@@ -730,6 +1041,7 @@ ENV AKAN_PUBLIC_LOCALES=${this.i18n.locales.join(",")}
|
|
|
730
1041
|
ENV AKAN_PUBLIC_API_PREFIX=${this.api.prefix}
|
|
731
1042
|
ENV AKAN_PUBLIC_WS_PREFIX=${this.api.websocketPrefix}
|
|
732
1043
|
ENV AKAN_PUBLIC_OPERATION_MODE=cloud
|
|
1044
|
+
ENV AKAN_DATABASE_MODES=${this.database.modes.join(",")}
|
|
733
1045
|
ENV AKAN_LOG_TO_FILE=0
|
|
734
1046
|
${webEnvLines}
|
|
735
1047
|
CMD [${command.map((c) => `"${c}"`).join(",")}]`;
|
|
@@ -743,509 +1055,157 @@ CMD [${command.map((c) => `"${c}"`).join(",")}]`;
|
|
|
743
1055
|
static async from(app, { bustImportCache = false } = {}) {
|
|
744
1056
|
const [configImp, baseDevEnv, libs, rootPackageJson] = await Promise.all([
|
|
745
1057
|
AkanAppConfig.importConfigModule(app.cwdPath, { bustImportCache }),
|
|
746
|
-
WorkspaceExecutor.getBaseDevEnv(
|
|
1058
|
+
WorkspaceExecutor.getBaseDevEnv(path4.join(app.workspace.workspaceRoot, ".env")),
|
|
747
1059
|
app.workspace.getLibs(),
|
|
748
1060
|
app.workspace.getPackageJson()
|
|
749
1061
|
]);
|
|
750
|
-
const resolved = typeof configImp === "function" ? configImp(app) : configImp;
|
|
751
|
-
const { plugins, ...config } = resolved ?? {};
|
|
752
|
-
const libContributions = await AkanAppConfig.#collectLibContributions(app, libs, bustImportCache);
|
|
753
|
-
return new AkanAppConfig(app, libs, rootPackageJson, config, baseDevEnv, plugins ?? [], libContributions);
|
|
754
|
-
}
|
|
755
|
-
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
|
-
docker: {
|
|
763
|
-
preRuns: libConfigs.flatMap((libConfig) => libConfig?.docker.preRuns ?? []),
|
|
764
|
-
postRuns: libConfigs.flatMap((libConfig) => libConfig?.docker.postRuns ?? [])
|
|
765
|
-
},
|
|
766
|
-
keepFonts: libConfigs.flatMap((libConfig) => (libConfig?.assets.keepFonts ?? []).map((glob) => `libs/${libConfig?.lib.name}/${glob}`))
|
|
767
|
-
};
|
|
768
|
-
}
|
|
769
|
-
#resolveProductionDependencyVersion(lib) {
|
|
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;
|
|
1062
|
+
const resolved = typeof configImp === "function" ? configImp(app) : configImp;
|
|
1063
|
+
const { plugins, ...config } = resolved ?? {};
|
|
1064
|
+
const libContributions = await AkanAppConfig.#collectLibContributions(app, libs, bustImportCache);
|
|
1065
|
+
return new AkanAppConfig(app, libs, rootPackageJson, config, baseDevEnv, plugins ?? [], libContributions);
|
|
966
1066
|
}
|
|
967
|
-
#
|
|
968
|
-
const
|
|
969
|
-
|
|
1067
|
+
static async#collectLibContributions(app, libs, bustImportCache) {
|
|
1068
|
+
const libConfigs = await Promise.all(libs.map(async (libName) => LibExecutor.from(app, libName).getConfig({ refresh: bustImportCache }).catch((error) => {
|
|
1069
|
+
app.logger.warn(`Skipped libs/${libName}/akan.config.ts contributions: ${String(error)}`);
|
|
1070
|
+
return null;
|
|
1071
|
+
})));
|
|
1072
|
+
return {
|
|
1073
|
+
externalLibs: libConfigs.flatMap((libConfig) => libConfig?.externalLibs ?? []),
|
|
1074
|
+
docker: {
|
|
1075
|
+
preRuns: libConfigs.flatMap((libConfig) => libConfig?.docker.preRuns ?? []),
|
|
1076
|
+
postRuns: libConfigs.flatMap((libConfig) => libConfig?.docker.postRuns ?? [])
|
|
1077
|
+
},
|
|
1078
|
+
keepFonts: libConfigs.flatMap((libConfig) => (libConfig?.assets.keepFonts ?? []).map((glob) => `libs/${libConfig?.lib.name}/${glob}`))
|
|
1079
|
+
};
|
|
970
1080
|
}
|
|
971
|
-
#
|
|
972
|
-
const
|
|
973
|
-
|
|
1081
|
+
#resolveProductionDependencyVersion(lib) {
|
|
1082
|
+
const rootVersion = this.rootPackageJson.dependencies?.[lib] ?? this.rootPackageJson.devDependencies?.[lib];
|
|
1083
|
+
if (rootVersion)
|
|
1084
|
+
return rootVersion;
|
|
1085
|
+
const akanPackageJson = getAkanPackageJson();
|
|
1086
|
+
const version = akanPackageJson.dependencies?.[lib] ?? akanPackageJson.peerDependencies?.[lib];
|
|
1087
|
+
if (!version)
|
|
1088
|
+
throw new Error(`Dependency ${lib} not found in package.json`);
|
|
1089
|
+
return version;
|
|
974
1090
|
}
|
|
975
|
-
|
|
976
|
-
return
|
|
1091
|
+
#getProductionRuntimePackages() {
|
|
1092
|
+
return [
|
|
1093
|
+
...this.externalLibs,
|
|
1094
|
+
...SSR_RUNTIME_PACKAGES,
|
|
1095
|
+
...this.database.modes.flatMap((mode) => this.getDatabaseModeRuntimePackages(mode))
|
|
1096
|
+
];
|
|
977
1097
|
}
|
|
978
|
-
|
|
979
|
-
|
|
980
|
-
|
|
981
|
-
|
|
982
|
-
|
|
983
|
-
|
|
984
|
-
|
|
985
|
-
|
|
986
|
-
|
|
987
|
-
|
|
988
|
-
|
|
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
|
|
1098
|
+
getDatabaseModeRuntimePackages(databaseMode) {
|
|
1099
|
+
return [...DatabaseModes.drivers[databaseMode]];
|
|
1100
|
+
}
|
|
1101
|
+
getMissingDatabaseModeDependencySpecs(databaseMode) {
|
|
1102
|
+
return this.#getMissingDependencySpecs(this.getDatabaseModeRuntimePackages(databaseMode));
|
|
1103
|
+
}
|
|
1104
|
+
resolveDatabaseMode() {
|
|
1105
|
+
return DatabaseModes.resolve({
|
|
1106
|
+
requested: process.env.AKAN_DATABASE_MODE,
|
|
1107
|
+
declared: this.database.modes.join(","),
|
|
1108
|
+
local: true
|
|
1048
1109
|
});
|
|
1049
1110
|
}
|
|
1050
|
-
|
|
1051
|
-
|
|
1052
|
-
|
|
1111
|
+
static #database(app, config) {
|
|
1112
|
+
const declared = config?.database?.modes?.filter((mode) => !!mode);
|
|
1113
|
+
const modes = declared?.length ? declared : ["single"];
|
|
1114
|
+
return { modes: DatabaseModes.parseList(modes.join(","), `database.modes in apps/${app.name}/akan.config.ts`) };
|
|
1053
1115
|
}
|
|
1054
|
-
|
|
1055
|
-
|
|
1056
|
-
|
|
1057
|
-
|
|
1058
|
-
}
|
|
1059
|
-
|
|
1060
|
-
|
|
1061
|
-
|
|
1062
|
-
return
|
|
1063
|
-
}
|
|
1064
|
-
|
|
1065
|
-
|
|
1066
|
-
|
|
1067
|
-
|
|
1068
|
-
|
|
1069
|
-
|
|
1070
|
-
|
|
1071
|
-
|
|
1072
|
-
|
|
1073
|
-
|
|
1116
|
+
#getMissingDependencySpecs(libs) {
|
|
1117
|
+
const rootDependencies = {
|
|
1118
|
+
...this.rootPackageJson.dependencies,
|
|
1119
|
+
...this.rootPackageJson.devDependencies
|
|
1120
|
+
};
|
|
1121
|
+
return libs.filter((lib) => !rootDependencies[lib]).map((lib) => `${lib}@${this.#resolveProductionDependencyVersion(lib)}`);
|
|
1122
|
+
}
|
|
1123
|
+
get akanVersion() {
|
|
1124
|
+
return getAkanPackageJson().version;
|
|
1125
|
+
}
|
|
1126
|
+
getProductionPackageJson(data = {}) {
|
|
1127
|
+
return {
|
|
1128
|
+
name: this.app.name,
|
|
1129
|
+
description: this.app.name,
|
|
1130
|
+
version: "1.0.0",
|
|
1131
|
+
main: "./main.js",
|
|
1132
|
+
dependencies: Object.fromEntries([...new Set(this.#getProductionRuntimePackages())].map((lib) => [
|
|
1133
|
+
lib,
|
|
1134
|
+
this.#resolveProductionDependencyVersion(lib)
|
|
1135
|
+
])),
|
|
1136
|
+
...data
|
|
1137
|
+
};
|
|
1074
1138
|
}
|
|
1075
1139
|
}
|
|
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
|
-
|
|
1103
|
-
|
|
1104
|
-
|
|
1105
|
-
|
|
1106
|
-
|
|
1107
|
-
|
|
1108
|
-
|
|
1109
|
-
|
|
1110
|
-
|
|
1111
|
-
|
|
1112
|
-
|
|
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;
|
|
1243
|
-
}
|
|
1244
|
-
async function resolveSsrPageEntriesForApp(app, pageKeys) {
|
|
1245
|
-
const config = await app.getConfig();
|
|
1246
|
-
return resolveSsrPageEntries({ appCwdPath: app.cwdPath, appName: app.name, pageKeys, basePaths: config.basePaths });
|
|
1140
|
+
var akanPackageJson = null;
|
|
1141
|
+
function getAkanPackageJson() {
|
|
1142
|
+
if (akanPackageJson)
|
|
1143
|
+
return akanPackageJson;
|
|
1144
|
+
const sourceDir = path4.dirname(fileURLToPath(import.meta.url));
|
|
1145
|
+
const packageJsonPaths = [
|
|
1146
|
+
path4.join(sourceDir, "../../../akanjs/package.json"),
|
|
1147
|
+
path4.join(process.cwd(), "pkgs/akanjs/package.json"),
|
|
1148
|
+
path4.join(path4.dirname(Bun.main), "node_modules/akanjs/package.json")
|
|
1149
|
+
];
|
|
1150
|
+
try {
|
|
1151
|
+
packageJsonPaths.unshift(Bun.resolveSync("akanjs/package.json", sourceDir));
|
|
1152
|
+
} catch {}
|
|
1153
|
+
for (const packageJsonPath of packageJsonPaths) {
|
|
1154
|
+
try {
|
|
1155
|
+
akanPackageJson = JSON.parse(fs2.readFileSync(packageJsonPath, "utf8"));
|
|
1156
|
+
return akanPackageJson;
|
|
1157
|
+
} catch {}
|
|
1158
|
+
}
|
|
1159
|
+
akanPackageJson = {
|
|
1160
|
+
name: "akanjs",
|
|
1161
|
+
version: "0.0.0",
|
|
1162
|
+
description: "akanjs",
|
|
1163
|
+
dependencies: {}
|
|
1164
|
+
};
|
|
1165
|
+
return akanPackageJson;
|
|
1166
|
+
}
|
|
1167
|
+
function mergeImageConfig(config = {}) {
|
|
1168
|
+
return {
|
|
1169
|
+
...DEFAULT_AKAN_IMAGE_CONFIG,
|
|
1170
|
+
...config,
|
|
1171
|
+
deviceSizes: config.deviceSizes ?? DEFAULT_AKAN_IMAGE_CONFIG.deviceSizes,
|
|
1172
|
+
imageSizes: config.imageSizes ?? DEFAULT_AKAN_IMAGE_CONFIG.imageSizes,
|
|
1173
|
+
formats: config.formats ?? DEFAULT_AKAN_IMAGE_CONFIG.formats,
|
|
1174
|
+
qualities: config.qualities ?? DEFAULT_AKAN_IMAGE_CONFIG.qualities,
|
|
1175
|
+
remotePatterns: config.remotePatterns ?? DEFAULT_AKAN_IMAGE_CONFIG.remotePatterns,
|
|
1176
|
+
localPatterns: config.localPatterns ?? DEFAULT_AKAN_IMAGE_CONFIG.localPatterns
|
|
1177
|
+
};
|
|
1247
1178
|
}
|
|
1248
1179
|
|
|
1180
|
+
class AkanLibConfig {
|
|
1181
|
+
lib;
|
|
1182
|
+
externalLibs;
|
|
1183
|
+
docker;
|
|
1184
|
+
assets;
|
|
1185
|
+
plugins;
|
|
1186
|
+
constructor(lib, config, plugins = []) {
|
|
1187
|
+
this.lib = lib;
|
|
1188
|
+
this.externalLibs = config?.externalLibs ?? [];
|
|
1189
|
+
this.docker = { preRuns: config?.docker?.preRuns ?? [], postRuns: config?.docker?.postRuns ?? [] };
|
|
1190
|
+
this.assets = { keepFonts: normalizeKeepFonts(config?.assets?.keepFonts) };
|
|
1191
|
+
this.plugins = plugins;
|
|
1192
|
+
}
|
|
1193
|
+
static async from(lib, { bustImportCache = false } = {}) {
|
|
1194
|
+
const configImp = await AkanAppConfig.importConfigModule(lib.cwdPath, { bustImportCache });
|
|
1195
|
+
const resolved = typeof configImp === "function" ? configImp(lib) : configImp;
|
|
1196
|
+
const { plugins, ...config } = resolved ?? {};
|
|
1197
|
+
return new AkanLibConfig(lib, config, plugins ?? []);
|
|
1198
|
+
}
|
|
1199
|
+
}
|
|
1200
|
+
//! need to refactor
|
|
1201
|
+
var shiftBuildNum = async (app, delta) => {
|
|
1202
|
+
const { buildNum } = (await AkanAppConfig.from(app)).mobile;
|
|
1203
|
+
const akanConfigPath = path4.join(app.cwdPath, "akan.config.ts");
|
|
1204
|
+
const akanConfig = fs2.readFileSync(akanConfigPath, "utf8");
|
|
1205
|
+
fs2.writeFileSync(akanConfigPath, akanConfig.replace(`buildNum: ${buildNum}`, `buildNum: ${buildNum + delta}`));
|
|
1206
|
+
};
|
|
1207
|
+
var increaseBuildNum = async (app) => await shiftBuildNum(app, 1);
|
|
1208
|
+
var decreaseBuildNum = async (app) => await shiftBuildNum(app, -1);
|
|
1249
1209
|
// pkgs/@akanjs/devkit/linter.ts
|
|
1250
1210
|
import { spawn } from "child_process";
|
|
1251
1211
|
import { existsSync, readFileSync } from "fs";
|
|
@@ -1299,15 +1259,24 @@ var asBiomeReport = (candidate) => {
|
|
|
1299
1259
|
}
|
|
1300
1260
|
};
|
|
1301
1261
|
var BIOME_CONFIG_FILES = ["biome.json", "biome.jsonc"];
|
|
1262
|
+
var emptyResult = (filePath) => ({
|
|
1263
|
+
filePath,
|
|
1264
|
+
messages: [],
|
|
1265
|
+
errorCount: 0,
|
|
1266
|
+
warningCount: 0,
|
|
1267
|
+
fixableErrorCount: 0,
|
|
1268
|
+
fixableWarningCount: 0
|
|
1269
|
+
});
|
|
1302
1270
|
|
|
1303
1271
|
class Linter {
|
|
1272
|
+
static biomeBinName = process.platform === "win32" ? "biome.exe" : "biome";
|
|
1304
1273
|
lintRoot;
|
|
1305
1274
|
configPath;
|
|
1306
1275
|
#biomeBin;
|
|
1307
1276
|
constructor(cwdPath) {
|
|
1308
1277
|
this.lintRoot = this.#findBiomeRootPath(cwdPath);
|
|
1309
1278
|
this.configPath = Linter.#configPathIn(this.lintRoot) ?? path5.join(this.lintRoot, "biome.json");
|
|
1310
|
-
const localBiomeBin = path5.join(this.lintRoot, "node_modules
|
|
1279
|
+
const localBiomeBin = path5.join(this.lintRoot, "node_modules", ".bin", Linter.biomeBinName);
|
|
1311
1280
|
this.#biomeBin = existsSync(localBiomeBin) ? localBiomeBin : "biome";
|
|
1312
1281
|
}
|
|
1313
1282
|
static #configPathIn(dir) {
|
|
@@ -1379,14 +1348,7 @@ class Linter {
|
|
|
1379
1348
|
if (diagnostic.severity !== "error" && diagnostic.severity !== "warning")
|
|
1380
1349
|
continue;
|
|
1381
1350
|
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
|
-
};
|
|
1351
|
+
const result = resultsByPath.get(diagnosticFilePath) ?? emptyResult(diagnosticFilePath);
|
|
1390
1352
|
const message = this.#createLintMessage(diagnostic);
|
|
1391
1353
|
result.messages.push(message);
|
|
1392
1354
|
if (message.severity === 2)
|
|
@@ -1396,14 +1358,7 @@ class Linter {
|
|
|
1396
1358
|
resultsByPath.set(diagnosticFilePath, result);
|
|
1397
1359
|
}
|
|
1398
1360
|
return [
|
|
1399
|
-
resultsByPath.get(filePath) ??
|
|
1400
|
-
filePath,
|
|
1401
|
-
messages: [],
|
|
1402
|
-
errorCount: 0,
|
|
1403
|
-
warningCount: 0,
|
|
1404
|
-
fixableErrorCount: 0,
|
|
1405
|
-
fixableWarningCount: 0
|
|
1406
|
-
},
|
|
1361
|
+
resultsByPath.get(filePath) ?? emptyResult(filePath),
|
|
1407
1362
|
...[...resultsByPath.entries()].filter(([resultPath]) => resultPath !== filePath).map(([, result]) => result)
|
|
1408
1363
|
];
|
|
1409
1364
|
}
|
|
@@ -1513,12 +1468,7 @@ ${errorText}, ${warningText} found`];
|
|
|
1513
1468
|
warningCount: acc.warningCount + result.warningCount,
|
|
1514
1469
|
fixableErrorCount: acc.fixableErrorCount + result.fixableErrorCount,
|
|
1515
1470
|
fixableWarningCount: acc.fixableWarningCount + result.fixableWarningCount
|
|
1516
|
-
}), {
|
|
1517
|
-
errorCount: 0,
|
|
1518
|
-
warningCount: 0,
|
|
1519
|
-
fixableErrorCount: 0,
|
|
1520
|
-
fixableWarningCount: 0
|
|
1521
|
-
});
|
|
1471
|
+
}), { errorCount: 0, warningCount: 0, fixableErrorCount: 0, fixableWarningCount: 0 });
|
|
1522
1472
|
return { results, details, stats };
|
|
1523
1473
|
}
|
|
1524
1474
|
async hasNoLintErrors(filePath) {
|
|
@@ -1576,12 +1526,9 @@ ${errorText}, ${warningText} found`];
|
|
|
1576
1526
|
async getProblematicRules(filePath) {
|
|
1577
1527
|
const { results } = await this.lintFile(filePath);
|
|
1578
1528
|
const ruleCounts = {};
|
|
1579
|
-
results.
|
|
1580
|
-
|
|
1581
|
-
|
|
1582
|
-
ruleCounts[message.ruleId] = (ruleCounts[message.ruleId] || 0) + 1;
|
|
1583
|
-
});
|
|
1584
|
-
});
|
|
1529
|
+
for (const message of results.flatMap((result) => result.messages))
|
|
1530
|
+
if (message.ruleId)
|
|
1531
|
+
ruleCounts[message.ruleId] = (ruleCounts[message.ruleId] || 0) + 1;
|
|
1585
1532
|
return ruleCounts;
|
|
1586
1533
|
}
|
|
1587
1534
|
}
|
|
@@ -1612,7 +1559,6 @@ var resolveRepoName = (workspaceRoot) => {
|
|
|
1612
1559
|
};
|
|
1613
1560
|
|
|
1614
1561
|
// pkgs/@akanjs/devkit/scanInfo.ts
|
|
1615
|
-
import { rm as rm2 } from "fs/promises";
|
|
1616
1562
|
import path7 from "path";
|
|
1617
1563
|
|
|
1618
1564
|
// pkgs/@akanjs/devkit/workspaceLayout.ts
|
|
@@ -1621,7 +1567,6 @@ var appRootAllowedFiles = new Set([
|
|
|
1621
1567
|
"CLAUDE.md",
|
|
1622
1568
|
"akan.app.json",
|
|
1623
1569
|
"akan.config.ts",
|
|
1624
|
-
"capacitor.config.ts",
|
|
1625
1570
|
"client.ts",
|
|
1626
1571
|
"main.ts",
|
|
1627
1572
|
"package.json",
|
|
@@ -1631,12 +1576,9 @@ var appRootAllowedFiles = new Set([
|
|
|
1631
1576
|
]);
|
|
1632
1577
|
var appRootAllowedDirs = new Set([
|
|
1633
1578
|
".akan",
|
|
1634
|
-
"android",
|
|
1635
1579
|
"common",
|
|
1636
1580
|
"env",
|
|
1637
|
-
"ios",
|
|
1638
1581
|
"lib",
|
|
1639
|
-
"mobile",
|
|
1640
1582
|
"page",
|
|
1641
1583
|
"plugin",
|
|
1642
1584
|
"private",
|
|
@@ -1688,13 +1630,20 @@ var libFacetRootAllowedFiles = new Set([
|
|
|
1688
1630
|
]);
|
|
1689
1631
|
var libFacetRootTestPattern = /^[A-Za-z][A-Za-z0-9_-]*\.signal\.(test|spec)\.(ts|tsx)$/;
|
|
1690
1632
|
var isAllowedLibFacetRootFile = (filename) => libFacetRootAllowedFiles.has(filename) || libFacetRootTestPattern.test(filename);
|
|
1633
|
+
var retiredAppRootEntries = new Set([
|
|
1634
|
+
"android",
|
|
1635
|
+
"ios",
|
|
1636
|
+
"mobile",
|
|
1637
|
+
"capacitor.config.ts",
|
|
1638
|
+
"capacitor.config.js",
|
|
1639
|
+
"capacitor.config.json"
|
|
1640
|
+
]);
|
|
1641
|
+
var rootEntryHintOf = (type, name) => type === "app" && retiredAppRootEntries.has(name) ? "a Capacitor-era entry; the native runtime generates its projects under .akan/mobile/<target>, so delete it" : null;
|
|
1691
1642
|
var isScannedRootEntry = (type, name) => !name.startsWith(".") || rootAllowedDirs[type].has(name);
|
|
1692
1643
|
|
|
1693
1644
|
// pkgs/@akanjs/devkit/scanInfo.ts
|
|
1694
|
-
var
|
|
1695
|
-
"constant",
|
|
1645
|
+
var serviceFileTypes = [
|
|
1696
1646
|
"dictionary",
|
|
1697
|
-
"document",
|
|
1698
1647
|
"service",
|
|
1699
1648
|
"signal",
|
|
1700
1649
|
"store",
|
|
@@ -1704,7 +1653,9 @@ var databaseFileTypes = [
|
|
|
1704
1653
|
"view",
|
|
1705
1654
|
"zone"
|
|
1706
1655
|
];
|
|
1707
|
-
var
|
|
1656
|
+
var databaseFileTypes = ["constant", "dictionary", "document", ...serviceFileTypes.slice(1)];
|
|
1657
|
+
var uiFileTypes = new Set(["template", "unit", "util", "view", "zone"]);
|
|
1658
|
+
var fileTypeOf = (filename) => databaseFileTypes.find((type) => filename.endsWith(uiFileTypes.has(type) ? `.${type[0].toUpperCase()}${type.slice(1)}.tsx` : `.${type}.ts`));
|
|
1708
1659
|
var internalLibDirs = new Set(["__lib", "__scalar"]);
|
|
1709
1660
|
var moduleNonUiFileTypes = {
|
|
1710
1661
|
database: new Set(["constant", "dictionary", "document", "service", "signal", "store"]),
|
|
@@ -1716,21 +1667,13 @@ var moduleUiFileTypes = {
|
|
|
1716
1667
|
service: new Set(["Util", "Zone"]),
|
|
1717
1668
|
scalar: new Set(["Template", "Unit"])
|
|
1718
1669
|
};
|
|
1719
|
-
var
|
|
1720
|
-
var createDependencyScanner = async (exec) => (await import("./dependencyScanner-shs82ynp.js")).TypeScriptDependencyScanner.from(exec);
|
|
1721
|
-
var isAllowedTestFile = (filename) => testFilePattern.test(filename);
|
|
1670
|
+
var createDependencyScanner = async (exec) => (await import("./dependencyScanner-ye2e6pdz.js")).TypeScriptDependencyScanner.from(exec);
|
|
1722
1671
|
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
|
-
}
|
|
1728
1672
|
var getModuleNameFromPath = (kind, modulePath) => {
|
|
1729
1673
|
const dirname = path7.basename(modulePath);
|
|
1730
1674
|
return kind === "service" ? dirname.replace(/^_+/, "") : dirname;
|
|
1731
1675
|
};
|
|
1732
1676
|
async function assertScanConvention(exec, libRoot) {
|
|
1733
|
-
await clearGeneratedRootCapacitorConfigs(exec);
|
|
1734
1677
|
const violations = [];
|
|
1735
1678
|
const addViolation = (relativePath, reason) => {
|
|
1736
1679
|
violations.push(`${getScanPath(exec, relativePath)}: ${reason}`);
|
|
@@ -1738,21 +1681,21 @@ async function assertScanConvention(exec, libRoot) {
|
|
|
1738
1681
|
const allowedRootFiles = rootAllowedFiles[exec.type];
|
|
1739
1682
|
const allowedRootDirs = rootAllowedDirs[exec.type];
|
|
1740
1683
|
const { files, dirs } = await exec.getFilesAndDirs(".");
|
|
1741
|
-
|
|
1742
|
-
|
|
1743
|
-
|
|
1744
|
-
|
|
1745
|
-
|
|
1746
|
-
|
|
1684
|
+
for (const filename of files)
|
|
1685
|
+
if (!allowedRootFiles.has(filename))
|
|
1686
|
+
addViolation(filename, rootEntryHintOf(exec.type, filename) ?? `unsupported ${exec.type} root file`);
|
|
1687
|
+
for (const dirname of dirs)
|
|
1688
|
+
if (!allowedRootDirs.has(dirname))
|
|
1689
|
+
addViolation(dirname, rootEntryHintOf(exec.type, dirname) ?? `unsupported ${exec.type} root folder`);
|
|
1747
1690
|
if (exec.type === "lib")
|
|
1748
1691
|
for (const { relativePath, reason } of await exec.getPageConventionViolations())
|
|
1749
1692
|
addViolation(relativePath, reason);
|
|
1750
|
-
|
|
1751
|
-
|
|
1752
|
-
|
|
1753
|
-
|
|
1754
|
-
|
|
1755
|
-
|
|
1693
|
+
for (const filename of libRoot.files)
|
|
1694
|
+
if (!isAllowedLibFacetRootFile(filename))
|
|
1695
|
+
addViolation(path7.join("lib", filename), "unsupported lib facet root file");
|
|
1696
|
+
for (const dirname of libRoot.dirs)
|
|
1697
|
+
if (dirname.startsWith("__") && !internalLibDirs.has(dirname))
|
|
1698
|
+
addViolation(path7.join("lib", dirname), "unsupported internal lib folder");
|
|
1756
1699
|
const databaseDirs = libRoot.dirs.filter((dirname) => !dirname.startsWith("_"));
|
|
1757
1700
|
const serviceDirs = libRoot.dirs.filter((dirname) => dirname.startsWith("_") && !dirname.startsWith("__"));
|
|
1758
1701
|
const scalarDirs = await exec.readdir("lib/__scalar");
|
|
@@ -1774,38 +1717,25 @@ async function validateModuleFiles(exec, violations, kind, modulePath) {
|
|
|
1774
1717
|
violations.push(`${getScanPath(exec, path7.join(modulePath, dirname))}: unsupported module folder`);
|
|
1775
1718
|
});
|
|
1776
1719
|
const uiModuleName = moduleName[0].toUpperCase() + moduleName.slice(1);
|
|
1777
|
-
|
|
1778
|
-
const
|
|
1779
|
-
if (filename === "index.ts" || filename === "index.tsx" ||
|
|
1780
|
-
|
|
1720
|
+
for (const filename of files) {
|
|
1721
|
+
const scanPath = getScanPath(exec, path7.join(modulePath, filename));
|
|
1722
|
+
if (filename === "index.ts" || filename === "index.tsx" || /\.(test|spec)\.(ts|tsx)$/.test(filename))
|
|
1723
|
+
continue;
|
|
1781
1724
|
if (filename === `${moduleName}.abstract.md`)
|
|
1782
|
-
|
|
1725
|
+
continue;
|
|
1783
1726
|
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;
|
|
1727
|
+
const match = uiMatch ?? filename.match(/^([a-z][a-zA-Z0-9]*)\.([a-z][a-z0-9]*)\.ts$/);
|
|
1728
|
+
if (!match) {
|
|
1729
|
+
violations.push(`${scanPath}: unsupported module file`);
|
|
1730
|
+
continue;
|
|
1806
1731
|
}
|
|
1807
|
-
|
|
1808
|
-
|
|
1732
|
+
const [, fileModuleName, fileType] = match;
|
|
1733
|
+
const expectedName = uiMatch ? uiModuleName : moduleName;
|
|
1734
|
+
if (fileModuleName !== expectedName)
|
|
1735
|
+
violations.push(`${scanPath}: module name mismatch: expected '${expectedName}', got '${fileModuleName}'`);
|
|
1736
|
+
if (!(uiMatch ? moduleUiFileTypes : moduleNonUiFileTypes)[kind].has(fileType))
|
|
1737
|
+
violations.push(`${scanPath}: unsupported ${kind}${uiMatch ? " UI" : ""} file`);
|
|
1738
|
+
}
|
|
1809
1739
|
}
|
|
1810
1740
|
|
|
1811
1741
|
class ScanInfo {
|
|
@@ -1853,79 +1783,17 @@ class ScanInfo {
|
|
|
1853
1783
|
} else
|
|
1854
1784
|
databaseDirs.push(name);
|
|
1855
1785
|
});
|
|
1786
|
+
const collect = async (dir, name, kind) => {
|
|
1787
|
+
for (const filename of await exec.readdir(dir)) {
|
|
1788
|
+
const type = fileTypeOf(filename);
|
|
1789
|
+
if (type)
|
|
1790
|
+
files[type][kind]?.push(name);
|
|
1791
|
+
}
|
|
1792
|
+
};
|
|
1856
1793
|
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
|
-
})
|
|
1794
|
+
...databaseDirs.map((name) => collect(path7.join("lib", name), name, "databases")),
|
|
1795
|
+
...serviceDirs.map((dirname) => collect(path7.join("lib", dirname), dirname.slice(1), "services")),
|
|
1796
|
+
...scalarDirs.map((name) => collect(path7.join("lib/__scalar", name), name, "scalars"))
|
|
1929
1797
|
]);
|
|
1930
1798
|
const routes = exec.type === "lib" ? [] : await exec.getPageKeys();
|
|
1931
1799
|
const common = {
|
|
@@ -1946,31 +1814,14 @@ class ScanInfo {
|
|
|
1946
1814
|
constructor(scanResult) {
|
|
1947
1815
|
this.name = scanResult.name;
|
|
1948
1816
|
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
|
-
});
|
|
1817
|
+
const modulesOf = { databases: this.database, services: this.service, scalars: this.scalar };
|
|
1818
|
+
for (const [key, groups] of Object.entries(scanResult.files))
|
|
1819
|
+
for (const kind of ["databases", "services", "scalars"])
|
|
1820
|
+
for (const name of groups[kind] ?? []) {
|
|
1821
|
+
modulesOf[kind].set(name, (modulesOf[kind].get(name) ?? new Set).add(key));
|
|
1822
|
+
this.file[key].all.add(name);
|
|
1823
|
+
this.file[key][kind].add(name);
|
|
1824
|
+
}
|
|
1974
1825
|
}
|
|
1975
1826
|
getScanResult() {
|
|
1976
1827
|
return this.scanResult;
|
|
@@ -1984,6 +1835,28 @@ class ScanInfo {
|
|
|
1984
1835
|
getScalarModules() {
|
|
1985
1836
|
return [...this.scalar.keys()];
|
|
1986
1837
|
}
|
|
1838
|
+
#sortedLibs = null;
|
|
1839
|
+
sortedLibs(libDeps) {
|
|
1840
|
+
if (this.#sortedLibs)
|
|
1841
|
+
return this.#sortedLibs;
|
|
1842
|
+
const libIndices = LibInfo.getSortedLibIndices();
|
|
1843
|
+
this.#sortedLibs = libDeps.sort((libNameA, libNameB) => {
|
|
1844
|
+
const indexA = libIndices.get(libNameA);
|
|
1845
|
+
const indexB = libIndices.get(libNameB);
|
|
1846
|
+
if (indexA === undefined || indexB === undefined)
|
|
1847
|
+
throw new Error(`LibInfo not found: ${libNameA} or ${libNameB}`);
|
|
1848
|
+
return indexA - indexB;
|
|
1849
|
+
});
|
|
1850
|
+
return this.#sortedLibs;
|
|
1851
|
+
}
|
|
1852
|
+
sortedLibInfos(libDeps) {
|
|
1853
|
+
return new Map(this.sortedLibs(libDeps).map((libName) => {
|
|
1854
|
+
const libInfo = LibInfo.libInfos.get(libName);
|
|
1855
|
+
if (!libInfo)
|
|
1856
|
+
throw new Error(`LibInfo not found: ${libName}`);
|
|
1857
|
+
return [libName, libInfo];
|
|
1858
|
+
}));
|
|
1859
|
+
}
|
|
1987
1860
|
}
|
|
1988
1861
|
var isAkanFrameworkDependency = (dep) => dep === "akanjs" || dep.startsWith("akanjs/");
|
|
1989
1862
|
|
|
@@ -2033,30 +1906,11 @@ class AppInfo extends ScanInfo {
|
|
|
2033
1906
|
}));
|
|
2034
1907
|
return [...libSet];
|
|
2035
1908
|
}
|
|
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
1909
|
getLibs() {
|
|
2051
|
-
return this
|
|
1910
|
+
return this.sortedLibs(this.libDeps);
|
|
2052
1911
|
}
|
|
2053
1912
|
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
|
-
}));
|
|
1913
|
+
return this.sortedLibInfos(this.libDeps);
|
|
2060
1914
|
}
|
|
2061
1915
|
}
|
|
2062
1916
|
|
|
@@ -2096,38 +1950,18 @@ class LibInfo extends ScanInfo {
|
|
|
2096
1950
|
getScanResult() {
|
|
2097
1951
|
return this.scanResult;
|
|
2098
1952
|
}
|
|
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
1953
|
getLibs() {
|
|
2114
|
-
return this
|
|
1954
|
+
return this.sortedLibs(this.scanResult.libDeps);
|
|
2115
1955
|
}
|
|
2116
1956
|
getLibInfo(libName) {
|
|
2117
1957
|
if (!this.getScanResult().libDeps.includes(libName))
|
|
2118
1958
|
return;
|
|
2119
|
-
|
|
2120
|
-
if (!libSet.has(libName))
|
|
1959
|
+
if (!this.getLibs().includes(libName))
|
|
2121
1960
|
throw new Error(`LibInfo is invalid: ${libName}`);
|
|
2122
1961
|
return LibInfo.libInfos.get(libName);
|
|
2123
1962
|
}
|
|
2124
1963
|
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
|
-
}));
|
|
1964
|
+
return this.sortedLibInfos(this.scanResult.libDeps);
|
|
2131
1965
|
}
|
|
2132
1966
|
}
|
|
2133
1967
|
|
|
@@ -2139,18 +1973,14 @@ class PkgInfo {
|
|
|
2139
1973
|
const [tsconfig, rootPackageJson] = await Promise.all([exec.getTsConfig(), exec.workspace.getPackageJson()]);
|
|
2140
1974
|
const scanner = await createDependencyScanner(exec);
|
|
2141
1975
|
const npmSet = new Set(Object.keys({ ...rootPackageJson.dependencies, ...rootPackageJson.devDependencies }));
|
|
2142
|
-
const
|
|
1976
|
+
const workspacePathOf = (resolve) => resolve.replace(/^\.\//, "");
|
|
1977
|
+
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
1978
|
const [npmDepSet, pkgPathDepSet] = await scanner.getImportSets([npmSet, pkgPathSet]);
|
|
2144
1979
|
const pkgDeps = [...pkgPathDepSet].map((path) => {
|
|
2145
1980
|
const pathSplitLength = path.split("/").length;
|
|
2146
|
-
return (tsconfig.compilerOptions.paths?.[path]?.[0] ?? "*").split("/").slice(1, 1 + pathSplitLength).join("/");
|
|
1981
|
+
return workspacePathOf(tsconfig.compilerOptions.paths?.[path]?.[0] ?? "*").split("/").slice(1, 1 + pathSplitLength).join("/");
|
|
2147
1982
|
}).filter((pkg) => pkg !== exec.name);
|
|
2148
|
-
|
|
2149
|
-
name: exec.name,
|
|
2150
|
-
pkgDeps,
|
|
2151
|
-
dependencies: [...npmDepSet]
|
|
2152
|
-
};
|
|
2153
|
-
return pkgScanResult;
|
|
1983
|
+
return { name: exec.name, pkgDeps, dependencies: [...npmDepSet] };
|
|
2154
1984
|
}
|
|
2155
1985
|
static #pkgInfos = new Map;
|
|
2156
1986
|
static async fromExecutor(exec, options = {}) {
|
|
@@ -2188,17 +2018,9 @@ class WorkspaceInfo {
|
|
|
2188
2018
|
return existingWorkspaceInfo;
|
|
2189
2019
|
const [appNames, libNames, pkgNames] = await Promise.all([exec.getApps(), exec.getLibs(), exec.getPkgs()]);
|
|
2190
2020
|
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
|
-
}))
|
|
2021
|
+
Promise.all(appNames.map(async (appName) => await AppExecutor.from(exec, appName).scan())),
|
|
2022
|
+
Promise.all(libNames.map(async (libName) => await LibExecutor.from(exec, libName).scan())),
|
|
2023
|
+
Promise.all(pkgNames.map(async (pkgName) => await PkgExecutor.from(exec, pkgName).scan()))
|
|
2202
2024
|
]);
|
|
2203
2025
|
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
2026
|
WorkspaceInfo.#workspaceInfos.set(exec.name, workspaceInfo);
|
|
@@ -2207,40 +2029,9 @@ class WorkspaceInfo {
|
|
|
2207
2029
|
}
|
|
2208
2030
|
|
|
2209
2031
|
// 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);
|
|
2032
|
+
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(" ")));
|
|
2033
|
+
var plainCommandArgPattern = process.platform === "win32" ? /^[\w@%+=:,./\\-]+$/ : /^[\w@%+=:,./-]+$/;
|
|
2034
|
+
var formatCommandArg = (value) => plainCommandArgPattern.test(value) ? value : JSON.stringify(value);
|
|
2244
2035
|
var formatCommandForDisplay = (command, args = []) => [command, ...args].map(formatCommandArg).join(" ");
|
|
2245
2036
|
|
|
2246
2037
|
class CommandExecutionError extends Error {
|
|
@@ -2314,6 +2105,10 @@ var parseEnvFile = (envPath) => {
|
|
|
2314
2105
|
}
|
|
2315
2106
|
return env;
|
|
2316
2107
|
};
|
|
2108
|
+
var withCapitalizedDict = (dict = {}) => ({
|
|
2109
|
+
...dict,
|
|
2110
|
+
...Object.fromEntries(Object.entries(dict).map(([key, value]) => [capitalize(key), capitalize(value)]))
|
|
2111
|
+
});
|
|
2317
2112
|
|
|
2318
2113
|
class Executor {
|
|
2319
2114
|
static verbose = false;
|
|
@@ -2340,144 +2135,51 @@ class Executor {
|
|
|
2340
2135
|
#stderr(data) {
|
|
2341
2136
|
Logger2.raw(chalk2.red(data.toString()));
|
|
2342
2137
|
}
|
|
2343
|
-
|
|
2344
|
-
const cwd = options.cwd?.toString() ?? this.cwdPath;
|
|
2345
|
-
const proc = exec(command, { cwd: this.cwdPath, ...options });
|
|
2138
|
+
#settle(proc, target, { onClose = false, keepLogs = false, redStderr = false } = {}) {
|
|
2346
2139
|
let stdout = "";
|
|
2347
2140
|
let stderr = "";
|
|
2348
2141
|
proc.stdout?.on("data", (data) => {
|
|
2349
2142
|
stdout += data.toString();
|
|
2143
|
+
if (keepLogs)
|
|
2144
|
+
this.logs.push(data.toString());
|
|
2350
2145
|
this.#stdout(data);
|
|
2351
2146
|
});
|
|
2352
2147
|
proc.stderr?.on("data", (data) => {
|
|
2353
2148
|
stderr += data.toString();
|
|
2354
|
-
|
|
2149
|
+
if (keepLogs)
|
|
2150
|
+
this.logs.push(data.toString());
|
|
2151
|
+
if (redStderr)
|
|
2152
|
+
this.#stderr(data);
|
|
2153
|
+
else
|
|
2154
|
+
this.#stdout(data);
|
|
2355
2155
|
});
|
|
2356
2156
|
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
|
-
}));
|
|
2157
|
+
proc.on("error", (cause) => reject(new CommandExecutionError({ ...target, code: null, signal: null, stdout, stderr, cause })));
|
|
2158
|
+
proc.on(onClose ? "close" : "exit", (code, signal) => {
|
|
2159
|
+
if ((onClose ? code !== 0 : code) || signal)
|
|
2160
|
+
reject(new CommandExecutionError({ ...target, code, signal, stdout, stderr }));
|
|
2378
2161
|
else
|
|
2379
|
-
resolve({ code, signal });
|
|
2162
|
+
resolve({ code, signal, stdout });
|
|
2380
2163
|
});
|
|
2381
2164
|
});
|
|
2382
2165
|
}
|
|
2166
|
+
exec(command, options = {}) {
|
|
2167
|
+
const target = { command, cwd: options.cwd?.toString() ?? this.cwdPath };
|
|
2168
|
+
const proc = exec(command, { cwd: this.cwdPath, ...options });
|
|
2169
|
+
return this.#settle(proc, target).then(({ code, signal }) => ({ code, signal }));
|
|
2170
|
+
}
|
|
2383
2171
|
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
|
-
});
|
|
2172
|
+
const target = { command, args, cwd: options.cwd?.toString() ?? this.cwdPath };
|
|
2173
|
+
const proc = spawn2(command, args, { cwd: this.cwdPath, ...options });
|
|
2174
|
+
return this.#settle(proc, target, { onClose: true, keepLogs: true }).then(({ stdout }) => stdout);
|
|
2429
2175
|
}
|
|
2430
2176
|
spawnSync(command, args = [], options = {}) {
|
|
2431
|
-
|
|
2432
|
-
cwd: this.cwdPath,
|
|
2433
|
-
...options
|
|
2434
|
-
});
|
|
2435
|
-
return proc;
|
|
2177
|
+
return spawn2(command, args, { cwd: this.cwdPath, ...options });
|
|
2436
2178
|
}
|
|
2437
2179
|
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
|
-
});
|
|
2180
|
+
const target = { command: modulePath, args, cwd: options.cwd?.toString() ?? this.cwdPath };
|
|
2181
|
+
const proc = fork(modulePath, args, { cwd: this.cwdPath, ...options });
|
|
2182
|
+
return this.#settle(proc, target, { redStderr: true }).then(({ code, signal }) => ({ code, signal }));
|
|
2481
2183
|
}
|
|
2482
2184
|
getPath(filePath) {
|
|
2483
2185
|
if (path8.isAbsolute(filePath))
|
|
@@ -2501,8 +2203,8 @@ class Executor {
|
|
|
2501
2203
|
}
|
|
2502
2204
|
async mkdir(dirPath) {
|
|
2503
2205
|
const writePath = this.getPath(dirPath);
|
|
2504
|
-
if (!await
|
|
2505
|
-
await
|
|
2206
|
+
if (!await FileSys2.dirExists(writePath))
|
|
2207
|
+
await mkdir2(writePath, { recursive: true });
|
|
2506
2208
|
this.logger.verbose(`Make directory ${writePath}`);
|
|
2507
2209
|
return this;
|
|
2508
2210
|
}
|
|
@@ -2521,27 +2223,25 @@ class Executor {
|
|
|
2521
2223
|
}
|
|
2522
2224
|
async getFilesAndDirs(dirPath) {
|
|
2523
2225
|
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 }));
|
|
2226
|
+
const glob = new Bun.Glob("*");
|
|
2227
|
+
const files = Array.from(glob.scanSync({ cwd: fullDirPath, onlyFiles: true }));
|
|
2228
|
+
const allEntries = Array.from(glob.scanSync({ cwd: fullDirPath, onlyFiles: false }));
|
|
2528
2229
|
const dirs = allEntries.filter((entry) => !files.includes(entry));
|
|
2529
2230
|
return { files, dirs };
|
|
2530
2231
|
}
|
|
2531
2232
|
async exists(filePath) {
|
|
2532
|
-
|
|
2533
|
-
return await FileSys.exists(readPath);
|
|
2233
|
+
return await FileSys2.exists(this.getPath(filePath));
|
|
2534
2234
|
}
|
|
2535
2235
|
async remove(filePath) {
|
|
2536
2236
|
const readPath = this.getPath(filePath);
|
|
2537
|
-
if (await
|
|
2538
|
-
await
|
|
2237
|
+
if (await FileSys2.fileExists(readPath))
|
|
2238
|
+
await FileSys2.delete(readPath);
|
|
2539
2239
|
this.logger.verbose(`Remove file ${readPath}`);
|
|
2540
2240
|
return this;
|
|
2541
2241
|
}
|
|
2542
2242
|
async removeDir(dirPath) {
|
|
2543
2243
|
const readPath = path8.join(this.getPath(dirPath), ".");
|
|
2544
|
-
if (await
|
|
2244
|
+
if (await FileSys2.entryExists(readPath))
|
|
2545
2245
|
await rm3(readPath, { recursive: true, force: true });
|
|
2546
2246
|
this.logger.verbose(`Remove directory ${readPath}`);
|
|
2547
2247
|
return this;
|
|
@@ -2549,22 +2249,22 @@ class Executor {
|
|
|
2549
2249
|
async writeFile(filePath, content, { overwrite = true, silent = false } = {}) {
|
|
2550
2250
|
const writePath = this.getPath(filePath);
|
|
2551
2251
|
const dir = path8.dirname(writePath);
|
|
2552
|
-
if (!await
|
|
2553
|
-
await
|
|
2252
|
+
if (!await FileSys2.dirExists(dir))
|
|
2253
|
+
await mkdir2(dir, { recursive: true });
|
|
2554
2254
|
let contentStr = typeof content === "string" ? content : `${JSON.stringify(content, null, 2)}
|
|
2555
2255
|
`;
|
|
2556
|
-
if (await
|
|
2557
|
-
const currentContent = await
|
|
2256
|
+
if (await FileSys2.fileExists(writePath)) {
|
|
2257
|
+
const currentContent = await FileSys2.readText(writePath);
|
|
2558
2258
|
if (currentContent === contentStr || !overwrite) {
|
|
2559
2259
|
this.logger.verbose(`File ${writePath} is unchanged`);
|
|
2560
2260
|
contentStr = currentContent;
|
|
2561
2261
|
} else {
|
|
2562
|
-
await
|
|
2262
|
+
await FileSys2.writeText(writePath, contentStr);
|
|
2563
2263
|
if (Logger2.isVerbose())
|
|
2564
2264
|
this.logger.rawLog(chalk2.yellow(`File Update: ${filePath}`));
|
|
2565
2265
|
}
|
|
2566
2266
|
} else {
|
|
2567
|
-
await
|
|
2267
|
+
await FileSys2.writeText(writePath, contentStr);
|
|
2568
2268
|
if (!silent)
|
|
2569
2269
|
this.logger.rawLog(chalk2.green(`File Create: ${filePath}`));
|
|
2570
2270
|
}
|
|
@@ -2579,22 +2279,20 @@ class Executor {
|
|
|
2579
2279
|
return { filePath, content };
|
|
2580
2280
|
}
|
|
2581
2281
|
async readFile(filePath) {
|
|
2582
|
-
|
|
2583
|
-
return await FileSys.readText(readPath);
|
|
2282
|
+
return await FileSys2.readText(this.getPath(filePath));
|
|
2584
2283
|
}
|
|
2585
2284
|
async readJson(filePath) {
|
|
2586
|
-
|
|
2587
|
-
return await FileSys.readJson(readPath);
|
|
2285
|
+
return await FileSys2.readJson(this.getPath(filePath));
|
|
2588
2286
|
}
|
|
2589
2287
|
async cp(srcPath, destPath, { dereference = false } = {}) {
|
|
2590
2288
|
const src = this.getPath(srcPath);
|
|
2591
2289
|
const dest = this.getPath(destPath);
|
|
2592
|
-
if (!await
|
|
2290
|
+
if (!await FileSys2.exists(src))
|
|
2593
2291
|
return;
|
|
2594
2292
|
const isDirectory = (await stat2(src)).isDirectory();
|
|
2595
|
-
if (!await
|
|
2596
|
-
await
|
|
2597
|
-
if (dereference)
|
|
2293
|
+
if (!await FileSys2.exists(dest) && isDirectory)
|
|
2294
|
+
await mkdir2(dest, { recursive: true });
|
|
2295
|
+
if (dereference || process.platform === "win32")
|
|
2598
2296
|
await cpEntry(src, dest, { recursive: isDirectory, dereference: true, force: true });
|
|
2599
2297
|
else
|
|
2600
2298
|
await $`cp -r ${src}${isDirectory ? "/." : ""} ${dest}`;
|
|
@@ -2618,19 +2316,10 @@ class Executor {
|
|
|
2618
2316
|
async getTsConfig(pathname = "tsconfig.json", { refresh } = {}) {
|
|
2619
2317
|
if (this.#tsconfig && !refresh)
|
|
2620
2318
|
return this.#tsconfig;
|
|
2621
|
-
|
|
2319
|
+
let tsconfig = await this.readJson(pathname);
|
|
2622
2320
|
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;
|
|
2321
|
+
const base = await this.getTsConfig(tsconfig.extends, { refresh });
|
|
2322
|
+
tsconfig = { ...base, ...tsconfig, compilerOptions: { ...base.compilerOptions, ...tsconfig.compilerOptions } };
|
|
2634
2323
|
}
|
|
2635
2324
|
this.#tsconfig = tsconfig;
|
|
2636
2325
|
return tsconfig;
|
|
@@ -2666,6 +2355,7 @@ class Executor {
|
|
|
2666
2355
|
scanInfo,
|
|
2667
2356
|
overwrite = true
|
|
2668
2357
|
}, dict = {}, options = {}) {
|
|
2358
|
+
const convertPath = (target) => Object.entries(dict).reduce((converted, [key, value]) => converted.replace(new RegExp(`__${key}__`, "g"), value), target);
|
|
2669
2359
|
if (targetPath.endsWith(".ts") || targetPath.endsWith(".tsx")) {
|
|
2670
2360
|
const getContent = await import(templatePath);
|
|
2671
2361
|
const result = await getContent.default(scanInfo ?? null, dict, options);
|
|
@@ -2673,24 +2363,21 @@ class Executor {
|
|
|
2673
2363
|
return null;
|
|
2674
2364
|
const filename = typeof result === "object" ? result.filename : path8.basename(targetPath).replace(".js", ".ts");
|
|
2675
2365
|
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}`);
|
|
2366
|
+
const convertedTargetPath = convertPath(`${path8.dirname(targetPath)}/${filename}`);
|
|
2678
2367
|
this.logger.verbose(`Apply template ${templatePath} to ${convertedTargetPath}`);
|
|
2679
2368
|
return this.writeFile(convertedTargetPath, content, { overwrite });
|
|
2680
2369
|
} else if (targetPath.endsWith(".template")) {
|
|
2681
|
-
const content = await
|
|
2682
|
-
const convertedTargetPath =
|
|
2370
|
+
const content = await FileSys2.readText(templatePath);
|
|
2371
|
+
const convertedTargetPath = convertPath(targetPath.slice(0, -9));
|
|
2683
2372
|
const convertedContent = Object.entries(dict).reduce((data, [key, value]) => data.replace(new RegExp(`<%= ${key} %>`, "g"), value), content);
|
|
2684
2373
|
this.logger.verbose(`Apply template ${templatePath} to ${convertedTargetPath}`);
|
|
2685
|
-
return this.writeFile(convertedTargetPath, convertedContent, {
|
|
2686
|
-
overwrite
|
|
2687
|
-
});
|
|
2374
|
+
return this.writeFile(convertedTargetPath, convertedContent, { overwrite });
|
|
2688
2375
|
} else if (staticTemplateFileExtensions.has(path8.extname(targetPath).toLowerCase())) {
|
|
2689
|
-
const convertedTargetPath =
|
|
2376
|
+
const convertedTargetPath = convertPath(targetPath);
|
|
2690
2377
|
const writePath = this.getPath(convertedTargetPath);
|
|
2691
2378
|
const dirname = path8.dirname(writePath);
|
|
2692
|
-
if (!await
|
|
2693
|
-
await
|
|
2379
|
+
if (!await FileSys2.dirExists(dirname))
|
|
2380
|
+
await mkdir2(dirname, { recursive: true });
|
|
2694
2381
|
await copyFile(templatePath, writePath);
|
|
2695
2382
|
this.logger.verbose(`Apply template ${templatePath} to ${convertedTargetPath}`);
|
|
2696
2383
|
return { filePath: writePath, content: "" };
|
|
@@ -2707,12 +2394,10 @@ class Executor {
|
|
|
2707
2394
|
}) {
|
|
2708
2395
|
const templateRoot = await this.#resolveTemplateRoot();
|
|
2709
2396
|
const templatePath = `${templateRoot}${template ? `/${template}` : ""}`;
|
|
2710
|
-
|
|
2711
|
-
if ((await stat2(prefixTemplatePath)).isFile()) {
|
|
2712
|
-
const filename = path8.basename(prefixTemplatePath);
|
|
2397
|
+
if ((await stat2(templatePath)).isFile()) {
|
|
2713
2398
|
const fileContent = await this.#applyTemplateFile({
|
|
2714
|
-
templatePath
|
|
2715
|
-
targetPath: path8.join(basePath,
|
|
2399
|
+
templatePath,
|
|
2400
|
+
targetPath: path8.join(basePath, path8.basename(templatePath)),
|
|
2716
2401
|
scanInfo,
|
|
2717
2402
|
overwrite
|
|
2718
2403
|
}, dict, options);
|
|
@@ -2748,17 +2433,13 @@ class Executor {
|
|
|
2748
2433
|
path8.resolve(getDirname2(import.meta.url), "../cli/templates")
|
|
2749
2434
|
];
|
|
2750
2435
|
for (const candidate of candidates) {
|
|
2751
|
-
if (await
|
|
2436
|
+
if (await FileSys2.dirExists(candidate))
|
|
2752
2437
|
return candidate;
|
|
2753
2438
|
}
|
|
2754
2439
|
return candidates[0];
|
|
2755
2440
|
}
|
|
2756
2441
|
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 });
|
|
2442
|
+
const fileContents = await this._applyTemplate({ ...options, dict: withCapitalizedDict(options.dict) });
|
|
2762
2443
|
await this.#formatAppliedTemplate(fileContents);
|
|
2763
2444
|
return fileContents;
|
|
2764
2445
|
}
|
|
@@ -2775,7 +2456,7 @@ class Executor {
|
|
|
2775
2456
|
}
|
|
2776
2457
|
}
|
|
2777
2458
|
async getTypeChecker() {
|
|
2778
|
-
const { TypeChecker } = await import("./typeChecker-
|
|
2459
|
+
const { TypeChecker } = await import("./typeChecker-px49mezc.js");
|
|
2779
2460
|
this.typeChecker ??= new TypeChecker(this);
|
|
2780
2461
|
return this.typeChecker;
|
|
2781
2462
|
}
|
|
@@ -2853,12 +2534,11 @@ class WorkspaceExecutor extends Executor {
|
|
|
2853
2534
|
this.workspaceRoot = workspaceRoot;
|
|
2854
2535
|
this.repoName = repoName;
|
|
2855
2536
|
}
|
|
2856
|
-
static #execs = new Map;
|
|
2857
2537
|
static fromRoot({
|
|
2858
2538
|
workspaceRoot = process.cwd(),
|
|
2859
2539
|
repoName = resolveRepoName(workspaceRoot)
|
|
2860
2540
|
} = {}) {
|
|
2861
|
-
return
|
|
2541
|
+
return new WorkspaceExecutor({ workspaceRoot, repoName });
|
|
2862
2542
|
}
|
|
2863
2543
|
static getBaseDevEnv(envPath) {
|
|
2864
2544
|
const sourceEnv = envPath ? { ...process.env, ...parseEnvFile(envPath) } : process.env;
|
|
@@ -2897,13 +2577,9 @@ class WorkspaceExecutor extends Executor {
|
|
|
2897
2577
|
return await WorkspaceInfo.fromExecutor(this);
|
|
2898
2578
|
}
|
|
2899
2579
|
async getApps() {
|
|
2900
|
-
if (!await FileSys.dirExists(`${this.workspaceRoot}/apps`))
|
|
2901
|
-
return [];
|
|
2902
2580
|
return await this.#getDirHasFile(`${this.workspaceRoot}/apps`, "akan.config.ts");
|
|
2903
2581
|
}
|
|
2904
2582
|
async getLibs() {
|
|
2905
|
-
if (!await FileSys.dirExists(`${this.workspaceRoot}/libs`))
|
|
2906
|
-
return [];
|
|
2907
2583
|
return await this.#getDirHasFile(`${this.workspaceRoot}/libs`, "akan.config.ts");
|
|
2908
2584
|
}
|
|
2909
2585
|
async getSyss() {
|
|
@@ -2911,8 +2587,6 @@ class WorkspaceExecutor extends Executor {
|
|
|
2911
2587
|
return [appNames, libNames];
|
|
2912
2588
|
}
|
|
2913
2589
|
async getPkgs() {
|
|
2914
|
-
if (!await FileSys.dirExists(`${this.workspaceRoot}/pkgs`))
|
|
2915
|
-
return [];
|
|
2916
2590
|
return await this.#getDirHasFile(`${this.workspaceRoot}/pkgs`, "package.json");
|
|
2917
2591
|
}
|
|
2918
2592
|
async getExecs() {
|
|
@@ -2977,6 +2651,8 @@ class WorkspaceExecutor extends Executor {
|
|
|
2977
2651
|
return stdout.split("\x00").filter((file) => !!file).sort();
|
|
2978
2652
|
}
|
|
2979
2653
|
async#getDirHasFile(basePath, targetFilename) {
|
|
2654
|
+
if (!await FileSys2.dirExists(basePath))
|
|
2655
|
+
return [];
|
|
2980
2656
|
const AVOID_DIRS = ["node_modules", "dist", "public", "webkit"];
|
|
2981
2657
|
const getDirs = async (dirname, maxDepth = 3, results = [], prefix = "") => {
|
|
2982
2658
|
const dirs = await this.readdir(dirname);
|
|
@@ -2984,8 +2660,8 @@ class WorkspaceExecutor extends Executor {
|
|
|
2984
2660
|
if (AVOID_DIRS.includes(dir))
|
|
2985
2661
|
return;
|
|
2986
2662
|
const dirPath = path8.join(dirname, dir);
|
|
2987
|
-
if (await
|
|
2988
|
-
const hasTargetFile = await
|
|
2663
|
+
if (await FileSys2.dirExists(dirPath)) {
|
|
2664
|
+
const hasTargetFile = await FileSys2.fileExists(path8.join(dirPath, targetFilename));
|
|
2989
2665
|
if (hasTargetFile)
|
|
2990
2666
|
results.push(`${prefix}${dir}`);
|
|
2991
2667
|
if (maxDepth > 0)
|
|
@@ -2994,39 +2670,25 @@ class WorkspaceExecutor extends Executor {
|
|
|
2994
2670
|
}));
|
|
2995
2671
|
return results;
|
|
2996
2672
|
};
|
|
2997
|
-
return await getDirs(basePath);
|
|
2673
|
+
return (await getDirs(basePath)).sort((a, b) => a.localeCompare(b));
|
|
2998
2674
|
}
|
|
2999
|
-
async
|
|
2675
|
+
async#fromEverySys(read) {
|
|
3000
2676
|
const [appNames, libNames] = await this.getSyss();
|
|
3001
|
-
const
|
|
3002
|
-
|
|
3003
|
-
|
|
3004
|
-
|
|
3005
|
-
|
|
2677
|
+
const fromApps = await Promise.all(appNames.map((appName) => read(AppExecutor.from(this, appName))));
|
|
2678
|
+
const fromLibs = await Promise.all(libNames.map((libName) => read(LibExecutor.from(this, libName))));
|
|
2679
|
+
return [...fromApps.flat(), ...fromLibs.flat()];
|
|
2680
|
+
}
|
|
2681
|
+
async getScalarConstantFiles() {
|
|
2682
|
+
return await this.#fromEverySys((sys) => sys.getScalarConstantFiles());
|
|
3006
2683
|
}
|
|
3007
2684
|
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;
|
|
2685
|
+
return await this.#fromEverySys((sys) => sys.getConstantFiles());
|
|
3014
2686
|
}
|
|
3015
2687
|
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;
|
|
2688
|
+
return await this.#fromEverySys((sys) => sys.getDictionaryFiles());
|
|
3022
2689
|
}
|
|
3023
2690
|
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;
|
|
2691
|
+
return await this.#fromEverySys((sys) => sys.getViewsSourceCode());
|
|
3030
2692
|
}
|
|
3031
2693
|
}
|
|
3032
2694
|
var scanFacetDirs = ["ui", "webkit", "srvkit", "common", "plugin"];
|
|
@@ -3090,24 +2752,11 @@ class SysExecutor extends Executor {
|
|
|
3090
2752
|
scanInfo,
|
|
3091
2753
|
options: { exec: this, facet }
|
|
3092
2754
|
})),
|
|
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
|
-
}))
|
|
2755
|
+
...[
|
|
2756
|
+
...scanInfo.getDatabaseModules().map((model) => [model, `lib/${model}`]),
|
|
2757
|
+
...scanInfo.getServiceModules().map((model) => [model, `lib/_${model}`]),
|
|
2758
|
+
...scanInfo.getScalarModules().map((model) => [model, `lib/__scalar/${model}`])
|
|
2759
|
+
].map(([model, basePath]) => this._applyTemplate({ basePath, template: "moduleRoot", scanInfo, dict: { model, Model: capitalize(model) } }))
|
|
3111
2760
|
];
|
|
3112
2761
|
}
|
|
3113
2762
|
async scan({
|
|
@@ -3174,40 +2823,38 @@ class SysExecutor extends Executor {
|
|
|
3174
2823
|
return { filePath, content };
|
|
3175
2824
|
}
|
|
3176
2825
|
async getDatabaseModules() {
|
|
3177
|
-
|
|
3178
|
-
return databaseModules;
|
|
2826
|
+
return (await this.readdir("lib")).filter((name) => !name.startsWith("_") && !name.endsWith(".ts")).filter((name) => existsSync2(`${this.cwdPath}/lib/${name}/${name}.constant.ts`));
|
|
3179
2827
|
}
|
|
3180
2828
|
async getServiceModules() {
|
|
3181
|
-
|
|
3182
|
-
return serviceModules;
|
|
2829
|
+
return (await this.readdir("lib")).filter((name) => name.startsWith("_") && !name.startsWith("__")).filter((name) => existsSync2(`${this.cwdPath}/lib/${name}/${name.slice(1)}.service.ts`));
|
|
3183
2830
|
}
|
|
3184
2831
|
async getScalarModules() {
|
|
3185
|
-
|
|
3186
|
-
|
|
2832
|
+
return (await this.readdir("lib/__scalar")).filter((name) => !name.startsWith("_")).filter((name) => existsSync2(`${this.cwdPath}/lib/__scalar/${name}/${name}.constant.ts`));
|
|
2833
|
+
}
|
|
2834
|
+
async#getComponentModules(role) {
|
|
2835
|
+
return (await this.readdir("lib")).filter((name) => !name.startsWith("_") && !name.endsWith(".ts")).filter((name) => existsSync2(`${this.cwdPath}/lib/${name}/${capitalize(name)}.${role}.tsx`));
|
|
2836
|
+
}
|
|
2837
|
+
async#getComponentSources(role) {
|
|
2838
|
+
const modules = await this.#getComponentModules(role);
|
|
2839
|
+
return Promise.all(modules.map((name) => this.getLocalFile(`lib/${name}/${capitalize(name)}.${role}.tsx`)));
|
|
3187
2840
|
}
|
|
3188
2841
|
async getViewComponents() {
|
|
3189
|
-
|
|
3190
|
-
return viewComponents;
|
|
2842
|
+
return await this.#getComponentModules("View");
|
|
3191
2843
|
}
|
|
3192
2844
|
async getUnitComponents() {
|
|
3193
|
-
|
|
3194
|
-
return unitComponents;
|
|
2845
|
+
return await this.#getComponentModules("Unit");
|
|
3195
2846
|
}
|
|
3196
2847
|
async getTemplateComponents() {
|
|
3197
|
-
|
|
3198
|
-
return templateComponents;
|
|
2848
|
+
return await this.#getComponentModules("Template");
|
|
3199
2849
|
}
|
|
3200
2850
|
async getViewsSourceCode() {
|
|
3201
|
-
|
|
3202
|
-
return Promise.all(viewComponents.map((viewComponent) => this.getLocalFile(`lib/${viewComponent}/${capitalize(viewComponent)}.View.tsx`)));
|
|
2851
|
+
return await this.#getComponentSources("View");
|
|
3203
2852
|
}
|
|
3204
2853
|
async getUnitsSourceCode() {
|
|
3205
|
-
|
|
3206
|
-
return Promise.all(unitComponents.map((unitComponent) => this.getLocalFile(`lib/${unitComponent}/${capitalize(unitComponent)}.Unit.tsx`)));
|
|
2854
|
+
return await this.#getComponentSources("Unit");
|
|
3207
2855
|
}
|
|
3208
2856
|
async getTemplatesSourceCode() {
|
|
3209
|
-
|
|
3210
|
-
return Promise.all(templateComponents.map((templateComponent) => this.getLocalFile(`lib/${templateComponent}/${capitalize(templateComponent)}.Template.tsx`)));
|
|
2857
|
+
return await this.#getComponentSources("Template");
|
|
3211
2858
|
}
|
|
3212
2859
|
async getScalarConstantFiles() {
|
|
3213
2860
|
const scalarModules = await this.getScalarModules();
|
|
@@ -3215,7 +2862,7 @@ class SysExecutor extends Executor {
|
|
|
3215
2862
|
}
|
|
3216
2863
|
async getScalarDictionaryFiles() {
|
|
3217
2864
|
const scalarModules = await this.getScalarModules();
|
|
3218
|
-
return Promise.all(scalarModules.map((scalarModule) => this.getLocalFile(`lib/${scalarModule}/${scalarModule}.dictionary.ts`)));
|
|
2865
|
+
return Promise.all(scalarModules.map((scalarModule) => this.getLocalFile(`lib/__scalar/${scalarModule}/${scalarModule}.dictionary.ts`)));
|
|
3219
2866
|
}
|
|
3220
2867
|
async getConstantFiles() {
|
|
3221
2868
|
const modules = await this.getModules();
|
|
@@ -3237,16 +2884,8 @@ class SysExecutor extends Executor {
|
|
|
3237
2884
|
return Promise.all(modules.map((module) => this.getLocalFile(`lib/${module}/${module}.dictionary.ts`)));
|
|
3238
2885
|
}
|
|
3239
2886
|
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
2887
|
const scanInfo = await this.scan();
|
|
3245
|
-
const fileContents = await this._applyTemplate({
|
|
3246
|
-
...options,
|
|
3247
|
-
scanInfo,
|
|
3248
|
-
dict
|
|
3249
|
-
});
|
|
2888
|
+
const fileContents = await this._applyTemplate({ ...options, scanInfo, dict: withCapitalizedDict(options.dict) });
|
|
3250
2889
|
await this.scan();
|
|
3251
2890
|
return fileContents;
|
|
3252
2891
|
}
|
|
@@ -3255,7 +2894,7 @@ class SysExecutor extends Executor {
|
|
|
3255
2894
|
class AppExecutor extends SysExecutor {
|
|
3256
2895
|
static #routeSourceValidator = null;
|
|
3257
2896
|
static async#getRouteSourceValidator() {
|
|
3258
|
-
AppExecutor.#routeSourceValidator ??= (await import("./routeSourceValidator-
|
|
2897
|
+
AppExecutor.#routeSourceValidator ??= (await import("./routeSourceValidator-7ycnfz0e.js")).RouteSourceValidator;
|
|
3259
2898
|
return AppExecutor.#routeSourceValidator;
|
|
3260
2899
|
}
|
|
3261
2900
|
dist;
|
|
@@ -3264,15 +2903,8 @@ class AppExecutor extends SysExecutor {
|
|
|
3264
2903
|
super({ workspace, name, type: "app" });
|
|
3265
2904
|
this.dist = new Executor(`dist/${name}`, `${this.workspace.workspaceRoot}/dist/apps/${name}`);
|
|
3266
2905
|
}
|
|
3267
|
-
static #execs = new Map;
|
|
3268
2906
|
static from(executor, name) {
|
|
3269
|
-
|
|
3270
|
-
if (exec)
|
|
3271
|
-
return exec;
|
|
3272
|
-
else if (executor instanceof WorkspaceExecutor)
|
|
3273
|
-
return new AppExecutor({ workspace: executor, name });
|
|
3274
|
-
else
|
|
3275
|
-
return new AppExecutor({ workspace: executor.workspace, name });
|
|
2907
|
+
return new AppExecutor({ workspace: executor instanceof WorkspaceExecutor ? executor : executor.workspace, name });
|
|
3276
2908
|
}
|
|
3277
2909
|
getEnv() {
|
|
3278
2910
|
return WorkspaceExecutor.getBaseDevEnv().env;
|
|
@@ -3288,10 +2920,8 @@ class AppExecutor extends SysExecutor {
|
|
|
3288
2920
|
return basePort + appIndex + portOffset;
|
|
3289
2921
|
}
|
|
3290
2922
|
getCommandEnv(env = {}) {
|
|
3291
|
-
const
|
|
3292
|
-
const
|
|
3293
|
-
const PORT = (basePort + portOffset).toString();
|
|
3294
|
-
const AKAN_PUBLIC_SERVER_PORT = portOffset ? (8282 + portOffset).toString() : undefined;
|
|
2923
|
+
const { portOffset } = WorkspaceExecutor.getBaseDevEnv();
|
|
2924
|
+
const PORT = (8282 + portOffset).toString();
|
|
3295
2925
|
return {
|
|
3296
2926
|
...process.env,
|
|
3297
2927
|
AKAN_PUBLIC_APP_NAME: this.name,
|
|
@@ -3299,18 +2929,24 @@ class AppExecutor extends SysExecutor {
|
|
|
3299
2929
|
NODE_NO_WARNINGS: "1",
|
|
3300
2930
|
PORT,
|
|
3301
2931
|
AKAN_PUBLIC_CLIENT_PORT: PORT,
|
|
3302
|
-
...
|
|
2932
|
+
...portOffset ? { AKAN_PUBLIC_SERVER_PORT: PORT } : {},
|
|
3303
2933
|
...env
|
|
3304
2934
|
};
|
|
3305
2935
|
}
|
|
2936
|
+
async getDatabaseModeEnv() {
|
|
2937
|
+
const akanConfig = await this.getConfig();
|
|
2938
|
+
return {
|
|
2939
|
+
AKAN_DATABASE_MODE: akanConfig.resolveDatabaseMode(),
|
|
2940
|
+
AKAN_DATABASE_MODES: akanConfig.database.modes.join(",")
|
|
2941
|
+
};
|
|
2942
|
+
}
|
|
3306
2943
|
async prepareCommand(type) {
|
|
3307
2944
|
const akanConfig = await this.getConfig();
|
|
3308
|
-
const databaseMode = process.env.AKAN_DATABASE_MODE ?? akanConfig.defaultDatabaseMode ?? "single";
|
|
3309
2945
|
const routeEnv = {
|
|
3310
2946
|
AKAN_PUBLIC_BASE_PATHS: [...akanConfig.basePaths].join(","),
|
|
3311
2947
|
AKAN_PUBLIC_API_PREFIX: akanConfig.api.prefix,
|
|
3312
2948
|
AKAN_PUBLIC_WS_PREFIX: akanConfig.api.websocketPrefix,
|
|
3313
|
-
|
|
2949
|
+
...await this.getDatabaseModeEnv()
|
|
3314
2950
|
};
|
|
3315
2951
|
Object.assign(process.env, routeEnv);
|
|
3316
2952
|
if (type === "build") {
|
|
@@ -3339,6 +2975,8 @@ class AppExecutor extends SysExecutor {
|
|
|
3339
2975
|
const buildEnv = { ...env };
|
|
3340
2976
|
delete buildEnv.AKAN_PUBLIC_CLIENT_PORT;
|
|
3341
2977
|
delete buildEnv.AKAN_PUBLIC_SERVER_PORT;
|
|
2978
|
+
delete buildEnv.AKAN_PUBLIC_OPERATION_MODE;
|
|
2979
|
+
delete process.env.AKAN_PUBLIC_OPERATION_MODE;
|
|
3342
2980
|
Object.assign(process.env, buildEnv);
|
|
3343
2981
|
}
|
|
3344
2982
|
return { env };
|
|
@@ -3348,16 +2986,7 @@ class AppExecutor extends SysExecutor {
|
|
|
3348
2986
|
if (this.#publicEnv)
|
|
3349
2987
|
return this.#publicEnv;
|
|
3350
2988
|
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
|
-
});
|
|
2989
|
+
const regexes = searchPatterns.map((pattern) => new RegExp(`^${pattern.split("*").map((part) => part.replace(/[.+^${}()|[\]\\?]/g, "\\$&")).join(".*")}$`));
|
|
3361
2990
|
const publicEnv = {};
|
|
3362
2991
|
for (const [k, v] of Object.entries(process.env)) {
|
|
3363
2992
|
if (typeof v !== "string")
|
|
@@ -3386,8 +3015,10 @@ class AppExecutor extends SysExecutor {
|
|
|
3386
3015
|
const owners = new Map;
|
|
3387
3016
|
const devOnlyKeys = new Set;
|
|
3388
3017
|
const devOnlyDirs = [];
|
|
3389
|
-
|
|
3390
|
-
|
|
3018
|
+
const pageRoots = await this.getPageRoots();
|
|
3019
|
+
const libKeyPrefixes = pageRoots.map((root) => root.keyPrefix).filter(Boolean);
|
|
3020
|
+
for (const root of pageRoots) {
|
|
3021
|
+
if (!await FileSys2.dirExists(root.dir))
|
|
3391
3022
|
continue;
|
|
3392
3023
|
for await (const rel of glob.scan({
|
|
3393
3024
|
cwd: root.dir,
|
|
@@ -3398,6 +3029,8 @@ class AppExecutor extends SysExecutor {
|
|
|
3398
3029
|
if (segments.some((s) => s === "node_modules"))
|
|
3399
3030
|
continue;
|
|
3400
3031
|
const posix = `${root.keyPrefix}${segments.join("/")}`;
|
|
3032
|
+
if (!root.keyPrefix && libKeyPrefixes.some((prefix) => posix.startsWith(prefix)))
|
|
3033
|
+
continue;
|
|
3401
3034
|
const absPath = path8.join(root.dir, ...segments);
|
|
3402
3035
|
validatePageSourceFile(posix, { filePath: absPath });
|
|
3403
3036
|
if (!isRouteSourceFile(posix))
|
|
@@ -3462,7 +3095,7 @@ class AppExecutor extends SysExecutor {
|
|
|
3462
3095
|
const entries = await readDirEntries(libsDir).catch(() => []);
|
|
3463
3096
|
for (const entry of entries.sort()) {
|
|
3464
3097
|
const dir = `${libsDir}/${entry}`;
|
|
3465
|
-
if (!await
|
|
3098
|
+
if (!await FileSys2.dirExists(dir))
|
|
3466
3099
|
continue;
|
|
3467
3100
|
roots.push({ dir, realDir: await realpath(dir), keyPrefix: `${parent}${AppExecutor.#pageLibsDir}/${entry}/` });
|
|
3468
3101
|
}
|
|
@@ -3487,7 +3120,8 @@ class AppExecutor extends SysExecutor {
|
|
|
3487
3120
|
return entries.map((entry) => `${parent}${AppExecutor.#pageLibsDir}/${entry}/`);
|
|
3488
3121
|
}))).flat();
|
|
3489
3122
|
const wanted = parents.flatMap((parent) => libs.map((lib) => `${parent}${AppExecutor.#pageLibsDir}/(${lib})/`));
|
|
3490
|
-
|
|
3123
|
+
const isLink = async (key) => (await lstat(`${this.cwdPath}/page/${key.slice(0, -1)}`).catch(() => null))?.isSymbolicLink() ?? false;
|
|
3124
|
+
if (linked.sort().join(",") === wanted.sort().join(",") && (await Promise.all(linked.map(isLink))).every(Boolean))
|
|
3491
3125
|
return false;
|
|
3492
3126
|
await Promise.all(parents.map((parent) => this.removeDir(`${this.cwdPath}/page/${parent}${AppExecutor.#pageLibsDir}`)));
|
|
3493
3127
|
for (const parent of parents) {
|
|
@@ -3503,7 +3137,7 @@ class AppExecutor extends SysExecutor {
|
|
|
3503
3137
|
async#resolvePageLibs(syncPageLibs, libDeps) {
|
|
3504
3138
|
if (!syncPageLibs)
|
|
3505
3139
|
return [];
|
|
3506
|
-
const hasPageDir = async (lib) => await
|
|
3140
|
+
const hasPageDir = async (lib) => await FileSys2.dirExists(`${this.workspace.workspaceRoot}/libs/${lib}/page`);
|
|
3507
3141
|
if (syncPageLibs === true) {
|
|
3508
3142
|
const libs = [];
|
|
3509
3143
|
for (const lib of libDeps)
|
|
@@ -3543,7 +3177,7 @@ class AppExecutor extends SysExecutor {
|
|
|
3543
3177
|
} catch (error) {
|
|
3544
3178
|
if (!isWindows)
|
|
3545
3179
|
throw error;
|
|
3546
|
-
await
|
|
3180
|
+
await mkdir2(linkPath, { recursive: true });
|
|
3547
3181
|
await cpEntry(targetPath, linkPath, { recursive: true, dereference: true, force: true });
|
|
3548
3182
|
}
|
|
3549
3183
|
}
|
|
@@ -3575,13 +3209,14 @@ class AppExecutor extends SysExecutor {
|
|
|
3575
3209
|
appPath: this.cwdPath,
|
|
3576
3210
|
executor: this,
|
|
3577
3211
|
getPath: (rel) => this.getPath(rel),
|
|
3578
|
-
fileExists: (rel) =>
|
|
3212
|
+
fileExists: (rel) => FileSys2.fileExists(this.getPath(rel)),
|
|
3213
|
+
readFile: (rel) => this.readFile(rel),
|
|
3579
3214
|
writeFile: async (rel, content, opts) => {
|
|
3580
3215
|
await this.writeFile(rel, content, opts);
|
|
3581
3216
|
},
|
|
3582
3217
|
readEnvClient: async () => {
|
|
3583
3218
|
const envClientPath = path8.join(this.cwdPath, "env", "env.client.ts");
|
|
3584
|
-
if (!await
|
|
3219
|
+
if (!await FileSys2.fileExists(envClientPath))
|
|
3585
3220
|
return null;
|
|
3586
3221
|
try {
|
|
3587
3222
|
const envUrl = pathToFileURL(envClientPath);
|
|
@@ -3612,16 +3247,6 @@ class AppExecutor extends SysExecutor {
|
|
|
3612
3247
|
return true;
|
|
3613
3248
|
});
|
|
3614
3249
|
}
|
|
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
3250
|
async increaseBuildNum() {
|
|
3626
3251
|
await increaseBuildNum(this);
|
|
3627
3252
|
}
|
|
@@ -3637,15 +3262,8 @@ class LibExecutor extends SysExecutor {
|
|
|
3637
3262
|
super({ workspace, name, type: "lib" });
|
|
3638
3263
|
this.dist = new Executor(`dist/${name}`, `${this.workspace.workspaceRoot}/dist/libs/${name}`);
|
|
3639
3264
|
}
|
|
3640
|
-
static #execs = new Map;
|
|
3641
3265
|
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 });
|
|
3266
|
+
return new LibExecutor({ workspace: executor instanceof WorkspaceExecutor ? executor : executor.workspace, name });
|
|
3649
3267
|
}
|
|
3650
3268
|
#akanConfig = null;
|
|
3651
3269
|
async getConfig({ refresh } = {}) {
|
|
@@ -3668,9 +3286,7 @@ class PkgExecutor extends Executor {
|
|
|
3668
3286
|
this.dist = new Executor(`dist/${name}`, `${this.workspace.workspaceRoot}/dist/pkgs/${name}`);
|
|
3669
3287
|
}
|
|
3670
3288
|
static from(executor, name) {
|
|
3671
|
-
|
|
3672
|
-
return new PkgExecutor({ workspace: executor, name });
|
|
3673
|
-
return new PkgExecutor({ workspace: executor.workspace, name });
|
|
3289
|
+
return new PkgExecutor({ workspace: executor instanceof WorkspaceExecutor ? executor : executor.workspace, name });
|
|
3674
3290
|
}
|
|
3675
3291
|
#scanInfo = null;
|
|
3676
3292
|
async scan({ refresh } = {}) {
|
|
@@ -3688,6 +3304,9 @@ class PkgExecutor extends Executor {
|
|
|
3688
3304
|
const rootVersion = rootDeps[dep];
|
|
3689
3305
|
if (rootVersion)
|
|
3690
3306
|
return rootVersion;
|
|
3307
|
+
const overrideVersion = rootPackageJson.overrides?.[dep];
|
|
3308
|
+
if (typeof overrideVersion === "string")
|
|
3309
|
+
return overrideVersion;
|
|
3691
3310
|
try {
|
|
3692
3311
|
const packageJsonPath = `pkgs/${dep}/package.json`;
|
|
3693
3312
|
if (!await Bun.file(path8.join(this.workspace.workspaceRoot, packageJsonPath)).exists())
|
|
@@ -3713,16 +3332,8 @@ class PkgExecutor extends Executor {
|
|
|
3713
3332
|
}
|
|
3714
3333
|
if (missingDeps.length > 0)
|
|
3715
3334
|
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
|
-
};
|
|
3335
|
+
const toDependencyMap = (names) => Object.fromEntries(names.map((dep) => [dep, dependencyVersions.get(dep)]));
|
|
3336
|
+
return { dependencies: toDependencyMap(dependencyNames), devDependencies: toDependencyMap(devDependencyNames) };
|
|
3726
3337
|
}
|
|
3727
3338
|
async updatePackageJsonDependencies(dependencies = [], devDependencies = []) {
|
|
3728
3339
|
const [rootPackageJson, pkgJson] = await Promise.all([this.workspace.getPackageJson(), this.getPackageJson()]);
|
|
@@ -3791,4 +3402,4 @@ class ModuleExecutor extends Executor {
|
|
|
3791
3402
|
}
|
|
3792
3403
|
}
|
|
3793
3404
|
|
|
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,
|
|
3405
|
+
export { AGENT_BLOCK_START, AGENT_BLOCK_END, stampBlockVersion, extractBlockVersion, readDevkitVersion, upsertAgentBlock, extractAgentBlock, renderRecipeEntries, collectScopeRecipeSources, renderScopeAgentBlock, CLAUDE_COMMENT_RULE, AsyncDefaultExportDetector, resolveSsrPageEntriesForApp, CodegenLock, Linter, rootAllowedFiles, rootAllowedDirs, isAllowedLibFacetRootFile, rootEntryHintOf, isScannedRootEntry, AppInfo, Spinner, Executor, WorkspaceExecutor, SysExecutor, AppExecutor, LibExecutor, PkgExecutor, ModuleExecutor };
|