@akanjs/cli 3.0.0-beta.20 → 3.0.0-beta.22
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-awv258wb.js → CodeAgent-0dh9fzzg.js} +2 -2
- package/{CodeTui-1nqh35ez.js → CodeTui-drwdvwg9.js} +1 -1
- package/{agent.command-1s9ag7sb.js → agent.command-ttn09rx3.js} +5 -5
- package/akanCode.js +2 -2
- package/{application.command-49hwy6va.js → application.command-mm5v2gzp.js} +43 -41
- package/{applicationBuildRunner-ybyp97pv.js → applicationBuildRunner-38exmejd.js} +2 -2
- package/buildBatch.proc.js +10 -4
- package/{cloud.command-d7rqtzt9.js → cloud.command-f1pnvp2a.js} +9 -8
- package/cloud.script-d61mcf0q.js +20 -0
- package/{code.command-nn48xzej.js → code.command-1jgk14tc.js} +6 -6
- package/commandManifest.json +1 -1
- package/{context.command-s3mcmvpa.js → context.command-4vpjmg22.js} +13 -13
- package/context.runner-ehjs419v.js +21 -0
- package/{guideline.command-sct4qnt5.js → guideline.command-7jk47fgq.js} +2 -2
- package/guidelines/agentRule/agentRule.instruction.md +3 -3
- package/guidelines/conventions/conventions.instruction.md +17 -5
- package/guidelines/runtimeRule/runtimeRule.instruction.md +3 -3
- package/incrementalBuilder.proc.js +1 -1
- package/{index-5pc1qrbv.js → index-1db2cw0y.js} +2 -2
- package/{index-13ffe9wp.js → index-2gh9sndr.js} +4 -4
- package/{index-ahemdr5n.js → index-4dn1p115.js} +3 -3
- package/{index-ygn77a5w.js → index-9629w7ns.js} +5 -3
- package/{index-ddxvdd1p.js → index-aadpbnt2.js} +5 -5
- package/{index-xsjh6nzh.js → index-aaz7n8s2.js} +3 -3
- package/{index-9d9bgcx4.js → index-ga6hygxr.js} +514 -480
- package/{index-ztp3e73t.js → index-hb8sgepf.js} +1 -1
- package/{index-xqpzbab5.js → index-m0ms3n1c.js} +7 -7
- package/{index-ww4z397j.js → index-m79y8b2h.js} +82 -168
- package/{index-as7xmyz8.js → index-mgfx3mfs.js} +5 -4
- package/{index-3sq321ak.js → index-pp54p5zk.js} +2 -2
- package/{index-xqjwd384.js → index-q44jbw48.js} +1 -1
- package/{index-w2zdcw3k.js → index-rmcw8yc8.js} +1 -1
- package/{index-dzf3r49a.js → index-rnt66vym.js} +1 -1
- package/{index-8sf7wmdr.js → index-sz0myv9k.js} +5 -5
- package/{index-8mna0947.js → index-tgjy8xf3.js} +4 -4
- package/{index-nv5976x3.js → index-vj1cwwy5.js} +24 -13
- package/{index-przybvy5.js → index-wp43xrjv.js} +4 -4
- package/{index-31yr441d.js → index-ya5qbmyg.js} +3 -3
- package/{index-axzjaqve.js → index-yjkte9aj.js} +5 -4
- package/index-ys6f0bvh.js +471 -0
- package/{index-myhc3c1j.js → index-yy4s1j72.js} +1 -1
- package/index.js +21 -21
- package/{library.command-1gx04jgn.js → library.command-ccb1cp92.js} +3 -3
- package/{localRegistry.command-zndxj1q6.js → localRegistry.command-pgbr7vjj.js} +8 -7
- package/{module.command-3waddk95.js → module.command-5k6h407d.js} +5 -5
- package/{package.command-zrc7br1y.js → package.command-e6ntvpez.js} +3 -3
- package/package.json +2 -3
- package/{page.command-vpjgardh.js → page.command-v4f90wb8.js} +3 -3
- package/{primitive.command-sz84tk4t.js → primitive.command-g6km57q5.js} +6 -6
- package/{quality.command-g869hafs.js → quality.command-de3p36pz.js} +5 -4
- package/{repair.command-chsnjvvv.js → repair.command-d3x1javq.js} +5 -5
- package/{scalar.command-2v21newx.js → scalar.command-7ezrac2b.js} +4 -4
- package/{subspace.command-ckcbwfp1.js → subspace.command-g2tq9nrn.js} +4 -4
- package/templates/workspaceRoot/.gitignore.template +0 -1
- package/{tunnel.command-kd2yr4fx.js → tunnel.command-hsrv2mny.js} +2 -2
- package/{workflow.command-44252cqv.js → workflow.command-z2p32nmf.js} +10 -10
- package/{workspace.command-fqcztt5p.js → workspace.command-s82wd61q.js} +20 -20
- package/applicationReleasePackager-esnrphw8.js +0 -224
- package/capacitorApp-1synz4en.js +0 -11
- package/cloud.script-bkbpcr96.js +0 -19
- package/context.runner-k99nb5pk.js +0 -21
- package/index-5e0n4hqx.js +0 -75
- package/index-mxav6j3c.js +0 -1377
|
@@ -4,9 +4,324 @@ import {
|
|
|
4
4
|
FileSys2
|
|
5
5
|
} from "./index-5b6tavv3.js";
|
|
6
6
|
|
|
7
|
-
// pkgs/@akanjs/devkit/
|
|
8
|
-
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";
|
|
9
13
|
import path from "path";
|
|
14
|
+
|
|
15
|
+
class AsyncDefaultExportDetector {
|
|
16
|
+
static #typescriptLoad;
|
|
17
|
+
static #loadTypescript() {
|
|
18
|
+
AsyncDefaultExportDetector.#typescriptLoad ??= import("typescript").then((mod) => mod.default ?? mod);
|
|
19
|
+
return AsyncDefaultExportDetector.#typescriptLoad;
|
|
20
|
+
}
|
|
21
|
+
static async detect(moduleAbsPath) {
|
|
22
|
+
try {
|
|
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;
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
#ts;
|
|
32
|
+
constructor(typescript) {
|
|
33
|
+
this.#ts = typescript;
|
|
34
|
+
}
|
|
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;
|
|
48
|
+
}
|
|
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
|
+
}
|
|
55
|
+
continue;
|
|
56
|
+
}
|
|
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
|
+
}
|
|
73
|
+
}
|
|
74
|
+
return defaultIdentifier ? asyncBindings.get(defaultIdentifier) === true : false;
|
|
75
|
+
}
|
|
76
|
+
#hasModifier(node, kind) {
|
|
77
|
+
const ts = this.#ts;
|
|
78
|
+
return ts.canHaveModifiers(node) && (ts.getModifiers(node)?.some((modifier) => modifier.kind === kind) ?? false);
|
|
79
|
+
}
|
|
80
|
+
#isAsyncFunctionExpression(node) {
|
|
81
|
+
const ts = this.#ts;
|
|
82
|
+
return Boolean(node && (ts.isArrowFunction(node) || ts.isFunctionExpression(node)) && this.#hasModifier(node, ts.SyntaxKind.AsyncKeyword));
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
|
|
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))};
|
|
103
|
+
|
|
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
|
+
});
|
|
156
|
+
}
|
|
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`);
|
|
175
|
+
}
|
|
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";
|
|
10
325
|
import { Logger } from "akanjs/common";
|
|
11
326
|
|
|
12
327
|
class CodegenLock {
|
|
@@ -17,7 +332,7 @@ class CodegenLock {
|
|
|
17
332
|
static #logger = new Logger("CodegenLock");
|
|
18
333
|
static #queue = Promise.resolve();
|
|
19
334
|
static pathIn(workspaceRoot) {
|
|
20
|
-
return
|
|
335
|
+
return path3.join(workspaceRoot, "local", ".akan", CodegenLock.fileName);
|
|
21
336
|
}
|
|
22
337
|
static async run(workspaceRoot, label, fn) {
|
|
23
338
|
const ahead = CodegenLock.#queue;
|
|
@@ -47,7 +362,7 @@ class CodegenLock {
|
|
|
47
362
|
}
|
|
48
363
|
static async#withFileLock(workspaceRoot, label, fn) {
|
|
49
364
|
const lockPath = CodegenLock.pathIn(workspaceRoot);
|
|
50
|
-
await mkdir(
|
|
365
|
+
await mkdir(path3.dirname(lockPath), { recursive: true }).catch(() => {
|
|
51
366
|
return;
|
|
52
367
|
});
|
|
53
368
|
const held = await CodegenLock.#acquire(lockPath, label);
|
|
@@ -55,7 +370,7 @@ class CodegenLock {
|
|
|
55
370
|
return await fn();
|
|
56
371
|
} finally {
|
|
57
372
|
if (held)
|
|
58
|
-
await
|
|
373
|
+
await rm2(lockPath, { force: true }).catch(() => {
|
|
59
374
|
return;
|
|
60
375
|
});
|
|
61
376
|
}
|
|
@@ -92,7 +407,7 @@ class CodegenLock {
|
|
|
92
407
|
return false;
|
|
93
408
|
} else if (Date.now() - info.mtimeMs < CodegenLock.unknownHolderStaleMs)
|
|
94
409
|
return false;
|
|
95
|
-
await
|
|
410
|
+
await rm2(lockPath, { force: true }).catch(() => {
|
|
96
411
|
return;
|
|
97
412
|
});
|
|
98
413
|
return true;
|
|
@@ -354,8 +669,8 @@ ${CLAUDE_COMMENT_RULE}
|
|
|
354
669
|
`;
|
|
355
670
|
|
|
356
671
|
// pkgs/@akanjs/devkit/akanConfig/akanConfig.ts
|
|
357
|
-
import
|
|
358
|
-
import
|
|
672
|
+
import fs2 from "fs";
|
|
673
|
+
import path4 from "path";
|
|
359
674
|
import { fileURLToPath } from "url";
|
|
360
675
|
import { DatabaseModes, normalizeRoutePrefix } from "akanjs/base";
|
|
361
676
|
import { resolveAkanI18nConfig } from "akanjs/common";
|
|
@@ -374,17 +689,6 @@ var DEFAULT_OPTIMIZE_IMPORTS = [
|
|
|
374
689
|
var WORKSPACE_BARREL_FACETS = ["ui", "webkit", "common", "client", "server"];
|
|
375
690
|
var DEFAULT_DOCKER_IMAGE = "oven/bun:1-slim";
|
|
376
691
|
var SSR_RUNTIME_PACKAGES = ["react", "react-dom", "react-server-dom-webpack"];
|
|
377
|
-
var MOBILE_RUNTIME_PACKAGES = [
|
|
378
|
-
"@capacitor/cli",
|
|
379
|
-
"@capacitor/core",
|
|
380
|
-
"@capacitor/ios",
|
|
381
|
-
"@capacitor/android",
|
|
382
|
-
"@capacitor/assets"
|
|
383
|
-
];
|
|
384
|
-
var MOBILE_APP_CAPACITOR_PLUGINS = [
|
|
385
|
-
..."app browser camera core device geolocation haptics inappbrowser keyboard preferences push-notifications".split(" ").map((name) => `@capacitor/${name}`),
|
|
386
|
-
"capacitor-plugin-safe-area"
|
|
387
|
-
];
|
|
388
692
|
var DEFAULT_AKAN_IMAGE_CONFIG = {
|
|
389
693
|
deviceSizes: [640, 750, 828, 1080, 1200, 1920, 2048, 3840],
|
|
390
694
|
imageSizes: [32, 48, 64, 96, 128, 256, 384],
|
|
@@ -530,7 +834,7 @@ class AkanAppConfig {
|
|
|
530
834
|
#resolveWebConfig(web) {
|
|
531
835
|
const resolved = typeof web === "object" ? { ssr: true, csr: web.csr } : { ssr: web ?? true, csr: web ?? true };
|
|
532
836
|
if (!resolved.csr && this.hasMobileConfig)
|
|
533
|
-
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.`);
|
|
534
838
|
return resolved;
|
|
535
839
|
}
|
|
536
840
|
#resolveMobileConfig(mobile) {
|
|
@@ -539,57 +843,113 @@ class AkanAppConfig {
|
|
|
539
843
|
indexPath: _indexPath,
|
|
540
844
|
...rawMobile
|
|
541
845
|
} = mobile ?? {};
|
|
846
|
+
const configPath = `apps/${this.app.name}/akan.config.ts`;
|
|
847
|
+
AkanAppConfig.#assertMobileKeys(rawMobile, AkanAppConfig.#mobileKeys, "mobile", configPath);
|
|
542
848
|
const appName = rawMobile.appName ?? this.app.name;
|
|
543
849
|
const appId = rawMobile.appId ?? deriveDefaultAppId(this.baseDevEnv.repoName, this.app.name);
|
|
544
850
|
const version = rawMobile.version ?? "0.0.1";
|
|
545
851
|
const buildNum = rawMobile.buildNum ?? 1;
|
|
852
|
+
const files = AkanAppConfig.#resolveMobileFiles(rawMobile.files, "mobile", configPath);
|
|
853
|
+
const native = rawMobile.native;
|
|
546
854
|
const defaultTargetName = this.#defaultMobileTargetName(rawTargets);
|
|
547
855
|
const targetEntries = Object.entries(rawTargets ?? {
|
|
548
856
|
[defaultTargetName]: {}
|
|
549
857
|
});
|
|
550
858
|
const targets = Object.fromEntries(targetEntries.map(([name, rawTarget]) => {
|
|
551
|
-
const target = rawTarget;
|
|
859
|
+
const target = rawTarget ?? {};
|
|
860
|
+
const where = `mobile.targets.${name}`;
|
|
861
|
+
AkanAppConfig.#assertMobileKeys(target, AkanAppConfig.#targetKeys, where, configPath);
|
|
552
862
|
const fallbackBasePath = !rawTargets && this.basePaths.has(name) ? name : undefined;
|
|
553
863
|
const basePath = (target.basePath ?? fallbackBasePath)?.replace(/^\/+|\/+$/g, "") || undefined;
|
|
554
864
|
const indexPath = normalizeIndexPath(target.indexPath);
|
|
555
865
|
const deepLinks = normalizeDeepLinks(target.deepLinks);
|
|
556
866
|
if (basePath && !this.basePaths.has(basePath)) {
|
|
557
|
-
throw new Error(`Mobile target '${name}' uses unknown basePath '${basePath}' in
|
|
867
|
+
throw new Error(`Mobile target '${name}' uses unknown basePath '${basePath}' in ${configPath}`);
|
|
558
868
|
}
|
|
869
|
+
const targetFiles = AkanAppConfig.#resolveMobileFiles(target.files, where, configPath);
|
|
870
|
+
const fileName = target.fileName ?? rawMobile.fileName;
|
|
559
871
|
const resolved = {
|
|
560
|
-
...rawMobile,
|
|
561
|
-
...target,
|
|
562
872
|
name,
|
|
563
873
|
basePath,
|
|
564
874
|
indexPath,
|
|
565
875
|
deepLinks,
|
|
566
876
|
appName: target.appName ?? appName,
|
|
567
877
|
appId: target.appId ?? appId,
|
|
878
|
+
...fileName ? { fileName } : {},
|
|
568
879
|
version: target.version ?? version,
|
|
569
880
|
buildNum: target.buildNum ?? buildNum,
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
}
|
|
574
|
-
android: {
|
|
575
|
-
...rawMobile.android,
|
|
576
|
-
...target.android
|
|
577
|
-
},
|
|
578
|
-
ios: {
|
|
579
|
-
...rawMobile.ios,
|
|
580
|
-
...target.ios
|
|
581
|
-
}
|
|
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) } : {}
|
|
582
885
|
};
|
|
583
886
|
return [name, resolved];
|
|
584
887
|
}));
|
|
585
888
|
return {
|
|
586
|
-
...rawMobile,
|
|
587
889
|
appName,
|
|
588
890
|
appId,
|
|
891
|
+
...rawMobile.fileName ? { fileName: rawMobile.fileName } : {},
|
|
589
892
|
version,
|
|
590
893
|
buildNum,
|
|
591
|
-
|
|
592
|
-
|
|
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 } : {}
|
|
593
953
|
};
|
|
594
954
|
}
|
|
595
955
|
#defaultMobileTargetName(rawTargets) {
|
|
@@ -695,7 +1055,7 @@ CMD [${command.map((c) => `"${c}"`).join(",")}]`;
|
|
|
695
1055
|
static async from(app, { bustImportCache = false } = {}) {
|
|
696
1056
|
const [configImp, baseDevEnv, libs, rootPackageJson] = await Promise.all([
|
|
697
1057
|
AkanAppConfig.importConfigModule(app.cwdPath, { bustImportCache }),
|
|
698
|
-
WorkspaceExecutor.getBaseDevEnv(
|
|
1058
|
+
WorkspaceExecutor.getBaseDevEnv(path4.join(app.workspace.workspaceRoot, ".env")),
|
|
699
1059
|
app.workspace.getLibs(),
|
|
700
1060
|
app.workspace.getPackageJson()
|
|
701
1061
|
]);
|
|
@@ -736,429 +1096,116 @@ CMD [${command.map((c) => `"${c}"`).join(",")}]`;
|
|
|
736
1096
|
];
|
|
737
1097
|
}
|
|
738
1098
|
getDatabaseModeRuntimePackages(databaseMode) {
|
|
739
|
-
return [...DatabaseModes.drivers[databaseMode]];
|
|
740
|
-
}
|
|
741
|
-
getMissingDatabaseModeDependencySpecs(databaseMode) {
|
|
742
|
-
return this.#getMissingDependencySpecs(this.getDatabaseModeRuntimePackages(databaseMode));
|
|
743
|
-
}
|
|
744
|
-
resolveDatabaseMode() {
|
|
745
|
-
return DatabaseModes.resolve({
|
|
746
|
-
requested: process.env.AKAN_DATABASE_MODE,
|
|
747
|
-
declared: this.database.modes.join(","),
|
|
748
|
-
local: true
|
|
749
|
-
});
|
|
750
|
-
}
|
|
751
|
-
static #database(app, config) {
|
|
752
|
-
const declared = config?.database?.modes?.filter((mode) => !!mode);
|
|
753
|
-
const modes = declared?.length ? declared : ["single"];
|
|
754
|
-
return { modes: DatabaseModes.parseList(modes.join(","), `database.modes in apps/${app.name}/akan.config.ts`) };
|
|
755
|
-
}
|
|
756
|
-
getMobileRuntimePackages() {
|
|
757
|
-
return [...new Set([...MOBILE_RUNTIME_PACKAGES, ...MOBILE_APP_CAPACITOR_PLUGINS])];
|
|
758
|
-
}
|
|
759
|
-
getMissingMobileDependencySpecs() {
|
|
760
|
-
return this.#getMissingDependencySpecs(this.getMobileRuntimePackages());
|
|
761
|
-
}
|
|
762
|
-
getMobileAppCapacitorPlugins() {
|
|
763
|
-
return [...MOBILE_APP_CAPACITOR_PLUGINS];
|
|
764
|
-
}
|
|
765
|
-
#getMissingDependencySpecs(libs) {
|
|
766
|
-
const rootDependencies = {
|
|
767
|
-
...this.rootPackageJson.dependencies,
|
|
768
|
-
...this.rootPackageJson.devDependencies
|
|
769
|
-
};
|
|
770
|
-
return libs.filter((lib) => !rootDependencies[lib]).map((lib) => `${lib}@${this.#resolveProductionDependencyVersion(lib)}`);
|
|
771
|
-
}
|
|
772
|
-
get akanVersion() {
|
|
773
|
-
return getAkanPackageJson().version;
|
|
774
|
-
}
|
|
775
|
-
getProductionPackageJson(data = {}) {
|
|
776
|
-
return {
|
|
777
|
-
name: this.app.name,
|
|
778
|
-
description: this.app.name,
|
|
779
|
-
version: "1.0.0",
|
|
780
|
-
main: "./main.js",
|
|
781
|
-
dependencies: Object.fromEntries([...new Set(this.#getProductionRuntimePackages())].map((lib) => [
|
|
782
|
-
lib,
|
|
783
|
-
this.#resolveProductionDependencyVersion(lib)
|
|
784
|
-
])),
|
|
785
|
-
...data
|
|
786
|
-
};
|
|
787
|
-
}
|
|
788
|
-
}
|
|
789
|
-
var akanPackageJson = null;
|
|
790
|
-
function getAkanPackageJson() {
|
|
791
|
-
if (akanPackageJson)
|
|
792
|
-
return akanPackageJson;
|
|
793
|
-
const sourceDir = path2.dirname(fileURLToPath(import.meta.url));
|
|
794
|
-
const packageJsonPaths = [
|
|
795
|
-
path2.join(sourceDir, "../../../akanjs/package.json"),
|
|
796
|
-
path2.join(process.cwd(), "pkgs/akanjs/package.json"),
|
|
797
|
-
path2.join(path2.dirname(Bun.main), "node_modules/akanjs/package.json")
|
|
798
|
-
];
|
|
799
|
-
try {
|
|
800
|
-
packageJsonPaths.unshift(Bun.resolveSync("akanjs/package.json", sourceDir));
|
|
801
|
-
} catch {}
|
|
802
|
-
for (const packageJsonPath of packageJsonPaths) {
|
|
803
|
-
try {
|
|
804
|
-
akanPackageJson = JSON.parse(fs.readFileSync(packageJsonPath, "utf8"));
|
|
805
|
-
return akanPackageJson;
|
|
806
|
-
} catch {}
|
|
807
|
-
}
|
|
808
|
-
akanPackageJson = {
|
|
809
|
-
name: "akanjs",
|
|
810
|
-
version: "0.0.0",
|
|
811
|
-
description: "akanjs",
|
|
812
|
-
dependencies: {}
|
|
813
|
-
};
|
|
814
|
-
return akanPackageJson;
|
|
815
|
-
}
|
|
816
|
-
function mergeImageConfig(config = {}) {
|
|
817
|
-
return {
|
|
818
|
-
...DEFAULT_AKAN_IMAGE_CONFIG,
|
|
819
|
-
...config,
|
|
820
|
-
deviceSizes: config.deviceSizes ?? DEFAULT_AKAN_IMAGE_CONFIG.deviceSizes,
|
|
821
|
-
imageSizes: config.imageSizes ?? DEFAULT_AKAN_IMAGE_CONFIG.imageSizes,
|
|
822
|
-
formats: config.formats ?? DEFAULT_AKAN_IMAGE_CONFIG.formats,
|
|
823
|
-
qualities: config.qualities ?? DEFAULT_AKAN_IMAGE_CONFIG.qualities,
|
|
824
|
-
remotePatterns: config.remotePatterns ?? DEFAULT_AKAN_IMAGE_CONFIG.remotePatterns,
|
|
825
|
-
localPatterns: config.localPatterns ?? DEFAULT_AKAN_IMAGE_CONFIG.localPatterns
|
|
826
|
-
};
|
|
827
|
-
}
|
|
828
|
-
|
|
829
|
-
class AkanLibConfig {
|
|
830
|
-
lib;
|
|
831
|
-
externalLibs;
|
|
832
|
-
docker;
|
|
833
|
-
assets;
|
|
834
|
-
plugins;
|
|
835
|
-
constructor(lib, config, plugins = []) {
|
|
836
|
-
this.lib = lib;
|
|
837
|
-
this.externalLibs = config?.externalLibs ?? [];
|
|
838
|
-
this.docker = { preRuns: config?.docker?.preRuns ?? [], postRuns: config?.docker?.postRuns ?? [] };
|
|
839
|
-
this.assets = { keepFonts: normalizeKeepFonts(config?.assets?.keepFonts) };
|
|
840
|
-
this.plugins = plugins;
|
|
841
|
-
}
|
|
842
|
-
static async from(lib, { bustImportCache = false } = {}) {
|
|
843
|
-
const configImp = await AkanAppConfig.importConfigModule(lib.cwdPath, { bustImportCache });
|
|
844
|
-
const resolved = typeof configImp === "function" ? configImp(lib) : configImp;
|
|
845
|
-
const { plugins, ...config } = resolved ?? {};
|
|
846
|
-
return new AkanLibConfig(lib, config, plugins ?? []);
|
|
847
|
-
}
|
|
848
|
-
}
|
|
849
|
-
//! need to refactor
|
|
850
|
-
var shiftBuildNum = async (app, delta) => {
|
|
851
|
-
const { buildNum } = (await AkanAppConfig.from(app)).mobile;
|
|
852
|
-
const akanConfigPath = path2.join(app.cwdPath, "akan.config.ts");
|
|
853
|
-
const akanConfig = fs.readFileSync(akanConfigPath, "utf8");
|
|
854
|
-
fs.writeFileSync(akanConfigPath, akanConfig.replace(`buildNum: ${buildNum}`, `buildNum: ${buildNum + delta}`));
|
|
855
|
-
};
|
|
856
|
-
var increaseBuildNum = async (app) => await shiftBuildNum(app, 1);
|
|
857
|
-
var decreaseBuildNum = async (app) => await shiftBuildNum(app, -1);
|
|
858
|
-
// pkgs/@akanjs/devkit/artifact/implicitRootLayout.ts
|
|
859
|
-
import path4 from "path";
|
|
860
|
-
|
|
861
|
-
// pkgs/@akanjs/devkit/transforms/asyncDefaultExportDetector.ts
|
|
862
|
-
import fs2 from "fs";
|
|
863
|
-
import path3 from "path";
|
|
864
|
-
|
|
865
|
-
class AsyncDefaultExportDetector {
|
|
866
|
-
static #typescriptLoad;
|
|
867
|
-
static #loadTypescript() {
|
|
868
|
-
AsyncDefaultExportDetector.#typescriptLoad ??= import("typescript").then((mod) => mod.default ?? mod);
|
|
869
|
-
return AsyncDefaultExportDetector.#typescriptLoad;
|
|
870
|
-
}
|
|
871
|
-
static async detect(moduleAbsPath) {
|
|
872
|
-
try {
|
|
873
|
-
const typescript = await AsyncDefaultExportDetector.#loadTypescript();
|
|
874
|
-
const source = fs2.readFileSync(path3.resolve(moduleAbsPath), "utf8");
|
|
875
|
-
const sourceFile = typescript.createSourceFile(moduleAbsPath, source, typescript.ScriptTarget.Latest, true, /\.[tj]sx$/.test(moduleAbsPath) ? typescript.ScriptKind.TSX : typescript.ScriptKind.TS);
|
|
876
|
-
return new AsyncDefaultExportDetector(typescript).detectInSourceFile(sourceFile);
|
|
877
|
-
} catch {
|
|
878
|
-
return false;
|
|
879
|
-
}
|
|
880
|
-
}
|
|
881
|
-
#ts;
|
|
882
|
-
constructor(typescript) {
|
|
883
|
-
this.#ts = typescript;
|
|
884
|
-
}
|
|
885
|
-
detectInSourceFile(sourceFile) {
|
|
886
|
-
const ts = this.#ts;
|
|
887
|
-
const asyncBindings = new Map;
|
|
888
|
-
let defaultIdentifier = null;
|
|
889
|
-
for (const statement of sourceFile.statements) {
|
|
890
|
-
if (ts.isFunctionDeclaration(statement)) {
|
|
891
|
-
if (this.#hasModifier(statement, ts.SyntaxKind.DefaultKeyword)) {
|
|
892
|
-
return this.#hasModifier(statement, ts.SyntaxKind.AsyncKeyword);
|
|
893
|
-
}
|
|
894
|
-
if (statement.name) {
|
|
895
|
-
asyncBindings.set(statement.name.text, this.#hasModifier(statement, ts.SyntaxKind.AsyncKeyword));
|
|
896
|
-
}
|
|
897
|
-
continue;
|
|
898
|
-
}
|
|
899
|
-
if (ts.isVariableStatement(statement)) {
|
|
900
|
-
for (const declaration of statement.declarationList.declarations) {
|
|
901
|
-
if (!ts.isIdentifier(declaration.name))
|
|
902
|
-
continue;
|
|
903
|
-
asyncBindings.set(declaration.name.text, this.#isAsyncFunctionExpression(declaration.initializer));
|
|
904
|
-
}
|
|
905
|
-
continue;
|
|
906
|
-
}
|
|
907
|
-
if (ts.isExportAssignment(statement)) {
|
|
908
|
-
if (this.#isAsyncFunctionExpression(statement.expression))
|
|
909
|
-
return true;
|
|
910
|
-
if (ts.isCallExpression(statement.expression))
|
|
911
|
-
return true;
|
|
912
|
-
if (ts.isIdentifier(statement.expression))
|
|
913
|
-
defaultIdentifier = statement.expression.text;
|
|
914
|
-
continue;
|
|
915
|
-
}
|
|
916
|
-
if (ts.isExportDeclaration(statement) && statement.exportClause && ts.isNamedExports(statement.exportClause)) {
|
|
917
|
-
for (const specifier of statement.exportClause.elements) {
|
|
918
|
-
if (specifier.name.text !== "default")
|
|
919
|
-
continue;
|
|
920
|
-
defaultIdentifier = specifier.propertyName?.text ?? specifier.name.text;
|
|
921
|
-
}
|
|
922
|
-
}
|
|
923
|
-
}
|
|
924
|
-
return defaultIdentifier ? asyncBindings.get(defaultIdentifier) === true : false;
|
|
925
|
-
}
|
|
926
|
-
#hasModifier(node, kind) {
|
|
927
|
-
const ts = this.#ts;
|
|
928
|
-
return ts.canHaveModifiers(node) && (ts.getModifiers(node)?.some((modifier) => modifier.kind === kind) ?? false);
|
|
929
|
-
}
|
|
930
|
-
#isAsyncFunctionExpression(node) {
|
|
931
|
-
const ts = this.#ts;
|
|
932
|
-
return Boolean(node && (ts.isArrowFunction(node) || ts.isFunctionExpression(node)) && this.#hasModifier(node, ts.SyntaxKind.AsyncKeyword));
|
|
933
|
-
}
|
|
934
|
-
}
|
|
935
|
-
|
|
936
|
-
// pkgs/@akanjs/devkit/artifact/implicitRootLayout.ts
|
|
937
|
-
var LAYOUT_KEY_RE = /^\.\/(.+\/)?_layout\.(tsx|ts|jsx|js)$/;
|
|
938
|
-
var IMPLICIT_LAYOUT_DIR = path4.join(".akan", "generated", "root-layouts");
|
|
939
|
-
var IMPLICIT_DICT_DIR = path4.join(".akan", "generated", "dict");
|
|
940
|
-
var IMPLICIT_OVERRIDES_DIR = path4.join(".akan", "generated", "overrides");
|
|
941
|
-
var OVERRIDES_KEY_RE = /^\.\/(.+\/)?_overrides\.(tsx|ts|jsx|js)$/;
|
|
942
|
-
var importSpecifier = (fromAbsPath, toAbsPath) => {
|
|
943
|
-
const rel = path4.relative(path4.dirname(fromAbsPath), toAbsPath).split(path4.sep).join("/");
|
|
944
|
-
return rel.startsWith(".") ? rel : `./${rel}`;
|
|
945
|
-
};
|
|
946
|
-
async function writeGeneratedOverridesLayoutFile(opts) {
|
|
947
|
-
const filename = `${opts.key.replace(/^\.\//, "").replace(/[^a-zA-Z0-9]+/g, "_")}.tsx`;
|
|
948
|
-
const absPath = path4.join(path4.resolve(opts.appCwdPath), IMPLICIT_OVERRIDES_DIR, filename);
|
|
949
|
-
const source = `"use client";
|
|
950
|
-
import { UiOverrideProvider } from "akanjs/ui";
|
|
951
|
-
import { createElement, type ReactNode } from "react";
|
|
952
|
-
import value from ${JSON.stringify(importSpecifier(absPath, opts.userAbsPath))};
|
|
953
|
-
|
|
954
|
-
export default function AkanUiOverridesLayout({ children }: { children?: ReactNode }) {
|
|
955
|
-
return createElement(UiOverrideProvider, { value }, children);
|
|
956
|
-
}
|
|
957
|
-
`;
|
|
958
|
-
await Bun.write(absPath, source);
|
|
959
|
-
return absPath;
|
|
960
|
-
}
|
|
961
|
-
function getRootBoundarySegments(key) {
|
|
962
|
-
const match = LAYOUT_KEY_RE.exec(key);
|
|
963
|
-
if (!match)
|
|
964
|
-
return null;
|
|
965
|
-
const prefix = match[1]?.replace(/\/$/, "");
|
|
966
|
-
if (!prefix)
|
|
967
|
-
return [];
|
|
968
|
-
return prefix.split("/").filter(Boolean);
|
|
969
|
-
}
|
|
970
|
-
function implicitRootLayoutKey(segments) {
|
|
971
|
-
return `./${[...segments, "__root_layout"].join("/")}.tsx`;
|
|
972
|
-
}
|
|
973
|
-
function implicitRootLayoutAbsPath(appCwdPath, segments) {
|
|
974
|
-
const filename = segments.length ? `${segments.join("__")}__root_layout.tsx` : "__root_layout.tsx";
|
|
975
|
-
return path4.join(path4.resolve(appCwdPath), IMPLICIT_LAYOUT_DIR, filename);
|
|
976
|
-
}
|
|
977
|
-
function isRootBoundarySegments(segments, basePaths) {
|
|
978
|
-
const firstVisibleIndex = segments.findIndex((segment) => !/^\(.+\)$/.test(segment));
|
|
979
|
-
if (firstVisibleIndex === -1)
|
|
980
|
-
return segments.length <= 1;
|
|
981
|
-
if (segments.slice(firstVisibleIndex + 1).some((segment) => /^\(.+\)$/.test(segment)))
|
|
982
|
-
return false;
|
|
983
|
-
const visible = segments.slice(firstVisibleIndex);
|
|
984
|
-
const allowedBasePaths = new Set([...basePaths].map((basePath) => basePath.trim()).filter(Boolean));
|
|
985
|
-
return visible.length === 1 && (firstVisibleIndex > 0 || allowedBasePaths.has(visible[0] ?? ""));
|
|
986
|
-
}
|
|
987
|
-
function findRootBoundaries(pageKeys, appCwdPath, basePaths) {
|
|
988
|
-
const boundaries = new Map;
|
|
989
|
-
for (const key of pageKeys) {
|
|
990
|
-
const segments = getRootBoundarySegments(key);
|
|
991
|
-
if (!segments)
|
|
992
|
-
continue;
|
|
993
|
-
if (!isRootBoundarySegments(segments, basePaths))
|
|
994
|
-
continue;
|
|
995
|
-
const id = segments.join("/");
|
|
996
|
-
boundaries.set(id, {
|
|
997
|
-
sourceKey: key,
|
|
998
|
-
sourceAbsPath: path4.resolve(appCwdPath, "page", key.replace(/^\.\//, "")),
|
|
999
|
-
segments
|
|
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
|
|
1000
1109
|
});
|
|
1001
1110
|
}
|
|
1002
|
-
|
|
1003
|
-
|
|
1004
|
-
|
|
1005
|
-
}
|
|
1006
|
-
|
|
1007
|
-
|
|
1008
|
-
|
|
1009
|
-
|
|
1010
|
-
|
|
1011
|
-
|
|
1012
|
-
return
|
|
1013
|
-
}
|
|
1014
|
-
|
|
1015
|
-
|
|
1016
|
-
|
|
1017
|
-
|
|
1018
|
-
|
|
1019
|
-
|
|
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`) };
|
|
1115
|
+
}
|
|
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
|
+
};
|
|
1020
1138
|
}
|
|
1021
1139
|
}
|
|
1022
|
-
|
|
1023
|
-
|
|
1024
|
-
|
|
1025
|
-
|
|
1026
|
-
|
|
1027
|
-
|
|
1028
|
-
|
|
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;
|
|
1029
1166
|
}
|
|
1030
|
-
|
|
1031
|
-
|
|
1032
|
-
|
|
1033
|
-
|
|
1034
|
-
|
|
1035
|
-
|
|
1036
|
-
|
|
1037
|
-
|
|
1038
|
-
|
|
1039
|
-
|
|
1040
|
-
|
|
1041
|
-
`;
|
|
1042
|
-
const inheritedLabel = inheritedSourceAbsPath ? path4.relative(opts.appCwdPath, inheritedSourceAbsPath) : "";
|
|
1043
|
-
const inheritedImport = inheritedSourceSpecifier ? `import * as inheritedModule from ${JSON.stringify(inheritedSourceSpecifier)};
|
|
1044
|
-
const inheritedLayout = resolveRouteModule(inheritedModule as never, ${JSON.stringify(inheritedLabel)}).module as LayoutModule;
|
|
1045
|
-
` : `const inheritedLayout: LayoutModule = {};
|
|
1046
|
-
`;
|
|
1047
|
-
const prefix = opts.boundary.segments.find((segment) => !/^\(.+\)$/.test(segment)) ?? null;
|
|
1048
|
-
const userLabel = opts.boundary.sourceAbsPath ? path4.relative(opts.appCwdPath, opts.boundary.sourceAbsPath) : "";
|
|
1049
|
-
const userImport = sourceSpecifier ? `import * as userModule from ${JSON.stringify(sourceSpecifier)};
|
|
1050
|
-
const userLayout = resolveRouteModule(userModule as never, ${JSON.stringify(userLabel)}).module as LayoutModule;
|
|
1051
|
-
const UserLayout = userLayout.default as (props: LayoutProps) => ReactNode | Promise<ReactNode>;
|
|
1052
|
-
` : `const UserLayout = ({ children }: LayoutProps) => children;
|
|
1053
|
-
const userLayout: LayoutModule = {};
|
|
1054
|
-
`;
|
|
1055
|
-
const isAsyncUserLayout = opts.boundary.sourceAbsPath ? await AsyncDefaultExportDetector.detect(opts.boundary.sourceAbsPath) : false;
|
|
1056
|
-
const userLayoutElement = "<UserLayout params={params} searchParams={searchParams}>{children}</UserLayout>";
|
|
1057
|
-
const layoutSignature = isAsyncUserLayout ? "export default async function GeneratedLayout" : "export default function GeneratedLayout";
|
|
1058
|
-
const layoutBinding = isAsyncUserLayout ? ` const layout =
|
|
1059
|
-
process.env.AKAN_PUBLIC_RENDER_ENV === "csr"
|
|
1060
|
-
? await UserLayout({ params, searchParams, children })
|
|
1061
|
-
: ${userLayoutElement};
|
|
1062
|
-
` : "";
|
|
1063
|
-
const layoutChild = isAsyncUserLayout ? "{layout}" : userLayoutElement;
|
|
1064
|
-
const layoutReturn = isAsyncUserLayout ? "layout" : userLayoutElement;
|
|
1065
|
-
const layoutExports = `export async function generateHead(props: PageProps) {
|
|
1066
|
-
if (userLayout.generateHead) return userLayout.generateHead(props);
|
|
1067
|
-
if (userLayout.head !== undefined) return userLayout.head;
|
|
1068
|
-
if (inheritedLayout.generateHead) return inheritedLayout.generateHead(props);
|
|
1069
|
-
return inheritedLayout.head;
|
|
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
|
+
};
|
|
1070
1178
|
}
|
|
1071
1179
|
|
|
1072
|
-
|
|
1073
|
-
|
|
1074
|
-
|
|
1075
|
-
|
|
1076
|
-
|
|
1077
|
-
|
|
1078
|
-
|
|
1079
|
-
|
|
1080
|
-
|
|
1081
|
-
|
|
1082
|
-
|
|
1083
|
-
|
|
1084
|
-
|
|
1085
|
-
|
|
1086
|
-
const
|
|
1087
|
-
|
|
1088
|
-
const
|
|
1089
|
-
|
|
1090
|
-
|
|
1091
|
-
${layoutExports}(
|
|
1092
|
-
<System.Provider
|
|
1093
|
-
of={GeneratedLayout as never}
|
|
1094
|
-
appName=${JSON.stringify(opts.appName)}
|
|
1095
|
-
${prefix ? `prefix=${JSON.stringify(prefix)}
|
|
1096
|
-
` : ""}params={params}
|
|
1097
|
-
manifest={userLayout.manifest ?? inheritedLayout.manifest}
|
|
1098
|
-
env={env}
|
|
1099
|
-
theme={userLayout.theme ?? inheritedLayout.theme}
|
|
1100
|
-
fonts={loadFonts(userFonts)}
|
|
1101
|
-
className={defaultFontClassName}
|
|
1102
|
-
layoutStyle={userLayout.layoutStyle ?? inheritedLayout.layoutStyle}
|
|
1103
|
-
reconnect={userLayout.reconnect ?? inheritedLayout.reconnect ?? false}
|
|
1104
|
-
wsConnect={userLayout.wsConnect ?? inheritedLayout.wsConnect ?? true}
|
|
1105
|
-
allDictionary={process.env.AKAN_PUBLIC_RENDER_ENV === "ssr" ? allDictionary : undefined}
|
|
1106
|
-
>
|
|
1107
|
-
${layoutChild}
|
|
1108
|
-
</System.Provider>
|
|
1109
|
-
);
|
|
1110
|
-
}
|
|
1111
|
-
` : `import type { LayoutModule, LayoutProps, PageProps } from "akanjs/client";
|
|
1112
|
-
import { resolveRouteModule } from "akanjs/client";
|
|
1113
|
-
import type { ReactNode } from "react";
|
|
1114
|
-
${inheritedImport}${userImport}
|
|
1115
|
-
${layoutExports}${layoutReturn};
|
|
1116
|
-
}
|
|
1117
|
-
`;
|
|
1118
|
-
await Bun.write(absPath, source);
|
|
1119
|
-
return absPath;
|
|
1120
|
-
}
|
|
1121
|
-
async function resolveSsrPageEntries(opts) {
|
|
1122
|
-
const absPageDir = path4.resolve(opts.appCwdPath, "page");
|
|
1123
|
-
const hasSt = await Bun.file(path4.join(opts.appCwdPath, "lib", "st.ts")).exists();
|
|
1124
|
-
const basePaths = opts.basePaths ?? [];
|
|
1125
|
-
const rootSourceAbsPath = findExplicitRootLayoutAbsPath(opts.pageKeys, opts.appCwdPath);
|
|
1126
|
-
const rootBoundaries = findRootBoundaries(opts.pageKeys, opts.appCwdPath, basePaths);
|
|
1127
|
-
const rootLayoutKeys = new Set(opts.pageKeys.filter((key) => {
|
|
1128
|
-
const segments = getRootBoundarySegments(key);
|
|
1129
|
-
return segments !== null && isRootBoundarySegments(segments, basePaths);
|
|
1130
|
-
}));
|
|
1131
|
-
const base = await Promise.all(opts.pageKeys.filter((key) => !rootLayoutKeys.has(key)).map(async (key) => {
|
|
1132
|
-
const userAbsPath = path4.resolve(absPageDir, key);
|
|
1133
|
-
if (OVERRIDES_KEY_RE.test(key)) {
|
|
1134
|
-
const moduleAbsPath = await writeGeneratedOverridesLayoutFile({
|
|
1135
|
-
appCwdPath: opts.appCwdPath,
|
|
1136
|
-
key,
|
|
1137
|
-
userAbsPath
|
|
1138
|
-
});
|
|
1139
|
-
return { key, moduleAbsPath, seedAbsPaths: [userAbsPath] };
|
|
1140
|
-
}
|
|
1141
|
-
return { key, moduleAbsPath: userAbsPath };
|
|
1142
|
-
}));
|
|
1143
|
-
const generated = await Promise.all(rootBoundaries.map(async (boundary) => ({
|
|
1144
|
-
key: implicitRootLayoutKey(boundary.segments),
|
|
1145
|
-
moduleAbsPath: await writeGeneratedRootLayoutFile({
|
|
1146
|
-
appCwdPath: opts.appCwdPath,
|
|
1147
|
-
appName: opts.appName,
|
|
1148
|
-
boundary,
|
|
1149
|
-
rootSourceAbsPath,
|
|
1150
|
-
includeStInit: hasSt && boundary.segments.length === 0,
|
|
1151
|
-
includeSystemProvider: !hasAncestorRootBoundary(boundary, rootBoundaries)
|
|
1152
|
-
}),
|
|
1153
|
-
seedAbsPaths: [...new Set([boundary.sourceAbsPath, rootSourceAbsPath].filter((absPath) => absPath !== null))]
|
|
1154
|
-
})));
|
|
1155
|
-
return [...base, ...generated].sort((a, b) => a.key.localeCompare(b.key));
|
|
1156
|
-
}
|
|
1157
|
-
async function resolveSsrPageEntriesForApp(app, pageKeys) {
|
|
1158
|
-
const config = await app.getConfig();
|
|
1159
|
-
return resolveSsrPageEntries({ appCwdPath: app.cwdPath, appName: app.name, pageKeys, basePaths: config.basePaths });
|
|
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
|
+
}
|
|
1160
1199
|
}
|
|
1161
|
-
|
|
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);
|
|
1162
1209
|
// pkgs/@akanjs/devkit/linter.ts
|
|
1163
1210
|
import { spawn } from "child_process";
|
|
1164
1211
|
import { existsSync, readFileSync } from "fs";
|
|
@@ -1512,7 +1559,6 @@ var resolveRepoName = (workspaceRoot) => {
|
|
|
1512
1559
|
};
|
|
1513
1560
|
|
|
1514
1561
|
// pkgs/@akanjs/devkit/scanInfo.ts
|
|
1515
|
-
import { rm as rm2 } from "fs/promises";
|
|
1516
1562
|
import path7 from "path";
|
|
1517
1563
|
|
|
1518
1564
|
// pkgs/@akanjs/devkit/workspaceLayout.ts
|
|
@@ -1521,7 +1567,6 @@ var appRootAllowedFiles = new Set([
|
|
|
1521
1567
|
"CLAUDE.md",
|
|
1522
1568
|
"akan.app.json",
|
|
1523
1569
|
"akan.config.ts",
|
|
1524
|
-
"capacitor.config.ts",
|
|
1525
1570
|
"client.ts",
|
|
1526
1571
|
"main.ts",
|
|
1527
1572
|
"package.json",
|
|
@@ -1531,12 +1576,9 @@ var appRootAllowedFiles = new Set([
|
|
|
1531
1576
|
]);
|
|
1532
1577
|
var appRootAllowedDirs = new Set([
|
|
1533
1578
|
".akan",
|
|
1534
|
-
"android",
|
|
1535
1579
|
"common",
|
|
1536
1580
|
"env",
|
|
1537
|
-
"ios",
|
|
1538
1581
|
"lib",
|
|
1539
|
-
"mobile",
|
|
1540
1582
|
"page",
|
|
1541
1583
|
"plugin",
|
|
1542
1584
|
"private",
|
|
@@ -1588,6 +1630,15 @@ var libFacetRootAllowedFiles = new Set([
|
|
|
1588
1630
|
]);
|
|
1589
1631
|
var libFacetRootTestPattern = /^[A-Za-z][A-Za-z0-9_-]*\.signal\.(test|spec)\.(ts|tsx)$/;
|
|
1590
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;
|
|
1591
1642
|
var isScannedRootEntry = (type, name) => !name.startsWith(".") || rootAllowedDirs[type].has(name);
|
|
1592
1643
|
|
|
1593
1644
|
// pkgs/@akanjs/devkit/scanInfo.ts
|
|
@@ -1605,7 +1656,6 @@ var serviceFileTypes = [
|
|
|
1605
1656
|
var databaseFileTypes = ["constant", "dictionary", "document", ...serviceFileTypes.slice(1)];
|
|
1606
1657
|
var uiFileTypes = new Set(["template", "unit", "util", "view", "zone"]);
|
|
1607
1658
|
var fileTypeOf = (filename) => databaseFileTypes.find((type) => filename.endsWith(uiFileTypes.has(type) ? `.${type[0].toUpperCase()}${type.slice(1)}.tsx` : `.${type}.ts`));
|
|
1608
|
-
var generatedRootCapacitorConfigFiles = ["capacitor.config.js", "capacitor.config.json"];
|
|
1609
1659
|
var internalLibDirs = new Set(["__lib", "__scalar"]);
|
|
1610
1660
|
var moduleNonUiFileTypes = {
|
|
1611
1661
|
database: new Set(["constant", "dictionary", "document", "service", "signal", "store"]),
|
|
@@ -1619,17 +1669,11 @@ var moduleUiFileTypes = {
|
|
|
1619
1669
|
};
|
|
1620
1670
|
var createDependencyScanner = async (exec) => (await import("./dependencyScanner-ye2e6pdz.js")).TypeScriptDependencyScanner.from(exec);
|
|
1621
1671
|
var getScanPath = (exec, relativePath) => path7.posix.join(`${exec.type}s`, exec.name, relativePath.split(path7.sep).join("/"));
|
|
1622
|
-
async function clearGeneratedRootCapacitorConfigs(exec) {
|
|
1623
|
-
if (exec.type !== "app")
|
|
1624
|
-
return;
|
|
1625
|
-
await Promise.all(generatedRootCapacitorConfigFiles.map((filename) => rm2(exec.getPath(filename), { force: true })));
|
|
1626
|
-
}
|
|
1627
1672
|
var getModuleNameFromPath = (kind, modulePath) => {
|
|
1628
1673
|
const dirname = path7.basename(modulePath);
|
|
1629
1674
|
return kind === "service" ? dirname.replace(/^_+/, "") : dirname;
|
|
1630
1675
|
};
|
|
1631
1676
|
async function assertScanConvention(exec, libRoot) {
|
|
1632
|
-
await clearGeneratedRootCapacitorConfigs(exec);
|
|
1633
1677
|
const violations = [];
|
|
1634
1678
|
const addViolation = (relativePath, reason) => {
|
|
1635
1679
|
violations.push(`${getScanPath(exec, relativePath)}: ${reason}`);
|
|
@@ -1639,10 +1683,10 @@ async function assertScanConvention(exec, libRoot) {
|
|
|
1639
1683
|
const { files, dirs } = await exec.getFilesAndDirs(".");
|
|
1640
1684
|
for (const filename of files)
|
|
1641
1685
|
if (!allowedRootFiles.has(filename))
|
|
1642
|
-
addViolation(filename, `unsupported ${exec.type} root file`);
|
|
1686
|
+
addViolation(filename, rootEntryHintOf(exec.type, filename) ?? `unsupported ${exec.type} root file`);
|
|
1643
1687
|
for (const dirname of dirs)
|
|
1644
1688
|
if (!allowedRootDirs.has(dirname))
|
|
1645
|
-
addViolation(dirname, `unsupported ${exec.type} root folder`);
|
|
1689
|
+
addViolation(dirname, rootEntryHintOf(exec.type, dirname) ?? `unsupported ${exec.type} root folder`);
|
|
1646
1690
|
if (exec.type === "lib")
|
|
1647
1691
|
for (const { relativePath, reason } of await exec.getPageConventionViolations())
|
|
1648
1692
|
addViolation(relativePath, reason);
|
|
@@ -3203,16 +3247,6 @@ class AppExecutor extends SysExecutor {
|
|
|
3203
3247
|
return true;
|
|
3204
3248
|
});
|
|
3205
3249
|
}
|
|
3206
|
-
async getPluginRuntimePackages() {
|
|
3207
|
-
const plugins = await this.collectPlugins();
|
|
3208
|
-
const appConfig = await this.getConfig();
|
|
3209
|
-
const ctx = {
|
|
3210
|
-
appName: this.name,
|
|
3211
|
-
mobile: appConfig.mobile,
|
|
3212
|
-
hasMobilePermission: (permission) => Object.values(appConfig.mobile.targets).some((target) => target.permissions?.includes(permission) ?? false)
|
|
3213
|
-
};
|
|
3214
|
-
return [...new Set(plugins.flatMap((plugin) => plugin.runtimePackages?.(ctx) ?? []))];
|
|
3215
|
-
}
|
|
3216
3250
|
async increaseBuildNum() {
|
|
3217
3251
|
await increaseBuildNum(this);
|
|
3218
3252
|
}
|
|
@@ -3368,4 +3402,4 @@ class ModuleExecutor extends Executor {
|
|
|
3368
3402
|
}
|
|
3369
3403
|
}
|
|
3370
3404
|
|
|
3371
|
-
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, 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 };
|