akanjs 2.0.0-rc.5 → 2.0.0-rc.7
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/cli/index.js +42 -14
- package/cli/templates/app/page/_index.tsx +144 -59
- package/cli/templates/app/page/styles.css.template +38 -0
- package/cli/templates/workspaceRoot/package.json.template +5 -1
- package/devkit/akanConfig/akanConfig.ts +1 -1
- package/devkit/executors.ts +38 -7
- package/devkit/scanInfo.ts +2 -2
- package/package.json +1 -1
- package/ui/styles.css +0 -1
package/cli/index.js
CHANGED
|
@@ -9346,6 +9346,7 @@ import {
|
|
|
9346
9346
|
fork,
|
|
9347
9347
|
spawn
|
|
9348
9348
|
} from "child_process";
|
|
9349
|
+
import { readFileSync as readFileSync3 } from "fs";
|
|
9349
9350
|
import { mkdir as mkdir2, readdir as readDirEntries, stat as stat2 } from "fs/promises";
|
|
9350
9351
|
import path7 from "path";
|
|
9351
9352
|
import chalk4 from "chalk";
|
|
@@ -9579,7 +9580,7 @@ CMD [${command.map((c) => `"${c}"`).join(",")}]`;
|
|
|
9579
9580
|
static async from(app) {
|
|
9580
9581
|
const [configImp, baseDevEnv, libs, rootPackageJson] = await Promise.all([
|
|
9581
9582
|
import(`${app.cwdPath}/akan.config.ts`).then((mod) => mod.default),
|
|
9582
|
-
WorkspaceExecutor.getBaseDevEnv(),
|
|
9583
|
+
WorkspaceExecutor.getBaseDevEnv(path.join(app.workspace.workspaceRoot, ".env")),
|
|
9583
9584
|
app.workspace.getLibs(),
|
|
9584
9585
|
app.workspace.getPackageJson()
|
|
9585
9586
|
]);
|
|
@@ -10225,7 +10226,7 @@ class ScanInfo {
|
|
|
10225
10226
|
name: exec.name,
|
|
10226
10227
|
type: exec.type,
|
|
10227
10228
|
repoName: exec.workspace.repoName,
|
|
10228
|
-
serveDomain: WorkspaceExecutor.getBaseDevEnv().serveDomain,
|
|
10229
|
+
serveDomain: WorkspaceExecutor.getBaseDevEnv(path5.join(exec.workspace.workspaceRoot, ".env")).serveDomain,
|
|
10229
10230
|
akanConfig: akanConfig2,
|
|
10230
10231
|
files,
|
|
10231
10232
|
libDeps,
|
|
@@ -10695,6 +10696,32 @@ var execEmoji = {
|
|
|
10695
10696
|
module: "\u2699\uFE0F",
|
|
10696
10697
|
default: "\u2708\uFE0F"
|
|
10697
10698
|
};
|
|
10699
|
+
var parseEnvFile = (envPath) => {
|
|
10700
|
+
const env = {};
|
|
10701
|
+
const content = (() => {
|
|
10702
|
+
try {
|
|
10703
|
+
return readFileSync3(envPath, "utf8");
|
|
10704
|
+
} catch {
|
|
10705
|
+
return "";
|
|
10706
|
+
}
|
|
10707
|
+
})();
|
|
10708
|
+
for (const line of content.split(/\r?\n/)) {
|
|
10709
|
+
const trimmed = line.trim();
|
|
10710
|
+
if (!trimmed || trimmed.startsWith("#"))
|
|
10711
|
+
continue;
|
|
10712
|
+
const normalized = trimmed.startsWith("export ") ? trimmed.slice("export ".length).trim() : trimmed;
|
|
10713
|
+
const separatorIndex = normalized.indexOf("=");
|
|
10714
|
+
if (separatorIndex <= 0)
|
|
10715
|
+
continue;
|
|
10716
|
+
const key = normalized.slice(0, separatorIndex).trim();
|
|
10717
|
+
let value = normalized.slice(separatorIndex + 1).trim();
|
|
10718
|
+
if (value.startsWith('"') && value.endsWith('"') || value.startsWith("'") && value.endsWith("'")) {
|
|
10719
|
+
value = value.slice(1, -1);
|
|
10720
|
+
}
|
|
10721
|
+
env[key] = value;
|
|
10722
|
+
}
|
|
10723
|
+
return env;
|
|
10724
|
+
};
|
|
10698
10725
|
var PAGE_ROUTE_EXPORTS = new Set(["default", "pageConfig", "head", "generateHead", "Loading"]);
|
|
10699
10726
|
var ROOT_LAYOUT_EXPORTS = new Set([
|
|
10700
10727
|
"default",
|
|
@@ -11151,17 +11178,18 @@ class WorkspaceExecutor extends Executor {
|
|
|
11151
11178
|
} = {}) {
|
|
11152
11179
|
return WorkspaceExecutor.#execs.get(repoName) ?? new WorkspaceExecutor({ workspaceRoot, repoName });
|
|
11153
11180
|
}
|
|
11154
|
-
static getBaseDevEnv() {
|
|
11155
|
-
const
|
|
11156
|
-
const
|
|
11157
|
-
const
|
|
11181
|
+
static getBaseDevEnv(envPath) {
|
|
11182
|
+
const sourceEnv = envPath ? { ...process.env, ...parseEnvFile(envPath) } : process.env;
|
|
11183
|
+
const appName = sourceEnv.AKAN_PUBLIC_APP_NAME;
|
|
11184
|
+
const workspaceRoot = sourceEnv.AKAN_WORKSPACE_ROOT;
|
|
11185
|
+
const repoName = sourceEnv.AKAN_PUBLIC_REPO_NAME;
|
|
11158
11186
|
if (!repoName)
|
|
11159
11187
|
throw new Error("AKAN_PUBLIC_REPO_NAME is not set");
|
|
11160
|
-
const serveDomain =
|
|
11188
|
+
const serveDomain = sourceEnv.AKAN_PUBLIC_SERVE_DOMAIN;
|
|
11161
11189
|
if (!serveDomain)
|
|
11162
11190
|
throw new Error("AKAN_PUBLIC_SERVE_DOMAIN is not set");
|
|
11163
|
-
const portOffset = parseInt(
|
|
11164
|
-
const env =
|
|
11191
|
+
const portOffset = parseInt(sourceEnv.PORT_OFFSET ?? "0");
|
|
11192
|
+
const env = sourceEnv.AKAN_PUBLIC_ENV ?? "debug";
|
|
11165
11193
|
if (!env)
|
|
11166
11194
|
throw new Error("AKAN_PUBLIC_ENV is not set");
|
|
11167
11195
|
return { ...appName ? { appName } : {}, workspaceRoot, repoName, serveDomain, env, portOffset };
|
|
@@ -17344,7 +17372,7 @@ var NODE_NATIVE_MODULE_SET = new Set([
|
|
|
17344
17372
|
|
|
17345
17373
|
import yaml from "js-yaml";
|
|
17346
17374
|
|
|
17347
|
-
import { readFileSync as
|
|
17375
|
+
import { readFileSync as readFileSync4, realpathSync } from "fs";
|
|
17348
17376
|
import ora2 from "ora";
|
|
17349
17377
|
import * as ts5 from "typescript";
|
|
17350
17378
|
var tsTranspiler = new Bun.Transpiler({ loader: "ts" });
|
|
@@ -17393,7 +17421,7 @@ var collectImportedFiles = (constantFilePath, parsedConfig) => {
|
|
|
17393
17421
|
if (analyzedFiles.has(filePath))
|
|
17394
17422
|
return;
|
|
17395
17423
|
analyzedFiles.add(filePath);
|
|
17396
|
-
const source =
|
|
17424
|
+
const source = readFileSync4(filePath, "utf-8");
|
|
17397
17425
|
for (const importPath of scanModuleSpecifiers(source, filePath, false)) {
|
|
17398
17426
|
if (!importPath.startsWith("."))
|
|
17399
17427
|
continue;
|
|
@@ -17473,7 +17501,7 @@ var analyzeProperties = (filesToAnalyze, program2, checker) => {
|
|
|
17473
17501
|
isLibModule: true,
|
|
17474
17502
|
isImport: false,
|
|
17475
17503
|
libName: left.text,
|
|
17476
|
-
source:
|
|
17504
|
+
source: readFileSync4(symbolFilePath, "utf-8"),
|
|
17477
17505
|
isScalar
|
|
17478
17506
|
});
|
|
17479
17507
|
} else {
|
|
@@ -17483,7 +17511,7 @@ var analyzeProperties = (filesToAnalyze, program2, checker) => {
|
|
|
17483
17511
|
isImport: false,
|
|
17484
17512
|
libName: left.text,
|
|
17485
17513
|
isScalar,
|
|
17486
|
-
source:
|
|
17514
|
+
source: readFileSync4(symbolFilePath, "utf-8")
|
|
17487
17515
|
});
|
|
17488
17516
|
}
|
|
17489
17517
|
}
|
|
@@ -17501,7 +17529,7 @@ var analyzeProperties = (filesToAnalyze, program2, checker) => {
|
|
|
17501
17529
|
isLibModule: false,
|
|
17502
17530
|
isImport: true,
|
|
17503
17531
|
isScalar,
|
|
17504
|
-
source:
|
|
17532
|
+
source: readFileSync4(resolved, "utf-8")
|
|
17505
17533
|
});
|
|
17506
17534
|
}
|
|
17507
17535
|
}
|
|
@@ -8,82 +8,167 @@ export default function getContent(scanInfo: AppInfo | LibInfo | null, dict: Dic
|
|
|
8
8
|
filename: "_index.tsx",
|
|
9
9
|
content: `
|
|
10
10
|
import { Link } from "akanjs/ui";
|
|
11
|
-
import {
|
|
11
|
+
import {
|
|
12
|
+
FaBolt,
|
|
13
|
+
FaBookOpen,
|
|
14
|
+
FaBoxOpen,
|
|
15
|
+
FaCheckCircle,
|
|
16
|
+
FaCodeBranch,
|
|
17
|
+
FaExternalLinkAlt,
|
|
18
|
+
FaLayerGroup,
|
|
19
|
+
FaRocket,
|
|
20
|
+
FaShieldAlt,
|
|
21
|
+
FaTerminal,
|
|
22
|
+
} from "react-icons/fa";
|
|
12
23
|
|
|
13
24
|
export const head = <title>Akan.js</title>;
|
|
14
25
|
|
|
15
26
|
export default function Page() {
|
|
27
|
+
const appName = ${JSON.stringify(dict.appName)};
|
|
28
|
+
|
|
29
|
+
const highlights = [
|
|
30
|
+
{
|
|
31
|
+
icon: <FaLayerGroup />,
|
|
32
|
+
title: "One Codebase",
|
|
33
|
+
description: "Design pages, services, server contracts, and deployment surfaces in one Akan workspace.",
|
|
34
|
+
},
|
|
35
|
+
{
|
|
36
|
+
icon: <FaShieldAlt />,
|
|
37
|
+
title: "Type-Safe Flow",
|
|
38
|
+
description: "Keep application contracts close to the business logic they protect.",
|
|
39
|
+
},
|
|
40
|
+
{
|
|
41
|
+
icon: <FaRocket />,
|
|
42
|
+
title: "Ready To Ship",
|
|
43
|
+
description: "Start locally, build production artifacts, and grow without changing the app shape.",
|
|
44
|
+
},
|
|
45
|
+
];
|
|
46
|
+
|
|
16
47
|
return (
|
|
17
|
-
<
|
|
18
|
-
<div className="
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
<div className="
|
|
25
|
-
<div className="
|
|
26
|
-
<FaBolt
|
|
27
|
-
All-in-One
|
|
28
|
-
</div>
|
|
29
|
-
<div className="badge badge-lg text-base-100 bg-secondary border-none">
|
|
30
|
-
<FaShieldAlt className="" />
|
|
31
|
-
Type-Safe
|
|
48
|
+
<main className="relative min-h-screen overflow-hidden bg-base-100 text-base-content">
|
|
49
|
+
<div className="absolute -left-48 -top-48 h-96 w-96 rounded-full bg-primary/25 blur-3xl" />
|
|
50
|
+
<div className="absolute -bottom-56 -right-40 h-112 w-md rounded-full bg-accent/20 blur-3xl" />
|
|
51
|
+
<div className="absolute inset-x-0 top-0 h-px bg-linear-to-r from-transparent via-primary/60 to-transparent" />
|
|
52
|
+
|
|
53
|
+
<section className="relative mx-auto flex min-h-screen w-full max-w-7xl flex-col px-6 py-8 lg:px-8">
|
|
54
|
+
<nav className="flex items-center justify-between">
|
|
55
|
+
<div className="flex items-center gap-3">
|
|
56
|
+
<div className="flex h-11 w-11 items-center justify-center rounded-2xl bg-primary text-base-100 shadow-lg shadow-primary/20">
|
|
57
|
+
<FaBolt />
|
|
32
58
|
</div>
|
|
33
|
-
<div
|
|
34
|
-
<
|
|
35
|
-
|
|
59
|
+
<div>
|
|
60
|
+
<p className="text-sm font-semibold uppercase tracking-[0.3em] text-primary">Akan.js</p>
|
|
61
|
+
<p className="text-xs text-base-content/60">Full-stack TypeScript framework</p>
|
|
36
62
|
</div>
|
|
37
63
|
</div>
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
<
|
|
49
|
-
<
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
<
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
64
|
+
<Link href="https://akanjs.com" target="_blank">
|
|
65
|
+
<button className="btn btn-sm border-base-content/10 bg-base-content/10 text-base-content hover:border-primary hover:bg-primary hover:text-base-100">
|
|
66
|
+
Official Site
|
|
67
|
+
<FaExternalLinkAlt />
|
|
68
|
+
</button>
|
|
69
|
+
</Link>
|
|
70
|
+
</nav>
|
|
71
|
+
|
|
72
|
+
<div className="grid flex-1 items-center gap-10 py-16 lg:grid-cols-[1.04fr_0.96fr] lg:py-10">
|
|
73
|
+
<div>
|
|
74
|
+
<div className="badge mb-6 border-primary/20 bg-primary/10 px-4 py-3 text-primary">
|
|
75
|
+
<FaCheckCircle />
|
|
76
|
+
Your app is running
|
|
77
|
+
</div>
|
|
78
|
+
<h1 className="max-w-4xl text-5xl font-black tracking-tight text-base-content sm:text-6xl lg:text-7xl">
|
|
79
|
+
Build your business app as one connected system.
|
|
80
|
+
</h1>
|
|
81
|
+
<p className="mt-6 max-w-2xl text-lg leading-8 text-base-content/70">
|
|
82
|
+
Akan.js helps solo developers and small teams create web, server, database, and deployment-ready
|
|
83
|
+
surfaces from a single TypeScript workspace.
|
|
84
|
+
</p>
|
|
85
|
+
|
|
86
|
+
<div className="mt-8 flex flex-wrap gap-3">
|
|
87
|
+
<span className="badge badge-lg border-base-content/10 bg-base-content/10 text-base-content">All-in-one</span>
|
|
88
|
+
<span className="badge badge-lg border-base-content/10 bg-base-content/10 text-base-content">Type-safe</span>
|
|
89
|
+
<span className="badge badge-lg border-base-content/10 bg-base-content/10 text-base-content">Minimal code</span>
|
|
90
|
+
<span className="badge badge-lg border-base-content/10 bg-base-content/10 text-base-content">Bun-first</span>
|
|
91
|
+
</div>
|
|
92
|
+
|
|
93
|
+
<div className="mt-10 flex flex-col gap-3 sm:flex-row">
|
|
94
|
+
<Link href="https://akanjs.com/docs/intro/quickstart" target="_blank">
|
|
95
|
+
<button className="btn border-none bg-primary text-base-100 hover:bg-primary/80">
|
|
96
|
+
Read Quick Start
|
|
97
|
+
<FaBookOpen />
|
|
98
|
+
</button>
|
|
99
|
+
</Link>
|
|
59
100
|
<Link href="https://akanjs.com/docs/intro/practice" target="_blank">
|
|
60
|
-
<button className="btn
|
|
101
|
+
<button className="btn border-base-content/10 bg-base-content/10 text-base-content hover:border-base-content/20 hover:bg-base-content/15">
|
|
102
|
+
Learn By Building
|
|
103
|
+
<FaCodeBranch />
|
|
104
|
+
</button>
|
|
61
105
|
</Link>
|
|
62
106
|
</div>
|
|
63
107
|
</div>
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
<
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
108
|
+
|
|
109
|
+
<div className="relative">
|
|
110
|
+
<div className="absolute inset-0 rotate-3 rounded-4xl bg-primary/20 blur-2xl" />
|
|
111
|
+
<div className="relative overflow-hidden rounded-4xl border border-base-content/10 bg-base-content/6 p-5 shadow-2xl backdrop-blur">
|
|
112
|
+
<div className="mb-5 flex items-center justify-between rounded-2xl border border-base-content/10 bg-base-100/70 px-4 py-3">
|
|
113
|
+
<div>
|
|
114
|
+
<p className="text-xs uppercase tracking-[0.24em] text-base-content/40">Current App</p>
|
|
115
|
+
<p className="text-lg font-semibold text-base-content">{appName}</p>
|
|
116
|
+
</div>
|
|
117
|
+
<div className="rounded-xl bg-primary/10 px-3 py-2 text-sm font-medium text-primary">Live</div>
|
|
118
|
+
</div>
|
|
119
|
+
|
|
120
|
+
<div className="mockup-code border border-base-content/10 bg-base-100 text-sm shadow-none">
|
|
121
|
+
<pre data-prefix="$">
|
|
122
|
+
<code className="text-primary">bun run akan start {appName}</code>
|
|
123
|
+
</pre>
|
|
124
|
+
<pre data-prefix="✓">
|
|
125
|
+
<code className="text-accent">server, web, and app surfaces ready</code>
|
|
126
|
+
</pre>
|
|
127
|
+
<pre data-prefix="→">
|
|
128
|
+
<code className="text-base-content/70">edit page/_index.tsx to begin</code>
|
|
129
|
+
</pre>
|
|
130
|
+
</div>
|
|
131
|
+
|
|
132
|
+
<div className="mt-5 grid gap-3 sm:grid-cols-3">
|
|
133
|
+
{[
|
|
134
|
+
["Pages", "UI routes"],
|
|
135
|
+
["Services", "Typed APIs"],
|
|
136
|
+
["Deploy", "Artifacts"],
|
|
137
|
+
].map(([title, description]) => (
|
|
138
|
+
<div key={title} className="rounded-2xl border border-base-content/10 bg-base-content/4 p-4">
|
|
139
|
+
<p className="text-sm font-semibold text-base-content">{title}</p>
|
|
140
|
+
<p className="mt-1 text-xs text-base-content/60">{description}</p>
|
|
141
|
+
</div>
|
|
142
|
+
))}
|
|
143
|
+
</div>
|
|
72
144
|
</div>
|
|
73
145
|
</div>
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
<
|
|
80
|
-
|
|
81
|
-
</
|
|
146
|
+
</div>
|
|
147
|
+
|
|
148
|
+
<div className="grid gap-4 pb-8 md:grid-cols-3">
|
|
149
|
+
{highlights.map((item) => (
|
|
150
|
+
<div key={item.title} className="rounded-3xl border border-base-content/10 bg-base-content/4 p-6 backdrop-blur">
|
|
151
|
+
<div className="mb-5 flex h-12 w-12 items-center justify-center rounded-2xl bg-primary/10 text-xl text-primary">
|
|
152
|
+
{item.icon}
|
|
153
|
+
</div>
|
|
154
|
+
<h2 className="text-lg font-bold text-base-content">{item.title}</h2>
|
|
155
|
+
<p className="mt-2 text-sm leading-6 text-base-content/60">{item.description}</p>
|
|
82
156
|
</div>
|
|
157
|
+
))}
|
|
158
|
+
</div>
|
|
159
|
+
|
|
160
|
+
<div className="flex flex-col items-start justify-between gap-4 rounded-3xl border border-base-content/10 bg-base-content/4 p-5 text-sm text-base-content/70 md:flex-row md:items-center">
|
|
161
|
+
<div className="flex items-center gap-3">
|
|
162
|
+
<FaTerminal className="text-primary" />
|
|
163
|
+
<span>Next: open your workspace and start shaping the product around your business model.</span>
|
|
164
|
+
</div>
|
|
165
|
+
<div className="flex items-center gap-2 text-base-content/40">
|
|
166
|
+
<FaBoxOpen />
|
|
167
|
+
<span>Akan.js template</span>
|
|
83
168
|
</div>
|
|
84
169
|
</div>
|
|
85
|
-
</
|
|
86
|
-
</
|
|
170
|
+
</section>
|
|
171
|
+
</main>
|
|
87
172
|
);
|
|
88
173
|
}`,
|
|
89
174
|
};
|
|
@@ -8,8 +8,46 @@
|
|
|
8
8
|
|
|
9
9
|
@plugin "daisyui/theme" {
|
|
10
10
|
name: "light";
|
|
11
|
+
--color-primary: #c33c32;
|
|
12
|
+
--color-secondary: #2b2e33;
|
|
13
|
+
--color-accent: #e67e22;
|
|
14
|
+
--color-accent-content: #ffffff;
|
|
15
|
+
--color-base-content: #2c3e50;
|
|
16
|
+
--color-base-100: #fafafa;
|
|
17
|
+
--color-base-200: #f5f5f5;
|
|
18
|
+
--color-info: #f1fdff;
|
|
19
|
+
--color-info-content: #2c3e50;
|
|
20
|
+
--color-neutral: #292c32;
|
|
21
|
+
--color-neutral-content: #fafafa;
|
|
22
|
+
--color-success: #2ecc71;
|
|
23
|
+
--color-success-content: #fafafa;
|
|
24
|
+
--color-warning: #f39c12;
|
|
25
|
+
--color-warning-content: #fafafa;
|
|
26
|
+
--color-error: #e74c3c;
|
|
27
|
+
--color-error-content: #fafafa;
|
|
28
|
+
--color-open: #27ae60;
|
|
29
|
+
--color-open-content: #fafafa;
|
|
11
30
|
}
|
|
12
31
|
|
|
13
32
|
@plugin "daisyui/theme" {
|
|
14
33
|
name: "dark";
|
|
34
|
+
default: true;
|
|
35
|
+
--color-primary: #ff493b;
|
|
36
|
+
--color-secondary: #2b2e33;
|
|
37
|
+
--color-accent: #d1a23b;
|
|
38
|
+
--color-accent-content: #ffffff;
|
|
39
|
+
--color-base-content: #ffffff;
|
|
40
|
+
--color-base-100: #1a1a1a;
|
|
41
|
+
--color-base-200: #2a2a2a;
|
|
42
|
+
--color-info: #173b78;
|
|
43
|
+
--color-info-content: #ffffff;
|
|
44
|
+
--color-neutral: #3ed13b;
|
|
45
|
+
--color-neutral-content: #ffffff;
|
|
46
|
+
--color-success: #255fc3;
|
|
47
|
+
--color-success-content: #ffffff;
|
|
48
|
+
--color-warning: #ffcc00;
|
|
49
|
+
--color-warning-content: #ffffff;
|
|
50
|
+
--color-error: #f02020;
|
|
51
|
+
--color-error-content: #ffffff;
|
|
52
|
+
--color-open: #10fc00;
|
|
15
53
|
}
|
|
@@ -2,6 +2,10 @@
|
|
|
2
2
|
"name": "<%= repoName %>",
|
|
3
3
|
"description": "<%= repoName %> workspace",
|
|
4
4
|
"version": "0.0.1",
|
|
5
|
-
"dependencies": {
|
|
5
|
+
"dependencies": {
|
|
6
|
+
"daisyui": "^5.5.20",
|
|
7
|
+
"tailwind-scrollbar": "^4.0.2",
|
|
8
|
+
"tailwindcss-radix": "^4.0.2"
|
|
9
|
+
},
|
|
6
10
|
"devDependencies": {}
|
|
7
11
|
}
|
|
@@ -256,7 +256,7 @@ CMD [${command.map((c) => `"${c}"`).join(",")}]`;
|
|
|
256
256
|
static async from(app: App) {
|
|
257
257
|
const [configImp, baseDevEnv, libs, rootPackageJson] = await Promise.all([
|
|
258
258
|
import(`${app.cwdPath}/akan.config.ts`).then((mod) => mod.default),
|
|
259
|
-
WorkspaceExecutor.getBaseDevEnv(),
|
|
259
|
+
WorkspaceExecutor.getBaseDevEnv(path.join(app.workspace.workspaceRoot, ".env")),
|
|
260
260
|
app.workspace.getLibs(),
|
|
261
261
|
app.workspace.getPackageJson(),
|
|
262
262
|
]);
|
package/devkit/executors.ts
CHANGED
|
@@ -7,6 +7,7 @@ import {
|
|
|
7
7
|
type SpawnOptions,
|
|
8
8
|
spawn,
|
|
9
9
|
} from "node:child_process";
|
|
10
|
+
import { readFileSync } from "node:fs";
|
|
10
11
|
import { mkdir, readdir as readDirEntries, stat } from "node:fs/promises";
|
|
11
12
|
import path from "node:path";
|
|
12
13
|
import {
|
|
@@ -40,6 +41,34 @@ export const execEmoji = {
|
|
|
40
41
|
default: "✈️", // for sys executor
|
|
41
42
|
};
|
|
42
43
|
|
|
44
|
+
const parseEnvFile = (envPath: string): Record<string, string> => {
|
|
45
|
+
const env: Record<string, string> = {};
|
|
46
|
+
const content = (() => {
|
|
47
|
+
try {
|
|
48
|
+
return readFileSync(envPath, "utf8");
|
|
49
|
+
} catch {
|
|
50
|
+
return "";
|
|
51
|
+
}
|
|
52
|
+
})();
|
|
53
|
+
for (const line of content.split(/\r?\n/)) {
|
|
54
|
+
const trimmed = line.trim();
|
|
55
|
+
if (!trimmed || trimmed.startsWith("#")) continue;
|
|
56
|
+
const normalized = trimmed.startsWith("export ") ? trimmed.slice("export ".length).trim() : trimmed;
|
|
57
|
+
const separatorIndex = normalized.indexOf("=");
|
|
58
|
+
if (separatorIndex <= 0) continue;
|
|
59
|
+
const key = normalized.slice(0, separatorIndex).trim();
|
|
60
|
+
let value = normalized.slice(separatorIndex + 1).trim();
|
|
61
|
+
if (
|
|
62
|
+
(value.startsWith('"') && value.endsWith('"')) ||
|
|
63
|
+
(value.startsWith("'") && value.endsWith("'"))
|
|
64
|
+
) {
|
|
65
|
+
value = value.slice(1, -1);
|
|
66
|
+
}
|
|
67
|
+
env[key] = value;
|
|
68
|
+
}
|
|
69
|
+
return env;
|
|
70
|
+
};
|
|
71
|
+
|
|
43
72
|
const PAGE_ROUTE_EXPORTS = new Set(["default", "pageConfig", "head", "generateHead", "Loading"]);
|
|
44
73
|
const ROOT_LAYOUT_EXPORTS = new Set([
|
|
45
74
|
"default",
|
|
@@ -563,20 +592,22 @@ export class WorkspaceExecutor extends Executor {
|
|
|
563
592
|
} = {}) {
|
|
564
593
|
return WorkspaceExecutor.#execs.get(repoName) ?? new WorkspaceExecutor({ workspaceRoot, repoName });
|
|
565
594
|
}
|
|
566
|
-
static getBaseDevEnv() {
|
|
595
|
+
static getBaseDevEnv(envPath?: string) {
|
|
567
596
|
|
|
568
|
-
const
|
|
569
|
-
|
|
597
|
+
const sourceEnv = envPath ? { ...process.env, ...parseEnvFile(envPath) } : process.env;
|
|
598
|
+
|
|
599
|
+
const appName = sourceEnv.AKAN_PUBLIC_APP_NAME;
|
|
600
|
+
const workspaceRoot = sourceEnv.AKAN_WORKSPACE_ROOT;
|
|
570
601
|
|
|
571
|
-
const repoName =
|
|
602
|
+
const repoName = sourceEnv.AKAN_PUBLIC_REPO_NAME;
|
|
572
603
|
if (!repoName) throw new Error("AKAN_PUBLIC_REPO_NAME is not set");
|
|
573
604
|
|
|
574
|
-
const serveDomain =
|
|
605
|
+
const serveDomain = sourceEnv.AKAN_PUBLIC_SERVE_DOMAIN;
|
|
575
606
|
if (!serveDomain) throw new Error("AKAN_PUBLIC_SERVE_DOMAIN is not set");
|
|
576
607
|
|
|
577
|
-
const portOffset = parseInt(
|
|
608
|
+
const portOffset = parseInt(sourceEnv.PORT_OFFSET ?? "0");
|
|
578
609
|
|
|
579
|
-
const env = (
|
|
610
|
+
const env = (sourceEnv.AKAN_PUBLIC_ENV ?? "debug") as
|
|
580
611
|
| "testing"
|
|
581
612
|
| "debug"
|
|
582
613
|
| "develop"
|
package/devkit/scanInfo.ts
CHANGED
|
@@ -284,12 +284,12 @@ class ScanInfo {
|
|
|
284
284
|
});
|
|
285
285
|
}),
|
|
286
286
|
]);
|
|
287
|
-
const routes = exec.type === "lib" ? [] : await exec.getPageKeys();
|
|
287
|
+
const routes = exec.type === "lib" ? [] : await (exec as AppExecutor).getPageKeys();
|
|
288
288
|
const scanResult: AppScanResult | LibScanResult = {
|
|
289
289
|
name: exec.name,
|
|
290
290
|
type: exec.type,
|
|
291
291
|
repoName: exec.workspace.repoName,
|
|
292
|
-
serveDomain: WorkspaceExecutor.getBaseDevEnv().serveDomain,
|
|
292
|
+
serveDomain: WorkspaceExecutor.getBaseDevEnv(path.join(exec.workspace.workspaceRoot, ".env")).serveDomain,
|
|
293
293
|
akanConfig,
|
|
294
294
|
files,
|
|
295
295
|
libDeps,
|
package/package.json
CHANGED