akanjs 2.0.0-beta.7 → 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 +117 -24
- package/cli/package/package.runner.ts +7 -3
- package/cli/templates/app/page/_layout.tsx +0 -1
- 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 +6 -0
- 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
- /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,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
|
@@ -4816,6 +4816,9 @@ var rewriteSingleStatement = (stmt, map) => {
|
|
|
4816
4816
|
return null;
|
|
4817
4817
|
const lines = [];
|
|
4818
4818
|
const tail = ";";
|
|
4819
|
+
if (shouldPreserveBarrelSideEffects(stmt.specifier)) {
|
|
4820
|
+
lines.push(`import "${stmt.specifier}"${tail}`);
|
|
4821
|
+
}
|
|
4819
4822
|
if (clause.defaultImport || remaining.length > 0) {
|
|
4820
4823
|
const parts = [];
|
|
4821
4824
|
if (clause.defaultImport)
|
|
@@ -4831,6 +4834,7 @@ var rewriteSingleStatement = (stmt, map) => {
|
|
|
4831
4834
|
return lines.join(`
|
|
4832
4835
|
`);
|
|
4833
4836
|
};
|
|
4837
|
+
var shouldPreserveBarrelSideEffects = (specifier) => /^@(apps|libs)\/[^/]+\/client$/.test(specifier);
|
|
4834
4838
|
var serializeNamedItem = (item) => {
|
|
4835
4839
|
const prefix = item.isType ? "type " : "";
|
|
4836
4840
|
if (item.imported === item.local)
|
|
@@ -6048,6 +6052,8 @@ class CssImportResolver {
|
|
|
6048
6052
|
|
|
6049
6053
|
var SOURCE_EXTS3 = [".ts", ".tsx", ".js", ".jsx", ".mjs", ".cjs"];
|
|
6050
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[\\/]/;
|
|
6051
6057
|
|
|
6052
6058
|
class CssCompiler {
|
|
6053
6059
|
#logger = new Logger("CssCompiler");
|
|
@@ -6115,7 +6121,7 @@ class CssCompiler {
|
|
|
6115
6121
|
const akanConfig2 = await this.#app.getConfig({ refresh });
|
|
6116
6122
|
while (queue.length > 0) {
|
|
6117
6123
|
const filePath = queue.shift();
|
|
6118
|
-
if (!filePath || sourceFiles.has(filePath) || filePath
|
|
6124
|
+
if (!filePath || sourceFiles.has(filePath) || isIgnoredNodeModuleSource(filePath))
|
|
6119
6125
|
continue;
|
|
6120
6126
|
sourceFiles.add(filePath);
|
|
6121
6127
|
let content;
|
|
@@ -6151,7 +6157,7 @@ class CssCompiler {
|
|
|
6151
6157
|
if (NON_SOURCE_EXT_RE3.test(spec))
|
|
6152
6158
|
continue;
|
|
6153
6159
|
const resolved = await this.#resolveSourceImport(spec, importerDir, resolvePackage);
|
|
6154
|
-
if (!resolved || sourceFiles.has(resolved) || resolved
|
|
6160
|
+
if (!resolved || sourceFiles.has(resolved) || isIgnoredNodeModuleSource(resolved))
|
|
6155
6161
|
continue;
|
|
6156
6162
|
queue.push(resolved);
|
|
6157
6163
|
}
|
|
@@ -6239,7 +6245,7 @@ class CssCompiler {
|
|
|
6239
6245
|
const files = new Set(sourcePaths);
|
|
6240
6246
|
await Promise.all(dirs.map(async (dir) => {
|
|
6241
6247
|
for await (const file of glob.scan({ cwd: dir, absolute: true })) {
|
|
6242
|
-
if (file
|
|
6248
|
+
if (isIgnoredNodeModuleSource(file))
|
|
6243
6249
|
continue;
|
|
6244
6250
|
files.add(file);
|
|
6245
6251
|
}
|
|
@@ -6286,6 +6292,9 @@ function resolveSourceWithRequire(id, fromBase) {
|
|
|
6286
6292
|
function isSourceFile(filePath) {
|
|
6287
6293
|
return SOURCE_EXTS3.includes(path23.extname(filePath));
|
|
6288
6294
|
}
|
|
6295
|
+
function isIgnoredNodeModuleSource(filePath) {
|
|
6296
|
+
return NODE_MODULES_RE3.test(filePath) && !AKANJS_NODE_MODULE_RE3.test(filePath);
|
|
6297
|
+
}
|
|
6289
6298
|
function getPageKeyBasePath(pageKey, basePaths) {
|
|
6290
6299
|
const normalized = pageKey.split(path23.sep).join("/").replace(/^\.\//, "");
|
|
6291
6300
|
const segments = normalized.split("/");
|
|
@@ -7903,21 +7912,65 @@ var getMobileTargets = async (app) => {
|
|
|
7903
7912
|
const config = await app.getConfig();
|
|
7904
7913
|
return Object.entries(config.mobile.targets).map(([name, target]) => ({ name, config: target }));
|
|
7905
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
|
+
};
|
|
7906
7943
|
var resolveMobileTargets = async (app, selection) => {
|
|
7944
|
+
const config = await app.getConfig();
|
|
7907
7945
|
const targets = await getMobileTargets(app);
|
|
7908
7946
|
if (targets.length === 0)
|
|
7909
7947
|
throw new Error(`No mobile targets configured for ${app.name}`);
|
|
7910
7948
|
if (!selection) {
|
|
7911
|
-
|
|
7912
|
-
|
|
7913
|
-
|
|
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>.`);
|
|
7914
7953
|
}
|
|
7915
|
-
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
|
+
}
|
|
7916
7964
|
return targets;
|
|
7965
|
+
}
|
|
7917
7966
|
const target = targets.find((candidate) => candidate.name === selection);
|
|
7918
|
-
if (
|
|
7919
|
-
|
|
7920
|
-
|
|
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(", ")}`);
|
|
7921
7974
|
};
|
|
7922
7975
|
var resolveMobilePath = (target, pathname) => {
|
|
7923
7976
|
const basePath2 = target.basePath?.replace(/^\/+|\/+$/g, "");
|
|
@@ -8114,7 +8167,8 @@ class CapacitorApp {
|
|
|
8114
8167
|
async#writeCapacitorConfig() {
|
|
8115
8168
|
await mkdir10(this.targetRoot, { recursive: true });
|
|
8116
8169
|
const appInfoPath = path34.relative(this.targetRoot, path34.join(this.app.cwdPath, "akan.app.json")).split(path34.sep).join("/");
|
|
8117
|
-
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}`}";
|
|
8118
8172
|
import appInfo from "${appInfoPath}";
|
|
8119
8173
|
|
|
8120
8174
|
export default withBase((config) => config, appInfo, "${this.target.name}");
|
|
@@ -8564,7 +8618,7 @@ var formatCommandHelp = (command, key) => {
|
|
|
8564
8618
|
const optName = `${flag}--${kebabName}`;
|
|
8565
8619
|
const optDesc = opt.desc ?? "";
|
|
8566
8620
|
const defaultVal = opt.default !== undefined ? chalk5.gray(` [default: ${String(opt.default)}]`) : "";
|
|
8567
|
-
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(", ")})`) : "";
|
|
8568
8622
|
lines.push(` ${chalk5.green(optName)} ${chalk5.gray(optDesc)}${defaultVal}${choices}`);
|
|
8569
8623
|
}
|
|
8570
8624
|
lines.push("");
|
|
@@ -8584,7 +8638,7 @@ var handleOption = (programCommand, argMeta) => {
|
|
|
8584
8638
|
ask
|
|
8585
8639
|
} = argMeta.argsOption;
|
|
8586
8640
|
const kebabName = camelToKebabCase2(argMeta.name);
|
|
8587
|
-
const choices = enumChoices
|
|
8641
|
+
const choices = enumChoices && typeof enumChoices !== "function" ? normalizeEnumChoices(enumChoices) : null;
|
|
8588
8642
|
programCommand.option(`-${flag}, --${kebabName}${type === "boolean" ? " [boolean]" : ` <${kebabName}>`}`, `${desc}${ask ? ` (${ask})` : ""}${example ? ` (example: ${example})` : ""}${choices ? ` (choices: ${choices.map((choice) => choice.name).join(", ")})` : ""}`);
|
|
8589
8643
|
return programCommand;
|
|
8590
8644
|
};
|
|
@@ -8601,7 +8655,16 @@ var convertArgValue = (value, type) => {
|
|
|
8601
8655
|
else
|
|
8602
8656
|
return value === true || value === "true";
|
|
8603
8657
|
};
|
|
8604
|
-
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) => {
|
|
8605
8668
|
const {
|
|
8606
8669
|
name,
|
|
8607
8670
|
argsOption: { enum: enumChoices, default: defaultValue, type, desc, nullable, example, ask }
|
|
@@ -8610,13 +8673,13 @@ var getOptionValue = async (argMeta, opt) => {
|
|
|
8610
8673
|
return convertArgValue(opt[argMeta.name], type ?? "string");
|
|
8611
8674
|
else if (defaultValue !== undefined)
|
|
8612
8675
|
return defaultValue;
|
|
8613
|
-
else if (nullable)
|
|
8614
|
-
return null;
|
|
8615
8676
|
if (enumChoices) {
|
|
8616
|
-
const choices =
|
|
8677
|
+
const choices = normalizeEnumChoices(await resolveEnumChoices(argMeta, context) ?? []);
|
|
8617
8678
|
const choice = await select2({ message: ask ?? desc ?? `Select the ${name} value`, choices });
|
|
8618
8679
|
return choice;
|
|
8619
|
-
} else if (
|
|
8680
|
+
} else if (nullable)
|
|
8681
|
+
return null;
|
|
8682
|
+
else if (type === "boolean") {
|
|
8620
8683
|
const message = ask ?? desc ?? `Do you want to set ${name}? ${desc ? ` (${desc})` : ""}: `;
|
|
8621
8684
|
return await confirm({ message });
|
|
8622
8685
|
} else {
|
|
@@ -8641,6 +8704,22 @@ var getArgumentValue = async (argMeta, value) => {
|
|
|
8641
8704
|
const message = ask ? `${ask}: ` : desc ? `${desc}: ` : `Enter the ${name} value${example ? ` (example: ${example})` : ""}: `;
|
|
8642
8705
|
return convertArgValue(await input2({ message }), type ?? "string");
|
|
8643
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
|
+
};
|
|
8644
8723
|
var assertCurrentDirectoryIsWorkspaceRoot = async () => {
|
|
8645
8724
|
const cwd = process.cwd();
|
|
8646
8725
|
const [hasPackageJson, hasTsConfig, hasEnv] = await Promise.all([
|
|
@@ -8816,15 +8895,17 @@ It may cause unexpected behavior. Run \`akan update\` to update latest akanjs.`)
|
|
|
8816
8895
|
if (targetMeta.targetOption.runsOnWorkspaceRoot)
|
|
8817
8896
|
await assertCurrentDirectoryIsWorkspaceRoot();
|
|
8818
8897
|
const workspace = WorkspaceExecutor.fromRoot();
|
|
8898
|
+
const commandContext = { values: {} };
|
|
8819
8899
|
for (const argMeta of allArgMetas) {
|
|
8820
8900
|
if (argMeta.type === "Option")
|
|
8821
|
-
commandArgs[argMeta.idx] = await getOptionValue(argMeta, opt);
|
|
8901
|
+
commandArgs[argMeta.idx] = await getOptionValue(argMeta, opt, commandContext);
|
|
8822
8902
|
else if (argMeta.type === "Argument")
|
|
8823
8903
|
commandArgs[argMeta.idx] = await getArgumentValue(argMeta, cmdArgs[argMeta.idx]);
|
|
8824
8904
|
else
|
|
8825
8905
|
commandArgs[argMeta.idx] = await getInternalArgumentValue(argMeta, cmdArgs[argMeta.idx], workspace);
|
|
8826
8906
|
if (commandArgs[argMeta.idx] instanceof AppExecutor)
|
|
8827
8907
|
process.env.AKAN_PUBLIC_APP_NAME = commandArgs[argMeta.idx].name;
|
|
8908
|
+
assignCommandContext(commandContext, argMeta, commandArgs[argMeta.idx]);
|
|
8828
8909
|
if (opt.verbose)
|
|
8829
8910
|
Executor.setVerbose(true);
|
|
8830
8911
|
}
|
|
@@ -9846,14 +9927,22 @@ class ApplicationCommand extends command("application", [ApplicationScript], ({
|
|
|
9846
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) {
|
|
9847
9928
|
await this.applicationScript.test(exec2, { write });
|
|
9848
9929
|
}),
|
|
9849
|
-
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, {
|
|
9850
9935
|
enum: ["local", "debug", "develop", "main"],
|
|
9851
9936
|
desc: "backend environment",
|
|
9852
9937
|
default: "debug"
|
|
9853
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) {
|
|
9854
9939
|
await this.applicationScript.buildIos(app, { target: target2, env: asMobileEnv(env), write, regenerate });
|
|
9855
9940
|
}),
|
|
9856
|
-
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, {
|
|
9857
9946
|
enum: ["local", "debug", "develop", "main"],
|
|
9858
9947
|
desc: "backend environment",
|
|
9859
9948
|
default: "debug"
|
|
@@ -9975,21 +10064,25 @@ class PackageRunner extends runner("package") {
|
|
|
9975
10064
|
const scanner = await TypeScriptDependencyScanner.from(pkg);
|
|
9976
10065
|
const { npmDeps, npmDevDeps, missingDeps } = await scanner.getPackageBuildDependencies(pkg.name);
|
|
9977
10066
|
const packageRuntimeDependencies = { akanjs: ["daisyui"] };
|
|
10067
|
+
const packageRuntimeDevDependencies = { akanjs: ["@biomejs/biome"] };
|
|
9978
10068
|
const forcedRuntimeDeps = packageRuntimeDependencies[pkg.name] ?? [];
|
|
10069
|
+
const forcedRuntimeDevDeps = packageRuntimeDevDependencies[pkg.name] ?? [];
|
|
9979
10070
|
const packageRuntimeDeps = [...new Set([...npmDeps, ...forcedRuntimeDeps])];
|
|
10071
|
+
const packageRuntimeDevDeps = [...new Set([...npmDevDeps, ...forcedRuntimeDevDeps])];
|
|
9980
10072
|
const rootPackageJson = await pkg.workspace.getPackageJson();
|
|
9981
10073
|
const rootDeps = { ...rootPackageJson.dependencies, ...rootPackageJson.devDependencies };
|
|
9982
10074
|
const missingForcedDeps = forcedRuntimeDeps.filter((dep) => !rootDeps[dep]);
|
|
9983
|
-
const
|
|
10075
|
+
const missingForcedDevDeps = forcedRuntimeDevDeps.filter((dep) => !rootDeps[dep]);
|
|
10076
|
+
const allMissingDeps = [...new Set([...missingDeps, ...missingForcedDeps, ...missingForcedDevDeps])].sort();
|
|
9984
10077
|
if (allMissingDeps.length > 0)
|
|
9985
10078
|
throw new Error(`Missing dependency versions in root package.json: ${allMissingDeps.join(", ")}`);
|
|
9986
|
-
await pkg.updatePackageJsonDependencies(packageRuntimeDeps,
|
|
10079
|
+
await pkg.updatePackageJsonDependencies(packageRuntimeDeps, packageRuntimeDevDeps);
|
|
9987
10080
|
const hasBuildFile = await Bun.file(`${pkg.cwdPath}/build.ts`).exists();
|
|
9988
10081
|
if (hasBuildFile) {
|
|
9989
10082
|
await pkg.workspace.spawn(process.execPath, [`${pkg.cwdPath}/build.ts`], { env: process.env, stdio: "inherit" });
|
|
9990
10083
|
} else {
|
|
9991
10084
|
await $2`cp -r ${pkg.cwdPath}/. ${pkg.dist.cwdPath}`;
|
|
9992
|
-
await Promise.all([pkg.generateDistPackageJson(packageRuntimeDeps,
|
|
10085
|
+
await Promise.all([pkg.generateDistPackageJson(packageRuntimeDeps, packageRuntimeDevDeps), pkg.generateTsconfigJson()]);
|
|
9993
10086
|
}
|
|
9994
10087
|
}
|
|
9995
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
|
|
|
@@ -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 = (
|
|
@@ -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);
|