@akanjs/cli 2.3.11-rc.6 → 2.3.11-rc.8
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 +239 -34
- package/index.js +285 -55
- package/package.json +2 -2
- package/templates/appSample/lib/task/task.service.ts +1 -1
- package/templates/module/__Model__.Template.tsx +4 -5
- package/templates/module/__Model__.Unit.tsx +1 -1
- package/templates/module/__Model__.View.tsx +1 -1
- package/templates/module/__Model__.Zone.tsx +5 -3
- package/templates/module/__model__.constant.ts +2 -1
- package/templates/module/__model__.dictionary.ts +3 -1
- package/templates/workspaceRoot/.gitignore.template +2 -0
- package/templates/workspaceRoot/AGENTS.md.template +55 -3
- package/templates/workspaceRoot/biome.json.template +4 -0
- package/templates/workspaceRoot/docs/GENERATED.md.template +0 -1
- package/templates/workspaceRoot/package.json.template +3 -3
|
@@ -666,6 +666,7 @@ import {
|
|
|
666
666
|
import { readFileSync as readFileSync3 } from "fs";
|
|
667
667
|
import { copyFile, mkdir as mkdir2, readdir as readDirEntries, stat as stat2 } from "fs/promises";
|
|
668
668
|
import path7 from "path";
|
|
669
|
+
import { pathToFileURL } from "url";
|
|
669
670
|
import {
|
|
670
671
|
capitalize,
|
|
671
672
|
isRouteSourceFile,
|
|
@@ -1107,6 +1108,63 @@ var decreaseBuildNum = async (app) => {
|
|
|
1107
1108
|
const akanConfigContent = akanConfig.replace(`buildNum: ${appConfig.mobile.buildNum}`, `buildNum: ${appConfig.mobile.buildNum - 1}`);
|
|
1108
1109
|
fs.writeFileSync(akanConfigPath, akanConfigContent);
|
|
1109
1110
|
};
|
|
1111
|
+
// pkgs/@akanjs/devkit/firebaseMessagingSw.ts
|
|
1112
|
+
var FIREBASE_WEB_SDK_VERSION = "12.13.0";
|
|
1113
|
+
function normalizeFirebaseClientConfig(config) {
|
|
1114
|
+
if (!config || typeof config !== "object")
|
|
1115
|
+
return null;
|
|
1116
|
+
const value = config;
|
|
1117
|
+
if (typeof value.apiKey !== "string" || typeof value.projectId !== "string" || typeof value.messagingSenderId !== "string" || typeof value.appId !== "string") {
|
|
1118
|
+
return null;
|
|
1119
|
+
}
|
|
1120
|
+
return {
|
|
1121
|
+
apiKey: value.apiKey,
|
|
1122
|
+
...typeof value.authDomain === "string" ? { authDomain: value.authDomain } : {},
|
|
1123
|
+
projectId: value.projectId,
|
|
1124
|
+
...typeof value.storageBucket === "string" ? { storageBucket: value.storageBucket } : {},
|
|
1125
|
+
messagingSenderId: value.messagingSenderId,
|
|
1126
|
+
appId: value.appId
|
|
1127
|
+
};
|
|
1128
|
+
}
|
|
1129
|
+
function createFirebaseMessagingServiceWorker(config) {
|
|
1130
|
+
const configJson = JSON.stringify(config);
|
|
1131
|
+
return `/* Generated by Akan.js. Do not edit. */
|
|
1132
|
+
const firebaseConfig = ${configJson};
|
|
1133
|
+
|
|
1134
|
+
if (firebaseConfig) {
|
|
1135
|
+
importScripts("https://www.gstatic.com/firebasejs/${FIREBASE_WEB_SDK_VERSION}/firebase-app-compat.js");
|
|
1136
|
+
importScripts("https://www.gstatic.com/firebasejs/${FIREBASE_WEB_SDK_VERSION}/firebase-messaging-compat.js");
|
|
1137
|
+
|
|
1138
|
+
firebase.initializeApp(firebaseConfig);
|
|
1139
|
+
const messaging = firebase.messaging();
|
|
1140
|
+
|
|
1141
|
+
const notificationUrl = (payload) =>
|
|
1142
|
+
payload?.data?.url || payload?.fcmOptions?.link || payload?.notification?.click_action;
|
|
1143
|
+
|
|
1144
|
+
messaging.onBackgroundMessage((payload) => {
|
|
1145
|
+
const title = payload?.notification?.title || "";
|
|
1146
|
+
const options = {
|
|
1147
|
+
body: payload?.notification?.body,
|
|
1148
|
+
icon: payload?.notification?.icon,
|
|
1149
|
+
image: payload?.notification?.image,
|
|
1150
|
+
data: {
|
|
1151
|
+
url: notificationUrl(payload),
|
|
1152
|
+
FCM_MSG: payload,
|
|
1153
|
+
},
|
|
1154
|
+
};
|
|
1155
|
+
self.registration.showNotification(title, options);
|
|
1156
|
+
});
|
|
1157
|
+
}
|
|
1158
|
+
|
|
1159
|
+
self.addEventListener("notificationclick", (event) => {
|
|
1160
|
+
const url = event.notification?.data?.url || event.notification?.data?.FCM_MSG?.data?.url;
|
|
1161
|
+
event.notification?.close();
|
|
1162
|
+
if (!url) return;
|
|
1163
|
+
event.waitUntil(clients.openWindow(url));
|
|
1164
|
+
});
|
|
1165
|
+
`;
|
|
1166
|
+
}
|
|
1167
|
+
|
|
1110
1168
|
// pkgs/@akanjs/devkit/getDirname.ts
|
|
1111
1169
|
import path2 from "path";
|
|
1112
1170
|
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
@@ -1392,6 +1450,7 @@ ${errorText}, ${warningText} found`];
|
|
|
1392
1450
|
|
|
1393
1451
|
// pkgs/@akanjs/devkit/scanInfo.ts
|
|
1394
1452
|
import path5 from "path";
|
|
1453
|
+
import { rm } from "fs/promises";
|
|
1395
1454
|
|
|
1396
1455
|
// pkgs/@akanjs/devkit/dependencyScanner.ts
|
|
1397
1456
|
import { builtinModules } from "module";
|
|
@@ -1713,6 +1772,7 @@ var appRootAllowedFiles = new Set([
|
|
|
1713
1772
|
"tsconfig.json",
|
|
1714
1773
|
"tsconfig.tsbuildinfo"
|
|
1715
1774
|
]);
|
|
1775
|
+
var generatedRootCapacitorConfigFiles = ["capacitor.config.js", "capacitor.config.json"];
|
|
1716
1776
|
var appRootAllowedDirs = new Set([
|
|
1717
1777
|
".akan",
|
|
1718
1778
|
"android",
|
|
@@ -1756,11 +1816,17 @@ var rootSignalTestFilePattern = /^[A-Za-z][A-Za-z0-9_-]*\.signal\.(test|spec)\.(
|
|
|
1756
1816
|
var isAllowedTestFile = (filename) => testFilePattern.test(filename);
|
|
1757
1817
|
var isAllowedLibRootFile = (filename) => libRootAllowedFiles.has(filename) || rootSignalTestFilePattern.test(filename);
|
|
1758
1818
|
var getScanPath = (exec, relativePath) => path5.posix.join(`${exec.type}s`, exec.name, relativePath.split(path5.sep).join("/"));
|
|
1819
|
+
async function clearGeneratedRootCapacitorConfigs(exec) {
|
|
1820
|
+
if (exec.type !== "app")
|
|
1821
|
+
return;
|
|
1822
|
+
await Promise.all(generatedRootCapacitorConfigFiles.map((filename) => rm(exec.getPath(filename), { force: true })));
|
|
1823
|
+
}
|
|
1759
1824
|
var getModuleNameFromPath = (kind, modulePath) => {
|
|
1760
1825
|
const dirname = path5.basename(modulePath);
|
|
1761
1826
|
return kind === "service" ? dirname.replace(/^_+/, "") : dirname;
|
|
1762
1827
|
};
|
|
1763
1828
|
async function assertScanConvention(exec, libRoot) {
|
|
1829
|
+
await clearGeneratedRootCapacitorConfigs(exec);
|
|
1764
1830
|
const violations = [];
|
|
1765
1831
|
const addViolation = (relativePath, reason) => {
|
|
1766
1832
|
violations.push(`${getScanPath(exec, relativePath)}: ${reason}`);
|
|
@@ -3577,6 +3643,8 @@ class AppExecutor extends SysExecutor {
|
|
|
3577
3643
|
...routeEnv,
|
|
3578
3644
|
...devPort ? { PORT: devPort, AKAN_PUBLIC_CLIENT_PORT: devPort, AKAN_PUBLIC_SERVER_PORT: devPort } : {}
|
|
3579
3645
|
});
|
|
3646
|
+
if (type === "build")
|
|
3647
|
+
Object.assign(process.env, env);
|
|
3580
3648
|
return { env };
|
|
3581
3649
|
}
|
|
3582
3650
|
#publicEnv = null;
|
|
@@ -3687,8 +3755,30 @@ class AppExecutor extends SysExecutor {
|
|
|
3687
3755
|
});
|
|
3688
3756
|
if (write)
|
|
3689
3757
|
await this.syncAssets(scanInfo.getScanResult().libDeps);
|
|
3758
|
+
if (write)
|
|
3759
|
+
await this.#syncFirebaseMessagingSw();
|
|
3690
3760
|
return scanInfo;
|
|
3691
3761
|
}
|
|
3762
|
+
async#syncFirebaseMessagingSw() {
|
|
3763
|
+
const swRelPath = "public/firebase-messaging-sw.js";
|
|
3764
|
+
if (await FileSys.fileExists(this.getPath(swRelPath)))
|
|
3765
|
+
return;
|
|
3766
|
+
const envClientPath = path7.join(this.cwdPath, "env", "env.client.ts");
|
|
3767
|
+
if (!await FileSys.fileExists(envClientPath))
|
|
3768
|
+
return;
|
|
3769
|
+
let firebaseConfig = null;
|
|
3770
|
+
try {
|
|
3771
|
+
const envUrl = pathToFileURL(envClientPath);
|
|
3772
|
+
envUrl.searchParams.set("t", String(Date.now()));
|
|
3773
|
+
const envModule = await import(envUrl.href);
|
|
3774
|
+
firebaseConfig = normalizeFirebaseClientConfig(envModule.env?.firebase);
|
|
3775
|
+
} catch {
|
|
3776
|
+
return;
|
|
3777
|
+
}
|
|
3778
|
+
if (!firebaseConfig)
|
|
3779
|
+
return;
|
|
3780
|
+
await this.writeFile(swRelPath, createFirebaseMessagingServiceWorker(firebaseConfig), { overwrite: false });
|
|
3781
|
+
}
|
|
3692
3782
|
async increaseBuildNum() {
|
|
3693
3783
|
await increaseBuildNum(this);
|
|
3694
3784
|
}
|
|
@@ -4996,7 +5086,6 @@ var workflowSyncStatePath = (target) => `${workflowSyncDir}/${syncStateSlug(targ
|
|
|
4996
5086
|
var generatedFilePathsForTarget = (targetRoot, reason = "Generated files were refreshed by sync.") => [
|
|
4997
5087
|
{ path: `${targetRoot}/lib/cnst.ts`, action: "sync", reason },
|
|
4998
5088
|
{ path: `${targetRoot}/lib/dict.ts`, action: "sync", reason },
|
|
4999
|
-
{ path: `${targetRoot}/lib/option.ts`, action: "sync", reason },
|
|
5000
5089
|
{ path: `${targetRoot}/lib/index.ts`, action: "sync", reason }
|
|
5001
5090
|
];
|
|
5002
5091
|
var writeGeneratedSyncState = async (workspace, state) => {
|
|
@@ -7650,13 +7739,59 @@ var resourceList = [
|
|
|
7650
7739
|
{ uri: "akan://workspace/modules", name: "Workspace modules", mimeType: "application/json" }
|
|
7651
7740
|
];
|
|
7652
7741
|
var cursorMcpConfigPath = ".cursor/mcp.json";
|
|
7742
|
+
var claudeMcpConfigPath = ".mcp.json";
|
|
7743
|
+
var codexMcpConfigPath = ".codex/config.toml";
|
|
7744
|
+
var akanMcpInstallTargets = ["cursor", "claude", "codex"];
|
|
7745
|
+
var akanMcpInstallConfigPaths = {
|
|
7746
|
+
cursor: cursorMcpConfigPath,
|
|
7747
|
+
claude: claudeMcpConfigPath,
|
|
7748
|
+
codex: codexMcpConfigPath
|
|
7749
|
+
};
|
|
7653
7750
|
var cursorWorkspaceFolder = "$" + "{workspaceFolder}";
|
|
7751
|
+
var claudeProjectDir = "$CLAUDE_PROJECT_DIR";
|
|
7752
|
+
var akanMcpCommand = (mode, { cd } = {}) => cd ? `cd "${cd}" && akan mcp --mode ${mode}` : `akan mcp --mode ${mode}`;
|
|
7654
7753
|
var createAkanCursorMcpServer = (mode = "readonly") => ({
|
|
7655
7754
|
type: "stdio",
|
|
7656
7755
|
command: "bash",
|
|
7657
|
-
args: ["-lc",
|
|
7756
|
+
args: ["-lc", akanMcpCommand(mode, { cd: cursorWorkspaceFolder })]
|
|
7757
|
+
});
|
|
7758
|
+
var createAkanClaudeMcpServer = (mode = "readonly") => ({
|
|
7759
|
+
type: "stdio",
|
|
7760
|
+
command: "bash",
|
|
7761
|
+
args: ["-lc", akanMcpCommand(mode, { cd: claudeProjectDir })]
|
|
7658
7762
|
});
|
|
7763
|
+
var createAkanMcpServer = (target, mode = "readonly") => target === "cursor" ? createAkanCursorMcpServer(mode) : createAkanClaudeMcpServer(mode);
|
|
7659
7764
|
var akanCursorMcpServer = createAkanCursorMcpServer();
|
|
7765
|
+
var codexMcpServerTableHeader = "[mcp_servers.akan]";
|
|
7766
|
+
var createAkanCodexMcpServerBlock = (mode = "readonly") => `${codexMcpServerTableHeader}
|
|
7767
|
+
command = "bash"
|
|
7768
|
+
args = ["-lc", "${akanMcpCommand(mode)}"]
|
|
7769
|
+
`;
|
|
7770
|
+
var codexAkanTablePattern = /^\[mcp_servers\.akan\][^\n]*\n(?:(?!\[)[^\n]*(?:\n|$))*/m;
|
|
7771
|
+
var upsertCodexMcpServerBlock = (existing, block, { force = false } = {}) => {
|
|
7772
|
+
const nextBlock = block.endsWith(`
|
|
7773
|
+
`) ? block : `${block}
|
|
7774
|
+
`;
|
|
7775
|
+
const match = existing.match(codexAkanTablePattern);
|
|
7776
|
+
if (!match) {
|
|
7777
|
+
if (!existing.trim())
|
|
7778
|
+
return nextBlock;
|
|
7779
|
+
return `${existing.replace(/\s*$/, "")}
|
|
7780
|
+
|
|
7781
|
+
${nextBlock}`;
|
|
7782
|
+
}
|
|
7783
|
+
if (match[0].trim() === nextBlock.trim())
|
|
7784
|
+
return existing;
|
|
7785
|
+
if (!force)
|
|
7786
|
+
throw new Error(`${codexMcpConfigPath} already has an "akan" MCP server. Re-run with --force to overwrite it.`);
|
|
7787
|
+
const start = match.index ?? 0;
|
|
7788
|
+
const before = existing.slice(0, start);
|
|
7789
|
+
const after = existing.slice(start + match[0].length);
|
|
7790
|
+
const separator = after && !after.startsWith(`
|
|
7791
|
+
`) ? `
|
|
7792
|
+
` : "";
|
|
7793
|
+
return `${before}${nextBlock}${separator}${after}`;
|
|
7794
|
+
};
|
|
7660
7795
|
var renderDoctorText = (result) => {
|
|
7661
7796
|
const lines = [`Akan doctor status: ${result.status}`];
|
|
7662
7797
|
if (result.diagnostics.length === 0) {
|
|
@@ -7679,7 +7814,6 @@ var generatedFiles = [
|
|
|
7679
7814
|
"*/lib/cnst.ts",
|
|
7680
7815
|
"*/lib/db.ts",
|
|
7681
7816
|
"*/lib/dict.ts",
|
|
7682
|
-
"*/lib/option.ts",
|
|
7683
7817
|
"*/lib/sig.ts",
|
|
7684
7818
|
"*/lib/srv.ts",
|
|
7685
7819
|
"*/lib/st.ts",
|
|
@@ -8673,7 +8807,7 @@ Caused by: ${ApplicationBuildReporter.formatError(error.cause)}` : "";
|
|
|
8673
8807
|
}
|
|
8674
8808
|
}
|
|
8675
8809
|
// pkgs/@akanjs/devkit/applicationBuildRunner.ts
|
|
8676
|
-
import { mkdir as mkdir8, rm as
|
|
8810
|
+
import { mkdir as mkdir8, rm as rm4 } from "fs/promises";
|
|
8677
8811
|
import path37 from "path";
|
|
8678
8812
|
|
|
8679
8813
|
// pkgs/@akanjs/devkit/frontendBuild/allRoutesBuilder.ts
|
|
@@ -10431,7 +10565,7 @@ class AllRoutesBuilder {
|
|
|
10431
10565
|
}
|
|
10432
10566
|
}
|
|
10433
10567
|
// pkgs/@akanjs/devkit/frontendBuild/csrArtifactBuilder.ts
|
|
10434
|
-
import { mkdir as mkdir5, rm, unlink } from "fs/promises";
|
|
10568
|
+
import { mkdir as mkdir5, rm as rm2, unlink } from "fs/promises";
|
|
10435
10569
|
import path24 from "path";
|
|
10436
10570
|
|
|
10437
10571
|
// pkgs/@akanjs/devkit/frontendBuild/pagesEntrySourceGenerator.ts
|
|
@@ -10561,7 +10695,7 @@ class CsrArtifactBuilder {
|
|
|
10561
10695
|
const artifact = await this.#loadCsrArtifact();
|
|
10562
10696
|
const csrBasePaths = [...akanConfig2.basePaths];
|
|
10563
10697
|
const htmlEntries = csrBasePaths.length > 0 ? csrBasePaths : ["index"];
|
|
10564
|
-
await
|
|
10698
|
+
await rm2(this.#outputDir, { recursive: true, force: true });
|
|
10565
10699
|
await mkdir5(path24.join(this.#app.cwdPath, ".akan/generated/csr"), { recursive: true });
|
|
10566
10700
|
const generatedHtmlFiles = Object.fromEntries(htmlEntries.map((basePath2) => this.#createHtmlFile(basePath2)));
|
|
10567
10701
|
const result = await Bun.build({
|
|
@@ -11295,7 +11429,7 @@ class DevChangePlanner {
|
|
|
11295
11429
|
}
|
|
11296
11430
|
var uniqueResolved = (files) => [...new Set(files.map((file) => path27.resolve(file)))].sort();
|
|
11297
11431
|
// pkgs/@akanjs/devkit/frontendBuild/devGeneratedIndexSync.ts
|
|
11298
|
-
import { mkdir as mkdir6, readdir as readdir2, readFile, rm as
|
|
11432
|
+
import { mkdir as mkdir6, readdir as readdir2, readFile, rm as rm3, stat as stat3, writeFile } from "fs/promises";
|
|
11299
11433
|
import path28 from "path";
|
|
11300
11434
|
var BARREL_FACETS2 = new Set(["common", "srvkit", "ui", "webkit"]);
|
|
11301
11435
|
var FACET_SOURCE_FILE_RE = /\.(ts|tsx)$/;
|
|
@@ -11378,7 +11512,7 @@ class DevGeneratedIndexSync {
|
|
|
11378
11512
|
if (content === null) {
|
|
11379
11513
|
if (!await exists(indexPath))
|
|
11380
11514
|
return false;
|
|
11381
|
-
await
|
|
11515
|
+
await rm3(indexPath, { force: true });
|
|
11382
11516
|
return true;
|
|
11383
11517
|
}
|
|
11384
11518
|
const current = await readFile(indexPath, "utf8").catch(() => null);
|
|
@@ -12525,7 +12659,7 @@ class ApplicationBuildRunner {
|
|
|
12525
12659
|
await this.#app.getPageKeys({ refresh: true });
|
|
12526
12660
|
const { typecheckDir, tsconfigPath } = await this.#writeTypecheckTsconfig({ incremental });
|
|
12527
12661
|
if (clean)
|
|
12528
|
-
await
|
|
12662
|
+
await rm4(path37.join(typecheckDir, "tsconfig.tsbuildinfo"), { force: true });
|
|
12529
12663
|
await this.#checkProjectInChildProcess(tsconfigPath);
|
|
12530
12664
|
}
|
|
12531
12665
|
async#runPhase(id, label, task, summarize, options = {}) {
|
|
@@ -12725,7 +12859,7 @@ void run().catch((error) => {
|
|
|
12725
12859
|
}
|
|
12726
12860
|
}
|
|
12727
12861
|
// pkgs/@akanjs/devkit/applicationReleasePackager.ts
|
|
12728
|
-
import { cp, mkdir as mkdir9, rm as
|
|
12862
|
+
import { cp, mkdir as mkdir9, rm as rm5 } from "fs/promises";
|
|
12729
12863
|
import path38 from "path";
|
|
12730
12864
|
|
|
12731
12865
|
// pkgs/@akanjs/devkit/uploadRelease.ts
|
|
@@ -12832,7 +12966,7 @@ class ApplicationReleasePackager {
|
|
|
12832
12966
|
const platformVersion = akanConfig2.mobile.version;
|
|
12833
12967
|
const buildRoot = `${this.#app.workspace.workspaceRoot}/releases/builds/${this.#app.name}`;
|
|
12834
12968
|
if (await FileSys.dirExists(buildRoot))
|
|
12835
|
-
await
|
|
12969
|
+
await rm5(buildRoot, { recursive: true, force: true });
|
|
12836
12970
|
await mkdir9(buildRoot, { recursive: true });
|
|
12837
12971
|
if (rebuild || !await FileSys.dirExists(`${this.#app.dist.cwdPath}/backend`))
|
|
12838
12972
|
await this.#build();
|
|
@@ -12841,7 +12975,7 @@ class ApplicationReleasePackager {
|
|
|
12841
12975
|
await mkdir9(buildPath, { recursive: true });
|
|
12842
12976
|
await cp(`${this.#app.dist.cwdPath}/backend`, `${buildPath}/backend`, { recursive: true });
|
|
12843
12977
|
await cp(this.#app.dist.cwdPath, buildRoot, { recursive: true });
|
|
12844
|
-
await
|
|
12978
|
+
await rm5(`${buildRoot}/frontend/.next`, { recursive: true, force: true });
|
|
12845
12979
|
const releaseRoot = this.#app.workspace.workspaceRoot;
|
|
12846
12980
|
const releaseArchivePath = path38.relative(releaseRoot, `${releaseRoot}/releases/builds/${this.#app.name}-release.tar.gz`).split(path38.sep).join("/");
|
|
12847
12981
|
await this.#app.workspace.spawn("tar", ["-zcf", releaseArchivePath, "-C", buildRoot, "./"], { cwd: releaseRoot });
|
|
@@ -12857,7 +12991,7 @@ class ApplicationReleasePackager {
|
|
|
12857
12991
|
`${this.#app.workspace.workspaceRoot}/releases/builds/${this.#app.name}-appBuild.zip`,
|
|
12858
12992
|
"./csr"
|
|
12859
12993
|
]);
|
|
12860
|
-
await
|
|
12994
|
+
await rm5("./csr", { recursive: true, force: true });
|
|
12861
12995
|
}
|
|
12862
12996
|
async#writeSourceArchive({ readme }) {
|
|
12863
12997
|
const sourceRoot = `${this.#app.workspace.workspaceRoot}/releases/sources/${this.#app.name}`;
|
|
@@ -12868,7 +13002,7 @@ class ApplicationReleasePackager {
|
|
|
12868
13002
|
await Promise.all([".next", "ios", "android", "public/libs"].map(async (path39) => {
|
|
12869
13003
|
const targetPath = `${sourceRoot}/apps/${this.#app.name}/${path39}`;
|
|
12870
13004
|
if (await FileSys.dirExists(targetPath))
|
|
12871
|
-
await
|
|
13005
|
+
await rm5(targetPath, { recursive: true, force: true });
|
|
12872
13006
|
}));
|
|
12873
13007
|
const syncPaths = [".husky", ".gitignore", "package.json"];
|
|
12874
13008
|
await Promise.all(syncPaths.map((path39) => cp(`${this.#app.workspace.cwdPath}/${path39}`, `${sourceRoot}/${path39}`, { recursive: true })));
|
|
@@ -12883,7 +13017,7 @@ class ApplicationReleasePackager {
|
|
|
12883
13017
|
const maxRetry = 3;
|
|
12884
13018
|
for (let i = 0;i < maxRetry; i++) {
|
|
12885
13019
|
try {
|
|
12886
|
-
await
|
|
13020
|
+
await rm5(sourceRoot, { recursive: true, force: true });
|
|
12887
13021
|
} catch {}
|
|
12888
13022
|
}
|
|
12889
13023
|
}
|
|
@@ -13116,7 +13250,7 @@ class Builder {
|
|
|
13116
13250
|
}
|
|
13117
13251
|
}
|
|
13118
13252
|
// pkgs/@akanjs/devkit/capacitorApp.ts
|
|
13119
|
-
import { cp as cp2, mkdir as mkdir11, readFile as readFile2, rm as
|
|
13253
|
+
import { cp as cp2, mkdir as mkdir11, readFile as readFile2, rm as rm6, writeFile as writeFile2 } from "fs/promises";
|
|
13120
13254
|
import os from "os";
|
|
13121
13255
|
import path41 from "path";
|
|
13122
13256
|
import { select as select2 } from "@inquirer/prompts";
|
|
@@ -13327,7 +13461,7 @@ var rootCapacitorConfigFilenames = [
|
|
|
13327
13461
|
];
|
|
13328
13462
|
var rootCapacitorConfigPaths = (appRoot) => rootCapacitorConfigFilenames.map((file) => path41.join(appRoot, file));
|
|
13329
13463
|
async function clearRootCapacitorConfigs(appRoot) {
|
|
13330
|
-
await Promise.all(rootCapacitorConfigPaths(appRoot).map((file) =>
|
|
13464
|
+
await Promise.all(rootCapacitorConfigPaths(appRoot).map((file) => rm6(file, { force: true })));
|
|
13331
13465
|
}
|
|
13332
13466
|
async function writeRootCapacitorConfig(appRoot, content) {
|
|
13333
13467
|
await clearRootCapacitorConfigs(appRoot);
|
|
@@ -13348,6 +13482,7 @@ var getLocalIP = () => {
|
|
|
13348
13482
|
var isRecord2 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
|
|
13349
13483
|
var asString = (value) => typeof value === "string" ? value : undefined;
|
|
13350
13484
|
var firstString = (...values) => values.find((value) => typeof value === "string");
|
|
13485
|
+
var resolveIosApnsEnvironment = ({ operation }) => operation === "release" ? "production" : "development";
|
|
13351
13486
|
var scoreIosDeviceTarget = (target) => {
|
|
13352
13487
|
const state = target.state?.toLowerCase() ?? "";
|
|
13353
13488
|
return (state.includes("available") ? 4 : 0) + (state.includes("wired") ? 2 : 0) + (state.includes("paired") ? 1 : 0);
|
|
@@ -13653,6 +13788,8 @@ function materializeCapacitorConfig(target, { operation, localServerUrl, localIp
|
|
|
13653
13788
|
const experimentalConfig = isRecord2(experimental) ? experimental : undefined;
|
|
13654
13789
|
const pluginsConfig = isRecord2(plugins) ? plugins : {};
|
|
13655
13790
|
const keyboardPluginConfig = isRecord2(pluginsConfig.Keyboard) ? pluginsConfig.Keyboard : {};
|
|
13791
|
+
const pushNotificationsPluginConfig = isRecord2(pluginsConfig.PushNotifications) ? pluginsConfig.PushNotifications : {};
|
|
13792
|
+
const usesPushNotifications = target.permissions?.includes("push") ?? false;
|
|
13656
13793
|
const config = {
|
|
13657
13794
|
...capacitorConfig,
|
|
13658
13795
|
appId,
|
|
@@ -13661,6 +13798,12 @@ function materializeCapacitorConfig(target, { operation, localServerUrl, localIp
|
|
|
13661
13798
|
plugins: {
|
|
13662
13799
|
CapacitorCookies: { enabled: true },
|
|
13663
13800
|
...pluginsConfig,
|
|
13801
|
+
...usesPushNotifications || isRecord2(pluginsConfig.PushNotifications) ? {
|
|
13802
|
+
PushNotifications: {
|
|
13803
|
+
...usesPushNotifications ? { presentationOptions: ["badge", "sound", "alert"] } : {},
|
|
13804
|
+
...pushNotificationsPluginConfig
|
|
13805
|
+
}
|
|
13806
|
+
} : {},
|
|
13664
13807
|
Keyboard: {
|
|
13665
13808
|
resize: "none",
|
|
13666
13809
|
...keyboardPluginConfig
|
|
@@ -13732,9 +13875,9 @@ class CapacitorApp {
|
|
|
13732
13875
|
await mkdir11(this.targetRoot, { recursive: true });
|
|
13733
13876
|
if (regenerate) {
|
|
13734
13877
|
if (!platform || platform === "ios")
|
|
13735
|
-
await
|
|
13878
|
+
await rm6(path41.join(this.app.cwdPath, this.iosRootPath), { recursive: true, force: true });
|
|
13736
13879
|
if (!platform || platform === "android")
|
|
13737
|
-
await
|
|
13880
|
+
await rm6(path41.join(this.app.cwdPath, this.androidRootPath), { recursive: true, force: true });
|
|
13738
13881
|
}
|
|
13739
13882
|
const project = this.project;
|
|
13740
13883
|
await this.project.load();
|
|
@@ -13756,7 +13899,7 @@ class CapacitorApp {
|
|
|
13756
13899
|
await this.#prepareTargetAssets();
|
|
13757
13900
|
await this.#prepareExternalFiles("ios");
|
|
13758
13901
|
await this.#applyIosMetadata();
|
|
13759
|
-
await this.#applyPermissions();
|
|
13902
|
+
await this.#applyPermissions({ operation, env });
|
|
13760
13903
|
await this.#applyDeepLinks("ios", { operation, env });
|
|
13761
13904
|
await this.project.commit();
|
|
13762
13905
|
await this.#generateAssets({ operation, env });
|
|
@@ -13899,7 +14042,7 @@ ${error.message}`;
|
|
|
13899
14042
|
await this.#prepareTargetAssets();
|
|
13900
14043
|
await this.#prepareExternalFiles("android");
|
|
13901
14044
|
await this.#applyAndroidMetadata();
|
|
13902
|
-
await this.#applyPermissions();
|
|
14045
|
+
await this.#applyPermissions({ operation, env });
|
|
13903
14046
|
await this.#applyDeepLinks("android", { operation, env });
|
|
13904
14047
|
await this.project.commit();
|
|
13905
14048
|
await this.#generateAssets({ operation, env });
|
|
@@ -14033,7 +14176,7 @@ ${error.message}`;
|
|
|
14033
14176
|
const htmlSource = path41.join(this.app.dist.cwdPath, "csr", targetHtmlFilename(this.target));
|
|
14034
14177
|
if (!await Bun.file(htmlSource).exists())
|
|
14035
14178
|
throw new Error(`CSR html for mobile target '${this.target.name}' not found: ${htmlSource}`);
|
|
14036
|
-
await
|
|
14179
|
+
await rm6(this.targetWebRoot, { recursive: true, force: true });
|
|
14037
14180
|
await mkdir11(this.targetWebRoot, { recursive: true });
|
|
14038
14181
|
await Bun.write(path41.join(this.targetWebRoot, "index.html"), this.#injectMobileTargetMeta(await Bun.file(htmlSource).text()));
|
|
14039
14182
|
}
|
|
@@ -14114,7 +14257,7 @@ ${error.message}`;
|
|
|
14114
14257
|
await this.project.android.setVersionCode(this.target.buildNum);
|
|
14115
14258
|
await this.project.android.setAppName(this.target.appName);
|
|
14116
14259
|
}
|
|
14117
|
-
async#applyPermissions() {
|
|
14260
|
+
async#applyPermissions({ operation, env }) {
|
|
14118
14261
|
for (const permission of this.target.permissions ?? []) {
|
|
14119
14262
|
if (permission === "camera")
|
|
14120
14263
|
await this.addCamera();
|
|
@@ -14123,7 +14266,7 @@ ${error.message}`;
|
|
|
14123
14266
|
else if (permission === "location")
|
|
14124
14267
|
await this.addLocation();
|
|
14125
14268
|
else if (permission === "push")
|
|
14126
|
-
await this.addPush();
|
|
14269
|
+
await this.addPush({ operation, env });
|
|
14127
14270
|
}
|
|
14128
14271
|
}
|
|
14129
14272
|
async#applyDeepLinks(platform, { operation, env }) {
|
|
@@ -14142,7 +14285,7 @@ ${error.message}`;
|
|
|
14142
14285
|
await this.#setUrlSchemesInIos(schemes);
|
|
14143
14286
|
}
|
|
14144
14287
|
if (domains.length > 0)
|
|
14145
|
-
await this.#setAssociatedDomainsInIos(domains);
|
|
14288
|
+
await this.#setAssociatedDomainsInIos(domains, { operation, env });
|
|
14146
14289
|
return;
|
|
14147
14290
|
}
|
|
14148
14291
|
if (platform === "android") {
|
|
@@ -14236,12 +14379,64 @@ ${error.message}`;
|
|
|
14236
14379
|
this.#setPermissionsInAndroid(["ACCESS_COARSE_LOCATION", "ACCESS_FINE_LOCATION"]);
|
|
14237
14380
|
this.#setFeaturesInAndroid(["android.hardware.location.gps"]);
|
|
14238
14381
|
}
|
|
14239
|
-
async addPush() {
|
|
14382
|
+
async addPush({ operation, env }) {
|
|
14240
14383
|
await this.#setPermissionInIos({
|
|
14241
14384
|
userNotificationsUsageDescription: "$(PRODUCT_NAME) uses notifications to keep you updated."
|
|
14242
14385
|
});
|
|
14386
|
+
await Promise.all([
|
|
14387
|
+
this.project.ios.updateInfoPlist(this.iosTargetName, "Debug", { UIBackgroundModes: ["remote-notification"] }),
|
|
14388
|
+
this.project.ios.updateInfoPlist(this.iosTargetName, "Release", { UIBackgroundModes: ["remote-notification"] }),
|
|
14389
|
+
this.#writeEntitlementsInIos(this.target.deepLinks?.domains ?? [], { operation, env }),
|
|
14390
|
+
this.#ensurePushAppDelegateInIos()
|
|
14391
|
+
]);
|
|
14243
14392
|
this.#setPermissionsInAndroid(["POST_NOTIFICATIONS"]);
|
|
14244
14393
|
}
|
|
14394
|
+
async#ensurePushAppDelegateInIos() {
|
|
14395
|
+
const appDelegatePath = path41.join(this.app.cwdPath, this.iosProjectPath, "App/AppDelegate.swift");
|
|
14396
|
+
if (!await Bun.file(appDelegatePath).exists())
|
|
14397
|
+
return;
|
|
14398
|
+
const editor = await FileEditor.create(appDelegatePath);
|
|
14399
|
+
let content = editor.getContent();
|
|
14400
|
+
if (!content.includes("import FirebaseCore")) {
|
|
14401
|
+
content = content.replace("import Capacitor", `import Capacitor
|
|
14402
|
+
import FirebaseCore`);
|
|
14403
|
+
}
|
|
14404
|
+
if (!content.includes("import FirebaseMessaging")) {
|
|
14405
|
+
content = content.replace("import FirebaseCore", `import FirebaseCore
|
|
14406
|
+
import FirebaseMessaging`);
|
|
14407
|
+
}
|
|
14408
|
+
if (!content.includes("FirebaseApp.configure()")) {
|
|
14409
|
+
content = content.replace(/\n(\s*)return true/, `
|
|
14410
|
+
$1FirebaseApp.configure()
|
|
14411
|
+
|
|
14412
|
+
$1return true`);
|
|
14413
|
+
}
|
|
14414
|
+
if (!content.includes("didRegisterForRemoteNotificationsWithDeviceToken")) {
|
|
14415
|
+
const delegateMethods = `
|
|
14416
|
+
func application(_ application: UIApplication,
|
|
14417
|
+
didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data) {
|
|
14418
|
+
Messaging.messaging().apnsToken = deviceToken
|
|
14419
|
+
NotificationCenter.default.post(
|
|
14420
|
+
name: .capacitorDidRegisterForRemoteNotifications,
|
|
14421
|
+
object: deviceToken
|
|
14422
|
+
)
|
|
14423
|
+
}
|
|
14424
|
+
|
|
14425
|
+
func application(_ application: UIApplication,
|
|
14426
|
+
didFailToRegisterForRemoteNotificationsWithError error: Error) {
|
|
14427
|
+
NotificationCenter.default.post(
|
|
14428
|
+
name: .capacitorDidFailToRegisterForRemoteNotifications,
|
|
14429
|
+
object: error
|
|
14430
|
+
)
|
|
14431
|
+
}
|
|
14432
|
+
`;
|
|
14433
|
+
content = content.replace(`
|
|
14434
|
+
func applicationWillResignActive`, `${delegateMethods}
|
|
14435
|
+
func applicationWillResignActive`);
|
|
14436
|
+
}
|
|
14437
|
+
if (content !== editor.getContent())
|
|
14438
|
+
await editor.setContent(content).save();
|
|
14439
|
+
}
|
|
14245
14440
|
async#setPermissionInIos(permissions) {
|
|
14246
14441
|
const updateNs = Object.fromEntries(Object.entries(permissions).map(([key, value]) => [`NS${capitalize5(key)}`, value]));
|
|
14247
14442
|
await Promise.all([
|
|
@@ -14259,25 +14454,35 @@ ${error.message}`;
|
|
|
14259
14454
|
this.project.ios.updateInfoPlist(this.iosTargetName, "Release", { CFBundleURLTypes: urlTypes })
|
|
14260
14455
|
]);
|
|
14261
14456
|
}
|
|
14262
|
-
async#setAssociatedDomainsInIos(domains) {
|
|
14457
|
+
async#setAssociatedDomainsInIos(domains, { operation, env }) {
|
|
14458
|
+
await this.#writeEntitlementsInIos(domains, { operation, env });
|
|
14459
|
+
}
|
|
14460
|
+
async#writeEntitlementsInIos(domains, runConfig) {
|
|
14263
14461
|
const entitlementsRelPath = "App/App.entitlements";
|
|
14264
14462
|
const entitlementsPath = path41.join(this.app.cwdPath, this.iosProjectPath, entitlementsRelPath);
|
|
14265
14463
|
const values = domains.map((domain) => `applinks:${domain}`);
|
|
14464
|
+
const usesPush = this.target.permissions?.includes("push") ?? false;
|
|
14465
|
+
const apsEnvironment = resolveIosApnsEnvironment(runConfig);
|
|
14266
14466
|
const body = [
|
|
14267
14467
|
'<?xml version="1.0" encoding="UTF-8"?>',
|
|
14268
14468
|
'<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">',
|
|
14269
14469
|
'<plist version="1.0">',
|
|
14270
14470
|
"<dict>",
|
|
14271
|
-
" <key>
|
|
14272
|
-
|
|
14273
|
-
|
|
14274
|
-
|
|
14471
|
+
...usesPush ? [" <key>aps-environment</key>", ` <string>${apsEnvironment}</string>`] : [],
|
|
14472
|
+
...values.length > 0 ? [
|
|
14473
|
+
" <key>com.apple.developer.associated-domains</key>",
|
|
14474
|
+
" <array>",
|
|
14475
|
+
...values.map((value) => ` <string>${value}</string>`),
|
|
14476
|
+
" </array>"
|
|
14477
|
+
] : [],
|
|
14275
14478
|
"</dict>",
|
|
14276
14479
|
"</plist>",
|
|
14277
14480
|
""
|
|
14278
14481
|
].join(`
|
|
14279
14482
|
`);
|
|
14280
|
-
await
|
|
14483
|
+
const currentBody = await Bun.file(entitlementsPath).exists() ? await Bun.file(entitlementsPath).text() : undefined;
|
|
14484
|
+
if (currentBody !== body)
|
|
14485
|
+
await writeFile2(entitlementsPath, body);
|
|
14281
14486
|
await this.#setCodeSignEntitlementsInIos(entitlementsRelPath);
|
|
14282
14487
|
}
|
|
14283
14488
|
async#setCodeSignEntitlementsInIos(entitlementsRelPath) {
|
|
@@ -14381,7 +14586,7 @@ ${filter}$1`);
|
|
|
14381
14586
|
for (const permission of permissions) {
|
|
14382
14587
|
if (this.#hasPermissionInAndroid(permission)) {
|
|
14383
14588
|
this.app.logger.info(`${permission} already exists in android`);
|
|
14384
|
-
|
|
14589
|
+
continue;
|
|
14385
14590
|
}
|
|
14386
14591
|
this.app.logger.info(`Adding ${permission} to android`);
|
|
14387
14592
|
this.project.android.getAndroidManifest().injectFragment("manifest", `<uses-permission android:name="android.permission.${permission}" />`);
|
|
@@ -14397,7 +14602,7 @@ ${filter}$1`);
|
|
|
14397
14602
|
return Array.from(usesPermission).map((permission) => permission.getAttribute("android:name"));
|
|
14398
14603
|
}
|
|
14399
14604
|
#hasPermissionInAndroid(permission) {
|
|
14400
|
-
return this.#getPermissionsInAndroid().includes(permission);
|
|
14605
|
+
return this.#getPermissionsInAndroid().includes(`android.permission.${permission}`);
|
|
14401
14606
|
}
|
|
14402
14607
|
}
|
|
14403
14608
|
// pkgs/@akanjs/devkit/commandDecorators/targetMeta.ts
|