akanjs 2.0.0-rc.6 → 2.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/base/primitiveRegistry.ts +28 -2
- package/cli/application/application.command.ts +11 -3
- package/cli/application/application.runner.ts +17 -1
- 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 +9321 -19222
- package/cli/library/library.runner.ts +14 -13
- package/cli/package/package.runner.ts +31 -6
- package/cli/package/package.script.ts +2 -2
- package/cli/templates/app/page/_index.tsx +200 -79
- package/cli/templates/app/page/_layout.tsx +0 -1
- package/cli/templates/app/public/favicon.ico.template +0 -0
- package/cli/templates/app/public/logo.png.template +0 -0
- 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 +7 -9
- package/cli/workspace/workspace.runner.ts +3 -13
- package/cli/workspace/workspace.script.ts +24 -9
- package/client/csrTypes.ts +1 -1
- package/constant/fieldInfo.ts +1 -1
- package/constant/serialize.ts +7 -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/dependencyScanner.ts +121 -15
- package/devkit/executors.ts +35 -23
- 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/lint/no-import-external-library.grit +1 -0
- package/devkit/mobile/mobileTarget.ts +48 -8
- package/devkit/scanInfo.ts +4 -1
- package/devkit/src/capacitorApp.ts +277 -0
- package/devkit/transforms/barrelImportsPlugin.ts +6 -0
- package/fetch/client/fetchClient.ts +1 -0
- package/fetch/client/httpClient.ts +13 -1
- package/package.json +37 -31
- package/server/akanServer.ts +21 -7
- package/server/hmr/clientScript.ts +8 -5
- package/server/resolver/resolver.contract.fixture.ts +1 -1
- package/test/index.ts +14 -0
- package/test/signalTest.preload.ts +10 -0
- package/test/signalTestRuntime.ts +126 -0
- package/test/testServer.ts +130 -25
- package/ui/Constant/Doc.tsx +696 -0
- package/ui/Constant/Mermaid.tsx +149 -0
- package/ui/Constant/index.ts +6 -0
- package/ui/Constant/schemaDoc.ts +324 -0
- 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/ui/index.ts +1 -0
- package/ui/styles.css +0 -2
- package/webkit/bootCsr.tsx +8 -5
- package/base/test-globals.d.ts +0 -4
- package/cli/templates/app/common/commonLogic.ts +0 -12
- package/cli/templates/app/common/index.ts +0 -10
- package/cli/templates/app/public/favicon.ico +0 -0
- package/cli/templates/app/public/icons/icon-128x128.png +0 -0
- package/cli/templates/app/public/icons/icon-144x144.png +0 -0
- package/cli/templates/app/public/icons/icon-152x152.png +0 -0
- package/cli/templates/app/public/icons/icon-192x192.png +0 -0
- package/cli/templates/app/public/icons/icon-256x256.png +0 -0
- package/cli/templates/app/public/icons/icon-384x384.png +0 -0
- package/cli/templates/app/public/icons/icon-48x48.png +0 -0
- package/cli/templates/app/public/icons/icon-512x512.png +0 -0
- package/cli/templates/app/public/icons/icon-72x72.png +0 -0
- package/cli/templates/app/public/icons/icon-96x96.png +0 -0
- package/cli/templates/app/public/logo.svg +0 -70
- package/cli/templates/app/public/manifest.json.template +0 -67
- 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
|
@@ -1,12 +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
5
|
async createWorkspace(
|
|
7
6
|
repoName: string,
|
|
8
7
|
appName: string,
|
|
9
|
-
{ dirname = ".",
|
|
8
|
+
{ dirname = ".", init = true, akanVersion }: { dirname?: string; init?: boolean; akanVersion: string },
|
|
10
9
|
) {
|
|
11
10
|
const cwdPath = process.cwd();
|
|
12
11
|
const workspaceRoot = path.join(cwdPath, dirname, repoName);
|
|
@@ -21,18 +20,17 @@ export class WorkspaceRunner extends runner("workspace") {
|
|
|
21
20
|
templateSpinner.succeed(`Workspace files created in ${dirname}/${repoName}`);
|
|
22
21
|
|
|
23
22
|
const rootPackageJson = await workspace.getPackageJson();
|
|
24
|
-
const latestPublishedVersion = await latestVersion("akanjs", { version: tag });
|
|
25
23
|
const packageJson: PackageJson = {
|
|
26
24
|
...rootPackageJson,
|
|
27
25
|
dependencies: {
|
|
28
26
|
...rootPackageJson.dependencies,
|
|
29
|
-
akanjs:
|
|
27
|
+
akanjs: akanVersion,
|
|
30
28
|
},
|
|
31
29
|
devDependencies: {
|
|
32
30
|
...rootPackageJson.devDependencies,
|
|
33
31
|
},
|
|
34
32
|
};
|
|
35
|
-
workspace.setPackageJson(packageJson);
|
|
33
|
+
await workspace.setPackageJson(packageJson);
|
|
36
34
|
|
|
37
35
|
if (init) {
|
|
38
36
|
const installSpinner = workspace.spinning("Installing dependencies with bun...");
|
|
@@ -40,14 +38,6 @@ export class WorkspaceRunner extends runner("workspace") {
|
|
|
40
38
|
installSpinner.succeed("Dependencies installed with bun");
|
|
41
39
|
}
|
|
42
40
|
|
|
43
|
-
const gitSpinner = workspace.spinning("Initializing git repository and commit...");
|
|
44
|
-
try {
|
|
45
|
-
await workspace.commit("Initial commit", { init: true });
|
|
46
|
-
gitSpinner.succeed("Git repository initialized and committed");
|
|
47
|
-
} catch (_) {
|
|
48
|
-
gitSpinner.fail("Git repository initialization failed. It's not fatal, you can commit manually");
|
|
49
|
-
}
|
|
50
|
-
|
|
51
41
|
return workspace;
|
|
52
42
|
}
|
|
53
43
|
async lint(exec: Exec, workspace: Workspace, { fix = true }: { fix?: boolean } = {}) {
|
|
@@ -1,29 +1,44 @@
|
|
|
1
|
+
import path from "node:path";
|
|
1
2
|
import { Logger } from "akanjs/common";
|
|
2
3
|
import { AppExecutor, type Exec, LibExecutor, PkgExecutor, script, type Workspace } from "akanjs/devkit";
|
|
3
4
|
|
|
4
5
|
import { ApplicationScript } from "../application/application.script";
|
|
5
6
|
import { LibraryScript } from "../library/library.script";
|
|
7
|
+
import { PackageScript } from "../package/package.script";
|
|
6
8
|
import { WorkspaceRunner } from "./workspace.runner";
|
|
7
9
|
|
|
8
|
-
export class WorkspaceScript extends script("workspace", [
|
|
10
|
+
export class WorkspaceScript extends script("workspace", [
|
|
11
|
+
WorkspaceRunner,
|
|
12
|
+
ApplicationScript,
|
|
13
|
+
LibraryScript,
|
|
14
|
+
PackageScript,
|
|
15
|
+
]) {
|
|
9
16
|
async createWorkspace(
|
|
10
17
|
repoName: string,
|
|
11
18
|
appName: string,
|
|
12
|
-
{
|
|
13
|
-
dirname = ".",
|
|
14
|
-
installLibs = false,
|
|
15
|
-
tag = "latest",
|
|
16
|
-
init = true,
|
|
17
|
-
}: { dirname?: string; installLibs?: boolean; tag?: string; init?: boolean },
|
|
19
|
+
{ dirname = ".", installLibs = false, init = true }: { dirname?: string; installLibs?: boolean; init?: boolean },
|
|
18
20
|
) {
|
|
19
|
-
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
|
+
});
|
|
20
27
|
if (installLibs) {
|
|
21
28
|
await this.libraryScript.installLibrary(workspace, "util");
|
|
22
29
|
await this.libraryScript.installLibrary(workspace, "shared");
|
|
23
30
|
}
|
|
24
31
|
await this.applicationScript.createApplication(appName, workspace, { libs: installLibs ? ["util", "shared"] : [] });
|
|
32
|
+
const gitSpinner = workspace.spinning("Initializing git repository and commit...");
|
|
33
|
+
try {
|
|
34
|
+
await workspace.commit("Initial commit", { init: true });
|
|
35
|
+
gitSpinner.succeed("Git repository initialized and committed");
|
|
36
|
+
} catch (_) {
|
|
37
|
+
gitSpinner.fail("Git repository initialization failed. It's not fatal, you can commit manually");
|
|
38
|
+
}
|
|
39
|
+
const workspacePath = path.join(dirname, repoName);
|
|
25
40
|
Logger.rawLog(`\n🎉 Welcome aboard! Workspace created in ${dirname}/${repoName}`);
|
|
26
|
-
Logger.rawLog(`🚀 Run \`cd ${
|
|
41
|
+
Logger.rawLog(`🚀 Run \`cd ${workspacePath} && akan start ${appName}\` to start the development server.`);
|
|
27
42
|
|
|
28
43
|
Logger.rawLog(`\n👋 Happy coding!`);
|
|
29
44
|
}
|
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/constant/serialize.ts
CHANGED
|
@@ -54,7 +54,7 @@ const serializeInput = <Input = unknown>(
|
|
|
54
54
|
Object.entries(modelRef[FIELD_META]).map(([key, field]) => [
|
|
55
55
|
key,
|
|
56
56
|
field.isClass && !field.isScalar
|
|
57
|
-
? serialize(ID, field.arrDepth, (value as Record<string, unknown>)?.[key], serializeType, {
|
|
57
|
+
? serialize(ID, field.arrDepth, getRelationId((value as Record<string, unknown>)?.[key]), serializeType, {
|
|
58
58
|
nullable: field.nullable,
|
|
59
59
|
key,
|
|
60
60
|
})
|
|
@@ -67,6 +67,12 @@ const serializeInput = <Input = unknown>(
|
|
|
67
67
|
}
|
|
68
68
|
};
|
|
69
69
|
|
|
70
|
+
const getRelationId = (value: unknown): unknown => {
|
|
71
|
+
if (Array.isArray(value)) return value.map((item) => getRelationId(item));
|
|
72
|
+
if (value && typeof value === "object" && "id" in value) return (value as { id: unknown }).id;
|
|
73
|
+
return value;
|
|
74
|
+
};
|
|
75
|
+
|
|
70
76
|
export const serialize = (
|
|
71
77
|
argRef: ConstantCls | PrimitiveScalar,
|
|
72
78
|
arrDepth: number,
|
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}");
|
|
@@ -18,19 +18,33 @@ export type InternalArgType = (typeof internalArgTypes)[number];
|
|
|
18
18
|
export type PrimitiveArgType = StringConstructor | NumberConstructor | BooleanConstructor;
|
|
19
19
|
export type NormalizedPrimitiveArgType = "string" | "number" | "boolean";
|
|
20
20
|
|
|
21
|
-
export
|
|
21
|
+
export type CommandContext = {
|
|
22
|
+
values: Record<string, unknown>;
|
|
23
|
+
app?: AppExecutor;
|
|
24
|
+
lib?: LibExecutor;
|
|
25
|
+
sys?: SysExecutor;
|
|
26
|
+
pkg?: PkgExecutor;
|
|
27
|
+
module?: ModuleExecutor;
|
|
28
|
+
exec?: Executor;
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
export type EnumChoice = string | number | { label: string; value: string | number | boolean };
|
|
32
|
+
export type EnumChoices = readonly EnumChoice[];
|
|
33
|
+
export type DynamicEnum<Context> = (context: Context) => EnumChoices | Promise<EnumChoices>;
|
|
34
|
+
|
|
35
|
+
export interface ArgsOption<Context = CommandContext> {
|
|
22
36
|
type?: "string" | "number" | "boolean";
|
|
23
37
|
flag?: string;
|
|
24
38
|
desc?: string;
|
|
25
39
|
default?: string | number | boolean;
|
|
26
40
|
nullable?: boolean;
|
|
27
41
|
example?: string | number | boolean;
|
|
28
|
-
enum?:
|
|
42
|
+
enum?: EnumChoices | DynamicEnum<Context>;
|
|
29
43
|
ask?: string;
|
|
30
44
|
}
|
|
31
|
-
export interface ArgMeta {
|
|
45
|
+
export interface ArgMeta<Context = CommandContext> {
|
|
32
46
|
name: string;
|
|
33
|
-
argsOption: ArgsOption
|
|
47
|
+
argsOption: ArgsOption<Context>;
|
|
34
48
|
key: string;
|
|
35
49
|
idx: number;
|
|
36
50
|
type: ArgType;
|
|
@@ -52,12 +66,12 @@ export const getArgMetas = (
|
|
|
52
66
|
return [allArgMetas, argMetas, internalArgMetas];
|
|
53
67
|
};
|
|
54
68
|
|
|
55
|
-
export interface InternalArgToken<T = unknown> {
|
|
56
|
-
type:
|
|
69
|
+
export interface InternalArgToken<T = unknown, Type extends InternalArgType = InternalArgType> {
|
|
70
|
+
type: Type;
|
|
57
71
|
_value: T;
|
|
58
72
|
}
|
|
59
73
|
|
|
60
|
-
const createInternalArgToken = <T>(type:
|
|
74
|
+
const createInternalArgToken = <T, Type extends InternalArgType>(type: Type) => ({ type }) as InternalArgToken<T, Type>;
|
|
61
75
|
|
|
62
76
|
export const normalizePrimitiveArgType = (type: PrimitiveArgType): NormalizedPrimitiveArgType => {
|
|
63
77
|
if (type === String) return "string";
|
|
@@ -66,23 +80,23 @@ export const normalizePrimitiveArgType = (type: PrimitiveArgType): NormalizedPri
|
|
|
66
80
|
throw new Error(`Invalid primitive argument type: ${type}`);
|
|
67
81
|
};
|
|
68
82
|
|
|
69
|
-
export const App = createInternalArgToken<AppExecutor>("App");
|
|
83
|
+
export const App = createInternalArgToken<AppExecutor, "App">("App");
|
|
70
84
|
export type App = AppExecutor;
|
|
71
85
|
|
|
72
|
-
export const Lib = createInternalArgToken<LibExecutor>("Lib");
|
|
86
|
+
export const Lib = createInternalArgToken<LibExecutor, "Lib">("Lib");
|
|
73
87
|
export type Lib = LibExecutor;
|
|
74
88
|
|
|
75
|
-
export const Sys = createInternalArgToken<SysExecutor>("Sys");
|
|
89
|
+
export const Sys = createInternalArgToken<SysExecutor, "Sys">("Sys");
|
|
76
90
|
export type Sys = SysExecutor;
|
|
77
91
|
|
|
78
|
-
export const Exec = createInternalArgToken<Executor>("Exec");
|
|
92
|
+
export const Exec = createInternalArgToken<Executor, "Exec">("Exec");
|
|
79
93
|
export type Exec = Executor;
|
|
80
94
|
|
|
81
|
-
export const Pkg = createInternalArgToken<PkgExecutor>("Pkg");
|
|
95
|
+
export const Pkg = createInternalArgToken<PkgExecutor, "Pkg">("Pkg");
|
|
82
96
|
export type Pkg = PkgExecutor;
|
|
83
97
|
|
|
84
|
-
export const Module = createInternalArgToken<ModuleExecutor>("Module");
|
|
98
|
+
export const Module = createInternalArgToken<ModuleExecutor, "Module">("Module");
|
|
85
99
|
export type Module = ModuleExecutor;
|
|
86
100
|
|
|
87
|
-
export const Workspace = createInternalArgToken<WorkspaceExecutor>("Workspace");
|
|
101
|
+
export const Workspace = createInternalArgToken<WorkspaceExecutor, "Workspace">("Workspace");
|
|
88
102
|
export type Workspace = WorkspaceExecutor;
|
|
@@ -5,7 +5,14 @@ import { type Command, program } from "commander";
|
|
|
5
5
|
|
|
6
6
|
import { FileSys, getDirname, type PackageJson } from "..";
|
|
7
7
|
import { AppExecutor, Executor, LibExecutor, ModuleExecutor, PkgExecutor, WorkspaceExecutor } from "../executors";
|
|
8
|
-
import {
|
|
8
|
+
import {
|
|
9
|
+
type ArgMeta,
|
|
10
|
+
type CommandContext,
|
|
11
|
+
type EnumChoice,
|
|
12
|
+
type EnumChoices,
|
|
13
|
+
getArgMetas,
|
|
14
|
+
type InternalArgMeta,
|
|
15
|
+
} from "./argMeta";
|
|
9
16
|
import { CommandContainer } from "./dependencyBuilder";
|
|
10
17
|
import { formatCommandHelp, formatHelp } from "./helpFormatter";
|
|
11
18
|
import { type CommandCls, getTargetMetas } from "./targetMeta";
|
|
@@ -22,11 +29,7 @@ const handleOption = (programCommand: Command, argMeta: ArgMeta) => {
|
|
|
22
29
|
ask,
|
|
23
30
|
} = argMeta.argsOption;
|
|
24
31
|
const kebabName = camelToKebabCase(argMeta.name);
|
|
25
|
-
const choices = enumChoices
|
|
26
|
-
typeof choice === "object"
|
|
27
|
-
? { value: choice.value, name: choice.label }
|
|
28
|
-
: { value: choice, name: choice.toString() },
|
|
29
|
-
);
|
|
32
|
+
const choices = enumChoices && typeof enumChoices !== "function" ? normalizeEnumChoices(enumChoices) : null;
|
|
30
33
|
programCommand.option(
|
|
31
34
|
`-${flag}, --${kebabName}${type === "boolean" ? " [boolean]" : ` <${kebabName}>`}`,
|
|
32
35
|
`${desc}${ask ? ` (${ask})` : ""}${example ? ` (example: ${example})` : ""}${choices ? ` (choices: ${choices.map((choice) => choice.name).join(", ")})` : ""}`,
|
|
@@ -48,24 +51,34 @@ const convertArgValue = (value: string | boolean, type: "string" | "number" | "b
|
|
|
48
51
|
else return value === true || value === "true";
|
|
49
52
|
};
|
|
50
53
|
|
|
51
|
-
const
|
|
54
|
+
const normalizeEnumChoices = (enumChoices: EnumChoices) =>
|
|
55
|
+
enumChoices.map((choice: EnumChoice) =>
|
|
56
|
+
typeof choice === "object"
|
|
57
|
+
? { value: choice.value, name: choice.label }
|
|
58
|
+
: { value: choice, name: choice.toString() },
|
|
59
|
+
);
|
|
60
|
+
|
|
61
|
+
const resolveEnumChoices = async (argMeta: ArgMeta, context: CommandContext) => {
|
|
62
|
+
const enumChoices = argMeta.argsOption.enum;
|
|
63
|
+
if (!enumChoices) return null;
|
|
64
|
+
if (typeof enumChoices === "function") return await enumChoices(context);
|
|
65
|
+
return enumChoices;
|
|
66
|
+
};
|
|
67
|
+
|
|
68
|
+
const getOptionValue = async (argMeta: ArgMeta, opt: Record<string, unknown>, context: CommandContext) => {
|
|
52
69
|
const {
|
|
53
70
|
name,
|
|
54
71
|
argsOption: { enum: enumChoices, default: defaultValue, type, desc, nullable, example, ask },
|
|
55
72
|
} = argMeta;
|
|
56
73
|
if (opt[argMeta.name] !== undefined) return convertArgValue(opt[argMeta.name] as string, type ?? "string");
|
|
57
74
|
else if (defaultValue !== undefined) return defaultValue;
|
|
58
|
-
else if (nullable) return null;
|
|
59
75
|
|
|
60
76
|
if (enumChoices) {
|
|
61
|
-
const choices =
|
|
62
|
-
typeof choice === "object"
|
|
63
|
-
? { value: choice.value, name: choice.label }
|
|
64
|
-
: { value: choice, name: choice.toString() },
|
|
65
|
-
);
|
|
77
|
+
const choices = normalizeEnumChoices((await resolveEnumChoices(argMeta, context)) ?? []);
|
|
66
78
|
const choice = await select({ message: ask ?? desc ?? `Select the ${name} value`, choices });
|
|
67
79
|
return choice;
|
|
68
|
-
} else if (
|
|
80
|
+
} else if (nullable) return null;
|
|
81
|
+
else if (type === "boolean") {
|
|
69
82
|
const message = ask ?? desc ?? `Do you want to set ${name}? ${desc ? ` (${desc})` : ""}: `;
|
|
70
83
|
return await confirm({ message });
|
|
71
84
|
} else {
|
|
@@ -96,6 +109,16 @@ const getArgumentValue = async (argMeta: ArgMeta, value: string | undefined) =>
|
|
|
96
109
|
return convertArgValue(await input({ message }), type ?? "string");
|
|
97
110
|
};
|
|
98
111
|
|
|
112
|
+
const assignCommandContext = (context: CommandContext, argMeta: ArgMeta | InternalArgMeta, value: unknown) => {
|
|
113
|
+
if (value instanceof AppExecutor) context.app = value;
|
|
114
|
+
else if (value instanceof LibExecutor) context.lib = value;
|
|
115
|
+
else if (value instanceof PkgExecutor) context.pkg = value;
|
|
116
|
+
else if (value instanceof ModuleExecutor) context.module = value;
|
|
117
|
+
else if (value instanceof Executor) context.exec = value;
|
|
118
|
+
if (argMeta.type === "Argument" || argMeta.type === "Option") context.values[argMeta.name] = value;
|
|
119
|
+
else context.values[argMeta.type.toLowerCase()] = value;
|
|
120
|
+
};
|
|
121
|
+
|
|
99
122
|
const assertCurrentDirectoryIsWorkspaceRoot = async () => {
|
|
100
123
|
const cwd = process.cwd();
|
|
101
124
|
const [hasPackageJson, hasTsConfig, hasEnv] = await Promise.all([
|
|
@@ -281,8 +304,10 @@ It may cause unexpected behavior. Run \`akan update\` to update latest akanjs.`,
|
|
|
281
304
|
const commandArgs = [] as unknown[];
|
|
282
305
|
if (targetMeta.targetOption.runsOnWorkspaceRoot) await assertCurrentDirectoryIsWorkspaceRoot();
|
|
283
306
|
const workspace = WorkspaceExecutor.fromRoot();
|
|
307
|
+
const commandContext: CommandContext = { values: {} };
|
|
284
308
|
for (const argMeta of allArgMetas) {
|
|
285
|
-
if (argMeta.type === "Option")
|
|
309
|
+
if (argMeta.type === "Option")
|
|
310
|
+
commandArgs[argMeta.idx] = await getOptionValue(argMeta, opt, commandContext);
|
|
286
311
|
else if (argMeta.type === "Argument")
|
|
287
312
|
commandArgs[argMeta.idx] = await getArgumentValue(argMeta, cmdArgs[argMeta.idx] as string);
|
|
288
313
|
else
|
|
@@ -294,6 +319,7 @@ It may cause unexpected behavior. Run \`akan update\` to update latest akanjs.`,
|
|
|
294
319
|
|
|
295
320
|
if (commandArgs[argMeta.idx] instanceof AppExecutor)
|
|
296
321
|
process.env.AKAN_PUBLIC_APP_NAME = (commandArgs[argMeta.idx] as AppExecutor).name;
|
|
322
|
+
assignCommandContext(commandContext, argMeta, commandArgs[argMeta.idx]);
|
|
297
323
|
if ((opt as { verbose?: boolean }).verbose) Executor.setVerbose(true);
|
|
298
324
|
}
|
|
299
325
|
const cmd = CommandContainer.get(command);
|
|
@@ -1,4 +1,17 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type {
|
|
2
|
+
App,
|
|
3
|
+
ArgMeta,
|
|
4
|
+
ArgsOption,
|
|
5
|
+
CommandContext,
|
|
6
|
+
Exec,
|
|
7
|
+
InternalArgMeta,
|
|
8
|
+
InternalArgToken,
|
|
9
|
+
Lib,
|
|
10
|
+
Module,
|
|
11
|
+
Pkg,
|
|
12
|
+
PrimitiveArgType,
|
|
13
|
+
Sys,
|
|
14
|
+
} from "./argMeta";
|
|
2
15
|
import { normalizePrimitiveArgType } from "./argMeta";
|
|
3
16
|
import { assertUniqueDependencies, type DependencyInstanceMap, injectDependencies } from "./dependencyBuilder";
|
|
4
17
|
import { COMMAND_META, type CommandCls, type TargetMeta, type TargetOption } from "./targetMeta";
|
|
@@ -9,9 +22,8 @@ type PrimitiveValue<T extends PrimitiveArgType> = T extends StringConstructor
|
|
|
9
22
|
: T extends NumberConstructor
|
|
10
23
|
? number
|
|
11
24
|
: boolean;
|
|
12
|
-
type
|
|
13
|
-
type
|
|
14
|
-
type AddArg<Params extends unknown[], Type extends PrimitiveArgType, Option extends ArgsOptionInput> = [
|
|
25
|
+
type MaybeNullable<Value, Option> = Option extends { nullable: true } ? Value | null : Value;
|
|
26
|
+
type AddArg<Params extends unknown[], Type extends PrimitiveArgType, Option> = [
|
|
15
27
|
...Params,
|
|
16
28
|
MaybeNullable<PrimitiveValue<Type>, Option>,
|
|
17
29
|
];
|
|
@@ -22,12 +34,31 @@ type AddInternalArgs<Params extends unknown[], Tokens extends readonly InternalA
|
|
|
22
34
|
]
|
|
23
35
|
? AddInternalArgs<AddInternalArg<Params, Head>, Rest>
|
|
24
36
|
: Params;
|
|
37
|
+
type ContextFromToken<Token extends InternalArgToken> = Token["_value"] extends App
|
|
38
|
+
? { app: App }
|
|
39
|
+
: Token["_value"] extends Lib
|
|
40
|
+
? { lib: Lib }
|
|
41
|
+
: Token["_value"] extends Sys
|
|
42
|
+
? { sys: Sys }
|
|
43
|
+
: Token["_value"] extends Pkg
|
|
44
|
+
? { pkg: Pkg }
|
|
45
|
+
: Token["_value"] extends Module
|
|
46
|
+
? { module: Module }
|
|
47
|
+
: Token["_value"] extends Exec
|
|
48
|
+
? { exec: Exec }
|
|
49
|
+
: object;
|
|
50
|
+
type AddInternalContext<Context, Tokens extends readonly InternalArgToken[]> = Tokens extends readonly [
|
|
51
|
+
infer Head extends InternalArgToken,
|
|
52
|
+
...infer Rest extends InternalArgToken[],
|
|
53
|
+
]
|
|
54
|
+
? AddInternalContext<Context & ContextFromToken<Head>, Rest>
|
|
55
|
+
: Context;
|
|
25
56
|
type CommandHandler<Deps extends readonly DependencyCls[], Params extends unknown[]> = (
|
|
26
57
|
this: DependencyInstanceMap<Deps>,
|
|
27
58
|
...args: Params
|
|
28
59
|
) => unknown | Promise<unknown>;
|
|
29
60
|
|
|
30
|
-
class TargetBuilder<Deps extends readonly DependencyCls[], Params extends unknown[] = []> {
|
|
61
|
+
class TargetBuilder<Deps extends readonly DependencyCls[], Params extends unknown[] = [], Context = object> {
|
|
31
62
|
readonly #args: (ArgMeta | InternalArgMeta)[];
|
|
32
63
|
|
|
33
64
|
constructor(
|
|
@@ -37,12 +68,12 @@ class TargetBuilder<Deps extends readonly DependencyCls[], Params extends unknow
|
|
|
37
68
|
this.#args = args;
|
|
38
69
|
}
|
|
39
70
|
|
|
40
|
-
arg<Type extends PrimitiveArgType, Option extends
|
|
71
|
+
arg<Type extends PrimitiveArgType, Option extends ArgsOption<Context> = ArgsOption<Context>>(
|
|
41
72
|
name: string,
|
|
42
73
|
type: Type,
|
|
43
74
|
argsOption: Option = {} as Option,
|
|
44
|
-
): TargetBuilder<Deps, AddArg<Params, Type, Option
|
|
45
|
-
return new TargetBuilder(this.targetOption, [
|
|
75
|
+
): TargetBuilder<Deps, AddArg<Params, Type, Option>, Context> {
|
|
76
|
+
return new TargetBuilder<Deps, AddArg<Params, Type, Option>, Context>(this.targetOption, [
|
|
46
77
|
...this.#args,
|
|
47
78
|
{
|
|
48
79
|
name,
|
|
@@ -50,16 +81,16 @@ class TargetBuilder<Deps extends readonly DependencyCls[], Params extends unknow
|
|
|
50
81
|
key: "",
|
|
51
82
|
idx: this.#args.length,
|
|
52
83
|
type: "Argument",
|
|
53
|
-
}
|
|
84
|
+
} as ArgMeta<CommandContext>,
|
|
54
85
|
]);
|
|
55
86
|
}
|
|
56
87
|
|
|
57
|
-
option<Type extends PrimitiveArgType, Option extends
|
|
88
|
+
option<Type extends PrimitiveArgType, Option extends ArgsOption<Context> = ArgsOption<Context>>(
|
|
58
89
|
name: string,
|
|
59
90
|
type: Type,
|
|
60
91
|
argsOption: Option = {} as Option,
|
|
61
|
-
): TargetBuilder<Deps, AddArg<Params, Type, Option
|
|
62
|
-
return new TargetBuilder(this.targetOption, [
|
|
92
|
+
): TargetBuilder<Deps, AddArg<Params, Type, Option>, Context> {
|
|
93
|
+
return new TargetBuilder<Deps, AddArg<Params, Type, Option>, Context>(this.targetOption, [
|
|
63
94
|
...this.#args,
|
|
64
95
|
{
|
|
65
96
|
name,
|
|
@@ -67,24 +98,27 @@ class TargetBuilder<Deps extends readonly DependencyCls[], Params extends unknow
|
|
|
67
98
|
key: "",
|
|
68
99
|
idx: this.#args.length,
|
|
69
100
|
type: "Option",
|
|
70
|
-
}
|
|
101
|
+
} as ArgMeta<CommandContext>,
|
|
71
102
|
]);
|
|
72
103
|
}
|
|
73
104
|
|
|
74
|
-
with<Tokens extends readonly InternalArgToken[]>(
|
|
105
|
+
with<const Tokens extends readonly InternalArgToken[]>(
|
|
75
106
|
...tokens: Tokens
|
|
76
|
-
): TargetBuilder<Deps, AddInternalArgs<Params, Tokens>> {
|
|
77
|
-
return new TargetBuilder
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
107
|
+
): TargetBuilder<Deps, AddInternalArgs<Params, Tokens>, AddInternalContext<Context, Tokens>> {
|
|
108
|
+
return new TargetBuilder<Deps, AddInternalArgs<Params, Tokens>, AddInternalContext<Context, Tokens>>(
|
|
109
|
+
this.targetOption,
|
|
110
|
+
[
|
|
111
|
+
...this.#args,
|
|
112
|
+
...tokens.map(
|
|
113
|
+
(token, offset) =>
|
|
114
|
+
({
|
|
115
|
+
key: "",
|
|
116
|
+
idx: this.#args.length + offset,
|
|
117
|
+
type: token.type,
|
|
118
|
+
}) satisfies InternalArgMeta,
|
|
119
|
+
),
|
|
120
|
+
],
|
|
121
|
+
);
|
|
88
122
|
}
|
|
89
123
|
|
|
90
124
|
exec(handler: CommandHandler<Deps, Params>) {
|
|
@@ -101,12 +135,12 @@ type CommandBuilderContext<Deps extends readonly DependencyCls[]> = {
|
|
|
101
135
|
public: (targetOption?: Omit<TargetOption, "type">) => TargetBuilder<Deps>;
|
|
102
136
|
cloud: (targetOption?: Omit<TargetOption, "type">) => TargetBuilder<Deps>;
|
|
103
137
|
dev: (targetOption?: Omit<TargetOption, "type">) => TargetBuilder<Deps>;
|
|
104
|
-
arg: <Type extends PrimitiveArgType, Option extends
|
|
138
|
+
arg: <Type extends PrimitiveArgType, Option extends ArgsOption<CommandContext> = ArgsOption<CommandContext>>(
|
|
105
139
|
name: string,
|
|
106
140
|
type: Type,
|
|
107
141
|
argsOption?: Option,
|
|
108
142
|
) => ArgMeta;
|
|
109
|
-
option: <Type extends PrimitiveArgType, Option extends
|
|
143
|
+
option: <Type extends PrimitiveArgType, Option extends ArgsOption<CommandContext> = ArgsOption<CommandContext>>(
|
|
110
144
|
name: string,
|
|
111
145
|
type: Type,
|
|
112
146
|
argsOption?: Option,
|
|
@@ -125,20 +159,22 @@ const createContext = <Deps extends readonly DependencyCls[]>(): CommandBuilderC
|
|
|
125
159
|
public: createTarget<Deps>("public"),
|
|
126
160
|
cloud: createTarget<Deps>("cloud"),
|
|
127
161
|
dev: createTarget<Deps>("dev"),
|
|
128
|
-
arg: (name, type, argsOption) =>
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
162
|
+
arg: (name, type, argsOption) =>
|
|
163
|
+
({
|
|
164
|
+
name,
|
|
165
|
+
argsOption: { ...(argsOption ?? {}), type: normalizePrimitiveArgType(type) },
|
|
166
|
+
key: "",
|
|
167
|
+
idx: -1,
|
|
168
|
+
type: "Argument",
|
|
169
|
+
}) as ArgMeta<CommandContext>,
|
|
170
|
+
option: (name, type, argsOption) =>
|
|
171
|
+
({
|
|
172
|
+
name,
|
|
173
|
+
argsOption: { ...(argsOption ?? {}), type: normalizePrimitiveArgType(type) },
|
|
174
|
+
key: "",
|
|
175
|
+
idx: -1,
|
|
176
|
+
type: "Option",
|
|
177
|
+
}) as ArgMeta<CommandContext>,
|
|
142
178
|
});
|
|
143
179
|
|
|
144
180
|
const buildCommandMeta = (definitions: Record<string, TargetDefinition>) => {
|
|
@@ -1,11 +1,10 @@
|
|
|
1
1
|
import chalk from "chalk";
|
|
2
2
|
|
|
3
|
-
import { getArgMetas } from "./argMeta";
|
|
3
|
+
import { type EnumChoice, getArgMetas } from "./argMeta";
|
|
4
4
|
import { type CommandCls, getTargetMetas } from "./targetMeta";
|
|
5
5
|
|
|
6
6
|
const camelToKebabCase = (str: string) => str.replace(/([A-Z])/g, "-$1").toLowerCase();
|
|
7
|
-
const formatChoice = (choice:
|
|
8
|
-
typeof choice === "object" ? choice.label : choice.toString();
|
|
7
|
+
const formatChoice = (choice: EnumChoice) => (typeof choice === "object" ? choice.label : choice.toString());
|
|
9
8
|
|
|
10
9
|
const groupCommands = (commands: CommandCls[]) => {
|
|
11
10
|
const groups = new Map<string, { name: string; commands: { key: string; args: string[]; desc?: string }[] }>();
|
|
@@ -207,7 +206,11 @@ export const formatCommandHelp = (command: CommandCls, key: string) => {
|
|
|
207
206
|
const optName = `${flag}--${kebabName}`;
|
|
208
207
|
const optDesc = opt.desc ?? "";
|
|
209
208
|
const defaultVal = opt.default !== undefined ? chalk.gray(` [default: ${String(opt.default)}]`) : "";
|
|
210
|
-
const choices = opt.enum
|
|
209
|
+
const choices = opt.enum
|
|
210
|
+
? chalk.gray(
|
|
211
|
+
typeof opt.enum === "function" ? " ([dynamic choices])" : ` (${opt.enum.map(formatChoice).join(", ")})`,
|
|
212
|
+
)
|
|
213
|
+
: "";
|
|
211
214
|
lines.push(` ${chalk.green(optName)} ${chalk.gray(optDesc)}${defaultVal}${choices}`);
|
|
212
215
|
}
|
|
213
216
|
lines.push("");
|