akanjs 2.0.0-beta.8 → 2.0.0-beta.9
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/index.js +113 -24
- package/cli/package/package.runner.ts +7 -3
- package/cli/templates/workspaceRoot/.gitignore.template +1 -11
- package/client/csrTypes.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/mobile/mobileTarget.ts +48 -8
- package/devkit/src/capacitorApp.ts +277 -0
- package/devkit/transforms/barrelImportsPlugin.ts +1 -2
- package/package.json +2 -1
- package/server/hmr/clientScript.ts +8 -5
- package/ui/Portal.tsx +2 -0
- package/ui/System/CSR.tsx +6 -5
- package/ui/System/SSR.tsx +2 -2
- 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
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { select } from "@inquirer/prompts";
|
|
2
|
-
import { App, command, Exec, Sys, Workspace } from "akanjs/devkit";
|
|
2
|
+
import { App, command, Exec, getMobileTargetChoices, Sys, Workspace } from "akanjs/devkit";
|
|
3
3
|
|
|
4
4
|
import { ApplicationScript } from "./application.script";
|
|
5
5
|
|
|
@@ -53,7 +53,11 @@ export class ApplicationCommand extends command("application", [ApplicationScrip
|
|
|
53
53
|
}),
|
|
54
54
|
buildIos: target({ short: true, desc: "Build iOS app with Capacitor" })
|
|
55
55
|
.with(App)
|
|
56
|
-
.option("target", String, {
|
|
56
|
+
.option("target", String, {
|
|
57
|
+
desc: "mobile target name or all",
|
|
58
|
+
ask: "Select mobile target",
|
|
59
|
+
enum: async ({ app }) => await getMobileTargetChoices(app),
|
|
60
|
+
})
|
|
57
61
|
.option("env", String, {
|
|
58
62
|
enum: ["local", "debug", "develop", "main"],
|
|
59
63
|
desc: "backend environment",
|
|
@@ -66,7 +70,11 @@ export class ApplicationCommand extends command("application", [ApplicationScrip
|
|
|
66
70
|
}),
|
|
67
71
|
buildAndroid: target({ short: true, desc: "Build Android app with Capacitor" })
|
|
68
72
|
.with(App)
|
|
69
|
-
.option("target", String, {
|
|
73
|
+
.option("target", String, {
|
|
74
|
+
desc: "mobile target name or all",
|
|
75
|
+
ask: "Select mobile target",
|
|
76
|
+
enum: async ({ app }) => await getMobileTargetChoices(app),
|
|
77
|
+
})
|
|
70
78
|
.option("env", String, {
|
|
71
79
|
enum: ["local", "debug", "develop", "main"],
|
|
72
80
|
desc: "backend environment",
|
package/cli/index.js
CHANGED
|
@@ -6052,6 +6052,8 @@ class CssImportResolver {
|
|
|
6052
6052
|
|
|
6053
6053
|
var SOURCE_EXTS3 = [".ts", ".tsx", ".js", ".jsx", ".mjs", ".cjs"];
|
|
6054
6054
|
var NON_SOURCE_EXT_RE3 = /\.(json|svg|png|jpe?g|webp|gif|avif|ico|woff2?|ttf|otf|mp3|mp4|wav)$/i;
|
|
6055
|
+
var NODE_MODULES_RE3 = /[\\/]node_modules[\\/]/;
|
|
6056
|
+
var AKANJS_NODE_MODULE_RE3 = /[\\/]node_modules[\\/]akanjs[\\/]/;
|
|
6055
6057
|
|
|
6056
6058
|
class CssCompiler {
|
|
6057
6059
|
#logger = new Logger("CssCompiler");
|
|
@@ -6119,7 +6121,7 @@ class CssCompiler {
|
|
|
6119
6121
|
const akanConfig2 = await this.#app.getConfig({ refresh });
|
|
6120
6122
|
while (queue.length > 0) {
|
|
6121
6123
|
const filePath = queue.shift();
|
|
6122
|
-
if (!filePath || sourceFiles.has(filePath) || filePath
|
|
6124
|
+
if (!filePath || sourceFiles.has(filePath) || isIgnoredNodeModuleSource(filePath))
|
|
6123
6125
|
continue;
|
|
6124
6126
|
sourceFiles.add(filePath);
|
|
6125
6127
|
let content;
|
|
@@ -6155,7 +6157,7 @@ class CssCompiler {
|
|
|
6155
6157
|
if (NON_SOURCE_EXT_RE3.test(spec))
|
|
6156
6158
|
continue;
|
|
6157
6159
|
const resolved = await this.#resolveSourceImport(spec, importerDir, resolvePackage);
|
|
6158
|
-
if (!resolved || sourceFiles.has(resolved) || resolved
|
|
6160
|
+
if (!resolved || sourceFiles.has(resolved) || isIgnoredNodeModuleSource(resolved))
|
|
6159
6161
|
continue;
|
|
6160
6162
|
queue.push(resolved);
|
|
6161
6163
|
}
|
|
@@ -6243,7 +6245,7 @@ class CssCompiler {
|
|
|
6243
6245
|
const files = new Set(sourcePaths);
|
|
6244
6246
|
await Promise.all(dirs.map(async (dir) => {
|
|
6245
6247
|
for await (const file of glob.scan({ cwd: dir, absolute: true })) {
|
|
6246
|
-
if (file
|
|
6248
|
+
if (isIgnoredNodeModuleSource(file))
|
|
6247
6249
|
continue;
|
|
6248
6250
|
files.add(file);
|
|
6249
6251
|
}
|
|
@@ -6290,6 +6292,9 @@ function resolveSourceWithRequire(id, fromBase) {
|
|
|
6290
6292
|
function isSourceFile(filePath) {
|
|
6291
6293
|
return SOURCE_EXTS3.includes(path23.extname(filePath));
|
|
6292
6294
|
}
|
|
6295
|
+
function isIgnoredNodeModuleSource(filePath) {
|
|
6296
|
+
return NODE_MODULES_RE3.test(filePath) && !AKANJS_NODE_MODULE_RE3.test(filePath);
|
|
6297
|
+
}
|
|
6293
6298
|
function getPageKeyBasePath(pageKey, basePaths) {
|
|
6294
6299
|
const normalized = pageKey.split(path23.sep).join("/").replace(/^\.\//, "");
|
|
6295
6300
|
const segments = normalized.split("/");
|
|
@@ -7907,21 +7912,65 @@ var getMobileTargets = async (app) => {
|
|
|
7907
7912
|
const config = await app.getConfig();
|
|
7908
7913
|
return Object.entries(config.mobile.targets).map(([name, target]) => ({ name, config: target }));
|
|
7909
7914
|
};
|
|
7915
|
+
var getMobileTargetChoices = async (app) => {
|
|
7916
|
+
const config = await app.getConfig();
|
|
7917
|
+
const targetNames = Object.keys(config.mobile.targets);
|
|
7918
|
+
if (targetNames.length > 1)
|
|
7919
|
+
return targetNames;
|
|
7920
|
+
const basePaths = [...config.basePaths];
|
|
7921
|
+
if (basePaths.length > 1)
|
|
7922
|
+
return basePaths;
|
|
7923
|
+
if (targetNames.length > 0)
|
|
7924
|
+
return targetNames;
|
|
7925
|
+
return basePaths;
|
|
7926
|
+
};
|
|
7927
|
+
var resolveMobileTargetByBasePath = (targets, basePath2) => {
|
|
7928
|
+
const normalizedBasePath = basePath2.replace(/^\/+|\/+$/g, "");
|
|
7929
|
+
const byBasePath = targets.find((target) => target.config.basePath?.replace(/^\/+|\/+$/g, "") === normalizedBasePath);
|
|
7930
|
+
if (byBasePath)
|
|
7931
|
+
return byBasePath;
|
|
7932
|
+
const [template] = targets;
|
|
7933
|
+
if (!template)
|
|
7934
|
+
return;
|
|
7935
|
+
return {
|
|
7936
|
+
name: template.name,
|
|
7937
|
+
config: {
|
|
7938
|
+
...template.config,
|
|
7939
|
+
basePath: normalizedBasePath
|
|
7940
|
+
}
|
|
7941
|
+
};
|
|
7942
|
+
};
|
|
7910
7943
|
var resolveMobileTargets = async (app, selection) => {
|
|
7944
|
+
const config = await app.getConfig();
|
|
7911
7945
|
const targets = await getMobileTargets(app);
|
|
7912
7946
|
if (targets.length === 0)
|
|
7913
7947
|
throw new Error(`No mobile targets configured for ${app.name}`);
|
|
7914
7948
|
if (!selection) {
|
|
7915
|
-
|
|
7916
|
-
|
|
7917
|
-
|
|
7949
|
+
const choices2 = await getMobileTargetChoices(app);
|
|
7950
|
+
if (choices2.length === 1)
|
|
7951
|
+
return resolveMobileTargets(app, choices2[0]);
|
|
7952
|
+
throw new Error(`Multiple mobile targets found for ${app.name}. Pass --target <${choices2.join("|")}|all>.`);
|
|
7918
7953
|
}
|
|
7919
|
-
if (selection === "all")
|
|
7954
|
+
if (selection === "all") {
|
|
7955
|
+
if (Object.keys(config.mobile.targets).length > 1)
|
|
7956
|
+
return targets;
|
|
7957
|
+
const basePaths = [...config.basePaths];
|
|
7958
|
+
if (basePaths.length > 1) {
|
|
7959
|
+
return basePaths.flatMap((basePath2) => {
|
|
7960
|
+
const resolved = resolveMobileTargetByBasePath(targets, basePath2);
|
|
7961
|
+
return resolved ? [resolved] : [];
|
|
7962
|
+
});
|
|
7963
|
+
}
|
|
7920
7964
|
return targets;
|
|
7965
|
+
}
|
|
7921
7966
|
const target = targets.find((candidate) => candidate.name === selection);
|
|
7922
|
-
if (
|
|
7923
|
-
|
|
7924
|
-
|
|
7967
|
+
if (target)
|
|
7968
|
+
return [target];
|
|
7969
|
+
const basePathTarget = resolveMobileTargetByBasePath(targets, selection);
|
|
7970
|
+
if (basePathTarget && config.basePaths.has(selection.replace(/^\/+|\/+$/g, "")))
|
|
7971
|
+
return [basePathTarget];
|
|
7972
|
+
const choices = await getMobileTargetChoices(app);
|
|
7973
|
+
throw new Error(`Mobile target '${selection}' was not found. Available: ${choices.join(", ")}`);
|
|
7925
7974
|
};
|
|
7926
7975
|
var resolveMobilePath = (target, pathname) => {
|
|
7927
7976
|
const basePath2 = target.basePath?.replace(/^\/+|\/+$/g, "");
|
|
@@ -8118,7 +8167,8 @@ class CapacitorApp {
|
|
|
8118
8167
|
async#writeCapacitorConfig() {
|
|
8119
8168
|
await mkdir10(this.targetRoot, { recursive: true });
|
|
8120
8169
|
const appInfoPath = path34.relative(this.targetRoot, path34.join(this.app.cwdPath, "akan.app.json")).split(path34.sep).join("/");
|
|
8121
|
-
const
|
|
8170
|
+
const baseConfigPath = path34.relative(this.targetRoot, path34.join(this.app.workspace.cwdPath, "pkgs/akanjs/devkit/capacitor.base.config")).split(path34.sep).join("/");
|
|
8171
|
+
const content = `import { withBase } from "${baseConfigPath.startsWith(".") ? baseConfigPath : `./${baseConfigPath}`}";
|
|
8122
8172
|
import appInfo from "${appInfoPath}";
|
|
8123
8173
|
|
|
8124
8174
|
export default withBase((config) => config, appInfo, "${this.target.name}");
|
|
@@ -8568,7 +8618,7 @@ var formatCommandHelp = (command, key) => {
|
|
|
8568
8618
|
const optName = `${flag}--${kebabName}`;
|
|
8569
8619
|
const optDesc = opt.desc ?? "";
|
|
8570
8620
|
const defaultVal = opt.default !== undefined ? chalk5.gray(` [default: ${String(opt.default)}]`) : "";
|
|
8571
|
-
const choices = opt.enum ? chalk5.gray(` (${opt.enum.map(formatChoice).join(", ")})`) : "";
|
|
8621
|
+
const choices = opt.enum ? chalk5.gray(typeof opt.enum === "function" ? " ([dynamic choices])" : ` (${opt.enum.map(formatChoice).join(", ")})`) : "";
|
|
8572
8622
|
lines.push(` ${chalk5.green(optName)} ${chalk5.gray(optDesc)}${defaultVal}${choices}`);
|
|
8573
8623
|
}
|
|
8574
8624
|
lines.push("");
|
|
@@ -8588,7 +8638,7 @@ var handleOption = (programCommand, argMeta) => {
|
|
|
8588
8638
|
ask
|
|
8589
8639
|
} = argMeta.argsOption;
|
|
8590
8640
|
const kebabName = camelToKebabCase2(argMeta.name);
|
|
8591
|
-
const choices = enumChoices
|
|
8641
|
+
const choices = enumChoices && typeof enumChoices !== "function" ? normalizeEnumChoices(enumChoices) : null;
|
|
8592
8642
|
programCommand.option(`-${flag}, --${kebabName}${type === "boolean" ? " [boolean]" : ` <${kebabName}>`}`, `${desc}${ask ? ` (${ask})` : ""}${example ? ` (example: ${example})` : ""}${choices ? ` (choices: ${choices.map((choice) => choice.name).join(", ")})` : ""}`);
|
|
8593
8643
|
return programCommand;
|
|
8594
8644
|
};
|
|
@@ -8605,7 +8655,16 @@ var convertArgValue = (value, type) => {
|
|
|
8605
8655
|
else
|
|
8606
8656
|
return value === true || value === "true";
|
|
8607
8657
|
};
|
|
8608
|
-
var
|
|
8658
|
+
var normalizeEnumChoices = (enumChoices) => enumChoices.map((choice) => typeof choice === "object" ? { value: choice.value, name: choice.label } : { value: choice, name: choice.toString() });
|
|
8659
|
+
var resolveEnumChoices = async (argMeta, context) => {
|
|
8660
|
+
const enumChoices = argMeta.argsOption.enum;
|
|
8661
|
+
if (!enumChoices)
|
|
8662
|
+
return null;
|
|
8663
|
+
if (typeof enumChoices === "function")
|
|
8664
|
+
return await enumChoices(context);
|
|
8665
|
+
return enumChoices;
|
|
8666
|
+
};
|
|
8667
|
+
var getOptionValue = async (argMeta, opt, context) => {
|
|
8609
8668
|
const {
|
|
8610
8669
|
name,
|
|
8611
8670
|
argsOption: { enum: enumChoices, default: defaultValue, type, desc, nullable, example, ask }
|
|
@@ -8614,13 +8673,13 @@ var getOptionValue = async (argMeta, opt) => {
|
|
|
8614
8673
|
return convertArgValue(opt[argMeta.name], type ?? "string");
|
|
8615
8674
|
else if (defaultValue !== undefined)
|
|
8616
8675
|
return defaultValue;
|
|
8617
|
-
else if (nullable)
|
|
8618
|
-
return null;
|
|
8619
8676
|
if (enumChoices) {
|
|
8620
|
-
const choices =
|
|
8677
|
+
const choices = normalizeEnumChoices(await resolveEnumChoices(argMeta, context) ?? []);
|
|
8621
8678
|
const choice = await select2({ message: ask ?? desc ?? `Select the ${name} value`, choices });
|
|
8622
8679
|
return choice;
|
|
8623
|
-
} else if (
|
|
8680
|
+
} else if (nullable)
|
|
8681
|
+
return null;
|
|
8682
|
+
else if (type === "boolean") {
|
|
8624
8683
|
const message = ask ?? desc ?? `Do you want to set ${name}? ${desc ? ` (${desc})` : ""}: `;
|
|
8625
8684
|
return await confirm({ message });
|
|
8626
8685
|
} else {
|
|
@@ -8645,6 +8704,22 @@ var getArgumentValue = async (argMeta, value) => {
|
|
|
8645
8704
|
const message = ask ? `${ask}: ` : desc ? `${desc}: ` : `Enter the ${name} value${example ? ` (example: ${example})` : ""}: `;
|
|
8646
8705
|
return convertArgValue(await input2({ message }), type ?? "string");
|
|
8647
8706
|
};
|
|
8707
|
+
var assignCommandContext = (context, argMeta, value) => {
|
|
8708
|
+
if (value instanceof AppExecutor)
|
|
8709
|
+
context.app = value;
|
|
8710
|
+
else if (value instanceof LibExecutor)
|
|
8711
|
+
context.lib = value;
|
|
8712
|
+
else if (value instanceof PkgExecutor)
|
|
8713
|
+
context.pkg = value;
|
|
8714
|
+
else if (value instanceof ModuleExecutor)
|
|
8715
|
+
context.module = value;
|
|
8716
|
+
else if (value instanceof Executor)
|
|
8717
|
+
context.exec = value;
|
|
8718
|
+
if (argMeta.type === "Argument" || argMeta.type === "Option")
|
|
8719
|
+
context.values[argMeta.name] = value;
|
|
8720
|
+
else
|
|
8721
|
+
context.values[argMeta.type.toLowerCase()] = value;
|
|
8722
|
+
};
|
|
8648
8723
|
var assertCurrentDirectoryIsWorkspaceRoot = async () => {
|
|
8649
8724
|
const cwd = process.cwd();
|
|
8650
8725
|
const [hasPackageJson, hasTsConfig, hasEnv] = await Promise.all([
|
|
@@ -8820,15 +8895,17 @@ It may cause unexpected behavior. Run \`akan update\` to update latest akanjs.`)
|
|
|
8820
8895
|
if (targetMeta.targetOption.runsOnWorkspaceRoot)
|
|
8821
8896
|
await assertCurrentDirectoryIsWorkspaceRoot();
|
|
8822
8897
|
const workspace = WorkspaceExecutor.fromRoot();
|
|
8898
|
+
const commandContext = { values: {} };
|
|
8823
8899
|
for (const argMeta of allArgMetas) {
|
|
8824
8900
|
if (argMeta.type === "Option")
|
|
8825
|
-
commandArgs[argMeta.idx] = await getOptionValue(argMeta, opt);
|
|
8901
|
+
commandArgs[argMeta.idx] = await getOptionValue(argMeta, opt, commandContext);
|
|
8826
8902
|
else if (argMeta.type === "Argument")
|
|
8827
8903
|
commandArgs[argMeta.idx] = await getArgumentValue(argMeta, cmdArgs[argMeta.idx]);
|
|
8828
8904
|
else
|
|
8829
8905
|
commandArgs[argMeta.idx] = await getInternalArgumentValue(argMeta, cmdArgs[argMeta.idx], workspace);
|
|
8830
8906
|
if (commandArgs[argMeta.idx] instanceof AppExecutor)
|
|
8831
8907
|
process.env.AKAN_PUBLIC_APP_NAME = commandArgs[argMeta.idx].name;
|
|
8908
|
+
assignCommandContext(commandContext, argMeta, commandArgs[argMeta.idx]);
|
|
8832
8909
|
if (opt.verbose)
|
|
8833
8910
|
Executor.setVerbose(true);
|
|
8834
8911
|
}
|
|
@@ -9850,14 +9927,22 @@ class ApplicationCommand extends command("application", [ApplicationScript], ({
|
|
|
9850
9927
|
test: target({ desc: "Prepare and test an app, library, or package" }).with(Exec).option("write", Boolean, { desc: "write code generation", default: true }).exec(async function(exec2, write) {
|
|
9851
9928
|
await this.applicationScript.test(exec2, { write });
|
|
9852
9929
|
}),
|
|
9853
|
-
buildIos: target({ short: true, desc: "Build iOS app with Capacitor" }).with(App).option("target", String, {
|
|
9930
|
+
buildIos: target({ short: true, desc: "Build iOS app with Capacitor" }).with(App).option("target", String, {
|
|
9931
|
+
desc: "mobile target name or all",
|
|
9932
|
+
ask: "Select mobile target",
|
|
9933
|
+
enum: async ({ app }) => await getMobileTargetChoices(app)
|
|
9934
|
+
}).option("env", String, {
|
|
9854
9935
|
enum: ["local", "debug", "develop", "main"],
|
|
9855
9936
|
desc: "backend environment",
|
|
9856
9937
|
default: "debug"
|
|
9857
9938
|
}).option("write", Boolean, { desc: "write code generation", default: true }).option("regenerate", Boolean, { flag: "g", desc: "delete and regenerate native project", default: false }).exec(async function(app, target2, env, write, regenerate) {
|
|
9858
9939
|
await this.applicationScript.buildIos(app, { target: target2, env: asMobileEnv(env), write, regenerate });
|
|
9859
9940
|
}),
|
|
9860
|
-
buildAndroid: target({ short: true, desc: "Build Android app with Capacitor" }).with(App).option("target", String, {
|
|
9941
|
+
buildAndroid: target({ short: true, desc: "Build Android app with Capacitor" }).with(App).option("target", String, {
|
|
9942
|
+
desc: "mobile target name or all",
|
|
9943
|
+
ask: "Select mobile target",
|
|
9944
|
+
enum: async ({ app }) => await getMobileTargetChoices(app)
|
|
9945
|
+
}).option("env", String, {
|
|
9861
9946
|
enum: ["local", "debug", "develop", "main"],
|
|
9862
9947
|
desc: "backend environment",
|
|
9863
9948
|
default: "debug"
|
|
@@ -9979,21 +10064,25 @@ class PackageRunner extends runner("package") {
|
|
|
9979
10064
|
const scanner = await TypeScriptDependencyScanner.from(pkg);
|
|
9980
10065
|
const { npmDeps, npmDevDeps, missingDeps } = await scanner.getPackageBuildDependencies(pkg.name);
|
|
9981
10066
|
const packageRuntimeDependencies = { akanjs: ["daisyui"] };
|
|
10067
|
+
const packageRuntimeDevDependencies = { akanjs: ["@biomejs/biome"] };
|
|
9982
10068
|
const forcedRuntimeDeps = packageRuntimeDependencies[pkg.name] ?? [];
|
|
10069
|
+
const forcedRuntimeDevDeps = packageRuntimeDevDependencies[pkg.name] ?? [];
|
|
9983
10070
|
const packageRuntimeDeps = [...new Set([...npmDeps, ...forcedRuntimeDeps])];
|
|
10071
|
+
const packageRuntimeDevDeps = [...new Set([...npmDevDeps, ...forcedRuntimeDevDeps])];
|
|
9984
10072
|
const rootPackageJson = await pkg.workspace.getPackageJson();
|
|
9985
10073
|
const rootDeps = { ...rootPackageJson.dependencies, ...rootPackageJson.devDependencies };
|
|
9986
10074
|
const missingForcedDeps = forcedRuntimeDeps.filter((dep) => !rootDeps[dep]);
|
|
9987
|
-
const
|
|
10075
|
+
const missingForcedDevDeps = forcedRuntimeDevDeps.filter((dep) => !rootDeps[dep]);
|
|
10076
|
+
const allMissingDeps = [...new Set([...missingDeps, ...missingForcedDeps, ...missingForcedDevDeps])].sort();
|
|
9988
10077
|
if (allMissingDeps.length > 0)
|
|
9989
10078
|
throw new Error(`Missing dependency versions in root package.json: ${allMissingDeps.join(", ")}`);
|
|
9990
|
-
await pkg.updatePackageJsonDependencies(packageRuntimeDeps,
|
|
10079
|
+
await pkg.updatePackageJsonDependencies(packageRuntimeDeps, packageRuntimeDevDeps);
|
|
9991
10080
|
const hasBuildFile = await Bun.file(`${pkg.cwdPath}/build.ts`).exists();
|
|
9992
10081
|
if (hasBuildFile) {
|
|
9993
10082
|
await pkg.workspace.spawn(process.execPath, [`${pkg.cwdPath}/build.ts`], { env: process.env, stdio: "inherit" });
|
|
9994
10083
|
} else {
|
|
9995
10084
|
await $2`cp -r ${pkg.cwdPath}/. ${pkg.dist.cwdPath}`;
|
|
9996
|
-
await Promise.all([pkg.generateDistPackageJson(packageRuntimeDeps,
|
|
10085
|
+
await Promise.all([pkg.generateDistPackageJson(packageRuntimeDeps, packageRuntimeDevDeps), pkg.generateTsconfigJson()]);
|
|
9997
10086
|
}
|
|
9998
10087
|
}
|
|
9999
10088
|
async updateWorskpaceRootPackageJson(workspace, rootPackageJson) {
|
|
@@ -39,23 +39,27 @@ export class PackageRunner extends runner("package") {
|
|
|
39
39
|
const scanner = await TypeScriptDependencyScanner.from(pkg);
|
|
40
40
|
const { npmDeps, npmDevDeps, missingDeps } = await scanner.getPackageBuildDependencies(pkg.name);
|
|
41
41
|
const packageRuntimeDependencies: Record<string, string[]> = { akanjs: ["daisyui"] };
|
|
42
|
+
const packageRuntimeDevDependencies: Record<string, string[]> = { akanjs: ["@biomejs/biome"] };
|
|
42
43
|
const forcedRuntimeDeps = packageRuntimeDependencies[pkg.name] ?? [];
|
|
44
|
+
const forcedRuntimeDevDeps = packageRuntimeDevDependencies[pkg.name] ?? [];
|
|
43
45
|
const packageRuntimeDeps = [...new Set([...npmDeps, ...forcedRuntimeDeps])];
|
|
46
|
+
const packageRuntimeDevDeps = [...new Set([...npmDevDeps, ...forcedRuntimeDevDeps])];
|
|
44
47
|
const rootPackageJson = await pkg.workspace.getPackageJson();
|
|
45
48
|
const rootDeps = { ...rootPackageJson.dependencies, ...rootPackageJson.devDependencies };
|
|
46
49
|
const missingForcedDeps = forcedRuntimeDeps.filter((dep) => !rootDeps[dep]);
|
|
47
|
-
const
|
|
50
|
+
const missingForcedDevDeps = forcedRuntimeDevDeps.filter((dep) => !rootDeps[dep]);
|
|
51
|
+
const allMissingDeps = [...new Set([...missingDeps, ...missingForcedDeps, ...missingForcedDevDeps])].sort();
|
|
48
52
|
if (allMissingDeps.length > 0)
|
|
49
53
|
throw new Error(`Missing dependency versions in root package.json: ${allMissingDeps.join(", ")}`);
|
|
50
54
|
|
|
51
|
-
await pkg.updatePackageJsonDependencies(packageRuntimeDeps,
|
|
55
|
+
await pkg.updatePackageJsonDependencies(packageRuntimeDeps, packageRuntimeDevDeps);
|
|
52
56
|
|
|
53
57
|
const hasBuildFile = await Bun.file(`${pkg.cwdPath}/build.ts`).exists();
|
|
54
58
|
if (hasBuildFile) {
|
|
55
59
|
await pkg.workspace.spawn(process.execPath, [`${pkg.cwdPath}/build.ts`], { env: process.env, stdio: "inherit" });
|
|
56
60
|
} else {
|
|
57
61
|
await $`cp -r ${pkg.cwdPath}/. ${pkg.dist.cwdPath}`;
|
|
58
|
-
await Promise.all([pkg.generateDistPackageJson(packageRuntimeDeps,
|
|
62
|
+
await Promise.all([pkg.generateDistPackageJson(packageRuntimeDeps, packageRuntimeDevDeps), pkg.generateTsconfigJson()]);
|
|
59
63
|
}
|
|
60
64
|
}
|
|
61
65
|
|
|
@@ -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
|
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/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);
|