akanjs 2.0.0-beta.1 → 2.0.0-beta.10
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/application/application.command.ts +11 -3
- package/cli/guidelines/databaseModule/databaseModule.instruction.md +1 -1
- package/cli/guidelines/modelConstant/modelConstant.instruction.md +5 -5
- package/cli/guidelines/modelDocument/modelDocument.instruction.md +34 -61
- package/cli/guidelines/modelService/modelService.instruction.md +1 -1
- package/cli/index.js +157 -58
- package/cli/package/package.runner.ts +24 -7
- package/cli/package/package.script.ts +2 -2
- package/cli/templates/app/page/_index.tsx +200 -76
- package/cli/templates/app/page/_layout.tsx +0 -1
- package/cli/templates/module/__Model__.Zone.tsx +1 -1
- package/cli/templates/module/__model__.document.ts +1 -1
- package/cli/templates/workspaceRoot/.gitignore.template +1 -11
- package/cli/templates/workspaceRoot/biome.json.template +16 -0
- package/cli/workspace/workspace.command.ts +2 -6
- package/cli/workspace/workspace.runner.ts +1 -7
- package/cli/workspace/workspace.script.ts +14 -8
- package/client/csrTypes.ts +1 -1
- package/constant/fieldInfo.ts +1 -1
- package/devkit/capacitor.base.config.ts +1 -1
- package/devkit/capacitorApp.ts +5 -1
- package/devkit/commandDecorators/argMeta.ts +28 -14
- package/devkit/commandDecorators/command.ts +41 -15
- package/devkit/commandDecorators/commandBuilder.ts +78 -42
- package/devkit/commandDecorators/helpFormatter.ts +7 -4
- package/devkit/frontendBuild/cssCompiler.ts +9 -3
- package/devkit/incrementalBuilder/incrementalBuilder.proc.ts +2 -1
- package/devkit/lint/no-deep-internal-import.grit +25 -0
- package/devkit/mobile/mobileTarget.ts +48 -8
- package/devkit/src/capacitorApp.ts +277 -0
- package/devkit/transforms/barrelImportsPlugin.ts +6 -0
- package/package.json +3 -1
- package/server/hmr/clientScript.ts +8 -5
- package/server/resolver/resolver.contract.fixture.ts +1 -1
- package/ui/Field.tsx +0 -1
- package/ui/Portal.tsx +2 -0
- package/ui/System/CSR.tsx +6 -5
- package/ui/System/SSR.tsx +1 -1
- package/ui/System/SelectLanguage.tsx +1 -1
- package/webkit/bootCsr.tsx +8 -5
- package/cli/templates/app/common/commonLogic.ts +0 -12
- package/cli/templates/app/common/index.ts +0 -10
- package/cli/templates/app/srvkit/backendLogic.ts +0 -12
- package/cli/templates/app/srvkit/index.ts +0 -10
- package/cli/templates/app/ui/UiComponent.ts +0 -16
- package/cli/templates/app/ui/index.ts +0 -10
- package/cli/templates/app/webkit/frontendLogic.ts +0 -12
- package/cli/templates/app/webkit/index.ts +0 -10
- package/cli/templates/module/index.tsx +0 -44
- /package/cli/templates/app/public/{favicon.ico → favicon.ico.template} +0 -0
- /package/cli/templates/app/public/{logo.png → logo.png.template} +0 -0
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import path from "node:path";
|
|
1
2
|
import { Logger } from "akanjs/common";
|
|
2
3
|
import {
|
|
3
4
|
FileSys,
|
|
@@ -10,10 +11,15 @@ import {
|
|
|
10
11
|
import { $ } from "bun";
|
|
11
12
|
|
|
12
13
|
export class PackageRunner extends runner("package") {
|
|
13
|
-
async version(workspace: Workspace) {
|
|
14
|
-
const pkgJson = await FileSys.readJson<PackageJson>(
|
|
14
|
+
async version(workspace: Workspace, { log = true }: { log?: boolean } = {}) {
|
|
15
|
+
const pkgJson = await FileSys.readJson<PackageJson>(
|
|
16
|
+
process.env.USE_AKANJS_PKGS === "true"
|
|
17
|
+
? `${workspace.workspaceRoot}/pkgs/akanjs/package.json`
|
|
18
|
+
: `${path.dirname(Bun.main)}/../package.json`,
|
|
19
|
+
);
|
|
15
20
|
const version = pkgJson.version;
|
|
16
|
-
Logger.rawLog(`${pkgJson.name}@${version}`);
|
|
21
|
+
if (log) Logger.rawLog(`${pkgJson.name}@${version}`);
|
|
22
|
+
return version;
|
|
17
23
|
}
|
|
18
24
|
async createPackage(workspace: Workspace, pkgName: string) {
|
|
19
25
|
await workspace.applyTemplate({ basePath: `pkgs/${pkgName}`, template: "pkgRoot", dict: { pkgName } });
|
|
@@ -32,17 +38,28 @@ export class PackageRunner extends runner("package") {
|
|
|
32
38
|
await pkg.dist.mkdir(pkg.dist.cwdPath);
|
|
33
39
|
const scanner = await TypeScriptDependencyScanner.from(pkg);
|
|
34
40
|
const { npmDeps, npmDevDeps, missingDeps } = await scanner.getPackageBuildDependencies(pkg.name);
|
|
35
|
-
|
|
36
|
-
|
|
41
|
+
const packageRuntimeDependencies: Record<string, string[]> = { akanjs: ["daisyui"] };
|
|
42
|
+
const packageRuntimeDevDependencies: Record<string, string[]> = { akanjs: ["@biomejs/biome"] };
|
|
43
|
+
const forcedRuntimeDeps = packageRuntimeDependencies[pkg.name] ?? [];
|
|
44
|
+
const forcedRuntimeDevDeps = packageRuntimeDevDependencies[pkg.name] ?? [];
|
|
45
|
+
const packageRuntimeDeps = [...new Set([...npmDeps, ...forcedRuntimeDeps])];
|
|
46
|
+
const packageRuntimeDevDeps = [...new Set([...npmDevDeps, ...forcedRuntimeDevDeps])];
|
|
47
|
+
const rootPackageJson = await pkg.workspace.getPackageJson();
|
|
48
|
+
const rootDeps = { ...rootPackageJson.dependencies, ...rootPackageJson.devDependencies };
|
|
49
|
+
const missingForcedDeps = forcedRuntimeDeps.filter((dep) => !rootDeps[dep]);
|
|
50
|
+
const missingForcedDevDeps = forcedRuntimeDevDeps.filter((dep) => !rootDeps[dep]);
|
|
51
|
+
const allMissingDeps = [...new Set([...missingDeps, ...missingForcedDeps, ...missingForcedDevDeps])].sort();
|
|
52
|
+
if (allMissingDeps.length > 0)
|
|
53
|
+
throw new Error(`Missing dependency versions in root package.json: ${allMissingDeps.join(", ")}`);
|
|
37
54
|
|
|
38
|
-
await pkg.updatePackageJsonDependencies(
|
|
55
|
+
await pkg.updatePackageJsonDependencies(packageRuntimeDeps, packageRuntimeDevDeps);
|
|
39
56
|
|
|
40
57
|
const hasBuildFile = await Bun.file(`${pkg.cwdPath}/build.ts`).exists();
|
|
41
58
|
if (hasBuildFile) {
|
|
42
59
|
await pkg.workspace.spawn(process.execPath, [`${pkg.cwdPath}/build.ts`], { env: process.env, stdio: "inherit" });
|
|
43
60
|
} else {
|
|
44
61
|
await $`cp -r ${pkg.cwdPath}/. ${pkg.dist.cwdPath}`;
|
|
45
|
-
await Promise.all([pkg.generateDistPackageJson(
|
|
62
|
+
await Promise.all([pkg.generateDistPackageJson(packageRuntimeDeps, packageRuntimeDevDeps), pkg.generateTsconfigJson()]);
|
|
46
63
|
}
|
|
47
64
|
}
|
|
48
65
|
|
|
@@ -3,8 +3,8 @@ import { type Pkg, script, type Workspace } from "akanjs/devkit";
|
|
|
3
3
|
import { PackageRunner } from "./package.runner";
|
|
4
4
|
|
|
5
5
|
export class PackageScript extends script("package", [PackageRunner]) {
|
|
6
|
-
async version(workspace: Workspace) {
|
|
7
|
-
await this.packageRunner.version(workspace);
|
|
6
|
+
async version(workspace: Workspace, { log = true }: { log?: boolean } = {}) {
|
|
7
|
+
return await this.packageRunner.version(workspace, { log });
|
|
8
8
|
}
|
|
9
9
|
async createPackage(workspace: Workspace, pkgName: string) {
|
|
10
10
|
const spinner = workspace.spinning(`Creating package in pkgs/${pkgName}...`);
|
|
@@ -7,7 +7,9 @@ export default function getContent(scanInfo: AppInfo | LibInfo | null, dict: Dic
|
|
|
7
7
|
return {
|
|
8
8
|
filename: "_index.tsx",
|
|
9
9
|
content: `
|
|
10
|
-
import {
|
|
10
|
+
import { getEnv } from "akanjs/base";
|
|
11
|
+
import { usePage } from "akanjs/client";
|
|
12
|
+
import { Link, System } from "akanjs/ui";
|
|
11
13
|
import {
|
|
12
14
|
FaBookOpen,
|
|
13
15
|
FaBoxOpen,
|
|
@@ -21,82 +23,87 @@ import {
|
|
|
21
23
|
} from "react-icons/fa";
|
|
22
24
|
|
|
23
25
|
export default function Page() {
|
|
24
|
-
const appName =
|
|
25
|
-
|
|
26
|
-
const highlights = [
|
|
27
|
-
{
|
|
28
|
-
icon: <FaLayerGroup />,
|
|
29
|
-
title: "One Codebase",
|
|
30
|
-
description: "Design pages, services, server contracts, and deployment surfaces in one Akan workspace.",
|
|
31
|
-
},
|
|
32
|
-
{
|
|
33
|
-
icon: <FaShieldAlt />,
|
|
34
|
-
title: "Type-Safe Flow",
|
|
35
|
-
description: "Keep application contracts close to the business logic they protect.",
|
|
36
|
-
},
|
|
37
|
-
{
|
|
38
|
-
icon: <FaRocket />,
|
|
39
|
-
title: "Ready To Ship",
|
|
40
|
-
description: "Start locally, build production artifacts, and grow without changing the app shape.",
|
|
41
|
-
},
|
|
42
|
-
];
|
|
43
|
-
|
|
26
|
+
const appName = getEnv().appName;
|
|
27
|
+
const { l } = usePage();
|
|
44
28
|
return (
|
|
45
29
|
<main className="relative min-h-screen overflow-hidden bg-base-100 text-base-content">
|
|
46
|
-
<div className="absolute -
|
|
47
|
-
<div className="absolute -
|
|
30
|
+
<div className="absolute -top-48 -left-48 h-96 w-96 rounded-full bg-primary/25 blur-3xl" />
|
|
31
|
+
<div className="absolute -right-40 -bottom-56 h-112 w-md rounded-full bg-accent/20 blur-3xl" />
|
|
48
32
|
<div className="absolute inset-x-0 top-0 h-px bg-linear-to-r from-transparent via-primary/60 to-transparent" />
|
|
49
33
|
|
|
50
34
|
<section className="relative mx-auto flex min-h-screen w-full max-w-7xl flex-col px-6 py-8 lg:px-8">
|
|
51
35
|
<nav className="flex items-center justify-between">
|
|
52
36
|
<div className="flex items-center gap-3">
|
|
53
|
-
<div className="flex h-11 w-11 items-center justify-center overflow-hidden rounded-2xl bg-
|
|
54
|
-
<img
|
|
37
|
+
<div className="flex h-11 w-11 items-center justify-center overflow-hidden rounded-2xl bg-base-100 shadow-lg shadow-primary/20">
|
|
38
|
+
<img
|
|
39
|
+
src="/logo.png"
|
|
40
|
+
alt={l.trans({ en: "Akan.js logo", ko: "Akan.js 로고" })}
|
|
41
|
+
className="h-full w-full object-cover"
|
|
42
|
+
/>
|
|
55
43
|
</div>
|
|
56
44
|
<div>
|
|
57
|
-
<p className="text-
|
|
58
|
-
<p className="text-
|
|
45
|
+
<p className="font-semibold text-primary text-sm tracking-[0.25em]">Akan.js</p>
|
|
46
|
+
<p className="text-base-content/60 text-xs">
|
|
47
|
+
{l.trans({ en: "Full-stack TypeScript framework", ko: "풀스택 타입스크립트 프레임워크" })}
|
|
48
|
+
</p>
|
|
59
49
|
</div>
|
|
60
50
|
</div>
|
|
61
|
-
<
|
|
62
|
-
<
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
51
|
+
<div className="flex items-center gap-2">
|
|
52
|
+
<System.ThemeToggle themes={["light", "dark"]} />
|
|
53
|
+
<System.SelectLanguage languages={["en", "ko"]} />
|
|
54
|
+
<Link href="https://akanjs.com" target="_blank">
|
|
55
|
+
<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">
|
|
56
|
+
{l.trans({ en: "Official Site", ko: "공식 사이트" })}
|
|
57
|
+
<FaExternalLinkAlt />
|
|
58
|
+
</button>
|
|
59
|
+
</Link>
|
|
60
|
+
</div>
|
|
67
61
|
</nav>
|
|
68
62
|
|
|
69
63
|
<div className="grid flex-1 items-center gap-10 py-16 lg:grid-cols-[1.04fr_0.96fr] lg:py-10">
|
|
70
64
|
<div>
|
|
71
65
|
<div className="badge mb-6 border-primary/20 bg-primary/10 px-4 py-3 text-primary">
|
|
72
66
|
<FaCheckCircle />
|
|
73
|
-
Your app is running
|
|
67
|
+
{l.trans({ en: "Your app is running", ko: "앱이 실행 중입니다" })}
|
|
74
68
|
</div>
|
|
75
|
-
<h1 className="max-w-4xl
|
|
76
|
-
|
|
69
|
+
<h1 className="max-w-4xl font-black text-5xl text-base-content tracking-tight sm:text-6xl lg:text-7xl">
|
|
70
|
+
{l.trans({
|
|
71
|
+
en: "Akan turns business intent into the whole product.",
|
|
72
|
+
ko: "Akan은 비즈니스 의도를 제품 전체로 바꿉니다.",
|
|
73
|
+
})}
|
|
77
74
|
</h1>
|
|
78
|
-
<p className="mt-6 max-w-2xl text-lg leading-8
|
|
79
|
-
|
|
80
|
-
|
|
75
|
+
<p className="mt-6 max-w-2xl text-base-content/70 text-lg leading-8">
|
|
76
|
+
{l.trans({
|
|
77
|
+
en: "Agents Write. Keep It Minimal. Always Readable. Nice To Review. Build with less code, fewer repeated decisions, and a clearer path from idea to production.",
|
|
78
|
+
ko: "Agents Write. Keep It Minimal. Always Readable. Nice To Review. 더 적은 코드, 더 적은 반복 결정, 더 선명한 출시 경로로 만드세요.",
|
|
79
|
+
})}
|
|
81
80
|
</p>
|
|
82
81
|
|
|
83
82
|
<div className="mt-8 flex flex-wrap gap-3">
|
|
84
|
-
<span className="badge badge-lg border-base-content/10 bg-base-content/10 text-base-content">
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
<span className="badge badge-lg border-base-content/10 bg-base-content/10 text-base-content">
|
|
83
|
+
<span className="badge badge-lg border-base-content/10 bg-base-content/10 text-base-content">
|
|
84
|
+
Agent-ready
|
|
85
|
+
</span>
|
|
86
|
+
<span className="badge badge-lg border-base-content/10 bg-base-content/10 text-base-content">
|
|
87
|
+
Minimal code
|
|
88
|
+
</span>
|
|
89
|
+
<span className="badge badge-lg border-base-content/10 bg-base-content/10 text-base-content">
|
|
90
|
+
Readable by default
|
|
91
|
+
</span>
|
|
92
|
+
<span className="badge badge-lg border-base-content/10 bg-base-content/10 text-base-content">
|
|
93
|
+
Review-friendly
|
|
94
|
+
</span>
|
|
88
95
|
</div>
|
|
89
96
|
|
|
90
97
|
<div className="mt-10 flex flex-col gap-3 sm:flex-row">
|
|
91
98
|
<Link href="https://akanjs.com/docs/intro/quickstart" target="_blank">
|
|
92
99
|
<button className="btn border-none bg-primary text-base-100 hover:bg-primary/80">
|
|
93
|
-
Read Quick Start
|
|
100
|
+
{l.trans({ en: "Read Quick Start", ko: "빠른 시작 읽기" })}
|
|
94
101
|
<FaBookOpen />
|
|
95
102
|
</button>
|
|
96
103
|
</Link>
|
|
97
104
|
<Link href="https://akanjs.com/docs/intro/practice" target="_blank">
|
|
98
105
|
<button className="btn border-base-content/10 bg-base-content/10 text-base-content hover:border-base-content/20 hover:bg-base-content/15">
|
|
99
|
-
Learn By Building
|
|
106
|
+
{l.trans({ en: "Learn By Building", ko: "만들면서 배우기" })}
|
|
100
107
|
<FaCodeBranch />
|
|
101
108
|
</button>
|
|
102
109
|
</Link>
|
|
@@ -108,60 +115,177 @@ export default function Page() {
|
|
|
108
115
|
<div className="relative overflow-hidden rounded-4xl border border-base-content/10 bg-base-content/6 p-5 shadow-2xl backdrop-blur">
|
|
109
116
|
<div className="mb-5 flex items-center justify-between rounded-2xl border border-base-content/10 bg-base-100/70 px-4 py-3">
|
|
110
117
|
<div>
|
|
111
|
-
<p className="text-xs uppercase tracking-[0.24em]
|
|
112
|
-
|
|
118
|
+
<p className="text-base-content/40 text-xs uppercase tracking-[0.24em]">
|
|
119
|
+
{l.trans({ en: "Akan Acrostic", ko: "Akan 사행시" })}
|
|
120
|
+
</p>
|
|
121
|
+
<p className="font-semibold text-base-content text-lg">
|
|
122
|
+
{l.trans({ en: "A framework for focused builders", ko: "집중하는 빌더를 위한 프레임워크" })}
|
|
123
|
+
</p>
|
|
124
|
+
</div>
|
|
125
|
+
<div className="rounded-xl bg-primary/10 px-3 py-2 font-medium text-primary text-sm">{appName}</div>
|
|
126
|
+
</div>
|
|
127
|
+
|
|
128
|
+
<div className="grid gap-3">
|
|
129
|
+
<div className="rounded-2xl border border-base-content/10 bg-base-100/80 p-4">
|
|
130
|
+
<div className="flex gap-4">
|
|
131
|
+
<div className="flex h-12 w-12 shrink-0 items-center justify-center rounded-2xl bg-base-100 font-black text-primary text-xl">
|
|
132
|
+
A
|
|
133
|
+
</div>
|
|
134
|
+
<div>
|
|
135
|
+
<p className="font-bold text-base-content">Agents Write</p>
|
|
136
|
+
<p className="mt-1 text-base-content/60 text-sm leading-6">
|
|
137
|
+
{l.trans({
|
|
138
|
+
en: "Business definitions become the source code, so teams and agents can focus on what to build.",
|
|
139
|
+
ko: "비즈니스 정의가 소스 코드가 되므로, 팀과 에이전트는 무엇을 만들지에 집중할 수 있습니다.",
|
|
140
|
+
})}
|
|
141
|
+
</p>
|
|
142
|
+
</div>
|
|
143
|
+
</div>
|
|
144
|
+
</div>
|
|
145
|
+
<div className="rounded-2xl border border-base-content/10 bg-base-100/80 p-4">
|
|
146
|
+
<div className="flex gap-4">
|
|
147
|
+
<div className="flex h-12 w-12 shrink-0 items-center justify-center rounded-2xl bg-base-100 font-black text-primary text-xl">
|
|
148
|
+
K
|
|
149
|
+
</div>
|
|
150
|
+
<div>
|
|
151
|
+
<p className="font-bold text-base-content">Keep It Minimal</p>
|
|
152
|
+
<p className="mt-1 text-base-content/60 text-sm leading-6">
|
|
153
|
+
{l.trans({
|
|
154
|
+
en: "One definition flows into web, app, server, database, and infrastructure without repeated logic.",
|
|
155
|
+
ko: "하나의 정의가 반복 로직 없이 웹, 앱, 서버, 데이터베이스, 인프라로 이어집니다.",
|
|
156
|
+
})}
|
|
157
|
+
</p>
|
|
158
|
+
</div>
|
|
159
|
+
</div>
|
|
160
|
+
</div>
|
|
161
|
+
<div className="rounded-2xl border border-base-content/10 bg-base-100/80 p-4">
|
|
162
|
+
<div className="flex gap-4">
|
|
163
|
+
<div className="flex h-12 w-12 shrink-0 items-center justify-center rounded-2xl bg-base-100 font-black text-primary text-xl">
|
|
164
|
+
A
|
|
165
|
+
</div>
|
|
166
|
+
<div>
|
|
167
|
+
<p className="font-bold text-base-content">Always Readable</p>
|
|
168
|
+
<p className="mt-1 text-base-content/60 text-sm leading-6">
|
|
169
|
+
{l.trans({
|
|
170
|
+
en: "Strict conventions keep the app easy to understand long after the first version ships.",
|
|
171
|
+
ko: "엄격한 컨벤션은 첫 버전 출시 후에도 앱을 쉽게 이해할 수 있게 지켜줍니다.",
|
|
172
|
+
})}
|
|
173
|
+
</p>
|
|
174
|
+
</div>
|
|
175
|
+
</div>
|
|
176
|
+
</div>
|
|
177
|
+
<div className="rounded-2xl border border-base-content/10 bg-base-100/80 p-4">
|
|
178
|
+
<div className="flex gap-4">
|
|
179
|
+
<div className="flex h-12 w-12 shrink-0 items-center justify-center rounded-2xl bg-base-100 font-black text-primary text-xl">
|
|
180
|
+
N
|
|
181
|
+
</div>
|
|
182
|
+
<div>
|
|
183
|
+
<p className="font-bold text-base-content">Nice To Review</p>
|
|
184
|
+
<p className="mt-1 text-base-content/60 text-sm leading-6">
|
|
185
|
+
{l.trans({
|
|
186
|
+
en: "Focused changes make business intent clear, so reviews stay fast and releases stay calm.",
|
|
187
|
+
ko: "집중된 변경은 비즈니스 의도를 선명하게 만들어 리뷰를 빠르게 하고 배포를 안정적으로 유지합니다.",
|
|
188
|
+
})}
|
|
189
|
+
</p>
|
|
190
|
+
</div>
|
|
191
|
+
</div>
|
|
113
192
|
</div>
|
|
114
|
-
<div className="rounded-xl bg-primary/10 px-3 py-2 text-sm font-medium text-primary">Live</div>
|
|
115
193
|
</div>
|
|
116
194
|
|
|
117
|
-
<div className="mockup-code border border-base-content/10 bg-base-100 text-sm shadow-none">
|
|
195
|
+
<div className="mockup-code mt-5 border border-base-content/10 bg-base-100 text-sm shadow-none">
|
|
118
196
|
<pre data-prefix="$">
|
|
119
197
|
<code className="text-primary">bun run akan start {appName}</code>
|
|
120
198
|
</pre>
|
|
121
199
|
<pre data-prefix="✓">
|
|
122
|
-
<code className="text-accent">
|
|
200
|
+
<code className="text-accent">
|
|
201
|
+
{l.trans({ en: "web, app, server, db, and infra ready", ko: "웹, 앱, 서버, DB, 인프라 준비 완료" })}
|
|
202
|
+
</code>
|
|
123
203
|
</pre>
|
|
124
204
|
<pre data-prefix="→">
|
|
125
|
-
<code className="text-base-content/70">
|
|
205
|
+
<code className="text-base-content/70">
|
|
206
|
+
{l.trans({
|
|
207
|
+
en: "edit page/_index.tsx around your business",
|
|
208
|
+
ko: "비즈니스에 맞게 page/_index.tsx를 수정하세요",
|
|
209
|
+
})}
|
|
210
|
+
</code>
|
|
126
211
|
</pre>
|
|
127
212
|
</div>
|
|
128
|
-
|
|
129
|
-
<div className="mt-5 grid gap-3 sm:grid-cols-3">
|
|
130
|
-
{[
|
|
131
|
-
["Pages", "UI routes"],
|
|
132
|
-
["Services", "Typed APIs"],
|
|
133
|
-
["Deploy", "Artifacts"],
|
|
134
|
-
].map(([title, description]) => (
|
|
135
|
-
<div key={title} className="rounded-2xl border border-base-content/10 bg-base-content/4 p-4">
|
|
136
|
-
<p className="text-sm font-semibold text-base-content">{title}</p>
|
|
137
|
-
<p className="mt-1 text-xs text-base-content/60">{description}</p>
|
|
138
|
-
</div>
|
|
139
|
-
))}
|
|
140
|
-
</div>
|
|
141
213
|
</div>
|
|
142
214
|
</div>
|
|
143
215
|
</div>
|
|
144
216
|
|
|
145
|
-
<div className="grid gap-4 pb-8 md:grid-cols-
|
|
146
|
-
|
|
147
|
-
<div
|
|
148
|
-
<
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
217
|
+
<div className="grid gap-4 pb-8 md:grid-cols-2 xl:grid-cols-4">
|
|
218
|
+
<div className="rounded-3xl border border-base-content/10 bg-base-content/4 p-6 backdrop-blur">
|
|
219
|
+
<div className="mb-5 flex h-12 w-12 items-center justify-center rounded-2xl bg-primary/10 text-primary text-xl">
|
|
220
|
+
<FaTerminal />
|
|
221
|
+
</div>
|
|
222
|
+
<h2 className="font-bold text-base-content text-lg">
|
|
223
|
+
{l.trans({ en: "Agentic By Design", ko: "에이전틱 설계" })}
|
|
224
|
+
</h2>
|
|
225
|
+
<p className="mt-2 text-base-content/60 text-sm leading-6">
|
|
226
|
+
{l.trans({
|
|
227
|
+
en: "Describe the business once and let Akan shape the application surfaces around it.",
|
|
228
|
+
ko: "비즈니스를 한 번 설명하면 Akan이 그 주변의 애플리케이션 표면을 구성합니다.",
|
|
229
|
+
})}
|
|
230
|
+
</p>
|
|
231
|
+
</div>
|
|
232
|
+
<div className="rounded-3xl border border-base-content/10 bg-base-content/4 p-6 backdrop-blur">
|
|
233
|
+
<div className="mb-5 flex h-12 w-12 items-center justify-center rounded-2xl bg-primary/10 text-primary text-xl">
|
|
234
|
+
<FaLayerGroup />
|
|
235
|
+
</div>
|
|
236
|
+
<h2 className="font-bold text-base-content text-lg">
|
|
237
|
+
{l.trans({ en: "One Definition", ko: "하나의 정의" })}
|
|
238
|
+
</h2>
|
|
239
|
+
<p className="mt-2 text-base-content/60 text-sm leading-6">
|
|
240
|
+
{l.trans({
|
|
241
|
+
en: "Pages, services, database models, and deployment artifacts stay connected in one workspace.",
|
|
242
|
+
ko: "페이지, 서비스, 데이터베이스 모델, 배포 산출물이 하나의 워크스페이스에서 연결됩니다.",
|
|
243
|
+
})}
|
|
244
|
+
</p>
|
|
245
|
+
</div>
|
|
246
|
+
<div className="rounded-3xl border border-base-content/10 bg-base-content/4 p-6 backdrop-blur">
|
|
247
|
+
<div className="mb-5 flex h-12 w-12 items-center justify-center rounded-2xl bg-primary/10 text-primary text-xl">
|
|
248
|
+
<FaRocket />
|
|
153
249
|
</div>
|
|
154
|
-
|
|
250
|
+
<h2 className="font-bold text-base-content text-lg">
|
|
251
|
+
{l.trans({ en: "Less To Review", ko: "리뷰할 것이 적습니다" })}
|
|
252
|
+
</h2>
|
|
253
|
+
<p className="mt-2 text-base-content/60 text-sm leading-6">
|
|
254
|
+
{l.trans({
|
|
255
|
+
en: "Smaller code surfaces make intent easier to inspect, approve, and ship.",
|
|
256
|
+
ko: "더 작은 코드 표면은 의도를 확인하고 승인하고 배포하기 쉽게 만듭니다.",
|
|
257
|
+
})}
|
|
258
|
+
</p>
|
|
259
|
+
</div>
|
|
260
|
+
<div className="rounded-3xl border border-base-content/10 bg-base-content/4 p-6 backdrop-blur">
|
|
261
|
+
<div className="mb-5 flex h-12 w-12 items-center justify-center rounded-2xl bg-primary/10 text-primary text-xl">
|
|
262
|
+
<FaShieldAlt />
|
|
263
|
+
</div>
|
|
264
|
+
<h2 className="font-bold text-base-content text-lg">
|
|
265
|
+
{l.trans({ en: "Type-Safe Growth", ko: "타입 안전한 성장" })}
|
|
266
|
+
</h2>
|
|
267
|
+
<p className="mt-2 text-base-content/60 text-sm leading-6">
|
|
268
|
+
{l.trans({
|
|
269
|
+
en: "Conventions and contracts keep the stack readable as the product expands.",
|
|
270
|
+
ko: "컨벤션과 계약은 제품이 확장되어도 스택을 읽기 쉽게 유지합니다.",
|
|
271
|
+
})}
|
|
272
|
+
</p>
|
|
273
|
+
</div>
|
|
155
274
|
</div>
|
|
156
275
|
|
|
157
|
-
<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-
|
|
276
|
+
<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-base-content/70 text-sm md:flex-row md:items-center">
|
|
158
277
|
<div className="flex items-center gap-3">
|
|
159
278
|
<FaTerminal className="text-primary" />
|
|
160
|
-
<span>
|
|
279
|
+
<span>
|
|
280
|
+
{l.trans({
|
|
281
|
+
en: "Next: define the business once, then let Akan carry it across every surface.",
|
|
282
|
+
ko: "다음 단계: 비즈니스를 한 번 정의하고, Akan이 모든 표면으로 이어가게 하세요.",
|
|
283
|
+
})}
|
|
284
|
+
</span>
|
|
161
285
|
</div>
|
|
162
286
|
<div className="flex items-center gap-2 text-base-content/40">
|
|
163
287
|
<FaBoxOpen />
|
|
164
|
-
<span>Akan.js template</span>
|
|
288
|
+
<span>{l.trans({ en: "Akan.js template", ko: "Akan.js 템플릿" })}</span>
|
|
165
289
|
</div>
|
|
166
290
|
</div>
|
|
167
291
|
</section>
|
|
@@ -10,7 +10,6 @@ export default function getContent(scanInfo: AppInfo | LibInfo | null, dict: Dic
|
|
|
10
10
|
content: `
|
|
11
11
|
import "./styles.css";
|
|
12
12
|
import type { LayoutProps } from "akanjs/client";
|
|
13
|
-
import { fetch } from "@${dict.appName}/client";
|
|
14
13
|
${isUsingShared ? "import { Auth } from '@shared/ui';" : ""}
|
|
15
14
|
|
|
16
15
|
export const head = (
|
|
@@ -12,7 +12,7 @@ export default function getContent(scanInfo: AppInfo | LibInfo | null, dict: Dic
|
|
|
12
12
|
"use client";
|
|
13
13
|
import { Load } from "akanjs/ui";
|
|
14
14
|
import { cnst, ${dict.Model} } from "@${dict.sysName}/client";
|
|
15
|
-
import type { ClientInit, ClientView } from "akanjs/fetch";
|
|
15
|
+
import type { ClientInit, ClientView, SliceMeta } from "akanjs/fetch";
|
|
16
16
|
|
|
17
17
|
interface CardProps {
|
|
18
18
|
className?: string;
|
|
@@ -7,7 +7,7 @@ interface Dict {
|
|
|
7
7
|
}
|
|
8
8
|
export default function getContent(scanInfo: AppInfo | LibInfo | null, dict: Dict) {
|
|
9
9
|
return `
|
|
10
|
-
import {
|
|
10
|
+
import { by, from, into, type SchemaOf } from "akanjs/document";
|
|
11
11
|
|
|
12
12
|
import * as cnst from "../cnst";
|
|
13
13
|
|
|
@@ -25,10 +25,6 @@ apps/*/scripts
|
|
|
25
25
|
**/*secrets.yaml
|
|
26
26
|
**/kubeconfig.yaml
|
|
27
27
|
**/secrets.*
|
|
28
|
-
**/.idea
|
|
29
|
-
apps/**/src/schema.gql
|
|
30
|
-
.next
|
|
31
|
-
.vite
|
|
32
28
|
dump
|
|
33
29
|
local
|
|
34
30
|
|
|
@@ -50,7 +46,6 @@ DerivedData
|
|
|
50
46
|
*.ipa
|
|
51
47
|
*.xcuserstate
|
|
52
48
|
|
|
53
|
-
# **/android
|
|
54
49
|
build/
|
|
55
50
|
.gradle
|
|
56
51
|
local.properties
|
|
@@ -122,9 +117,4 @@ libs/*/common/index.ts
|
|
|
122
117
|
libs/*/client.ts
|
|
123
118
|
libs/*/server.ts
|
|
124
119
|
libs/*/index.ts
|
|
125
|
-
|
|
126
|
-
# **/postcss.config.js
|
|
127
|
-
# **/playwright.config.ts
|
|
128
|
-
# **/next-env.d.ts
|
|
129
|
-
# **/tsconfig.json
|
|
130
|
-
# **/tsconfig.spec.json
|
|
120
|
+
**/.akan
|
|
@@ -142,6 +142,22 @@
|
|
|
142
142
|
"includes": ["**/*.constant.ts", "**/*.document.ts", "**/*.service.ts", "**/*.store.ts"],
|
|
143
143
|
"plugins": ["./node_modules/akanjs/devkit/lint/no-js-private-class-method.grit"]
|
|
144
144
|
},
|
|
145
|
+
{
|
|
146
|
+
"includes": [
|
|
147
|
+
"**/*.constant.ts",
|
|
148
|
+
"**/*.dictionary.ts",
|
|
149
|
+
"**/*.document.ts",
|
|
150
|
+
"**/*.service.ts",
|
|
151
|
+
"**/*.signal.ts",
|
|
152
|
+
"**/*.store.ts",
|
|
153
|
+
"**/*.Template.tsx",
|
|
154
|
+
"**/*.Unit.tsx",
|
|
155
|
+
"**/*.Util.tsx",
|
|
156
|
+
"**/*.View.tsx",
|
|
157
|
+
"**/*.Zone.tsx"
|
|
158
|
+
],
|
|
159
|
+
"plugins": ["./node_modules/akanjs/devkit/lint/no-deep-internal-import.grit"]
|
|
160
|
+
},
|
|
145
161
|
{
|
|
146
162
|
"includes": [
|
|
147
163
|
"**/page/**/*.ts",
|
|
@@ -22,20 +22,16 @@ export class WorkspaceCommand extends command("workspace", [WorkspaceScript], ({
|
|
|
22
22
|
},
|
|
23
23
|
],
|
|
24
24
|
})
|
|
25
|
-
.option("tag", String, {
|
|
26
|
-
desc: "tag of the update",
|
|
27
|
-
default: "latest",
|
|
28
|
-
})
|
|
29
25
|
.option("init", Boolean, {
|
|
30
26
|
desc: "Do you want to initialize the workspace? (Recommended)",
|
|
31
27
|
default: true,
|
|
32
28
|
})
|
|
33
|
-
.exec(async function (workspaceName, app, dir, libs,
|
|
29
|
+
.exec(async function (workspaceName, app, dir, libs, init) {
|
|
34
30
|
const appName = app || "app";
|
|
35
31
|
await this.workspaceScript.createWorkspace(
|
|
36
32
|
workspaceName.toLowerCase().replace(/ /g, "-"),
|
|
37
33
|
appName.toLowerCase().replace(/ /g, "-"),
|
|
38
|
-
{ dirname: dir, installLibs: libs,
|
|
34
|
+
{ dirname: dir, installLibs: libs, init },
|
|
39
35
|
);
|
|
40
36
|
}),
|
|
41
37
|
lint: target({ desc: "Lint and fix code in a specific app/lib/pkg" })
|
|
@@ -1,16 +1,11 @@
|
|
|
1
1
|
import path from "node:path";
|
|
2
2
|
import { type Exec, type PackageJson, runner, type Workspace, WorkspaceExecutor } from "akanjs/devkit";
|
|
3
|
-
import latestVersion from "latest-version";
|
|
4
3
|
|
|
5
4
|
export class WorkspaceRunner extends runner("workspace") {
|
|
6
|
-
async #resolveAkanVersion(tag: string) {
|
|
7
|
-
return /^\d+\.\d+\.\d+(?:[-+][0-9A-Za-z.-]+)?$/.test(tag) ? tag : await latestVersion("akanjs", { version: tag });
|
|
8
|
-
}
|
|
9
|
-
|
|
10
5
|
async createWorkspace(
|
|
11
6
|
repoName: string,
|
|
12
7
|
appName: string,
|
|
13
|
-
{ dirname = ".",
|
|
8
|
+
{ dirname = ".", init = true, akanVersion }: { dirname?: string; init?: boolean; akanVersion: string },
|
|
14
9
|
) {
|
|
15
10
|
const cwdPath = process.cwd();
|
|
16
11
|
const workspaceRoot = path.join(cwdPath, dirname, repoName);
|
|
@@ -25,7 +20,6 @@ export class WorkspaceRunner extends runner("workspace") {
|
|
|
25
20
|
templateSpinner.succeed(`Workspace files created in ${dirname}/${repoName}`);
|
|
26
21
|
|
|
27
22
|
const rootPackageJson = await workspace.getPackageJson();
|
|
28
|
-
const akanVersion = await this.#resolveAkanVersion(tag);
|
|
29
23
|
const packageJson: PackageJson = {
|
|
30
24
|
...rootPackageJson,
|
|
31
25
|
dependencies: {
|
|
@@ -4,20 +4,26 @@ import { AppExecutor, type Exec, LibExecutor, PkgExecutor, script, type Workspac
|
|
|
4
4
|
|
|
5
5
|
import { ApplicationScript } from "../application/application.script";
|
|
6
6
|
import { LibraryScript } from "../library/library.script";
|
|
7
|
+
import { PackageScript } from "../package/package.script";
|
|
7
8
|
import { WorkspaceRunner } from "./workspace.runner";
|
|
8
9
|
|
|
9
|
-
export class WorkspaceScript extends script("workspace", [
|
|
10
|
+
export class WorkspaceScript extends script("workspace", [
|
|
11
|
+
WorkspaceRunner,
|
|
12
|
+
ApplicationScript,
|
|
13
|
+
LibraryScript,
|
|
14
|
+
PackageScript,
|
|
15
|
+
]) {
|
|
10
16
|
async createWorkspace(
|
|
11
17
|
repoName: string,
|
|
12
18
|
appName: string,
|
|
13
|
-
{
|
|
14
|
-
dirname = ".",
|
|
15
|
-
installLibs = false,
|
|
16
|
-
tag = "latest",
|
|
17
|
-
init = true,
|
|
18
|
-
}: { dirname?: string; installLibs?: boolean; tag?: string; init?: boolean },
|
|
19
|
+
{ dirname = ".", installLibs = false, init = true }: { dirname?: string; installLibs?: boolean; init?: boolean },
|
|
19
20
|
) {
|
|
20
|
-
const
|
|
21
|
+
const akanVersion = await this.packageScript.version({ log: false });
|
|
22
|
+
const workspace = await this.workspaceRunner.createWorkspace(repoName, appName, {
|
|
23
|
+
dirname,
|
|
24
|
+
init,
|
|
25
|
+
akanVersion,
|
|
26
|
+
});
|
|
21
27
|
if (installLibs) {
|
|
22
28
|
await this.libraryScript.installLibrary(workspace, "util");
|
|
23
29
|
await this.libraryScript.installLibrary(workspace, "shared");
|
package/client/csrTypes.ts
CHANGED
|
@@ -111,7 +111,7 @@ export interface LayoutModule {
|
|
|
111
111
|
}
|
|
112
112
|
export type RouteModule = PageModule | LayoutModule;
|
|
113
113
|
export interface Route {
|
|
114
|
-
|
|
114
|
+
PageConfig?: PageConfig;
|
|
115
115
|
path: string;
|
|
116
116
|
renderPage?: RouteRender;
|
|
117
117
|
renderLayout?: RouteRender;
|
package/constant/fieldInfo.ts
CHANGED
|
@@ -190,7 +190,7 @@ export class ConstantField<
|
|
|
190
190
|
}
|
|
191
191
|
static getBaseInsightField(): FieldObject {
|
|
192
192
|
return {
|
|
193
|
-
count: field(Int, { default: 0, accumulate: {
|
|
193
|
+
count: field(Int, { default: 0, accumulate: {} }).toField(),
|
|
194
194
|
};
|
|
195
195
|
}
|
|
196
196
|
readonly nullable: Nullable;
|
package/devkit/capacitorApp.ts
CHANGED
|
@@ -201,7 +201,11 @@ export class CapacitorApp {
|
|
|
201
201
|
.relative(this.targetRoot, path.join(this.app.cwdPath, "akan.app.json"))
|
|
202
202
|
.split(path.sep)
|
|
203
203
|
.join("/");
|
|
204
|
-
const
|
|
204
|
+
const baseConfigPath = path
|
|
205
|
+
.relative(this.targetRoot, path.join(this.app.workspace.cwdPath, "pkgs/akanjs/devkit/capacitor.base.config"))
|
|
206
|
+
.split(path.sep)
|
|
207
|
+
.join("/");
|
|
208
|
+
const content = `import { withBase } from "${baseConfigPath.startsWith(".") ? baseConfigPath : `./${baseConfigPath}`}";
|
|
205
209
|
import appInfo from "${appInfoPath}";
|
|
206
210
|
|
|
207
211
|
export default withBase((config) => config, appInfo, "${this.target.name}");
|