@akanjs/cli 2.3.9-rc.9 → 2.3.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/incrementalBuilder.proc.js +637 -79
- package/index.js +721 -124
- package/package.json +2 -2
package/index.js
CHANGED
|
@@ -12502,6 +12502,7 @@ void run().catch((error) => {
|
|
|
12502
12502
|
}
|
|
12503
12503
|
// pkgs/@akanjs/devkit/applicationReleasePackager.ts
|
|
12504
12504
|
import { cp, mkdir as mkdir9, rm as rm4 } from "fs/promises";
|
|
12505
|
+
import path38 from "path";
|
|
12505
12506
|
|
|
12506
12507
|
// pkgs/@akanjs/devkit/uploadRelease.ts
|
|
12507
12508
|
import { HttpClient as HttpClient2, Logger as Logger9 } from "akanjs/common";
|
|
@@ -12617,13 +12618,9 @@ class ApplicationReleasePackager {
|
|
|
12617
12618
|
await cp(`${this.#app.dist.cwdPath}/backend`, `${buildPath}/backend`, { recursive: true });
|
|
12618
12619
|
await cp(this.#app.dist.cwdPath, buildRoot, { recursive: true });
|
|
12619
12620
|
await rm4(`${buildRoot}/frontend/.next`, { recursive: true, force: true });
|
|
12620
|
-
|
|
12621
|
-
|
|
12622
|
-
|
|
12623
|
-
"-C",
|
|
12624
|
-
buildRoot,
|
|
12625
|
-
"./"
|
|
12626
|
-
]);
|
|
12621
|
+
const releaseRoot = this.#app.workspace.workspaceRoot;
|
|
12622
|
+
const releaseArchivePath = path38.relative(releaseRoot, `${releaseRoot}/releases/builds/${this.#app.name}-release.tar.gz`).split(path38.sep).join("/");
|
|
12623
|
+
await this.#app.workspace.spawn("tar", ["-zcf", releaseArchivePath, "-C", buildRoot, "./"], { cwd: releaseRoot });
|
|
12627
12624
|
await this.#writeCsrZipIfPresent();
|
|
12628
12625
|
return { platformVersion };
|
|
12629
12626
|
}
|
|
@@ -12644,22 +12641,18 @@ class ApplicationReleasePackager {
|
|
|
12644
12641
|
await cp(this.#app.dist.cwdPath, `${sourceRoot}/apps/${this.#app.name}`, { recursive: true });
|
|
12645
12642
|
const libDeps = ["social", "shared", "platform", "util"];
|
|
12646
12643
|
await Promise.all(libDeps.map((lib) => cp(`${this.#app.workspace.cwdPath}/libs/${lib}`, `${sourceRoot}/libs/${lib}`, { recursive: true })));
|
|
12647
|
-
await Promise.all([".next", "ios", "android", "public/libs"].map(async (
|
|
12648
|
-
const targetPath = `${sourceRoot}/apps/${this.#app.name}/${
|
|
12644
|
+
await Promise.all([".next", "ios", "android", "public/libs"].map(async (path39) => {
|
|
12645
|
+
const targetPath = `${sourceRoot}/apps/${this.#app.name}/${path39}`;
|
|
12649
12646
|
if (await FileSys.dirExists(targetPath))
|
|
12650
12647
|
await rm4(targetPath, { recursive: true, force: true });
|
|
12651
12648
|
}));
|
|
12652
12649
|
const syncPaths = [".husky", ".gitignore", "package.json"];
|
|
12653
|
-
await Promise.all(syncPaths.map((
|
|
12650
|
+
await Promise.all(syncPaths.map((path39) => cp(`${this.#app.workspace.cwdPath}/${path39}`, `${sourceRoot}/${path39}`, { recursive: true })));
|
|
12654
12651
|
await this.#writeSourceTsconfig(sourceRoot, libDeps);
|
|
12655
12652
|
await Bun.write(`${sourceRoot}/README.md`, readme);
|
|
12656
|
-
|
|
12657
|
-
|
|
12658
|
-
|
|
12659
|
-
"-C",
|
|
12660
|
-
sourceRoot,
|
|
12661
|
-
"./"
|
|
12662
|
-
]);
|
|
12653
|
+
const sourceCwd = this.#app.workspace.cwdPath;
|
|
12654
|
+
const sourceArchivePath = path38.relative(sourceCwd, `${sourceCwd}/releases/sources/${this.#app.name}-source.tar.gz`).split(path38.sep).join("/");
|
|
12655
|
+
await this.#app.workspace.spawn("tar", ["-zcf", sourceArchivePath, "-C", sourceRoot, "./"], { cwd: sourceCwd });
|
|
12663
12656
|
}
|
|
12664
12657
|
async#resetSourceRoot(sourceRoot) {
|
|
12665
12658
|
if (await FileSys.dirExists(sourceRoot)) {
|
|
@@ -12741,20 +12734,20 @@ class ApplicationReleasePackager {
|
|
|
12741
12734
|
}
|
|
12742
12735
|
}
|
|
12743
12736
|
// pkgs/@akanjs/devkit/applicationTestPreload.ts
|
|
12744
|
-
import
|
|
12737
|
+
import path39 from "path";
|
|
12745
12738
|
var SIGNAL_TEST_PRELOAD_PATH = "test/signalTest.preload.ts";
|
|
12746
12739
|
async function resolveSignalTestPreloadPath(target) {
|
|
12747
12740
|
const candidates = [];
|
|
12748
12741
|
const addResolvedPackageCandidate = (basePath2) => {
|
|
12749
12742
|
try {
|
|
12750
|
-
candidates.push(
|
|
12743
|
+
candidates.push(path39.join(path39.dirname(Bun.resolveSync("akanjs/package.json", basePath2)), SIGNAL_TEST_PRELOAD_PATH));
|
|
12751
12744
|
} catch {}
|
|
12752
12745
|
};
|
|
12753
12746
|
addResolvedPackageCandidate(target.cwdPath);
|
|
12754
12747
|
addResolvedPackageCandidate(process.cwd());
|
|
12755
|
-
addResolvedPackageCandidate(
|
|
12748
|
+
addResolvedPackageCandidate(path39.dirname(Bun.main));
|
|
12756
12749
|
addResolvedPackageCandidate(import.meta.dir);
|
|
12757
|
-
candidates.push(
|
|
12750
|
+
candidates.push(path39.join(target.cwdPath, "../../node_modules/akanjs", SIGNAL_TEST_PRELOAD_PATH), path39.join(target.cwdPath, "../../pkgs/akanjs", SIGNAL_TEST_PRELOAD_PATH), path39.join(process.cwd(), "node_modules/akanjs", SIGNAL_TEST_PRELOAD_PATH), path39.join(process.cwd(), "pkgs/akanjs", SIGNAL_TEST_PRELOAD_PATH), path39.join(path39.dirname(Bun.main), "../../akanjs", SIGNAL_TEST_PRELOAD_PATH), path39.resolve(import.meta.dir, "../../akanjs", SIGNAL_TEST_PRELOAD_PATH));
|
|
12758
12751
|
for (const candidate of [...new Set(candidates)]) {
|
|
12759
12752
|
if (await Bun.file(candidate).exists())
|
|
12760
12753
|
return candidate;
|
|
@@ -12764,7 +12757,7 @@ async function resolveSignalTestPreloadPath(target) {
|
|
|
12764
12757
|
// pkgs/@akanjs/devkit/builder.ts
|
|
12765
12758
|
import { existsSync as existsSync2 } from "fs";
|
|
12766
12759
|
import { mkdir as mkdir10 } from "fs/promises";
|
|
12767
|
-
import
|
|
12760
|
+
import path40 from "path";
|
|
12768
12761
|
var SKIP_ENTRY_DIR_SET = new Set(["node_modules", "dist", "build", ".git", ".next"]);
|
|
12769
12762
|
var assetExtensions = [".css", ".md", ".js", ".png", ".ico", ".svg", ".json", ".template"];
|
|
12770
12763
|
var assetLoader = Object.fromEntries(assetExtensions.map((ext) => [ext, "file"]));
|
|
@@ -12781,14 +12774,14 @@ class Builder {
|
|
|
12781
12774
|
#globEntrypoints(cwd, pattern) {
|
|
12782
12775
|
const glob = new Bun.Glob(pattern);
|
|
12783
12776
|
return Array.from(glob.scanSync({ cwd, onlyFiles: true })).filter((relativePath) => {
|
|
12784
|
-
const segments = relativePath.split(
|
|
12777
|
+
const segments = relativePath.split(path40.sep);
|
|
12785
12778
|
return !segments.some((segment) => SKIP_ENTRY_DIR_SET.has(segment));
|
|
12786
|
-
}).map((rel) =>
|
|
12779
|
+
}).map((rel) => path40.join(cwd, rel));
|
|
12787
12780
|
}
|
|
12788
12781
|
#globFiles(cwd, pattern = "**/*.*") {
|
|
12789
12782
|
const glob = new Bun.Glob(pattern);
|
|
12790
12783
|
return Array.from(glob.scanSync({ cwd, onlyFiles: true })).filter((relativePath) => {
|
|
12791
|
-
const segments = relativePath.split(
|
|
12784
|
+
const segments = relativePath.split(path40.sep);
|
|
12792
12785
|
return !segments.some((segment) => SKIP_ENTRY_DIR_SET.has(segment));
|
|
12793
12786
|
});
|
|
12794
12787
|
}
|
|
@@ -12796,17 +12789,17 @@ class Builder {
|
|
|
12796
12789
|
const out = [];
|
|
12797
12790
|
for (const p of additionalEntryPoints) {
|
|
12798
12791
|
if (p.includes("*")) {
|
|
12799
|
-
const rel = p.startsWith(`${cwd}/`) || p.startsWith(`${cwd}${
|
|
12792
|
+
const rel = p.startsWith(`${cwd}/`) || p.startsWith(`${cwd}${path40.sep}`) ? p.slice(cwd.length + 1) : p;
|
|
12800
12793
|
out.push(...this.#globEntrypoints(cwd, rel));
|
|
12801
12794
|
} else
|
|
12802
|
-
out.push(
|
|
12795
|
+
out.push(path40.isAbsolute(p) ? p : path40.join(cwd, p));
|
|
12803
12796
|
}
|
|
12804
12797
|
return out;
|
|
12805
12798
|
}
|
|
12806
12799
|
#getBuildOptions({ bundle = false, additionalEntryPoints = [] } = {}) {
|
|
12807
12800
|
const cwd = this.#executor.cwdPath;
|
|
12808
12801
|
const entrypoints = [
|
|
12809
|
-
...bundle ? [
|
|
12802
|
+
...bundle ? [path40.join(cwd, "index.ts")] : this.#globEntrypoints(cwd, "**/*.{ts,tsx}"),
|
|
12810
12803
|
...this.#resolveAdditionalEntrypoints(cwd, additionalEntryPoints)
|
|
12811
12804
|
];
|
|
12812
12805
|
return {
|
|
@@ -12827,9 +12820,9 @@ class Builder {
|
|
|
12827
12820
|
for (const relativePath of this.#globFiles(cwd)) {
|
|
12828
12821
|
if (relativePath === "package.json")
|
|
12829
12822
|
continue;
|
|
12830
|
-
const sourcePath =
|
|
12831
|
-
const targetPath =
|
|
12832
|
-
await mkdir10(
|
|
12823
|
+
const sourcePath = path40.join(cwd, relativePath);
|
|
12824
|
+
const targetPath = path40.join(this.#distExecutor.cwdPath, relativePath);
|
|
12825
|
+
await mkdir10(path40.dirname(targetPath), { recursive: true });
|
|
12833
12826
|
await Bun.write(targetPath, Bun.file(sourcePath));
|
|
12834
12827
|
}
|
|
12835
12828
|
}
|
|
@@ -12840,13 +12833,13 @@ class Builder {
|
|
|
12840
12833
|
return withoutFormatDir;
|
|
12841
12834
|
if (!hasDotSlash && withoutFormatDir === publishedPath)
|
|
12842
12835
|
return publishedPath;
|
|
12843
|
-
const parsed =
|
|
12836
|
+
const parsed = path40.posix.parse(withoutFormatDir);
|
|
12844
12837
|
if (![".js", ".mjs", ".cjs"].includes(parsed.ext))
|
|
12845
12838
|
return withoutFormatDir;
|
|
12846
|
-
const withoutExt =
|
|
12839
|
+
const withoutExt = path40.posix.join(parsed.dir, parsed.name);
|
|
12847
12840
|
const sourcePath = withoutExt.startsWith("./") ? withoutExt.slice(2) : withoutExt;
|
|
12848
12841
|
const sourceCandidates = [`${sourcePath}.ts`, `${sourcePath}.tsx`];
|
|
12849
|
-
const matchedSource = sourceCandidates.find((candidate) => existsSync2(
|
|
12842
|
+
const matchedSource = sourceCandidates.find((candidate) => existsSync2(path40.join(this.#executor.cwdPath, candidate)));
|
|
12850
12843
|
if (!matchedSource)
|
|
12851
12844
|
return withoutFormatDir;
|
|
12852
12845
|
return hasDotSlash ? `./${matchedSource}` : matchedSource;
|
|
@@ -12897,7 +12890,7 @@ class Builder {
|
|
|
12897
12890
|
// pkgs/@akanjs/devkit/capacitorApp.ts
|
|
12898
12891
|
import { cp as cp2, mkdir as mkdir11, rm as rm5 } from "fs/promises";
|
|
12899
12892
|
import os from "os";
|
|
12900
|
-
import
|
|
12893
|
+
import path41 from "path";
|
|
12901
12894
|
import { select as select2 } from "@inquirer/prompts";
|
|
12902
12895
|
import { MobileProject } from "@trapezedev/project";
|
|
12903
12896
|
import { capitalize as capitalize5 } from "akanjs/common";
|
|
@@ -13104,13 +13097,13 @@ var rootCapacitorConfigFilenames = [
|
|
|
13104
13097
|
"capacitor.config.js",
|
|
13105
13098
|
"capacitor.config.json"
|
|
13106
13099
|
];
|
|
13107
|
-
var rootCapacitorConfigPaths = (appRoot) => rootCapacitorConfigFilenames.map((file) =>
|
|
13100
|
+
var rootCapacitorConfigPaths = (appRoot) => rootCapacitorConfigFilenames.map((file) => path41.join(appRoot, file));
|
|
13108
13101
|
async function clearRootCapacitorConfigs(appRoot) {
|
|
13109
13102
|
await Promise.all(rootCapacitorConfigPaths(appRoot).map((file) => rm5(file, { force: true })));
|
|
13110
13103
|
}
|
|
13111
13104
|
async function writeRootCapacitorConfig(appRoot, content) {
|
|
13112
13105
|
await clearRootCapacitorConfigs(appRoot);
|
|
13113
|
-
await Bun.write(
|
|
13106
|
+
await Bun.write(path41.join(appRoot, "capacitor.config.json"), content);
|
|
13114
13107
|
}
|
|
13115
13108
|
var getLocalIP = () => {
|
|
13116
13109
|
const interfaces = os.networkInterfaces();
|
|
@@ -13219,13 +13212,13 @@ function buildIosNativeRunCommand({
|
|
|
13219
13212
|
scheme = "App",
|
|
13220
13213
|
configuration = "Debug"
|
|
13221
13214
|
}) {
|
|
13222
|
-
const derivedDataPath =
|
|
13215
|
+
const derivedDataPath = path41.join(appRoot, "ios/DerivedData", device.id);
|
|
13223
13216
|
const productPlatform = device.kind === "device" ? "iphoneos" : "iphonesimulator";
|
|
13224
13217
|
const destination = device.kind === "device" ? `id=${device.xcodebuildId ?? device.id}` : `platform=iOS Simulator,id=${device.id}`;
|
|
13225
13218
|
return {
|
|
13226
13219
|
configuration,
|
|
13227
13220
|
derivedDataPath,
|
|
13228
|
-
appPath:
|
|
13221
|
+
appPath: path41.join(derivedDataPath, "Build/Products", `${configuration}-${productPlatform}`, "App.app"),
|
|
13229
13222
|
xcodebuildArgs: [
|
|
13230
13223
|
"-project",
|
|
13231
13224
|
"App.xcodeproj",
|
|
@@ -13435,7 +13428,7 @@ function materializeCapacitorConfig(target, { operation, localServerUrl, localIp
|
|
|
13435
13428
|
...capacitorConfig,
|
|
13436
13429
|
appId,
|
|
13437
13430
|
appName,
|
|
13438
|
-
webDir:
|
|
13431
|
+
webDir: path41.posix.join(".akan", "mobile", name, "www"),
|
|
13439
13432
|
plugins: {
|
|
13440
13433
|
CapacitorCookies: { enabled: true },
|
|
13441
13434
|
...pluginsConfig,
|
|
@@ -13492,10 +13485,10 @@ class CapacitorApp {
|
|
|
13492
13485
|
constructor(app, target) {
|
|
13493
13486
|
this.app = app;
|
|
13494
13487
|
this.target = target;
|
|
13495
|
-
this.targetRootPath =
|
|
13496
|
-
this.targetRoot =
|
|
13497
|
-
this.targetWebRoot =
|
|
13498
|
-
this.targetAssetRoot =
|
|
13488
|
+
this.targetRootPath = path41.posix.join(".akan", "mobile", this.target.name);
|
|
13489
|
+
this.targetRoot = path41.join(this.app.cwdPath, this.targetRootPath);
|
|
13490
|
+
this.targetWebRoot = path41.join(this.targetRoot, "www");
|
|
13491
|
+
this.targetAssetRoot = path41.join(this.targetRoot, "assets");
|
|
13499
13492
|
this.project = new MobileProject(this.app.cwdPath, {
|
|
13500
13493
|
android: { path: this.androidRootPath },
|
|
13501
13494
|
ios: { path: this.iosProjectPath }
|
|
@@ -13510,9 +13503,9 @@ class CapacitorApp {
|
|
|
13510
13503
|
await mkdir11(this.targetRoot, { recursive: true });
|
|
13511
13504
|
if (regenerate) {
|
|
13512
13505
|
if (!platform || platform === "ios")
|
|
13513
|
-
await rm5(
|
|
13506
|
+
await rm5(path41.join(this.app.cwdPath, this.iosRootPath), { recursive: true, force: true });
|
|
13514
13507
|
if (!platform || platform === "android")
|
|
13515
|
-
await rm5(
|
|
13508
|
+
await rm5(path41.join(this.app.cwdPath, this.androidRootPath), { recursive: true, force: true });
|
|
13516
13509
|
}
|
|
13517
13510
|
const project = this.project;
|
|
13518
13511
|
await this.project.load();
|
|
@@ -13635,7 +13628,7 @@ ${textError instanceof Error ? textError.message : ""}`);
|
|
|
13635
13628
|
const xcodebuildArgs = noAllowProvisioningUpdates ? command.xcodebuildArgs : [...command.xcodebuildArgs.slice(0, -1), "-allowProvisioningUpdates", ...command.xcodebuildArgs.slice(-1)];
|
|
13636
13629
|
try {
|
|
13637
13630
|
await this.#spawn("xcodebuild", xcodebuildArgs, {
|
|
13638
|
-
cwd:
|
|
13631
|
+
cwd: path41.join(this.app.cwdPath, this.iosProjectPath),
|
|
13639
13632
|
env: mobileEnv
|
|
13640
13633
|
});
|
|
13641
13634
|
const devicectlId = runTarget.devicectlId ?? runTarget.id;
|
|
@@ -13660,7 +13653,7 @@ ${textError instanceof Error ? textError.message : ""}`);
|
|
|
13660
13653
|
return isRecord2(this.target.ios) && typeof this.target.ios.scheme === "string" ? this.target.ios.scheme : "App";
|
|
13661
13654
|
}
|
|
13662
13655
|
async#getIosDevelopmentTeam() {
|
|
13663
|
-
const pbxprojPath =
|
|
13656
|
+
const pbxprojPath = path41.join(this.app.cwdPath, this.iosProjectPath, "App.xcodeproj/project.pbxproj");
|
|
13664
13657
|
if (!await Bun.file(pbxprojPath).exists())
|
|
13665
13658
|
return;
|
|
13666
13659
|
return (await Bun.file(pbxprojPath).text()).match(/DEVELOPMENT_TEAM = ([^;]+);/)?.[1]?.trim();
|
|
@@ -13686,7 +13679,7 @@ ${error.message}`;
|
|
|
13686
13679
|
await this.#spawnMobile("npx", ["cap", "sync", "android"], { operation, env });
|
|
13687
13680
|
}
|
|
13688
13681
|
async#updateAndroidBuildTypes() {
|
|
13689
|
-
const appGradle = await FileEditor.create(
|
|
13682
|
+
const appGradle = await FileEditor.create(path41.join(this.app.cwdPath, this.androidRootPath, "app/build.gradle"));
|
|
13690
13683
|
const buildTypesBlock = `
|
|
13691
13684
|
debug {
|
|
13692
13685
|
applicationIdSuffix ".debug"
|
|
@@ -13730,7 +13723,7 @@ ${error.message}`;
|
|
|
13730
13723
|
const gradleCommand = isWindows ? "gradlew.bat" : "./gradlew";
|
|
13731
13724
|
await this.app.spawn(gradleCommand, [assembleType === "apk" ? "assembleRelease" : "bundleRelease"], {
|
|
13732
13725
|
stdio: "inherit",
|
|
13733
|
-
cwd:
|
|
13726
|
+
cwd: path41.join(this.app.cwdPath, this.androidRootPath),
|
|
13734
13727
|
env: await this.#commandEnv("release", env)
|
|
13735
13728
|
});
|
|
13736
13729
|
}
|
|
@@ -13738,10 +13731,10 @@ ${error.message}`;
|
|
|
13738
13731
|
await this.#spawnMobile("npx", ["cap", "open", "android"], { operation: "local", env: "local" });
|
|
13739
13732
|
}
|
|
13740
13733
|
async#ensureAndroidAssetsDir() {
|
|
13741
|
-
await mkdir11(
|
|
13734
|
+
await mkdir11(path41.join(this.app.cwdPath, this.androidAssetsPath), { recursive: true });
|
|
13742
13735
|
}
|
|
13743
13736
|
async#ensureAndroidDebugKeystore() {
|
|
13744
|
-
const keystorePath =
|
|
13737
|
+
const keystorePath = path41.join(this.app.cwdPath, this.androidRootPath, "app/debug.keystore");
|
|
13745
13738
|
if (await Bun.file(keystorePath).exists())
|
|
13746
13739
|
return;
|
|
13747
13740
|
await this.#spawn("keytool", [
|
|
@@ -13780,7 +13773,7 @@ ${error.message}`;
|
|
|
13780
13773
|
await this.#spawnMobile("npx", args, { operation, env }, { stdio: "inherit" });
|
|
13781
13774
|
}
|
|
13782
13775
|
async#assertAndroidReleaseSigningConfig() {
|
|
13783
|
-
const gradlePropertiesPath =
|
|
13776
|
+
const gradlePropertiesPath = path41.join(this.app.cwdPath, this.androidRootPath, "gradle.properties");
|
|
13784
13777
|
const gradleProperties = await Bun.file(gradlePropertiesPath).exists() ? await Bun.file(gradlePropertiesPath).text() : "";
|
|
13785
13778
|
const missingKeys = getMissingAndroidReleaseSigningKeys({ gradleProperties });
|
|
13786
13779
|
if (missingKeys.length > 0)
|
|
@@ -13807,12 +13800,12 @@ ${error.message}`;
|
|
|
13807
13800
|
await this.#prepareAndroid({ operation: "release", env: "main" });
|
|
13808
13801
|
}
|
|
13809
13802
|
async prepareWww() {
|
|
13810
|
-
const htmlSource =
|
|
13803
|
+
const htmlSource = path41.join(this.app.dist.cwdPath, "csr", targetHtmlFilename(this.target));
|
|
13811
13804
|
if (!await Bun.file(htmlSource).exists())
|
|
13812
13805
|
throw new Error(`CSR html for mobile target '${this.target.name}' not found: ${htmlSource}`);
|
|
13813
13806
|
await rm5(this.targetWebRoot, { recursive: true, force: true });
|
|
13814
13807
|
await mkdir11(this.targetWebRoot, { recursive: true });
|
|
13815
|
-
await Bun.write(
|
|
13808
|
+
await Bun.write(path41.join(this.targetWebRoot, "index.html"), this.#injectMobileTargetMeta(await Bun.file(htmlSource).text()));
|
|
13816
13809
|
}
|
|
13817
13810
|
#injectMobileTargetMeta(html) {
|
|
13818
13811
|
const basePath2 = this.target.basePath?.replace(/^\/+|\/+$/g, "") ?? "";
|
|
@@ -13832,7 +13825,7 @@ ${error.message}`;
|
|
|
13832
13825
|
});
|
|
13833
13826
|
const content = `${JSON.stringify(config, null, 2)}
|
|
13834
13827
|
`;
|
|
13835
|
-
await Bun.write(
|
|
13828
|
+
await Bun.write(path41.join(this.targetRoot, "capacitor.config.json"), content);
|
|
13836
13829
|
return content;
|
|
13837
13830
|
}
|
|
13838
13831
|
async#prepareTargetAssets() {
|
|
@@ -13840,11 +13833,11 @@ ${error.message}`;
|
|
|
13840
13833
|
return;
|
|
13841
13834
|
await mkdir11(this.targetAssetRoot, { recursive: true });
|
|
13842
13835
|
if (this.target.assets.icon)
|
|
13843
|
-
await cp2(
|
|
13836
|
+
await cp2(path41.join(this.app.cwdPath, this.target.assets.icon), path41.join(this.targetAssetRoot, "icon.png"), {
|
|
13844
13837
|
force: true
|
|
13845
13838
|
});
|
|
13846
13839
|
if (this.target.assets.splash)
|
|
13847
|
-
await cp2(
|
|
13840
|
+
await cp2(path41.join(this.app.cwdPath, this.target.assets.splash), path41.join(this.targetAssetRoot, "splash.png"), {
|
|
13848
13841
|
force: true
|
|
13849
13842
|
});
|
|
13850
13843
|
}
|
|
@@ -13852,11 +13845,11 @@ ${error.message}`;
|
|
|
13852
13845
|
const files = this.target.files?.[platform];
|
|
13853
13846
|
if (!files)
|
|
13854
13847
|
return;
|
|
13855
|
-
const platformRoot =
|
|
13848
|
+
const platformRoot = path41.join(this.app.cwdPath, platform === "ios" ? this.iosRootPath : this.androidRootPath);
|
|
13856
13849
|
await Promise.all(Object.entries(files).map(async ([to, from]) => {
|
|
13857
|
-
const targetPath =
|
|
13858
|
-
await mkdir11(
|
|
13859
|
-
await cp2(
|
|
13850
|
+
const targetPath = path41.join(platformRoot, to);
|
|
13851
|
+
await mkdir11(path41.dirname(targetPath), { recursive: true });
|
|
13852
|
+
await cp2(path41.join(this.app.cwdPath, from), targetPath, { force: true });
|
|
13860
13853
|
}));
|
|
13861
13854
|
}
|
|
13862
13855
|
async#generateAssets({ operation, env }) {
|
|
@@ -13866,7 +13859,7 @@ ${error.message}`;
|
|
|
13866
13859
|
"@capacitor/assets",
|
|
13867
13860
|
"generate",
|
|
13868
13861
|
"--assetPath",
|
|
13869
|
-
|
|
13862
|
+
path41.posix.join(this.targetRootPath, "assets"),
|
|
13870
13863
|
"--iosProject",
|
|
13871
13864
|
this.iosProjectPath,
|
|
13872
13865
|
"--androidProject",
|
|
@@ -14076,7 +14069,7 @@ var Pkg = createInternalArgToken("Pkg");
|
|
|
14076
14069
|
var Module = createInternalArgToken("Module");
|
|
14077
14070
|
var Workspace = createInternalArgToken("Workspace");
|
|
14078
14071
|
// pkgs/@akanjs/devkit/commandDecorators/command.ts
|
|
14079
|
-
import
|
|
14072
|
+
import path42 from "path";
|
|
14080
14073
|
import { confirm, input as input2, select as select3 } from "@inquirer/prompts";
|
|
14081
14074
|
import { Logger as Logger10 } from "akanjs/common";
|
|
14082
14075
|
import chalk6 from "chalk";
|
|
@@ -14580,7 +14573,7 @@ var runCommands = async (...commands) => {
|
|
|
14580
14573
|
process.exit(1);
|
|
14581
14574
|
});
|
|
14582
14575
|
const __dirname2 = getDirname(import.meta.url);
|
|
14583
|
-
const packageJsonCandidates = [`${
|
|
14576
|
+
const packageJsonCandidates = [`${path42.dirname(Bun.main)}/package.json`, `${__dirname2}/../package.json`];
|
|
14584
14577
|
let cliPackageJson = null;
|
|
14585
14578
|
for (const packageJsonPath of packageJsonCandidates) {
|
|
14586
14579
|
if (!await FileSys.fileExists(packageJsonPath))
|
|
@@ -14860,8 +14853,8 @@ var scanModuleSpecifiers = (source2, filePath, includeExports) => {
|
|
|
14860
14853
|
return importSpecifiers;
|
|
14861
14854
|
};
|
|
14862
14855
|
var parseTsConfig = (tsConfigPath = "./tsconfig.json") => {
|
|
14863
|
-
const configFile = ts10.readConfigFile(tsConfigPath, (
|
|
14864
|
-
return ts10.sys.readFile(
|
|
14856
|
+
const configFile = ts10.readConfigFile(tsConfigPath, (path43) => {
|
|
14857
|
+
return ts10.sys.readFile(path43);
|
|
14865
14858
|
});
|
|
14866
14859
|
return ts10.parseJsonConfigFileContent(configFile.config, ts10.sys, realpathSync(tsConfigPath).replace(/[^/\\]+$/, ""));
|
|
14867
14860
|
};
|
|
@@ -15077,6 +15070,571 @@ ${document}
|
|
|
15077
15070
|
`;
|
|
15078
15071
|
}
|
|
15079
15072
|
}
|
|
15073
|
+
// pkgs/@akanjs/devkit/qualityScanner.ts
|
|
15074
|
+
import { createHash } from "crypto";
|
|
15075
|
+
import { readdir as readdir3, readFile as readFile2, stat as stat4 } from "fs/promises";
|
|
15076
|
+
import path43 from "path";
|
|
15077
|
+
import ignore2 from "ignore";
|
|
15078
|
+
import ts11 from "typescript";
|
|
15079
|
+
var MAX_FILE_LINES = 2000;
|
|
15080
|
+
var PLACEHOLDER_EXPORT_NAMES = new Set([
|
|
15081
|
+
"aa",
|
|
15082
|
+
"dumb",
|
|
15083
|
+
"dumb2",
|
|
15084
|
+
"someBaseLogic",
|
|
15085
|
+
"someCommonLogic",
|
|
15086
|
+
"someFrontendLogic",
|
|
15087
|
+
"someBackendLogic"
|
|
15088
|
+
]);
|
|
15089
|
+
var SUGGESTED_RULES = [
|
|
15090
|
+
"Keep generated scanSync index files out of hand-written changes; generated indexes should only contain one-depth export statements.",
|
|
15091
|
+
"Generated Akan index files should not contain placeholder exports such as aa, dumb, dumb2, or some*Logic.",
|
|
15092
|
+
"Dictionary text should not contain scaffold wording, misspellings, or stale copied domain nouns.",
|
|
15093
|
+
"Warn earlier on very long Akan files: 500 lines for services, 800 lines for Template/Zone files, and 1000 lines for Util files.",
|
|
15094
|
+
"Global declarations, Window augmentation, and prototype mutation should stay in explicitly approved low-level integration files.",
|
|
15095
|
+
"Keep app root folders small and conventional: application code belongs under common, env, lib, page, private, public, script, srvkit, ui, or webkit.",
|
|
15096
|
+
"Keep apps/*/lib and libs/*/lib root files limited to generated support facets such as cnst.ts, db.ts, dict.ts, sig.ts, srv.ts, st.ts, useClient.ts, and useServer.ts.",
|
|
15097
|
+
"Use domain module folders consistently: lib/<model> for database modules, lib/_<service> for service modules, and lib/__scalar/<scalar> for scalar modules.",
|
|
15098
|
+
"Keep module UI filenames predictable: database modules use <Model>.Template/Unit/Util/View/Zone.tsx, service modules use <Service>.Util/Zone.tsx, and scalar modules use <Scalar>.Template/Unit.tsx.",
|
|
15099
|
+
"Move shared app utilities to apps/*/common instead of creating apps/*/base.",
|
|
15100
|
+
"Avoid large mixed-purpose class files; class export files should import helpers from neighboring utility files instead of declaring them inline."
|
|
15101
|
+
];
|
|
15102
|
+
var APP_ROOT_FILES = new Set([
|
|
15103
|
+
"akan.app.json",
|
|
15104
|
+
"akan.config.ts",
|
|
15105
|
+
"capacitor.config.ts",
|
|
15106
|
+
"client.ts",
|
|
15107
|
+
"main.ts",
|
|
15108
|
+
"package.json",
|
|
15109
|
+
"server.ts",
|
|
15110
|
+
"tsconfig.json"
|
|
15111
|
+
]);
|
|
15112
|
+
var LIB_ROOT_FILES = new Set([
|
|
15113
|
+
"cnst.ts",
|
|
15114
|
+
"db.ts",
|
|
15115
|
+
"dict.ts",
|
|
15116
|
+
"option.ts",
|
|
15117
|
+
"sig.ts",
|
|
15118
|
+
"srv.ts",
|
|
15119
|
+
"st.ts",
|
|
15120
|
+
"useClient.ts",
|
|
15121
|
+
"useServer.ts"
|
|
15122
|
+
]);
|
|
15123
|
+
var CONVENTION_SUFFIXES = [
|
|
15124
|
+
".constant.ts",
|
|
15125
|
+
".dictionary.ts",
|
|
15126
|
+
".document.ts",
|
|
15127
|
+
".service.ts",
|
|
15128
|
+
".signal.ts",
|
|
15129
|
+
".store.ts"
|
|
15130
|
+
];
|
|
15131
|
+
|
|
15132
|
+
class AkanQualityScanner {
|
|
15133
|
+
async scan(workspaceRoot) {
|
|
15134
|
+
const targetFiles = await this.#collectTargetFiles(workspaceRoot);
|
|
15135
|
+
const sourceFiles = await Promise.all(targetFiles.map((file) => this.#readSourceFile(workspaceRoot, file)));
|
|
15136
|
+
const warnings = [
|
|
15137
|
+
...this.#scanGlobalQuality(sourceFiles),
|
|
15138
|
+
...sourceFiles.flatMap((sourceFile2) => this.#scanSingleFileQuality(sourceFile2)),
|
|
15139
|
+
...sourceFiles.flatMap((sourceFile2) => this.#scanConventionQuality(sourceFile2)),
|
|
15140
|
+
...sourceFiles.flatMap((sourceFile2) => this.#scanLayoutQuality(sourceFile2))
|
|
15141
|
+
];
|
|
15142
|
+
return {
|
|
15143
|
+
workspaceRoot,
|
|
15144
|
+
scannedFiles: sourceFiles.length,
|
|
15145
|
+
warnings: warnings.sort(compareWarnings),
|
|
15146
|
+
suggestedRules: SUGGESTED_RULES
|
|
15147
|
+
};
|
|
15148
|
+
}
|
|
15149
|
+
async#collectTargetFiles(workspaceRoot) {
|
|
15150
|
+
const ignoreFilter = ignore2().add(await this.#readGitIgnore(workspaceRoot));
|
|
15151
|
+
const files = [];
|
|
15152
|
+
for (const targetRoot of ["apps", "libs"]) {
|
|
15153
|
+
const absoluteTargetRoot = path43.join(workspaceRoot, targetRoot);
|
|
15154
|
+
if (!await isDirectory(absoluteTargetRoot))
|
|
15155
|
+
continue;
|
|
15156
|
+
await this.#walkTargetFiles(workspaceRoot, absoluteTargetRoot, ignoreFilter, files);
|
|
15157
|
+
}
|
|
15158
|
+
return files.sort();
|
|
15159
|
+
}
|
|
15160
|
+
async#readGitIgnore(workspaceRoot) {
|
|
15161
|
+
const gitIgnorePath = path43.join(workspaceRoot, ".gitignore");
|
|
15162
|
+
if (!await Bun.file(gitIgnorePath).exists())
|
|
15163
|
+
return [];
|
|
15164
|
+
return (await readFile2(gitIgnorePath, "utf8")).split(/\r?\n/);
|
|
15165
|
+
}
|
|
15166
|
+
async#walkTargetFiles(workspaceRoot, currentPath, ignoreFilter, files) {
|
|
15167
|
+
const entries = await readdir3(currentPath, { withFileTypes: true });
|
|
15168
|
+
for (const entry of entries) {
|
|
15169
|
+
const absolutePath = path43.join(currentPath, entry.name);
|
|
15170
|
+
const relativePath = toPosix(path43.relative(workspaceRoot, absolutePath));
|
|
15171
|
+
if (shouldSkipPath(relativePath, entry.isDirectory(), ignoreFilter))
|
|
15172
|
+
continue;
|
|
15173
|
+
if (entry.isDirectory()) {
|
|
15174
|
+
await this.#walkTargetFiles(workspaceRoot, absolutePath, ignoreFilter, files);
|
|
15175
|
+
continue;
|
|
15176
|
+
}
|
|
15177
|
+
if ((relativePath.endsWith(".ts") || relativePath.endsWith(".tsx")) && !relativePath.endsWith(".d.ts")) {
|
|
15178
|
+
files.push(relativePath);
|
|
15179
|
+
}
|
|
15180
|
+
}
|
|
15181
|
+
}
|
|
15182
|
+
async#readSourceFile(workspaceRoot, file) {
|
|
15183
|
+
const absolutePath = path43.join(workspaceRoot, file);
|
|
15184
|
+
const content = await readFile2(absolutePath, "utf8");
|
|
15185
|
+
return {
|
|
15186
|
+
file,
|
|
15187
|
+
absolutePath,
|
|
15188
|
+
content,
|
|
15189
|
+
sourceFile: ts11.createSourceFile(file, content, ts11.ScriptTarget.Latest, true, getScriptKind(file))
|
|
15190
|
+
};
|
|
15191
|
+
}
|
|
15192
|
+
#scanGlobalQuality(sourceFiles) {
|
|
15193
|
+
const exportedFunctionLikes = sourceFiles.flatMap((sourceFile2) => getExportedFunctionLikes(sourceFile2));
|
|
15194
|
+
const warnings = [];
|
|
15195
|
+
for (const [name, declarations] of groupBy(exportedFunctionLikes, (declaration) => declaration.name)) {
|
|
15196
|
+
if (declarations.length < 2)
|
|
15197
|
+
continue;
|
|
15198
|
+
warnings.push({
|
|
15199
|
+
rule: "akan.global.duplicate-exported-function-name",
|
|
15200
|
+
scope: "global",
|
|
15201
|
+
severity: "warning",
|
|
15202
|
+
message: `Exported function or class name "${name}" is declared in ${declarations.length} files.`,
|
|
15203
|
+
locations: declarations.map(({ file, line }) => ({ file, line }))
|
|
15204
|
+
});
|
|
15205
|
+
}
|
|
15206
|
+
const declarationsWithBody = exportedFunctionLikes.filter((declaration) => declaration.bodyFingerprint);
|
|
15207
|
+
for (const [fingerprint, declarations] of groupBy(declarationsWithBody, (declaration) => declaration.bodyFingerprint ?? "")) {
|
|
15208
|
+
const uniqueNames = new Set(declarations.map((declaration) => declaration.name));
|
|
15209
|
+
if (declarations.length < 2 || uniqueNames.size < 2 || fingerprint === "")
|
|
15210
|
+
continue;
|
|
15211
|
+
warnings.push({
|
|
15212
|
+
rule: "akan.global.duplicate-exported-function-body",
|
|
15213
|
+
scope: "global",
|
|
15214
|
+
severity: "warning",
|
|
15215
|
+
message: `Exported functions/classes share the same implementation body: ${declarations.map((declaration) => declaration.name).join(", ")}.`,
|
|
15216
|
+
locations: declarations.map(({ file, line }) => ({ file, line }))
|
|
15217
|
+
});
|
|
15218
|
+
}
|
|
15219
|
+
return warnings;
|
|
15220
|
+
}
|
|
15221
|
+
#scanSingleFileQuality(sourceFile2) {
|
|
15222
|
+
const warnings = [];
|
|
15223
|
+
const lineCount = sourceFile2.content.split(/\r?\n/).length;
|
|
15224
|
+
const recommendedLineLimit = getRecommendedLineLimit(sourceFile2.file);
|
|
15225
|
+
if (recommendedLineLimit && lineCount > recommendedLineLimit) {
|
|
15226
|
+
warnings.push({
|
|
15227
|
+
rule: "akan.file.recommended-max-lines",
|
|
15228
|
+
scope: "file",
|
|
15229
|
+
severity: "warning",
|
|
15230
|
+
file: sourceFile2.file,
|
|
15231
|
+
message: `File has ${lineCount} lines. Recommended limit for this file type is ${recommendedLineLimit} lines.`
|
|
15232
|
+
});
|
|
15233
|
+
}
|
|
15234
|
+
if (lineCount > MAX_FILE_LINES) {
|
|
15235
|
+
warnings.push({
|
|
15236
|
+
rule: "akan.file.max-lines",
|
|
15237
|
+
scope: "file",
|
|
15238
|
+
severity: "warning",
|
|
15239
|
+
file: sourceFile2.file,
|
|
15240
|
+
message: `File has ${lineCount} lines. Keep single files under ${MAX_FILE_LINES} lines.`
|
|
15241
|
+
});
|
|
15242
|
+
}
|
|
15243
|
+
warnings.push(...getPlaceholderExportWarnings(sourceFile2));
|
|
15244
|
+
warnings.push(...getDictionaryTextWarnings(sourceFile2));
|
|
15245
|
+
warnings.push(...getGlobalMutationWarnings(sourceFile2));
|
|
15246
|
+
const exportedClassNames = getExportedClassNames(sourceFile2.sourceFile);
|
|
15247
|
+
if (exportedClassNames.length === 0)
|
|
15248
|
+
return warnings;
|
|
15249
|
+
const allowedInterfaceNames = new Set(exportedClassNames.map((name) => `${name}Options`));
|
|
15250
|
+
for (const declaration of getTopLevelDeclarations(sourceFile2)) {
|
|
15251
|
+
if (declaration.kind === "class" && exportedClassNames.includes(declaration.name))
|
|
15252
|
+
continue;
|
|
15253
|
+
if (declaration.kind === "interface" && allowedInterfaceNames.has(declaration.name))
|
|
15254
|
+
continue;
|
|
15255
|
+
warnings.push({
|
|
15256
|
+
rule: "akan.file.class-export-global-declaration",
|
|
15257
|
+
scope: "file",
|
|
15258
|
+
severity: "warning",
|
|
15259
|
+
file: sourceFile2.file,
|
|
15260
|
+
line: declaration.line,
|
|
15261
|
+
message: `Class export files should not declare top-level ${declaration.kind} "${declaration.name}". Move helpers to another file and import them.`
|
|
15262
|
+
});
|
|
15263
|
+
}
|
|
15264
|
+
return warnings;
|
|
15265
|
+
}
|
|
15266
|
+
#scanConventionQuality(sourceFile2) {
|
|
15267
|
+
const suffix = CONVENTION_SUFFIXES.find((candidate) => sourceFile2.file.endsWith(candidate));
|
|
15268
|
+
if (!suffix)
|
|
15269
|
+
return [];
|
|
15270
|
+
const modelName = toPascalCase(path43.basename(sourceFile2.file, suffix));
|
|
15271
|
+
const warnings = [];
|
|
15272
|
+
for (const declaration of getTopLevelDeclarations(sourceFile2)) {
|
|
15273
|
+
if (isAllowedConventionDeclaration(suffix, modelName, declaration))
|
|
15274
|
+
continue;
|
|
15275
|
+
warnings.push({
|
|
15276
|
+
rule: `akan.convention${suffix.replace(".ts", "")}`,
|
|
15277
|
+
scope: "convention",
|
|
15278
|
+
severity: "warning",
|
|
15279
|
+
file: sourceFile2.file,
|
|
15280
|
+
line: declaration.line,
|
|
15281
|
+
message: `${path43.basename(sourceFile2.file)} should not declare top-level ${declaration.kind} "${declaration.name}". Allowed declarations: ${getConventionDescription(suffix, modelName)}.`
|
|
15282
|
+
});
|
|
15283
|
+
}
|
|
15284
|
+
return warnings;
|
|
15285
|
+
}
|
|
15286
|
+
#scanLayoutQuality(sourceFile2) {
|
|
15287
|
+
const segments = sourceFile2.file.split("/");
|
|
15288
|
+
const warnings = [];
|
|
15289
|
+
if (segments[0] === "apps" && segments.length === 3 && !APP_ROOT_FILES.has(segments[2])) {
|
|
15290
|
+
warnings.push({
|
|
15291
|
+
rule: "akan.layout.app-root-file",
|
|
15292
|
+
scope: "layout",
|
|
15293
|
+
severity: "warning",
|
|
15294
|
+
file: sourceFile2.file,
|
|
15295
|
+
message: `Unexpected app root file "${segments[2]}". Keep application code in conventional app folders.`
|
|
15296
|
+
});
|
|
15297
|
+
}
|
|
15298
|
+
const libRootFile = getLibRootFile(sourceFile2.file);
|
|
15299
|
+
if (libRootFile && !LIB_ROOT_FILES.has(libRootFile)) {
|
|
15300
|
+
warnings.push({
|
|
15301
|
+
rule: "akan.layout.lib-root-file",
|
|
15302
|
+
scope: "layout",
|
|
15303
|
+
severity: "warning",
|
|
15304
|
+
file: sourceFile2.file,
|
|
15305
|
+
message: `Unexpected lib root file "${libRootFile}". Keep direct lib root files limited to generated support facets.`
|
|
15306
|
+
});
|
|
15307
|
+
}
|
|
15308
|
+
const moduleUiWarning = getModuleUiWarning(sourceFile2.file);
|
|
15309
|
+
if (moduleUiWarning)
|
|
15310
|
+
warnings.push(moduleUiWarning);
|
|
15311
|
+
return warnings;
|
|
15312
|
+
}
|
|
15313
|
+
}
|
|
15314
|
+
function formatQualityScanResult(result) {
|
|
15315
|
+
const sections = [
|
|
15316
|
+
"Akan Code Quality Scan",
|
|
15317
|
+
`workspace: ${result.workspaceRoot}`,
|
|
15318
|
+
`scanned files: ${result.scannedFiles}`,
|
|
15319
|
+
`warnings: ${result.warnings.length}`,
|
|
15320
|
+
"",
|
|
15321
|
+
"Warnings:",
|
|
15322
|
+
"",
|
|
15323
|
+
...formatQualityWarnings(result.warnings),
|
|
15324
|
+
"",
|
|
15325
|
+
"Suggested quality rules:",
|
|
15326
|
+
"",
|
|
15327
|
+
...result.suggestedRules.map((rule) => ` - ${rule}`)
|
|
15328
|
+
];
|
|
15329
|
+
return sections.join(`
|
|
15330
|
+
`);
|
|
15331
|
+
}
|
|
15332
|
+
function formatQualityWarnings(warnings) {
|
|
15333
|
+
if (warnings.length === 0)
|
|
15334
|
+
return ["No warnings found."];
|
|
15335
|
+
return warnings.flatMap((warning) => {
|
|
15336
|
+
const location = formatQualityLocation(warning.file, warning.line);
|
|
15337
|
+
const lines = [`${location} - warning ${warning.rule}: ${warning.message}`];
|
|
15338
|
+
if (warning.locations?.length) {
|
|
15339
|
+
lines.push(...warning.locations.map(({ file, line }) => ` note: related location ${formatQualityLocation(file, line)}`));
|
|
15340
|
+
}
|
|
15341
|
+
return lines;
|
|
15342
|
+
});
|
|
15343
|
+
}
|
|
15344
|
+
function formatQualityLocation(file, line) {
|
|
15345
|
+
return `${file ?? "<global>"}:${line ?? 1}:1`;
|
|
15346
|
+
}
|
|
15347
|
+
function getExportedFunctionLikes(sourceFile2) {
|
|
15348
|
+
const declarations = [];
|
|
15349
|
+
for (const statement of sourceFile2.sourceFile.statements) {
|
|
15350
|
+
if (ts11.isFunctionDeclaration(statement) && statement.name && isExported(statement)) {
|
|
15351
|
+
declarations.push({
|
|
15352
|
+
name: statement.name.text,
|
|
15353
|
+
kind: "function",
|
|
15354
|
+
file: sourceFile2.file,
|
|
15355
|
+
line: getLine(sourceFile2.sourceFile, statement),
|
|
15356
|
+
bodyFingerprint: getBodyFingerprint(sourceFile2.sourceFile, statement.body)
|
|
15357
|
+
});
|
|
15358
|
+
}
|
|
15359
|
+
if (ts11.isClassDeclaration(statement) && statement.name && isExported(statement)) {
|
|
15360
|
+
declarations.push({
|
|
15361
|
+
name: statement.name.text,
|
|
15362
|
+
kind: "class",
|
|
15363
|
+
file: sourceFile2.file,
|
|
15364
|
+
line: getLine(sourceFile2.sourceFile, statement),
|
|
15365
|
+
bodyFingerprint: getBodyFingerprint(sourceFile2.sourceFile, statement)
|
|
15366
|
+
});
|
|
15367
|
+
}
|
|
15368
|
+
if (ts11.isVariableStatement(statement) && isExported(statement)) {
|
|
15369
|
+
for (const declaration of statement.declarationList.declarations) {
|
|
15370
|
+
if (!ts11.isIdentifier(declaration.name) || !isFunctionLikeInitializer(declaration.initializer))
|
|
15371
|
+
continue;
|
|
15372
|
+
declarations.push({
|
|
15373
|
+
name: declaration.name.text,
|
|
15374
|
+
kind: "function-variable",
|
|
15375
|
+
file: sourceFile2.file,
|
|
15376
|
+
line: getLine(sourceFile2.sourceFile, declaration),
|
|
15377
|
+
bodyFingerprint: getBodyFingerprint(sourceFile2.sourceFile, declaration.initializer)
|
|
15378
|
+
});
|
|
15379
|
+
}
|
|
15380
|
+
}
|
|
15381
|
+
}
|
|
15382
|
+
return declarations;
|
|
15383
|
+
}
|
|
15384
|
+
function getExportedClassNames(sourceFile2) {
|
|
15385
|
+
return sourceFile2.statements.filter((statement) => ts11.isClassDeclaration(statement) && !!statement.name).filter((statement) => isExported(statement)).map((statement) => statement.name.text);
|
|
15386
|
+
}
|
|
15387
|
+
function getPlaceholderExportWarnings(sourceFile2) {
|
|
15388
|
+
if (!sourceFile2.file.endsWith("/index.ts") && !sourceFile2.file.endsWith("/index.tsx"))
|
|
15389
|
+
return [];
|
|
15390
|
+
return getTopLevelDeclarations(sourceFile2).filter((declaration) => declaration.exported && PLACEHOLDER_EXPORT_NAMES.has(declaration.name)).map((declaration) => ({
|
|
15391
|
+
rule: "akan.file.placeholder-export",
|
|
15392
|
+
scope: "file",
|
|
15393
|
+
severity: "warning",
|
|
15394
|
+
file: sourceFile2.file,
|
|
15395
|
+
line: declaration.line,
|
|
15396
|
+
message: `Generated or barrel index file should not export placeholder "${declaration.name}".`
|
|
15397
|
+
}));
|
|
15398
|
+
}
|
|
15399
|
+
function getDictionaryTextWarnings(sourceFile2) {
|
|
15400
|
+
if (!sourceFile2.file.endsWith(".dictionary.ts"))
|
|
15401
|
+
return [];
|
|
15402
|
+
const stalePatterns = [
|
|
15403
|
+
{ pattern: /\b[A-Z][A-Za-z0-9]* description\b/, label: "scaffold description text" },
|
|
15404
|
+
{ pattern: /settting/, label: "misspelling: settting" },
|
|
15405
|
+
{ pattern: /\uBC30\uB108 \uC218/, label: "stale copied Korean domain noun: \uBC30\uB108 \uC218" }
|
|
15406
|
+
];
|
|
15407
|
+
return stalePatterns.flatMap(({ pattern, label }) => findPatternLines(sourceFile2.content, pattern).map((line) => ({
|
|
15408
|
+
rule: "akan.file.dictionary-stale-text",
|
|
15409
|
+
scope: "file",
|
|
15410
|
+
severity: "warning",
|
|
15411
|
+
file: sourceFile2.file,
|
|
15412
|
+
line,
|
|
15413
|
+
message: `Dictionary text appears to contain ${label}.`
|
|
15414
|
+
})));
|
|
15415
|
+
}
|
|
15416
|
+
function getGlobalMutationWarnings(sourceFile2) {
|
|
15417
|
+
const warnings = [];
|
|
15418
|
+
for (const statement of sourceFile2.sourceFile.statements) {
|
|
15419
|
+
if (ts11.isModuleDeclaration(statement) && statement.name.getText(sourceFile2.sourceFile) === "global") {
|
|
15420
|
+
warnings.push({
|
|
15421
|
+
rule: "akan.file.global-declaration",
|
|
15422
|
+
scope: "file",
|
|
15423
|
+
severity: "warning",
|
|
15424
|
+
file: sourceFile2.file,
|
|
15425
|
+
line: getLine(sourceFile2.sourceFile, statement),
|
|
15426
|
+
message: "Global declarations require an explicit low-level integration allowlist."
|
|
15427
|
+
});
|
|
15428
|
+
}
|
|
15429
|
+
if (ts11.isInterfaceDeclaration(statement) && statement.name.text === "Window") {
|
|
15430
|
+
warnings.push({
|
|
15431
|
+
rule: "akan.file.window-augmentation",
|
|
15432
|
+
scope: "file",
|
|
15433
|
+
severity: "warning",
|
|
15434
|
+
file: sourceFile2.file,
|
|
15435
|
+
line: getLine(sourceFile2.sourceFile, statement),
|
|
15436
|
+
message: "Window augmentation should be isolated to approved browser integration files."
|
|
15437
|
+
});
|
|
15438
|
+
}
|
|
15439
|
+
if (ts11.isExpressionStatement(statement) && statement.expression.getText(sourceFile2.sourceFile).includes(".prototype.")) {
|
|
15440
|
+
warnings.push({
|
|
15441
|
+
rule: "akan.file.prototype-mutation",
|
|
15442
|
+
scope: "file",
|
|
15443
|
+
severity: "warning",
|
|
15444
|
+
file: sourceFile2.file,
|
|
15445
|
+
line: getLine(sourceFile2.sourceFile, statement),
|
|
15446
|
+
message: "Prototype mutation should be avoided or isolated to approved low-level integration files."
|
|
15447
|
+
});
|
|
15448
|
+
}
|
|
15449
|
+
}
|
|
15450
|
+
return warnings;
|
|
15451
|
+
}
|
|
15452
|
+
function getTopLevelDeclarations(sourceFile2) {
|
|
15453
|
+
return sourceFile2.sourceFile.statements.flatMap((statement) => getTopLevelDeclaration(sourceFile2.sourceFile, statement));
|
|
15454
|
+
}
|
|
15455
|
+
function getTopLevelDeclaration(sourceFile2, statement) {
|
|
15456
|
+
const line = getLine(sourceFile2, statement);
|
|
15457
|
+
if (ts11.isClassDeclaration(statement) && statement.name) {
|
|
15458
|
+
return [{ name: statement.name.text, kind: "class", line, exported: isExported(statement), node: statement }];
|
|
15459
|
+
}
|
|
15460
|
+
if (ts11.isFunctionDeclaration(statement) && statement.name) {
|
|
15461
|
+
return [{ name: statement.name.text, kind: "function", line, exported: isExported(statement), node: statement }];
|
|
15462
|
+
}
|
|
15463
|
+
if (ts11.isInterfaceDeclaration(statement)) {
|
|
15464
|
+
return [{ name: statement.name.text, kind: "interface", line, exported: isExported(statement), node: statement }];
|
|
15465
|
+
}
|
|
15466
|
+
if (ts11.isTypeAliasDeclaration(statement)) {
|
|
15467
|
+
return [{ name: statement.name.text, kind: "type", line, exported: isExported(statement), node: statement }];
|
|
15468
|
+
}
|
|
15469
|
+
if (ts11.isEnumDeclaration(statement)) {
|
|
15470
|
+
return [{ name: statement.name.text, kind: "enum", line, exported: isExported(statement), node: statement }];
|
|
15471
|
+
}
|
|
15472
|
+
if (ts11.isVariableStatement(statement)) {
|
|
15473
|
+
return statement.declarationList.declarations.filter((declaration) => ts11.isIdentifier(declaration.name)).map((declaration) => ({
|
|
15474
|
+
name: declaration.name.text,
|
|
15475
|
+
kind: "variable",
|
|
15476
|
+
line: getLine(sourceFile2, declaration),
|
|
15477
|
+
exported: isExported(statement),
|
|
15478
|
+
node: statement
|
|
15479
|
+
}));
|
|
15480
|
+
}
|
|
15481
|
+
if (ts11.isExportDeclaration(statement)) {
|
|
15482
|
+
return [{ name: "export declaration", kind: "export", line, exported: true, node: statement }];
|
|
15483
|
+
}
|
|
15484
|
+
return [];
|
|
15485
|
+
}
|
|
15486
|
+
function isAllowedConventionDeclaration(suffix, modelName, declaration) {
|
|
15487
|
+
if (suffix === ".dictionary.ts")
|
|
15488
|
+
return isExportedConst(declaration) && declaration.name === "dictionary";
|
|
15489
|
+
if (suffix === ".constant.ts")
|
|
15490
|
+
return isAllowedConstantDeclaration(modelName, declaration);
|
|
15491
|
+
if (suffix === ".document.ts")
|
|
15492
|
+
return declaration.kind === "class" && [`${modelName}Filter`, modelName, `${modelName}Model`].includes(declaration.name);
|
|
15493
|
+
if (suffix === ".service.ts")
|
|
15494
|
+
return declaration.kind === "class" && declaration.name === `${modelName}Service`;
|
|
15495
|
+
if (suffix === ".signal.ts")
|
|
15496
|
+
return declaration.kind === "class" && [`${modelName}Internal`, `${modelName}Slice`, `${modelName}Endpoint`].includes(declaration.name);
|
|
15497
|
+
if (suffix === ".store.ts")
|
|
15498
|
+
return declaration.kind === "class" && declaration.name === `${modelName}Store`;
|
|
15499
|
+
return false;
|
|
15500
|
+
}
|
|
15501
|
+
function isAllowedConstantDeclaration(modelName, declaration) {
|
|
15502
|
+
if (declaration.kind !== "class")
|
|
15503
|
+
return false;
|
|
15504
|
+
if ([`${modelName}Input`, `${modelName}Object`, modelName, `Light${modelName}`, `${modelName}Insight`].includes(declaration.name))
|
|
15505
|
+
return true;
|
|
15506
|
+
if (!ts11.isClassDeclaration(declaration.node))
|
|
15507
|
+
return false;
|
|
15508
|
+
const heritageClause = declaration.node.heritageClauses?.find((clause) => clause.token === ts11.SyntaxKind.ExtendsKeyword);
|
|
15509
|
+
const expression = heritageClause?.types[0]?.expression;
|
|
15510
|
+
return !!expression && expression.getText().startsWith("enumOf(");
|
|
15511
|
+
}
|
|
15512
|
+
function getConventionDescription(suffix, modelName) {
|
|
15513
|
+
if (suffix === ".dictionary.ts")
|
|
15514
|
+
return "export const dictionary";
|
|
15515
|
+
if (suffix === ".constant.ts")
|
|
15516
|
+
return `${modelName}Input, ${modelName}Object, ${modelName}, Light${modelName}, ${modelName}Insight, or enumOf classes`;
|
|
15517
|
+
if (suffix === ".document.ts")
|
|
15518
|
+
return `${modelName}Filter, ${modelName}, or ${modelName}Model`;
|
|
15519
|
+
if (suffix === ".service.ts")
|
|
15520
|
+
return `${modelName}Service`;
|
|
15521
|
+
if (suffix === ".signal.ts")
|
|
15522
|
+
return `${modelName}Internal, ${modelName}Slice, or ${modelName}Endpoint`;
|
|
15523
|
+
return `${modelName}Store`;
|
|
15524
|
+
}
|
|
15525
|
+
function getLibRootFile(file) {
|
|
15526
|
+
const segments = file.split("/");
|
|
15527
|
+
if (segments[0] === "libs" && segments.length === 4 && segments[2] === "lib")
|
|
15528
|
+
return segments[3];
|
|
15529
|
+
if (segments[0] === "apps" && segments.length === 5 && segments[2] === "lib")
|
|
15530
|
+
return segments[4];
|
|
15531
|
+
return null;
|
|
15532
|
+
}
|
|
15533
|
+
function getModuleUiWarning(file) {
|
|
15534
|
+
if (!file.endsWith(".tsx"))
|
|
15535
|
+
return null;
|
|
15536
|
+
const moduleInfo = getModuleInfo(file);
|
|
15537
|
+
if (!moduleInfo)
|
|
15538
|
+
return null;
|
|
15539
|
+
const { moduleName, fileName, kind } = moduleInfo;
|
|
15540
|
+
const pascalName = toPascalCase(moduleName.replace(/^_+/, ""));
|
|
15541
|
+
const allowedSuffixes = kind === "database" ? ["Template", "Unit", "Util", "View", "Zone"] : kind === "service" ? ["Util", "Zone"] : ["Template", "Unit"];
|
|
15542
|
+
const allowedFileNames = new Set(allowedSuffixes.map((suffix) => `${pascalName}.${suffix}.tsx`));
|
|
15543
|
+
if (allowedFileNames.has(fileName) || fileName.endsWith(".test.tsx") || fileName.endsWith(".spec.tsx"))
|
|
15544
|
+
return null;
|
|
15545
|
+
return {
|
|
15546
|
+
rule: "akan.layout.module-ui-file",
|
|
15547
|
+
scope: "layout",
|
|
15548
|
+
severity: "warning",
|
|
15549
|
+
file,
|
|
15550
|
+
message: `Unexpected ${kind} module UI filename "${fileName}". Expected one of: ${[...allowedFileNames].join(", ")}.`
|
|
15551
|
+
};
|
|
15552
|
+
}
|
|
15553
|
+
function getModuleInfo(file) {
|
|
15554
|
+
const segments = file.split("/");
|
|
15555
|
+
const libIndex = segments.indexOf("lib");
|
|
15556
|
+
if (libIndex < 0)
|
|
15557
|
+
return null;
|
|
15558
|
+
const moduleName = segments[libIndex + 1];
|
|
15559
|
+
if (moduleName === "__scalar") {
|
|
15560
|
+
if (segments.length !== libIndex + 4)
|
|
15561
|
+
return null;
|
|
15562
|
+
return { moduleName: segments[libIndex + 2], fileName: segments[libIndex + 3], kind: "scalar" };
|
|
15563
|
+
}
|
|
15564
|
+
if (segments.length !== libIndex + 3)
|
|
15565
|
+
return null;
|
|
15566
|
+
const fileName = segments[libIndex + 2];
|
|
15567
|
+
if (moduleName.startsWith("__")) {
|
|
15568
|
+
const scalarName = moduleName === "__scalar" ? segments[libIndex + 2] : moduleName;
|
|
15569
|
+
return { moduleName: scalarName, fileName, kind: "scalar" };
|
|
15570
|
+
}
|
|
15571
|
+
if (moduleName.startsWith("_"))
|
|
15572
|
+
return { moduleName, fileName, kind: "service" };
|
|
15573
|
+
return { moduleName, fileName, kind: "database" };
|
|
15574
|
+
}
|
|
15575
|
+
function isExportedConst(declaration) {
|
|
15576
|
+
return declaration.exported && ts11.isVariableStatement(declaration.node) && (declaration.node.declarationList.flags & ts11.NodeFlags.Const) !== 0;
|
|
15577
|
+
}
|
|
15578
|
+
function isExported(node) {
|
|
15579
|
+
return !!ts11.getCombinedModifierFlags(node) && !!(ts11.getCombinedModifierFlags(node) & ts11.ModifierFlags.Export);
|
|
15580
|
+
}
|
|
15581
|
+
function isFunctionLikeInitializer(node) {
|
|
15582
|
+
return !!node && (ts11.isArrowFunction(node) || ts11.isFunctionExpression(node));
|
|
15583
|
+
}
|
|
15584
|
+
function getBodyFingerprint(sourceFile2, node) {
|
|
15585
|
+
if (!node)
|
|
15586
|
+
return;
|
|
15587
|
+
const normalizedBody = node.getText(sourceFile2).replace(/\s+/g, " ").trim();
|
|
15588
|
+
if (normalizedBody.length < 80)
|
|
15589
|
+
return;
|
|
15590
|
+
return createHash("sha256").update(normalizedBody).digest("hex");
|
|
15591
|
+
}
|
|
15592
|
+
function shouldSkipPath(relativePath, isDirectory, ignoreFilter) {
|
|
15593
|
+
const ignorePath = isDirectory ? `${relativePath}/` : relativePath;
|
|
15594
|
+
return relativePath === ".git" || relativePath.includes("/.git/") || relativePath.includes("/node_modules/") || ignoreFilter.ignores(relativePath) || ignoreFilter.ignores(ignorePath);
|
|
15595
|
+
}
|
|
15596
|
+
async function isDirectory(absolutePath) {
|
|
15597
|
+
try {
|
|
15598
|
+
return (await stat4(absolutePath)).isDirectory();
|
|
15599
|
+
} catch {
|
|
15600
|
+
return false;
|
|
15601
|
+
}
|
|
15602
|
+
}
|
|
15603
|
+
function getScriptKind(file) {
|
|
15604
|
+
return file.endsWith(".tsx") ? ts11.ScriptKind.TSX : ts11.ScriptKind.TS;
|
|
15605
|
+
}
|
|
15606
|
+
function getLine(sourceFile2, node) {
|
|
15607
|
+
return sourceFile2.getLineAndCharacterOfPosition(node.getStart(sourceFile2)).line + 1;
|
|
15608
|
+
}
|
|
15609
|
+
function getRecommendedLineLimit(file) {
|
|
15610
|
+
if (file.endsWith(".service.ts"))
|
|
15611
|
+
return 500;
|
|
15612
|
+
if (file.endsWith(".Template.tsx") || file.endsWith(".Zone.tsx"))
|
|
15613
|
+
return 800;
|
|
15614
|
+
if (file.endsWith(".Util.tsx"))
|
|
15615
|
+
return 1000;
|
|
15616
|
+
return null;
|
|
15617
|
+
}
|
|
15618
|
+
function findPatternLines(content, pattern) {
|
|
15619
|
+
return content.split(/\r?\n/).flatMap((line, index) => pattern.test(line) ? [index + 1] : []);
|
|
15620
|
+
}
|
|
15621
|
+
function toPascalCase(value) {
|
|
15622
|
+
return value.replace(/(^|[-_./])([a-zA-Z0-9])/g, (_, __, char) => char.toUpperCase()).replace(/[-_./]/g, "");
|
|
15623
|
+
}
|
|
15624
|
+
function toPosix(value) {
|
|
15625
|
+
return value.split(path43.sep).join("/");
|
|
15626
|
+
}
|
|
15627
|
+
function groupBy(items, getKey) {
|
|
15628
|
+
const grouped = new Map;
|
|
15629
|
+
for (const item of items) {
|
|
15630
|
+
const key = getKey(item);
|
|
15631
|
+
grouped.set(key, [...grouped.get(key) ?? [], item]);
|
|
15632
|
+
}
|
|
15633
|
+
return grouped;
|
|
15634
|
+
}
|
|
15635
|
+
function compareWarnings(a, b) {
|
|
15636
|
+
return a.scope.localeCompare(b.scope) || (a.file ?? "").localeCompare(b.file ?? "") || (a.line ?? 0) - (b.line ?? 0) || a.rule.localeCompare(b.rule);
|
|
15637
|
+
}
|
|
15080
15638
|
// pkgs/@akanjs/devkit/selectModel.ts
|
|
15081
15639
|
import { select as select5 } from "@inquirer/prompts";
|
|
15082
15640
|
// pkgs/@akanjs/devkit/streamAi.ts
|
|
@@ -16038,7 +16596,7 @@ class ApplicationCommand extends command("application", [ApplicationScript], ({
|
|
|
16038
16596
|
import { Logger as Logger16 } from "akanjs/common";
|
|
16039
16597
|
|
|
16040
16598
|
// pkgs/@akanjs/cli/package/package.runner.ts
|
|
16041
|
-
import
|
|
16599
|
+
import path44 from "path";
|
|
16042
16600
|
import { Logger as Logger14 } from "akanjs/common";
|
|
16043
16601
|
var {$: $2 } = globalThis.Bun;
|
|
16044
16602
|
|
|
@@ -16058,11 +16616,11 @@ class PackageRunner extends runner("package") {
|
|
|
16058
16616
|
}
|
|
16059
16617
|
async#getInstalledPackageJson() {
|
|
16060
16618
|
const packageJsonCandidates = [
|
|
16061
|
-
`${
|
|
16619
|
+
`${path44.dirname(Bun.main)}/package.json`,
|
|
16062
16620
|
`${process.cwd()}/node_modules/akanjs/package.json`
|
|
16063
16621
|
];
|
|
16064
16622
|
try {
|
|
16065
|
-
packageJsonCandidates.unshift(Bun.resolveSync("akanjs/package.json",
|
|
16623
|
+
packageJsonCandidates.unshift(Bun.resolveSync("akanjs/package.json", path44.dirname(Bun.main)));
|
|
16066
16624
|
} catch {}
|
|
16067
16625
|
for (const packageJsonPath of packageJsonCandidates) {
|
|
16068
16626
|
if (!await Bun.file(packageJsonPath).exists())
|
|
@@ -16071,7 +16629,7 @@ class PackageRunner extends runner("package") {
|
|
|
16071
16629
|
if (packageJson.name === "akanjs" || packageJson.name === "@akanjs/cli")
|
|
16072
16630
|
return packageJson;
|
|
16073
16631
|
}
|
|
16074
|
-
throw new Error(`[package] failed to locate akanjs package.json from ${
|
|
16632
|
+
throw new Error(`[package] failed to locate akanjs package.json from ${path44.dirname(Bun.main)}`);
|
|
16075
16633
|
}
|
|
16076
16634
|
async createPackage(workspace, pkgName) {
|
|
16077
16635
|
await workspace.applyTemplate({ basePath: `pkgs/${pkgName}`, template: "pkgRoot", dict: { pkgName } });
|
|
@@ -16246,7 +16804,7 @@ class PackageScript extends script("package", [PackageRunner]) {
|
|
|
16246
16804
|
}
|
|
16247
16805
|
|
|
16248
16806
|
// pkgs/@akanjs/cli/cloud/cloud.runner.ts
|
|
16249
|
-
import
|
|
16807
|
+
import path45 from "path";
|
|
16250
16808
|
import { confirm as confirm4, input as input5, select as select8 } from "@inquirer/prompts";
|
|
16251
16809
|
import { Logger as Logger15, sleep } from "akanjs/common";
|
|
16252
16810
|
import chalk7 from "chalk";
|
|
@@ -16517,7 +17075,7 @@ ${chalk7.green("\u27A4")} Authentication Required`));
|
|
|
16517
17075
|
await Promise.all(akanPkgs.map(async (library) => {
|
|
16518
17076
|
Logger15.info(`Publishing ${library}@${nextVersion} to ${registry ?? "npm"}...`);
|
|
16519
17077
|
await workspace.spawn("npm", ["publish", "--tag", tag, ...this.#getRegistryArgs(registry), ...this.#getLocalRegistryAuthArgs(registry)], {
|
|
16520
|
-
cwd:
|
|
17078
|
+
cwd: path45.join(workspace.workspaceRoot, "dist/pkgs", library),
|
|
16521
17079
|
env: this.#getRegistryEnv(registry),
|
|
16522
17080
|
stdio: "inherit"
|
|
16523
17081
|
});
|
|
@@ -16571,11 +17129,12 @@ ${chalk7.green("\u27A4")} Authentication Required`));
|
|
|
16571
17129
|
async downloadEnv(cloudApi2, workspace, workspaceId) {
|
|
16572
17130
|
await workspace.mkdir("local");
|
|
16573
17131
|
const localPath = await cloudApi2.downloadEnv(workspaceId);
|
|
16574
|
-
|
|
17132
|
+
const relativePath = path45.relative(workspace.workspaceRoot, localPath).split(path45.sep).join("/");
|
|
17133
|
+
await workspace.spawn("tar", ["-xf", relativePath], { cwd: workspace.workspaceRoot });
|
|
16575
17134
|
await workspace.remove(localPath);
|
|
16576
17135
|
}
|
|
16577
17136
|
async uploadEnv(cloudApi2, workspaceId, filePath) {
|
|
16578
|
-
const file = new File([Bun.file(filePath)],
|
|
17137
|
+
const file = new File([Bun.file(filePath)], path45.basename(filePath));
|
|
16579
17138
|
await cloudApi2.uploadEnv(workspaceId, file);
|
|
16580
17139
|
}
|
|
16581
17140
|
async downloadEnvByScp(workspace) {
|
|
@@ -16667,14 +17226,14 @@ class CloudScript extends script("cloud", [CloudRunner, ApplicationScript, Packa
|
|
|
16667
17226
|
}
|
|
16668
17227
|
async uploadEnv(workspace) {
|
|
16669
17228
|
const workspaceId = workspace.getWorkspaceId({ allowEmpty: true });
|
|
16670
|
-
const { path:
|
|
17229
|
+
const { path: path46 } = await this.cloudRunner.gatherEnvFiles(workspace);
|
|
16671
17230
|
if (workspaceId) {
|
|
16672
17231
|
await this.login(workspace);
|
|
16673
17232
|
const cloudApi2 = await CloudApi.fromHost(workspace);
|
|
16674
|
-
await this.cloudRunner.uploadEnv(cloudApi2, workspaceId,
|
|
17233
|
+
await this.cloudRunner.uploadEnv(cloudApi2, workspaceId, path46);
|
|
16675
17234
|
return;
|
|
16676
17235
|
}
|
|
16677
|
-
await this.cloudRunner.uploadEnvByScp(workspace,
|
|
17236
|
+
await this.cloudRunner.uploadEnvByScp(workspace, path46);
|
|
16678
17237
|
}
|
|
16679
17238
|
async deployAkan(workspace, { test = true, registryUrl } = {}) {
|
|
16680
17239
|
const akanPkgs = await this.cloudRunner.getAkanPkgs(workspace);
|
|
@@ -16915,8 +17474,8 @@ class RepairRunner extends runner("repair") {
|
|
|
16915
17474
|
}
|
|
16916
17475
|
|
|
16917
17476
|
// pkgs/@akanjs/cli/workflow/workflow.runner.ts
|
|
16918
|
-
import { mkdir as mkdir12, readFile as
|
|
16919
|
-
import
|
|
17477
|
+
import { mkdir as mkdir12, readFile as readFile3, writeFile as writeFile2 } from "fs/promises";
|
|
17478
|
+
import path46 from "path";
|
|
16920
17479
|
import { capitalize as capitalize8 } from "akanjs/common";
|
|
16921
17480
|
|
|
16922
17481
|
// pkgs/@akanjs/cli/workflows/shared.ts
|
|
@@ -17459,7 +18018,7 @@ var workflowSpecs = [
|
|
|
17459
18018
|
];
|
|
17460
18019
|
|
|
17461
18020
|
// pkgs/@akanjs/cli/workflow/workflow.runner.ts
|
|
17462
|
-
var resolvePath = (filePath) =>
|
|
18021
|
+
var resolvePath = (filePath) => path46.isAbsolute(filePath) ? filePath : path46.join(process.cwd(), filePath);
|
|
17463
18022
|
var isRecord3 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
|
|
17464
18023
|
var isWorkflowPlan2 = (value) => {
|
|
17465
18024
|
if (!isRecord3(value))
|
|
@@ -17557,7 +18116,7 @@ var defaultValidationExecutor = (workspace) => async (command3) => {
|
|
|
17557
18116
|
};
|
|
17558
18117
|
}
|
|
17559
18118
|
};
|
|
17560
|
-
var readJsonFile = async (filePath) => JSON.parse(await
|
|
18119
|
+
var readJsonFile = async (filePath) => JSON.parse(await readFile3(resolvePath(filePath), "utf8"));
|
|
17561
18120
|
var planInputString2 = (plan2, key) => {
|
|
17562
18121
|
const value = plan2.inputs[key];
|
|
17563
18122
|
return typeof value === "string" ? value : "";
|
|
@@ -17653,7 +18212,7 @@ class WorkflowRunner extends runner("workflow") {
|
|
|
17653
18212
|
const plan2 = createWorkflowPlan(spec, compactWorkflowInputs(inputs));
|
|
17654
18213
|
if (out) {
|
|
17655
18214
|
const outPath = resolvePath(out);
|
|
17656
|
-
await mkdir12(
|
|
18215
|
+
await mkdir12(path46.dirname(outPath), { recursive: true });
|
|
17657
18216
|
await writeFile2(outPath, jsonText(plan2));
|
|
17658
18217
|
}
|
|
17659
18218
|
return format === "json" ? jsonText(plan2) : renderWorkflowPlan(plan2);
|
|
@@ -17672,7 +18231,7 @@ class WorkflowRunner extends runner("workflow") {
|
|
|
17672
18231
|
};
|
|
17673
18232
|
let plan2;
|
|
17674
18233
|
try {
|
|
17675
|
-
const parsed = JSON.parse(await
|
|
18234
|
+
const parsed = JSON.parse(await readFile3(resolvePath(planPath), "utf8"));
|
|
17676
18235
|
if (!isWorkflowPlan2(parsed)) {
|
|
17677
18236
|
const report = failedApplyReport("unknown", [
|
|
17678
18237
|
{
|
|
@@ -19477,14 +20036,14 @@ class GuidelinePrompt extends Prompter {
|
|
|
19477
20036
|
async#getScanFilePaths(matchPattern, { avoidDirs = ["node_modules", ".next"], filterText } = {}) {
|
|
19478
20037
|
const glob = new Bun.Glob(matchPattern);
|
|
19479
20038
|
const paths = [];
|
|
19480
|
-
for await (const
|
|
19481
|
-
if (avoidDirs.some((dir) =>
|
|
20039
|
+
for await (const path47 of glob.scan({ cwd: this.workspace.workspaceRoot, absolute: true })) {
|
|
20040
|
+
if (avoidDirs.some((dir) => path47.includes(dir)))
|
|
19482
20041
|
continue;
|
|
19483
|
-
const fileContent = await FileSys.readText(
|
|
20042
|
+
const fileContent = await FileSys.readText(path47);
|
|
19484
20043
|
const textFilter = filterText ? new RegExp(filterText) : null;
|
|
19485
20044
|
if (filterText && !textFilter?.test(fileContent))
|
|
19486
20045
|
continue;
|
|
19487
|
-
paths.push(
|
|
20046
|
+
paths.push(path47);
|
|
19488
20047
|
}
|
|
19489
20048
|
return paths;
|
|
19490
20049
|
}
|
|
@@ -19804,7 +20363,7 @@ class LibraryCommand extends command("library", [LibraryScript], ({ public: targ
|
|
|
19804
20363
|
|
|
19805
20364
|
// pkgs/@akanjs/cli/localRegistry/localRegistry.runner.ts
|
|
19806
20365
|
import { mkdir as mkdir13, rm as rm6 } from "fs/promises";
|
|
19807
|
-
import
|
|
20366
|
+
import path47 from "path";
|
|
19808
20367
|
import { Logger as Logger19 } from "akanjs/common";
|
|
19809
20368
|
var defaultLocalRegistryUrl = "http://127.0.0.1:4873";
|
|
19810
20369
|
var containerName = "akan-verdaccio";
|
|
@@ -19822,8 +20381,8 @@ class LocalRegistryRunner extends runner("localRegistry") {
|
|
|
19822
20381
|
Logger19.info(`Local registry is already running at ${registry}`);
|
|
19823
20382
|
return registry;
|
|
19824
20383
|
} catch {}
|
|
19825
|
-
const configPath2 =
|
|
19826
|
-
const storagePath =
|
|
20384
|
+
const configPath2 = path47.join(workspace.workspaceRoot, "pkgs/@akanjs/cli/localRegistry/verdaccio.yaml");
|
|
20385
|
+
const storagePath = path47.join(workspace.workspaceRoot, ".akan/verdaccio/storage");
|
|
19827
20386
|
await mkdir13(storagePath, { recursive: true });
|
|
19828
20387
|
await workspace.spawn("docker", [
|
|
19829
20388
|
"run",
|
|
@@ -19846,13 +20405,13 @@ class LocalRegistryRunner extends runner("localRegistry") {
|
|
|
19846
20405
|
try {
|
|
19847
20406
|
await workspace.spawn("docker", ["rm", "-f", containerName], { stdio: "inherit" });
|
|
19848
20407
|
} catch {}
|
|
19849
|
-
await rm6(
|
|
20408
|
+
await rm6(path47.join(workspace.workspaceRoot, ".akan/verdaccio"), { recursive: true, force: true });
|
|
19850
20409
|
Logger19.info("Local registry storage has been reset");
|
|
19851
20410
|
}
|
|
19852
20411
|
async smoke(workspace, { registryUrl } = {}) {
|
|
19853
20412
|
const registry = this.getRegistryUrl(registryUrl);
|
|
19854
|
-
const smokeRoot =
|
|
19855
|
-
await rm6(
|
|
20413
|
+
const smokeRoot = path47.join(workspace.workspaceRoot, ".akan/e2e");
|
|
20414
|
+
await rm6(path47.join(smokeRoot, smokeRepoName), { recursive: true, force: true });
|
|
19856
20415
|
await workspace.spawn(process.execPath, [
|
|
19857
20416
|
"dist/pkgs/create-akan-workspace/index.js",
|
|
19858
20417
|
smokeRepoName,
|
|
@@ -19869,12 +20428,12 @@ class LocalRegistryRunner extends runner("localRegistry") {
|
|
|
19869
20428
|
stdio: "inherit"
|
|
19870
20429
|
});
|
|
19871
20430
|
await workspace.spawn("akan", ["typecheck", smokeAppName], {
|
|
19872
|
-
cwd:
|
|
20431
|
+
cwd: path47.join(smokeRoot, smokeRepoName),
|
|
19873
20432
|
env: { ...process.env, AKAN_NPM_REGISTRY: registry, NPM_CONFIG_REGISTRY: registry },
|
|
19874
20433
|
stdio: "inherit"
|
|
19875
20434
|
});
|
|
19876
20435
|
await workspace.spawn("akan", ["build", smokeAppName], {
|
|
19877
|
-
cwd:
|
|
20436
|
+
cwd: path47.join(smokeRoot, smokeRepoName),
|
|
19878
20437
|
env: { ...process.env, AKAN_NPM_REGISTRY: registry, NPM_CONFIG_REGISTRY: registry },
|
|
19879
20438
|
stdio: "inherit"
|
|
19880
20439
|
});
|
|
@@ -20053,8 +20612,46 @@ class PrimitiveCommand extends command("primitive", [PrimitiveScript], ({ public
|
|
|
20053
20612
|
})) {
|
|
20054
20613
|
}
|
|
20055
20614
|
|
|
20056
|
-
// pkgs/@akanjs/cli/
|
|
20615
|
+
// pkgs/@akanjs/cli/quality/quality.script.ts
|
|
20057
20616
|
import { Logger as Logger22 } from "akanjs/common";
|
|
20617
|
+
|
|
20618
|
+
// pkgs/@akanjs/cli/quality/quality.runner.ts
|
|
20619
|
+
class QualityRunner extends runner("quality") {
|
|
20620
|
+
async scan(workspace) {
|
|
20621
|
+
return await new AkanQualityScanner().scan(workspace.workspaceRoot);
|
|
20622
|
+
}
|
|
20623
|
+
}
|
|
20624
|
+
|
|
20625
|
+
// pkgs/@akanjs/cli/quality/quality.script.ts
|
|
20626
|
+
class QualityScript extends script("quality", [QualityRunner]) {
|
|
20627
|
+
async scan(workspace, format = "text") {
|
|
20628
|
+
const spinner2 = workspace.spinning("Scanning Akan code quality...");
|
|
20629
|
+
const result = await this.qualityRunner.scan(workspace);
|
|
20630
|
+
spinner2.succeed(`Quality scan completed (${result.scannedFiles} files, ${result.warnings.length} warnings)`);
|
|
20631
|
+
Logger22.rawLog(format === "json" ? JSON.stringify(result, null, 2) : formatQualityScanResult(result));
|
|
20632
|
+
}
|
|
20633
|
+
}
|
|
20634
|
+
|
|
20635
|
+
// pkgs/@akanjs/cli/quality/quality.command.ts
|
|
20636
|
+
class QualityCommand extends command("quality", [QualityScript], ({ public: target }) => ({
|
|
20637
|
+
quality: target({ desc: "Scan apps and libs for Akan code quality warnings" }).arg("action", String, {
|
|
20638
|
+
desc: "quality action",
|
|
20639
|
+
default: "scan",
|
|
20640
|
+
enum: ["scan"]
|
|
20641
|
+
}).option("format", String, {
|
|
20642
|
+
desc: "output format",
|
|
20643
|
+
default: "text",
|
|
20644
|
+
enum: ["text", "json"]
|
|
20645
|
+
}).with(Workspace).exec(async function(action, format, workspace) {
|
|
20646
|
+
if (action !== "scan")
|
|
20647
|
+
throw new Error(`Unknown quality action: ${action}. Use "scan".`);
|
|
20648
|
+
await this.qualityScript.scan(workspace, format);
|
|
20649
|
+
})
|
|
20650
|
+
})) {
|
|
20651
|
+
}
|
|
20652
|
+
|
|
20653
|
+
// pkgs/@akanjs/cli/repair/repair.script.ts
|
|
20654
|
+
import { Logger as Logger23 } from "akanjs/common";
|
|
20058
20655
|
class RepairScript extends script("repair", [RepairRunner]) {
|
|
20059
20656
|
async repair(kind, {
|
|
20060
20657
|
workspace,
|
|
@@ -20063,7 +20660,7 @@ class RepairScript extends script("repair", [RepairRunner]) {
|
|
|
20063
20660
|
target = null,
|
|
20064
20661
|
format = "markdown"
|
|
20065
20662
|
}) {
|
|
20066
|
-
|
|
20663
|
+
Logger23.rawLog(await this.repairRunner.repair(kind, {
|
|
20067
20664
|
workspace,
|
|
20068
20665
|
app,
|
|
20069
20666
|
module,
|
|
@@ -20103,12 +20700,12 @@ class RepairCommand extends command("repair", [RepairScript], ({ public: target
|
|
|
20103
20700
|
}
|
|
20104
20701
|
|
|
20105
20702
|
// pkgs/@akanjs/cli/scalar/scalar.command.ts
|
|
20106
|
-
import { Logger as
|
|
20703
|
+
import { Logger as Logger24, lowerlize as lowerlize3 } from "akanjs/common";
|
|
20107
20704
|
class ScalarCommand extends command("scalar", [ScalarScript], ({ public: target }) => ({
|
|
20108
20705
|
createScalar: target({ desc: "Create a new scalar type (simple data model without DB)" }).arg("scalarName", String, { desc: "name of scalar" }).with(Sys).option("ai", Boolean, { default: false, desc: "use ai to create scalar" }).option("format", String, { flag: "o", desc: "output format", default: "markdown", enum: ["markdown", "json"] }).exec(async function(scalarName, sys3, ai, format) {
|
|
20109
20706
|
const name = lowerlize3(scalarName.replace(/ /g, ""));
|
|
20110
20707
|
const report = ai ? await this.scalarScript.createScalarWithAi(sys3, name) : await this.scalarScript.createScalar(sys3, name);
|
|
20111
|
-
|
|
20708
|
+
Logger24.rawLog(renderPrimitiveReport(report, format));
|
|
20112
20709
|
}),
|
|
20113
20710
|
removeScalar: target({ desc: "Remove a scalar type from an app or library" }).arg("scalarName", String, { desc: "name of scalar" }).with(Sys).exec(async function(scalarName, sys3) {
|
|
20114
20711
|
await this.scalarScript.removeScalar(sys3, scalarName);
|
|
@@ -20117,7 +20714,7 @@ class ScalarCommand extends command("scalar", [ScalarScript], ({ public: target
|
|
|
20117
20714
|
}
|
|
20118
20715
|
|
|
20119
20716
|
// pkgs/@akanjs/cli/workflow/workflow.script.ts
|
|
20120
|
-
import { Logger as
|
|
20717
|
+
import { Logger as Logger25 } from "akanjs/common";
|
|
20121
20718
|
class WorkflowScript extends script("workflow", [WorkflowRunner, ModuleScript, ScalarScript, PrimitiveScript]) {
|
|
20122
20719
|
async workflow(action, workflow2, inputs, {
|
|
20123
20720
|
format = "markdown",
|
|
@@ -20126,23 +20723,23 @@ class WorkflowScript extends script("workflow", [WorkflowRunner, ModuleScript, S
|
|
|
20126
20723
|
workspace
|
|
20127
20724
|
} = {}) {
|
|
20128
20725
|
if (action === "list") {
|
|
20129
|
-
|
|
20726
|
+
Logger25.rawLog(this.workflowRunner.list({ format }));
|
|
20130
20727
|
return;
|
|
20131
20728
|
}
|
|
20132
20729
|
if (!workflow2)
|
|
20133
20730
|
throw new Error(`Workflow name is required for "${action}".`);
|
|
20134
20731
|
if (action === "explain") {
|
|
20135
|
-
|
|
20732
|
+
Logger25.rawLog(this.workflowRunner.explain(workflow2, { format }));
|
|
20136
20733
|
return;
|
|
20137
20734
|
}
|
|
20138
20735
|
if (action === "plan") {
|
|
20139
|
-
|
|
20736
|
+
Logger25.rawLog(await this.workflowRunner.plan(workflow2, inputs, { format, out }));
|
|
20140
20737
|
return;
|
|
20141
20738
|
}
|
|
20142
20739
|
if (action === "apply") {
|
|
20143
20740
|
if (!workspace)
|
|
20144
20741
|
throw new Error("Workspace is required for workflow apply.");
|
|
20145
|
-
|
|
20742
|
+
Logger25.rawLog(await this.workflowRunner.apply(workflow2, {
|
|
20146
20743
|
dryRun,
|
|
20147
20744
|
format,
|
|
20148
20745
|
workspace,
|
|
@@ -20160,13 +20757,13 @@ class WorkflowScript extends script("workflow", [WorkflowRunner, ModuleScript, S
|
|
|
20160
20757
|
if (action === "validate") {
|
|
20161
20758
|
if (!workspace)
|
|
20162
20759
|
throw new Error("Workspace is required for workflow validate.");
|
|
20163
|
-
|
|
20760
|
+
Logger25.rawLog(await this.workflowRunner.validate(workflow2, { format, workspace }));
|
|
20164
20761
|
return;
|
|
20165
20762
|
}
|
|
20166
20763
|
if (action === "report") {
|
|
20167
20764
|
if (!workspace)
|
|
20168
20765
|
throw new Error("Workspace is required for workflow report.");
|
|
20169
|
-
|
|
20766
|
+
Logger25.rawLog(await this.workflowRunner.report(workflow2, { format, workspace }));
|
|
20170
20767
|
return;
|
|
20171
20768
|
}
|
|
20172
20769
|
throw new Error(`Unknown workflow action: ${action}. Use list, explain, plan, apply, validate, or report.`);
|
|
@@ -20190,11 +20787,11 @@ class WorkflowCommand extends command("workflow", [WorkflowScript], ({ public: t
|
|
|
20190
20787
|
}
|
|
20191
20788
|
|
|
20192
20789
|
// pkgs/@akanjs/cli/workspace/workspace.script.ts
|
|
20193
|
-
import
|
|
20194
|
-
import { Logger as
|
|
20790
|
+
import path49 from "path";
|
|
20791
|
+
import { Logger as Logger26 } from "akanjs/common";
|
|
20195
20792
|
|
|
20196
20793
|
// pkgs/@akanjs/cli/workspace/workspace.runner.ts
|
|
20197
|
-
import
|
|
20794
|
+
import path48 from "path";
|
|
20198
20795
|
var defaultWorkspacePeerDependencies = new Set([
|
|
20199
20796
|
"@react-spring/web",
|
|
20200
20797
|
"@use-gesture/react",
|
|
@@ -20245,7 +20842,7 @@ class WorkspaceRunner extends runner("workspace") {
|
|
|
20245
20842
|
owner = ""
|
|
20246
20843
|
}) {
|
|
20247
20844
|
const cwdPath = process.cwd();
|
|
20248
|
-
const workspaceRoot =
|
|
20845
|
+
const workspaceRoot = path48.join(cwdPath, dirname2, repoName);
|
|
20249
20846
|
const normalizedRegistryUrl = registryUrl ? getNpmRegistryUrl(registryUrl) : undefined;
|
|
20250
20847
|
const workspace = WorkspaceExecutor.fromRoot({ workspaceRoot, repoName });
|
|
20251
20848
|
const templateSpinner = workspace.spinning(`Creating workspace template files in ${dirname2}/${repoName}...`);
|
|
@@ -20301,9 +20898,9 @@ class WorkspaceRunner extends runner("workspace") {
|
|
|
20301
20898
|
}
|
|
20302
20899
|
async#getCliPackageJson() {
|
|
20303
20900
|
const packageJsonCandidates = [
|
|
20304
|
-
|
|
20305
|
-
|
|
20306
|
-
|
|
20901
|
+
path48.join(import.meta.dir, "../package.json"),
|
|
20902
|
+
path48.join(import.meta.dir, "package.json"),
|
|
20903
|
+
path48.join(path48.dirname(Bun.main), "package.json")
|
|
20307
20904
|
];
|
|
20308
20905
|
try {
|
|
20309
20906
|
packageJsonCandidates.unshift(Bun.resolveSync("@akanjs/cli/package.json", import.meta.dir));
|
|
@@ -20319,9 +20916,9 @@ class WorkspaceRunner extends runner("workspace") {
|
|
|
20319
20916
|
}
|
|
20320
20917
|
async#getAkanPackageJson() {
|
|
20321
20918
|
const packageJsonCandidates = [
|
|
20322
|
-
|
|
20323
|
-
|
|
20324
|
-
|
|
20919
|
+
path48.join(import.meta.dir, "../../../akanjs/package.json"),
|
|
20920
|
+
path48.join(process.cwd(), "pkgs/akanjs/package.json"),
|
|
20921
|
+
path48.join(path48.dirname(Bun.main), "node_modules/akanjs/package.json")
|
|
20325
20922
|
];
|
|
20326
20923
|
try {
|
|
20327
20924
|
packageJsonCandidates.unshift(Bun.resolveSync("akanjs/package.json", import.meta.dir));
|
|
@@ -20335,13 +20932,13 @@ class WorkspaceRunner extends runner("workspace") {
|
|
|
20335
20932
|
}
|
|
20336
20933
|
let current = import.meta.dir;
|
|
20337
20934
|
for (let depth = 0;depth < 6; depth++) {
|
|
20338
|
-
const packageJsonPath =
|
|
20935
|
+
const packageJsonPath = path48.join(current, "package.json");
|
|
20339
20936
|
if (await Bun.file(packageJsonPath).exists()) {
|
|
20340
20937
|
const packageJson = await FileSys.readJson(packageJsonPath);
|
|
20341
20938
|
if (packageJson.name === "akanjs")
|
|
20342
20939
|
return packageJson;
|
|
20343
20940
|
}
|
|
20344
|
-
const parent =
|
|
20941
|
+
const parent = path48.dirname(current);
|
|
20345
20942
|
if (parent === current)
|
|
20346
20943
|
break;
|
|
20347
20944
|
current = parent;
|
|
@@ -20418,11 +21015,11 @@ class WorkspaceScript extends script("workspace", [
|
|
|
20418
21015
|
} catch (_) {
|
|
20419
21016
|
gitSpinner.fail("Git repository initialization failed. It's not fatal, you can commit manually");
|
|
20420
21017
|
}
|
|
20421
|
-
const workspacePath2 =
|
|
20422
|
-
|
|
21018
|
+
const workspacePath2 = path49.join(dirname2, repoName);
|
|
21019
|
+
Logger26.rawLog(`
|
|
20423
21020
|
\uD83C\uDF89 Welcome aboard! Workspace created in ${dirname2}/${repoName}`);
|
|
20424
|
-
|
|
20425
|
-
|
|
21021
|
+
Logger26.rawLog(`\uD83D\uDE80 Run \`cd ${workspacePath2} && akan start ${appName}\` to start the development server.`);
|
|
21022
|
+
Logger26.rawLog(`
|
|
20426
21023
|
\uD83D\uDC4B Happy coding!`);
|
|
20427
21024
|
}
|
|
20428
21025
|
async generateAgentRules(workspace, { overwrite = false, cursorRules = true } = {}) {
|
|
@@ -20538,4 +21135,4 @@ class WorkspaceCommand extends command("workspace", [WorkspaceScript], ({ public
|
|
|
20538
21135
|
}
|
|
20539
21136
|
|
|
20540
21137
|
// pkgs/@akanjs/cli/index.ts
|
|
20541
|
-
runCommands(WorkspaceCommand, AgentCommand, ApplicationCommand, LibraryCommand, LocalRegistryCommand, PackageCommand, ModuleCommand, PageCommand, ContextCommand, CloudCommand, GuidelineCommand, ScalarCommand, PrimitiveCommand, RepairCommand, WorkflowCommand);
|
|
21138
|
+
runCommands(WorkspaceCommand, AgentCommand, ApplicationCommand, LibraryCommand, LocalRegistryCommand, PackageCommand, ModuleCommand, PageCommand, ContextCommand, CloudCommand, GuidelineCommand, ScalarCommand, PrimitiveCommand, QualityCommand, RepairCommand, WorkflowCommand);
|