akanjs 2.0.0-beta.1 → 2.0.0-beta.10
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/cli/application/application.command.ts +11 -3
- package/cli/guidelines/databaseModule/databaseModule.instruction.md +1 -1
- package/cli/guidelines/modelConstant/modelConstant.instruction.md +5 -5
- package/cli/guidelines/modelDocument/modelDocument.instruction.md +34 -61
- package/cli/guidelines/modelService/modelService.instruction.md +1 -1
- package/cli/index.js +157 -58
- package/cli/package/package.runner.ts +24 -7
- package/cli/package/package.script.ts +2 -2
- package/cli/templates/app/page/_index.tsx +200 -76
- package/cli/templates/app/page/_layout.tsx +0 -1
- package/cli/templates/module/__Model__.Zone.tsx +1 -1
- package/cli/templates/module/__model__.document.ts +1 -1
- package/cli/templates/workspaceRoot/.gitignore.template +1 -11
- package/cli/templates/workspaceRoot/biome.json.template +16 -0
- package/cli/workspace/workspace.command.ts +2 -6
- package/cli/workspace/workspace.runner.ts +1 -7
- package/cli/workspace/workspace.script.ts +14 -8
- package/client/csrTypes.ts +1 -1
- package/constant/fieldInfo.ts +1 -1
- package/devkit/capacitor.base.config.ts +1 -1
- package/devkit/capacitorApp.ts +5 -1
- package/devkit/commandDecorators/argMeta.ts +28 -14
- package/devkit/commandDecorators/command.ts +41 -15
- package/devkit/commandDecorators/commandBuilder.ts +78 -42
- package/devkit/commandDecorators/helpFormatter.ts +7 -4
- package/devkit/frontendBuild/cssCompiler.ts +9 -3
- package/devkit/incrementalBuilder/incrementalBuilder.proc.ts +2 -1
- package/devkit/lint/no-deep-internal-import.grit +25 -0
- package/devkit/mobile/mobileTarget.ts +48 -8
- package/devkit/src/capacitorApp.ts +277 -0
- package/devkit/transforms/barrelImportsPlugin.ts +6 -0
- package/package.json +3 -1
- package/server/hmr/clientScript.ts +8 -5
- package/server/resolver/resolver.contract.fixture.ts +1 -1
- package/ui/Field.tsx +0 -1
- package/ui/Portal.tsx +2 -0
- package/ui/System/CSR.tsx +6 -5
- package/ui/System/SSR.tsx +1 -1
- package/ui/System/SelectLanguage.tsx +1 -1
- package/webkit/bootCsr.tsx +8 -5
- package/cli/templates/app/common/commonLogic.ts +0 -12
- package/cli/templates/app/common/index.ts +0 -10
- package/cli/templates/app/srvkit/backendLogic.ts +0 -12
- package/cli/templates/app/srvkit/index.ts +0 -10
- package/cli/templates/app/ui/UiComponent.ts +0 -16
- package/cli/templates/app/ui/index.ts +0 -10
- package/cli/templates/app/webkit/frontendLogic.ts +0 -12
- package/cli/templates/app/webkit/index.ts +0 -10
- package/cli/templates/module/index.tsx +0 -44
- /package/cli/templates/app/public/{favicon.ico → favicon.ico.template} +0 -0
- /package/cli/templates/app/public/{logo.png → logo.png.template} +0 -0
|
@@ -1,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",
|
|
@@ -94,7 +94,7 @@ Create the MongoDB schema with document methods and middleware:
|
|
|
94
94
|
|
|
95
95
|
```typescript
|
|
96
96
|
import { dayjs } from "akanjs/base";
|
|
97
|
-
import {
|
|
97
|
+
import { by, Database, into, Loader, type SchemaOf } from "akanjs/document";
|
|
98
98
|
import { hashPassword } from "@libs/shared/server";
|
|
99
99
|
|
|
100
100
|
import * as cnst from "../cnst";
|
|
@@ -80,7 +80,7 @@ export class Drone extends Full(DroneObject, LightDrone) {
|
|
|
80
80
|
// 7. Insight Model
|
|
81
81
|
|
|
82
82
|
export class DroneInsight {
|
|
83
|
-
@Field.Prop(() => Int, { default: 0, accumulate: {
|
|
83
|
+
@Field.Prop(() => Int, { default: 0, accumulate: {} })
|
|
84
84
|
count: number;
|
|
85
85
|
}
|
|
86
86
|
|
|
@@ -223,7 +223,7 @@ Insight model defines statistical fields:
|
|
|
223
223
|
|
|
224
224
|
```typescript
|
|
225
225
|
export class DroneInsight {
|
|
226
|
-
@Field.Prop(() => Int, { default: 0, accumulate: {
|
|
226
|
+
@Field.Prop(() => Int, { default: 0, accumulate: {} })
|
|
227
227
|
count: number;
|
|
228
228
|
}
|
|
229
229
|
```
|
|
@@ -314,8 +314,8 @@ const drones = await this.listByName("myDrone", { sort: "alphabetical" });
|
|
|
314
314
|
| `enum` | `any[]` | - | Enum values restriction | `@Field.Prop(()=> String, { enum: ["active","inactive"] }) status;` |
|
|
315
315
|
| `minlength` | `number` | - | Minimum string length | `@Field.Prop(()=> String, { minlength: 2 }) name: string;` |
|
|
316
316
|
| `maxlength` | `number` | - | Maximum string length | `@Field.Prop(()=> String, { maxlength: 30 }) title: string;` |
|
|
317
|
-
| `query` | `object` | - | Query value for Summary fields | `@Field.Prop(() => Int, { query: { status: {
|
|
318
|
-
| `accumulate` | `object` | - |
|
|
317
|
+
| `query` | `object` | - | Query value for Summary fields | `@Field.Prop(() => Int, { query: { status: { ne: "inactive" } } })` |
|
|
318
|
+
| `accumulate` | `object` | - | Document query filter for Insight count fields | `@Field.Prop(() => Int, { accumulate: { status: "active" } }) activeCount: number;` |
|
|
319
319
|
| `example` | `any` | - | Example value for API docs | `@Field.Prop(()=> String, { example: "contact@akanjs.com" }) email;` |
|
|
320
320
|
| `of` | `any` | - | Value type for Map fields | `@Field.Prop(()=> Map, { of: Date }) readAts: Map<string, Dayjs>;` |
|
|
321
321
|
| `validate` | `(value) => boolean` | - | Custom validation function | `@Field.Prop(()=> String, { validate: (v)=> v.includes("@") }) email;` |
|
|
@@ -477,7 +477,7 @@ export class Drone extends Full(DroneObject, LightDrone) {
|
|
|
477
477
|
// Insight Model
|
|
478
478
|
|
|
479
479
|
export class DroneInsight {
|
|
480
|
-
@Field.Prop(() => Int, { accumulate: {
|
|
480
|
+
@Field.Prop(() => Int, { accumulate: {} })
|
|
481
481
|
count: number;
|
|
482
482
|
}
|
|
483
483
|
|
|
@@ -118,26 +118,14 @@ export class OrderModel extends into(Order, cnst.orderCnst) {
|
|
|
118
118
|
.save();
|
|
119
119
|
}
|
|
120
120
|
|
|
121
|
-
//
|
|
121
|
+
// Insight-style count example
|
|
122
122
|
async getOrderStatsByUser(userId: string) {
|
|
123
|
-
const
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
totalAmount: { $sum: "$totalAmount" },
|
|
130
|
-
},
|
|
131
|
-
},
|
|
132
|
-
]);
|
|
133
|
-
|
|
134
|
-
return result.reduce((acc, item) => {
|
|
135
|
-
acc[item._id] = {
|
|
136
|
-
count: item.count,
|
|
137
|
-
totalAmount: item.totalAmount,
|
|
138
|
-
};
|
|
139
|
-
return acc;
|
|
140
|
-
}, {});
|
|
123
|
+
const baseQuery = { user: userId };
|
|
124
|
+
|
|
125
|
+
return {
|
|
126
|
+
active: await this.count({ ...baseQuery, status: "active" }),
|
|
127
|
+
refunded: await this.count({ ...baseQuery, status: "refunded" }),
|
|
128
|
+
};
|
|
141
129
|
}
|
|
142
130
|
}
|
|
143
131
|
```
|
|
@@ -191,37 +179,30 @@ Indexes are crucial for query performance. Define them in the Middleware's `onSc
|
|
|
191
179
|
- **Unique index** - Ensure field values are unique across the collection
|
|
192
180
|
- **Sparse index** - Only include documents that have the indexed field
|
|
193
181
|
|
|
194
|
-
## How to
|
|
182
|
+
## How to Query Documents
|
|
195
183
|
|
|
196
|
-
The Akan.js framework
|
|
184
|
+
The Akan.js framework uses document query objects for common filtering and counting:
|
|
197
185
|
|
|
198
186
|
```typescript
|
|
199
187
|
// Find operations
|
|
200
|
-
const product = await productModel.
|
|
201
|
-
const activeProducts = await productModel.
|
|
188
|
+
const product = await productModel.pickById(id);
|
|
189
|
+
const activeProducts = await productModel.list({ status: "active" });
|
|
202
190
|
|
|
203
191
|
// Update operations
|
|
204
|
-
await productModel.
|
|
192
|
+
await productModel.updateManyByQuery({ category: "electronics" }, { set: { onSale: true } });
|
|
205
193
|
|
|
206
|
-
//
|
|
207
|
-
const
|
|
208
|
-
|
|
209
|
-
{
|
|
210
|
-
$group: {
|
|
211
|
-
_id: "$category",
|
|
212
|
-
count: { $sum: 1 },
|
|
213
|
-
avgPrice: { $avg: "$price" },
|
|
214
|
-
},
|
|
215
|
-
},
|
|
216
|
-
]);
|
|
194
|
+
// Insight counts
|
|
195
|
+
const activeCount = await productModel.count({ status: "active" });
|
|
196
|
+
const categoryCount = await productModel.count({ category: "electronics" });
|
|
217
197
|
|
|
218
198
|
// Advanced querying
|
|
219
|
-
const products = await productModel.
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
}
|
|
223
|
-
|
|
224
|
-
|
|
199
|
+
const products = await productModel.list(
|
|
200
|
+
{
|
|
201
|
+
price: { gte: 100, lte: 500 },
|
|
202
|
+
category: { oneOf: ["electronics", "gadgets"] },
|
|
203
|
+
},
|
|
204
|
+
{ sort: { rating: -1 }, limit: 10 },
|
|
205
|
+
);
|
|
225
206
|
```
|
|
226
207
|
|
|
227
208
|
## How to Use DataLoader
|
|
@@ -422,16 +403,17 @@ export class ProductModel extends into(Product, cnst.productCnst) {
|
|
|
422
403
|
tagProductsLoader: Loader<string, Product[]>;
|
|
423
404
|
|
|
424
405
|
async findPopularProducts(limit = 10) {
|
|
425
|
-
return this.
|
|
406
|
+
return this.list(
|
|
407
|
+
{
|
|
426
408
|
status: "active",
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
409
|
+
popularity: { gte: 4 },
|
|
410
|
+
},
|
|
411
|
+
{ sort: { popularity: -1 }, limit },
|
|
412
|
+
);
|
|
431
413
|
}
|
|
432
414
|
|
|
433
415
|
async updatePrices(categoryId: string, increasePercentage: number) {
|
|
434
|
-
const products = await this.
|
|
416
|
+
const products = await this.list({ category: categoryId });
|
|
435
417
|
|
|
436
418
|
const updates = products.map((product) => {
|
|
437
419
|
const newPrice = product.price * (1 + increasePercentage / 100);
|
|
@@ -441,20 +423,11 @@ export class ProductModel extends into(Product, cnst.productCnst) {
|
|
|
441
423
|
return Promise.all(updates);
|
|
442
424
|
}
|
|
443
425
|
|
|
444
|
-
async getCategoryStats() {
|
|
445
|
-
const result =
|
|
446
|
-
|
|
447
|
-
{
|
|
448
|
-
|
|
449
|
-
_id: "$category",
|
|
450
|
-
count: { $sum: 1 },
|
|
451
|
-
avgPrice: { $avg: "$price" },
|
|
452
|
-
minPrice: { $min: "$price" },
|
|
453
|
-
maxPrice: { $max: "$price" },
|
|
454
|
-
},
|
|
455
|
-
},
|
|
456
|
-
{ $sort: { count: -1 } },
|
|
457
|
-
]);
|
|
426
|
+
async getCategoryStats(categoryId: string) {
|
|
427
|
+
const result = {
|
|
428
|
+
activeCount: await this.count({ status: "active", category: categoryId }),
|
|
429
|
+
popularCount: await this.count({ status: "active", category: categoryId, popularity: { gte: 4 } }),
|
|
430
|
+
};
|
|
458
431
|
|
|
459
432
|
// Cache the results
|
|
460
433
|
await this.ProductCache.set("stats", "categories", JSON.stringify(result), { expireAt: dayjs().add(1, "hour") });
|
|
@@ -644,7 +644,7 @@ export class OrderService extends DbService(db.orderDb) {
|
|
|
644
644
|
// Return stock to inventory
|
|
645
645
|
for (const item of order.items) {
|
|
646
646
|
await this.productService.updateProduct(item.product, {
|
|
647
|
-
|
|
647
|
+
inc: { stock: item.quantity },
|
|
648
648
|
});
|
|
649
649
|
}
|
|
650
650
|
|
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"
|
|
@@ -9946,13 +10035,16 @@ class ApplicationCommand extends command("application", [ApplicationScript], ({
|
|
|
9946
10035
|
})) {
|
|
9947
10036
|
}
|
|
9948
10037
|
|
|
10038
|
+
import path36 from "path";
|
|
9949
10039
|
var {$: $2 } = globalThis.Bun;
|
|
9950
10040
|
|
|
9951
10041
|
class PackageRunner extends runner("package") {
|
|
9952
|
-
async version(workspace) {
|
|
9953
|
-
const pkgJson = await FileSys.readJson("package.json
|
|
10042
|
+
async version(workspace, { log = true } = {}) {
|
|
10043
|
+
const pkgJson = await FileSys.readJson(process.env.USE_AKANJS_PKGS === "true" ? `${workspace.workspaceRoot}/pkgs/akanjs/package.json` : `${path36.dirname(Bun.main)}/../package.json`);
|
|
9954
10044
|
const version = pkgJson.version;
|
|
9955
|
-
|
|
10045
|
+
if (log)
|
|
10046
|
+
Logger.rawLog(`${pkgJson.name}@${version}`);
|
|
10047
|
+
return version;
|
|
9956
10048
|
}
|
|
9957
10049
|
async createPackage(workspace, pkgName) {
|
|
9958
10050
|
await workspace.applyTemplate({ basePath: `pkgs/${pkgName}`, template: "pkgRoot", dict: { pkgName } });
|
|
@@ -9971,15 +10063,26 @@ class PackageRunner extends runner("package") {
|
|
|
9971
10063
|
await pkg.dist.mkdir(pkg.dist.cwdPath);
|
|
9972
10064
|
const scanner = await TypeScriptDependencyScanner.from(pkg);
|
|
9973
10065
|
const { npmDeps, npmDevDeps, missingDeps } = await scanner.getPackageBuildDependencies(pkg.name);
|
|
9974
|
-
|
|
9975
|
-
|
|
9976
|
-
|
|
10066
|
+
const packageRuntimeDependencies = { akanjs: ["daisyui"] };
|
|
10067
|
+
const packageRuntimeDevDependencies = { akanjs: ["@biomejs/biome"] };
|
|
10068
|
+
const forcedRuntimeDeps = packageRuntimeDependencies[pkg.name] ?? [];
|
|
10069
|
+
const forcedRuntimeDevDeps = packageRuntimeDevDependencies[pkg.name] ?? [];
|
|
10070
|
+
const packageRuntimeDeps = [...new Set([...npmDeps, ...forcedRuntimeDeps])];
|
|
10071
|
+
const packageRuntimeDevDeps = [...new Set([...npmDevDeps, ...forcedRuntimeDevDeps])];
|
|
10072
|
+
const rootPackageJson = await pkg.workspace.getPackageJson();
|
|
10073
|
+
const rootDeps = { ...rootPackageJson.dependencies, ...rootPackageJson.devDependencies };
|
|
10074
|
+
const missingForcedDeps = forcedRuntimeDeps.filter((dep) => !rootDeps[dep]);
|
|
10075
|
+
const missingForcedDevDeps = forcedRuntimeDevDeps.filter((dep) => !rootDeps[dep]);
|
|
10076
|
+
const allMissingDeps = [...new Set([...missingDeps, ...missingForcedDeps, ...missingForcedDevDeps])].sort();
|
|
10077
|
+
if (allMissingDeps.length > 0)
|
|
10078
|
+
throw new Error(`Missing dependency versions in root package.json: ${allMissingDeps.join(", ")}`);
|
|
10079
|
+
await pkg.updatePackageJsonDependencies(packageRuntimeDeps, packageRuntimeDevDeps);
|
|
9977
10080
|
const hasBuildFile = await Bun.file(`${pkg.cwdPath}/build.ts`).exists();
|
|
9978
10081
|
if (hasBuildFile) {
|
|
9979
10082
|
await pkg.workspace.spawn(process.execPath, [`${pkg.cwdPath}/build.ts`], { env: process.env, stdio: "inherit" });
|
|
9980
10083
|
} else {
|
|
9981
10084
|
await $2`cp -r ${pkg.cwdPath}/. ${pkg.dist.cwdPath}`;
|
|
9982
|
-
await Promise.all([pkg.generateDistPackageJson(
|
|
10085
|
+
await Promise.all([pkg.generateDistPackageJson(packageRuntimeDeps, packageRuntimeDevDeps), pkg.generateTsconfigJson()]);
|
|
9983
10086
|
}
|
|
9984
10087
|
}
|
|
9985
10088
|
async updateWorskpaceRootPackageJson(workspace, rootPackageJson) {
|
|
@@ -9996,8 +10099,8 @@ class PackageRunner extends runner("package") {
|
|
|
9996
10099
|
}
|
|
9997
10100
|
|
|
9998
10101
|
class PackageScript extends script("package", [PackageRunner]) {
|
|
9999
|
-
async version(workspace) {
|
|
10000
|
-
await this.packageRunner.version(workspace);
|
|
10102
|
+
async version(workspace, { log = true } = {}) {
|
|
10103
|
+
return await this.packageRunner.version(workspace, { log });
|
|
10001
10104
|
}
|
|
10002
10105
|
async createPackage(workspace, pkgName) {
|
|
10003
10106
|
const spinner2 = workspace.spinning(`Creating package in pkgs/${pkgName}...`);
|
|
@@ -10256,14 +10359,14 @@ class GuidelinePrompt extends Prompter {
|
|
|
10256
10359
|
async#getScanFilePaths(matchPattern, { avoidDirs = ["node_modules", ".next"], filterText } = {}) {
|
|
10257
10360
|
const glob = new Bun.Glob(matchPattern);
|
|
10258
10361
|
const paths = [];
|
|
10259
|
-
for await (const
|
|
10260
|
-
if (avoidDirs.some((dir) =>
|
|
10362
|
+
for await (const path37 of glob.scan({ cwd: this.workspace.workspaceRoot, absolute: true })) {
|
|
10363
|
+
if (avoidDirs.some((dir) => path37.includes(dir)))
|
|
10261
10364
|
continue;
|
|
10262
|
-
const fileContent = await FileSys.readText(
|
|
10365
|
+
const fileContent = await FileSys.readText(path37);
|
|
10263
10366
|
const textFilter = filterText ? new RegExp(filterText) : null;
|
|
10264
10367
|
if (filterText && !textFilter?.test(fileContent))
|
|
10265
10368
|
continue;
|
|
10266
|
-
paths.push(
|
|
10369
|
+
paths.push(path37);
|
|
10267
10370
|
}
|
|
10268
10371
|
return paths;
|
|
10269
10372
|
}
|
|
@@ -11249,18 +11352,13 @@ class ScalarCommand extends command("scalar", [ScalarScript], ({ public: target
|
|
|
11249
11352
|
})) {
|
|
11250
11353
|
}
|
|
11251
11354
|
|
|
11252
|
-
import
|
|
11253
|
-
|
|
11254
|
-
import path36 from "path";
|
|
11255
|
-
import latestVersion2 from "latest-version";
|
|
11355
|
+
import path38 from "path";
|
|
11256
11356
|
|
|
11357
|
+
import path37 from "path";
|
|
11257
11358
|
class WorkspaceRunner extends runner("workspace") {
|
|
11258
|
-
async
|
|
11259
|
-
return /^\d+\.\d+\.\d+(?:[-+][0-9A-Za-z.-]+)?$/.test(tag) ? tag : await latestVersion2("akanjs", { version: tag });
|
|
11260
|
-
}
|
|
11261
|
-
async createWorkspace(repoName, appName, { dirname: dirname3 = ".", tag = "latest", init = true }) {
|
|
11359
|
+
async createWorkspace(repoName, appName, { dirname: dirname3 = ".", init = true, akanVersion }) {
|
|
11262
11360
|
const cwdPath = process.cwd();
|
|
11263
|
-
const workspaceRoot =
|
|
11361
|
+
const workspaceRoot = path37.join(cwdPath, dirname3, repoName);
|
|
11264
11362
|
const workspace = WorkspaceExecutor.fromRoot({ workspaceRoot, repoName });
|
|
11265
11363
|
const templateSpinner = workspace.spinning(`Creating workspace template files in ${dirname3}/${repoName}...`);
|
|
11266
11364
|
await workspace.applyTemplate({
|
|
@@ -11270,7 +11368,6 @@ class WorkspaceRunner extends runner("workspace") {
|
|
|
11270
11368
|
});
|
|
11271
11369
|
templateSpinner.succeed(`Workspace files created in ${dirname3}/${repoName}`);
|
|
11272
11370
|
const rootPackageJson = await workspace.getPackageJson();
|
|
11273
|
-
const akanVersion = await this.#resolveAkanVersion(tag);
|
|
11274
11371
|
const packageJson = {
|
|
11275
11372
|
...rootPackageJson,
|
|
11276
11373
|
dependencies: {
|
|
@@ -11299,14 +11396,19 @@ class WorkspaceRunner extends runner("workspace") {
|
|
|
11299
11396
|
}
|
|
11300
11397
|
}
|
|
11301
11398
|
|
|
11302
|
-
class WorkspaceScript extends script("workspace", [
|
|
11303
|
-
|
|
11304
|
-
|
|
11305
|
-
|
|
11306
|
-
|
|
11307
|
-
|
|
11308
|
-
}) {
|
|
11309
|
-
const
|
|
11399
|
+
class WorkspaceScript extends script("workspace", [
|
|
11400
|
+
WorkspaceRunner,
|
|
11401
|
+
ApplicationScript,
|
|
11402
|
+
LibraryScript,
|
|
11403
|
+
PackageScript
|
|
11404
|
+
]) {
|
|
11405
|
+
async createWorkspace(repoName, appName, { dirname: dirname3 = ".", installLibs = false, init = true }) {
|
|
11406
|
+
const akanVersion = await this.packageScript.version({ log: false });
|
|
11407
|
+
const workspace = await this.workspaceRunner.createWorkspace(repoName, appName, {
|
|
11408
|
+
dirname: dirname3,
|
|
11409
|
+
init,
|
|
11410
|
+
akanVersion
|
|
11411
|
+
});
|
|
11310
11412
|
if (installLibs) {
|
|
11311
11413
|
await this.libraryScript.installLibrary(workspace, "util");
|
|
11312
11414
|
await this.libraryScript.installLibrary(workspace, "shared");
|
|
@@ -11319,7 +11421,7 @@ class WorkspaceScript extends script("workspace", [WorkspaceRunner, ApplicationS
|
|
|
11319
11421
|
} catch (_) {
|
|
11320
11422
|
gitSpinner.fail("Git repository initialization failed. It's not fatal, you can commit manually");
|
|
11321
11423
|
}
|
|
11322
|
-
const workspacePath =
|
|
11424
|
+
const workspacePath = path38.join(dirname3, repoName);
|
|
11323
11425
|
Logger.rawLog(`
|
|
11324
11426
|
\uD83C\uDF89 Welcome aboard! Workspace created in ${dirname3}/${repoName}`);
|
|
11325
11427
|
Logger.rawLog(`\uD83D\uDE80 Run \`cd ${workspacePath} && akan start ${appName}\` to start the development server.`);
|
|
@@ -11374,15 +11476,12 @@ class WorkspaceCommand extends command("workspace", [WorkspaceScript], ({ public
|
|
|
11374
11476
|
value: true
|
|
11375
11477
|
}
|
|
11376
11478
|
]
|
|
11377
|
-
}).option("tag", String, {
|
|
11378
|
-
desc: "tag of the update",
|
|
11379
|
-
default: "latest"
|
|
11380
11479
|
}).option("init", Boolean, {
|
|
11381
11480
|
desc: "Do you want to initialize the workspace? (Recommended)",
|
|
11382
11481
|
default: true
|
|
11383
|
-
}).exec(async function(workspaceName, app, dir, libs,
|
|
11482
|
+
}).exec(async function(workspaceName, app, dir, libs, init) {
|
|
11384
11483
|
const appName = app || "app";
|
|
11385
|
-
await this.workspaceScript.createWorkspace(workspaceName.toLowerCase().replace(/ /g, "-"), appName.toLowerCase().replace(/ /g, "-"), { dirname: dir, installLibs: libs,
|
|
11484
|
+
await this.workspaceScript.createWorkspace(workspaceName.toLowerCase().replace(/ /g, "-"), appName.toLowerCase().replace(/ /g, "-"), { dirname: dir, installLibs: libs, init });
|
|
11386
11485
|
}),
|
|
11387
11486
|
lint: target({ desc: "Lint and fix code in a specific app/lib/pkg" }).with(Exec).option("fix", Boolean, { default: true }).with(Workspace).exec(async function(exec2, fix, workspace) {
|
|
11388
11487
|
await this.workspaceScript.lint(exec2, workspace, { fix });
|