@akanjs/cli 2.3.5-rc.9 → 2.3.6-rc.0
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.
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
var __require = import.meta.require;
|
|
3
3
|
|
|
4
4
|
// pkgs/@akanjs/devkit/incrementalBuilder/incrementalBuilder.proc.ts
|
|
5
|
-
import
|
|
5
|
+
import path40 from "path";
|
|
6
6
|
|
|
7
7
|
// pkgs/@akanjs/devkit/aiEditor.ts
|
|
8
8
|
import { input, select } from "@inquirer/prompts";
|
|
@@ -3940,6 +3940,36 @@ var createTunnel = async (service, { app, environment, port = service === "postg
|
|
|
3940
3940
|
return `localhost:${port}`;
|
|
3941
3941
|
};
|
|
3942
3942
|
|
|
3943
|
+
// pkgs/@akanjs/devkit/incrementalBuilder/devWatchBatch.ts
|
|
3944
|
+
var prepareDevWatchBatch = ({
|
|
3945
|
+
generation,
|
|
3946
|
+
batch,
|
|
3947
|
+
indexSync,
|
|
3948
|
+
changePlanner
|
|
3949
|
+
}) => {
|
|
3950
|
+
const files = [...new Set([...batch.files, ...indexSync.changedFiles])].sort();
|
|
3951
|
+
const kindSet = new Set(batch.kinds);
|
|
3952
|
+
if (indexSync.changedFiles.length > 0)
|
|
3953
|
+
kindSet.add("code");
|
|
3954
|
+
const kinds = [...kindSet];
|
|
3955
|
+
const expandedBatch = { files, kinds: kindSet };
|
|
3956
|
+
const devPlan = changePlanner.plan({
|
|
3957
|
+
generation,
|
|
3958
|
+
files,
|
|
3959
|
+
kinds,
|
|
3960
|
+
generatedFiles: indexSync.changedFiles
|
|
3961
|
+
});
|
|
3962
|
+
if (indexSync.errors.length > 0 && !devPlan.actions.includes("report-error")) {
|
|
3963
|
+
devPlan.actions = [...devPlan.actions, "report-error"].sort();
|
|
3964
|
+
}
|
|
3965
|
+
return {
|
|
3966
|
+
files,
|
|
3967
|
+
kinds,
|
|
3968
|
+
expandedBatch,
|
|
3969
|
+
event: { type: "invalidate", kinds, files, generation, devPlan },
|
|
3970
|
+
hasSyncErrors: indexSync.errors.length > 0
|
|
3971
|
+
};
|
|
3972
|
+
};
|
|
3943
3973
|
// pkgs/@akanjs/devkit/incrementalBuilder/incrementalBuilder.host.ts
|
|
3944
3974
|
import path8 from "path";
|
|
3945
3975
|
import { Logger as Logger4 } from "akanjs/common";
|
|
@@ -3948,7 +3978,8 @@ var builderMsgTypeSet = new Set([
|
|
|
3948
3978
|
"builder-ready",
|
|
3949
3979
|
"invalidate",
|
|
3950
3980
|
"css-updated",
|
|
3951
|
-
"pages-updated"
|
|
3981
|
+
"pages-updated",
|
|
3982
|
+
"build-status"
|
|
3952
3983
|
]);
|
|
3953
3984
|
|
|
3954
3985
|
class IncrementalBuilderHost {
|
|
@@ -4091,12 +4122,76 @@ var BUILDER_READY_TIMEOUT_MS = 15000;
|
|
|
4091
4122
|
var BUILDER_START_MAX_ATTEMPTS = 3;
|
|
4092
4123
|
var SOURCE_EXTS = new Set([".ts", ".tsx", ".js", ".jsx", ".mjs", ".cjs"]);
|
|
4093
4124
|
var NON_SOURCE_EXT_RE = /\.(css|scss|sass|less|json|svg|png|jpe?g|webp|gif|avif|ico|woff2?|ttf|otf|mp3|mp4|wav|html)$/i;
|
|
4125
|
+
var SERVER_SUFFIXES = [".service.ts", ".document.ts"];
|
|
4126
|
+
var SHARED_SUFFIXES = [".constant.ts", ".dictionary.ts", ".signal.ts"];
|
|
4127
|
+
var RUNTIME_METADATA_BASENAMES = new Set(["dict.ts", "sig.ts", "useClient.ts", "useServer.ts"]);
|
|
4094
4128
|
var GRAPH_IMPORT_KINDS = new Set([
|
|
4095
4129
|
"import-statement",
|
|
4096
4130
|
"require-call",
|
|
4097
4131
|
"require-resolve",
|
|
4098
4132
|
"dynamic-import"
|
|
4099
4133
|
]);
|
|
4134
|
+
var shouldRestartBackendByDevPlan = (message) => {
|
|
4135
|
+
if (!message.devPlan)
|
|
4136
|
+
return null;
|
|
4137
|
+
if (message.devPlan.actions.includes("report-error"))
|
|
4138
|
+
return false;
|
|
4139
|
+
return message.devPlan.actions.includes("restart-backend");
|
|
4140
|
+
};
|
|
4141
|
+
var shouldRestartDevHostByDevPlan = (message) => message.devPlan?.actions.includes("restart-dev-host") ?? message.kinds.includes("config");
|
|
4142
|
+
var RESTART_ROLE_ORDER = ["server", "shared", "barrel", "config"];
|
|
4143
|
+
var generationValue = (generation) => generation ?? -1;
|
|
4144
|
+
var isLegacyBackendFallbackFile = (file, workspaceRoot) => {
|
|
4145
|
+
const abs = path9.resolve(file);
|
|
4146
|
+
const ext = path9.extname(abs).toLowerCase();
|
|
4147
|
+
if (!SOURCE_EXTS.has(ext))
|
|
4148
|
+
return false;
|
|
4149
|
+
const rel = path9.relative(path9.resolve(workspaceRoot), abs);
|
|
4150
|
+
if (!rel || rel.startsWith("..") || path9.isAbsolute(rel))
|
|
4151
|
+
return false;
|
|
4152
|
+
const parts = rel.split(path9.sep).filter(Boolean);
|
|
4153
|
+
const [scope] = parts;
|
|
4154
|
+
if (scope !== "apps" && scope !== "libs" && scope !== "pkgs")
|
|
4155
|
+
return false;
|
|
4156
|
+
const base = path9.basename(abs);
|
|
4157
|
+
return parts.includes("srvkit") || parts.includes("common") || SERVER_SUFFIXES.some((suffix) => base.endsWith(suffix)) || SHARED_SUFFIXES.some((suffix) => base.endsWith(suffix)) || RUNTIME_METADATA_BASENAMES.has(base) || base === "main.ts" || base === "server.ts";
|
|
4158
|
+
};
|
|
4159
|
+
var shouldMarkBuildPhaseRecovered = (previousByPhase, status) => {
|
|
4160
|
+
const previous = previousByPhase.get(status.phase);
|
|
4161
|
+
return Boolean(previous && status.ok && !previous.ok && generationValue(status.generation) >= previous.generation);
|
|
4162
|
+
};
|
|
4163
|
+
var createBackendBuildStatus = ({
|
|
4164
|
+
generation,
|
|
4165
|
+
ok,
|
|
4166
|
+
files = [],
|
|
4167
|
+
message
|
|
4168
|
+
}) => ({
|
|
4169
|
+
generation,
|
|
4170
|
+
phase: "backend",
|
|
4171
|
+
ok,
|
|
4172
|
+
files,
|
|
4173
|
+
message
|
|
4174
|
+
});
|
|
4175
|
+
var backendRestartReasonFromMessage = (message) => {
|
|
4176
|
+
const roleSet = new Set;
|
|
4177
|
+
for (const role of message.devPlan?.roles ?? []) {
|
|
4178
|
+
if (role === "server" || role === "shared" || role === "barrel" || role === "config")
|
|
4179
|
+
roleSet.add(role);
|
|
4180
|
+
}
|
|
4181
|
+
return {
|
|
4182
|
+
generation: message.devPlan?.generation ?? message.generation,
|
|
4183
|
+
files: [...new Set(message.files)].sort(),
|
|
4184
|
+
roles: RESTART_ROLE_ORDER.filter((role) => roleSet.has(role))
|
|
4185
|
+
};
|
|
4186
|
+
};
|
|
4187
|
+
var mergeBackendRestartReasons = (current, next) => ({
|
|
4188
|
+
generation: generationValue(next.generation) >= generationValue(current?.generation) ? next.generation : current?.generation,
|
|
4189
|
+
files: [...new Set([...current?.files ?? [], ...next.files])].sort(),
|
|
4190
|
+
roles: RESTART_ROLE_ORDER.filter((role) => current?.roles.includes(role) || next.roles.includes(role))
|
|
4191
|
+
});
|
|
4192
|
+
var shouldReplaceLastGoodMessage = (current, next) => !current || generationValue(next.data.generation) >= generationValue(current.data.generation);
|
|
4193
|
+
var shouldQueueBuildStatusReplay = (backendReady, pendingReplayCount) => !backendReady || pendingReplayCount > 0;
|
|
4194
|
+
var buildStatusReplaySequence = (pendingReplay, latestByPhase) => [...pendingReplay, ...latestByPhase.values()];
|
|
4100
4195
|
|
|
4101
4196
|
class BackendImportGraph {
|
|
4102
4197
|
#app;
|
|
@@ -4107,6 +4202,7 @@ class BackendImportGraph {
|
|
|
4107
4202
|
#jsxTranspiler = new Bun.Transpiler({ loader: "jsx" });
|
|
4108
4203
|
#files = new Set;
|
|
4109
4204
|
#ready = false;
|
|
4205
|
+
#lastRefreshSucceeded = false;
|
|
4110
4206
|
constructor(app, logger) {
|
|
4111
4207
|
this.#app = app;
|
|
4112
4208
|
this.#logger = logger;
|
|
@@ -4114,6 +4210,9 @@ class BackendImportGraph {
|
|
|
4114
4210
|
get ready() {
|
|
4115
4211
|
return this.#ready;
|
|
4116
4212
|
}
|
|
4213
|
+
get lastRefreshSucceeded() {
|
|
4214
|
+
return this.#lastRefreshSucceeded;
|
|
4215
|
+
}
|
|
4117
4216
|
has(file) {
|
|
4118
4217
|
return this.#files.has(path9.resolve(file));
|
|
4119
4218
|
}
|
|
@@ -4122,10 +4221,12 @@ class BackendImportGraph {
|
|
|
4122
4221
|
const files = await this.#build();
|
|
4123
4222
|
this.#files = files;
|
|
4124
4223
|
this.#ready = true;
|
|
4224
|
+
this.#lastRefreshSucceeded = true;
|
|
4125
4225
|
this.#logger.verbose(`[backend-graph] scanned ${files.size} files`);
|
|
4126
4226
|
return true;
|
|
4127
4227
|
} catch (err) {
|
|
4128
4228
|
this.#ready = this.#files.size > 0;
|
|
4229
|
+
this.#lastRefreshSucceeded = false;
|
|
4129
4230
|
this.#logger.warn(`[backend-graph] scan failed; ${this.#ready ? "using previous graph" : "using fallback rules"}: ${err instanceof Error ? err.message : String(err)}`);
|
|
4130
4231
|
return this.#ready;
|
|
4131
4232
|
}
|
|
@@ -4212,9 +4313,13 @@ class AkanAppHost {
|
|
|
4212
4313
|
#restartTimer = null;
|
|
4213
4314
|
#backendRecoveryTimer = null;
|
|
4214
4315
|
#backendRecoveryAttempts = 0;
|
|
4215
|
-
#
|
|
4216
|
-
#
|
|
4217
|
-
#
|
|
4316
|
+
#backendLifecycleState = "stopped";
|
|
4317
|
+
#pendingRestartReason = null;
|
|
4318
|
+
#backendStartStatus = null;
|
|
4319
|
+
#backendBuildStatusGeneration = 0;
|
|
4320
|
+
#lastGoodFrontend = {};
|
|
4321
|
+
#buildStatusByPhase = new Map;
|
|
4322
|
+
#pendingBuildStatusReplay = [];
|
|
4218
4323
|
#builderMessageQueue = Promise.resolve();
|
|
4219
4324
|
#backendGraph;
|
|
4220
4325
|
constructor(app, { env, withInk = false }) {
|
|
@@ -4259,7 +4364,9 @@ class AkanAppHost {
|
|
|
4259
4364
|
return "localhost";
|
|
4260
4365
|
return await createTunnel(type, { app: this.app, environment });
|
|
4261
4366
|
}
|
|
4262
|
-
#startBackend() {
|
|
4367
|
+
#startBackend(startStatus = null) {
|
|
4368
|
+
this.#backendStartStatus = startStatus;
|
|
4369
|
+
this.#setBackendLifecycleState("starting");
|
|
4263
4370
|
this.#backendReady = false;
|
|
4264
4371
|
const backend = Bun.spawn(["bun", `apps/${this.app.name}/main.ts`], {
|
|
4265
4372
|
cwd: this.app.workspace.workspaceRoot,
|
|
@@ -4271,6 +4378,8 @@ class AkanAppHost {
|
|
|
4271
4378
|
if (msg.type === "backend-ready") {
|
|
4272
4379
|
this.#backendReady = true;
|
|
4273
4380
|
this.#backendRecoveryAttempts = 0;
|
|
4381
|
+
this.#setBackendLifecycleState("ready", `pid=${msg.pid}`);
|
|
4382
|
+
this.#recordBackendReadyStatus();
|
|
4274
4383
|
this.logger.verbose(`backend ready pid=${msg.pid}`);
|
|
4275
4384
|
this.#replayBuilderState();
|
|
4276
4385
|
return;
|
|
@@ -4293,9 +4402,53 @@ class AkanAppHost {
|
|
|
4293
4402
|
this.#backend = backend;
|
|
4294
4403
|
this.logger.verbose(`backend spawned pid=${backend.pid}`);
|
|
4295
4404
|
}
|
|
4405
|
+
#nextBackendBuildStatusGeneration(generation) {
|
|
4406
|
+
if (typeof generation === "number") {
|
|
4407
|
+
this.#backendBuildStatusGeneration = Math.max(this.#backendBuildStatusGeneration, generation);
|
|
4408
|
+
return generation;
|
|
4409
|
+
}
|
|
4410
|
+
this.#backendBuildStatusGeneration += 1;
|
|
4411
|
+
return this.#backendBuildStatusGeneration;
|
|
4412
|
+
}
|
|
4413
|
+
#recordBackendBuildStatus({
|
|
4414
|
+
generation,
|
|
4415
|
+
ok,
|
|
4416
|
+
files,
|
|
4417
|
+
message
|
|
4418
|
+
}) {
|
|
4419
|
+
const status = createBackendBuildStatus({
|
|
4420
|
+
generation: this.#nextBackendBuildStatusGeneration(generation),
|
|
4421
|
+
ok,
|
|
4422
|
+
files,
|
|
4423
|
+
message
|
|
4424
|
+
});
|
|
4425
|
+
this.#recordBuildStatus(status);
|
|
4426
|
+
return status;
|
|
4427
|
+
}
|
|
4428
|
+
#recordBackendReadyStatus() {
|
|
4429
|
+
const previous = this.#buildStatusByPhase.get("backend");
|
|
4430
|
+
const startStatus = this.#backendStartStatus;
|
|
4431
|
+
if (startStatus || previous?.ok === false) {
|
|
4432
|
+
const status = this.#recordBackendBuildStatus({
|
|
4433
|
+
generation: startStatus?.generation ?? previous?.generation,
|
|
4434
|
+
ok: true,
|
|
4435
|
+
files: startStatus?.files ?? previous?.files ?? [],
|
|
4436
|
+
message: "Backend ready"
|
|
4437
|
+
});
|
|
4438
|
+
this.#sendOrQueueBuildStatus(status);
|
|
4439
|
+
}
|
|
4440
|
+
this.#backendStartStatus = null;
|
|
4441
|
+
}
|
|
4442
|
+
#setBackendLifecycleState(next, detail) {
|
|
4443
|
+
if (this.#backendLifecycleState === next && !detail)
|
|
4444
|
+
return;
|
|
4445
|
+
const prev = this.#backendLifecycleState;
|
|
4446
|
+
this.#backendLifecycleState = next;
|
|
4447
|
+
this.logger.verbose(`[backend-lifecycle] ${prev} -> ${next}${detail ? ` ${detail}` : ""}`);
|
|
4448
|
+
}
|
|
4296
4449
|
#sendToBackend(message) {
|
|
4297
4450
|
if (!this.#backend || !this.#backendReady) {
|
|
4298
|
-
if (message.type === "css-updated" || message.type === "pages-updated") {
|
|
4451
|
+
if (message.type === "css-updated" || message.type === "pages-updated" || message.type === "build-status") {
|
|
4299
4452
|
this.logger.verbose(`backend is not ready; will replay ${message.type}`);
|
|
4300
4453
|
return;
|
|
4301
4454
|
}
|
|
@@ -4315,6 +4468,7 @@ class AkanAppHost {
|
|
|
4315
4468
|
const backend = this.#backend;
|
|
4316
4469
|
this.#plannedBackendStops.add(backend);
|
|
4317
4470
|
this.#backendReady = false;
|
|
4471
|
+
this.#setBackendLifecycleState("stopping", `pid=${backend.pid}`);
|
|
4318
4472
|
this.logger.verbose(`stopping backend pid=${backend.pid}`);
|
|
4319
4473
|
try {
|
|
4320
4474
|
backend.kill("SIGTERM");
|
|
@@ -4330,11 +4484,13 @@ class AkanAppHost {
|
|
|
4330
4484
|
} finally {
|
|
4331
4485
|
if (this.#backend === backend)
|
|
4332
4486
|
this.#backend = null;
|
|
4487
|
+
this.#setBackendLifecycleState("stopped", `pid=${backend.pid}`);
|
|
4333
4488
|
}
|
|
4334
4489
|
}
|
|
4335
|
-
#scheduleBackendRestart(
|
|
4336
|
-
|
|
4337
|
-
|
|
4490
|
+
#scheduleBackendRestart(reason) {
|
|
4491
|
+
this.#pendingRestartReason = mergeBackendRestartReasons(this.#pendingRestartReason, reason);
|
|
4492
|
+
const pending = this.#pendingRestartReason;
|
|
4493
|
+
this.#setBackendLifecycleState("restart-pending", `generation=${pending.generation ?? "(unknown)"} files=${pending.files.length} roles=${pending.roles.join(",") || "(none)"}`);
|
|
4338
4494
|
if (this.#backendRecoveryTimer) {
|
|
4339
4495
|
clearTimeout(this.#backendRecoveryTimer);
|
|
4340
4496
|
this.#backendRecoveryTimer = null;
|
|
@@ -4343,23 +4499,31 @@ class AkanAppHost {
|
|
|
4343
4499
|
clearTimeout(this.#restartTimer);
|
|
4344
4500
|
this.#restartTimer = setTimeout(() => {
|
|
4345
4501
|
this.#restartTimer = null;
|
|
4346
|
-
const
|
|
4347
|
-
this.#
|
|
4348
|
-
|
|
4502
|
+
const next = this.#pendingRestartReason;
|
|
4503
|
+
this.#pendingRestartReason = null;
|
|
4504
|
+
if (next)
|
|
4505
|
+
this.#restartBackend(next);
|
|
4349
4506
|
}, BACKEND_RESTART_DEBOUNCE_MS);
|
|
4350
4507
|
}
|
|
4351
|
-
async#restartBackend(
|
|
4352
|
-
this.logger.verbose(`[backend-reload] restarting backend
|
|
4508
|
+
async#restartBackend(reason) {
|
|
4509
|
+
this.logger.verbose(`[backend-reload] restarting backend generation=${reason.generation ?? "(unknown)"} files=${reason.files.length} roles=${reason.roles.join(",") || "(none)"}`);
|
|
4353
4510
|
this.#backendRecoveryAttempts = 0;
|
|
4354
4511
|
await Promise.all([this.#stopBackend(), this.#backendGraph.refresh()]);
|
|
4355
|
-
this.#startBackend();
|
|
4512
|
+
this.#startBackend({ generation: reason.generation, files: reason.files });
|
|
4356
4513
|
}
|
|
4357
4514
|
#scheduleBackendRecovery(reason) {
|
|
4358
4515
|
if (this.#backendRecoveryTimer || this.#backend)
|
|
4359
4516
|
return;
|
|
4517
|
+
this.#setBackendLifecycleState("recovering", reason);
|
|
4360
4518
|
const attempt = this.#backendRecoveryAttempts;
|
|
4361
4519
|
const delay = Math.min(BACKEND_RECOVERY_BASE_DELAY_MS * 2 ** attempt, BACKEND_RECOVERY_MAX_DELAY_MS);
|
|
4362
4520
|
this.#backendRecoveryAttempts = attempt + 1;
|
|
4521
|
+
const failureStatus = this.#recordBackendBuildStatus({
|
|
4522
|
+
ok: false,
|
|
4523
|
+
files: [],
|
|
4524
|
+
message: `Backend exited unexpectedly (${reason}); restarting in ${delay}ms`
|
|
4525
|
+
});
|
|
4526
|
+
this.#sendOrQueueBuildStatus(failureStatus);
|
|
4363
4527
|
this.logger.warn(`[backend-recovery] backend exited unexpectedly (${reason}); restarting in ${delay}ms (attempt ${this.#backendRecoveryAttempts})`);
|
|
4364
4528
|
this.#backendRecoveryTimer = setTimeout(() => {
|
|
4365
4529
|
this.#backendRecoveryTimer = null;
|
|
@@ -4367,7 +4531,7 @@ class AkanAppHost {
|
|
|
4367
4531
|
return;
|
|
4368
4532
|
this.#backendGraph.refresh().finally(() => {
|
|
4369
4533
|
if (!this.#backend)
|
|
4370
|
-
this.#startBackend();
|
|
4534
|
+
this.#startBackend({ generation: failureStatus.generation, files: failureStatus.files });
|
|
4371
4535
|
});
|
|
4372
4536
|
}, delay);
|
|
4373
4537
|
}
|
|
@@ -4377,10 +4541,15 @@ class AkanAppHost {
|
|
|
4377
4541
|
});
|
|
4378
4542
|
}
|
|
4379
4543
|
async#handleBuilderMessage(message) {
|
|
4544
|
+
if (message.type === "build-status") {
|
|
4545
|
+
this.#recordBuildStatus(message.data);
|
|
4546
|
+
this.#sendOrQueueBuildStatus(message.data);
|
|
4547
|
+
return;
|
|
4548
|
+
}
|
|
4380
4549
|
if (message.type === "pages-updated")
|
|
4381
|
-
this.#
|
|
4550
|
+
this.#recordLastGood(message);
|
|
4382
4551
|
if (message.type === "css-updated")
|
|
4383
|
-
this.#
|
|
4552
|
+
this.#recordLastGood(message);
|
|
4384
4553
|
if (message.type === "invalidate") {
|
|
4385
4554
|
await this.#handleInvalidate(message);
|
|
4386
4555
|
return;
|
|
@@ -4388,26 +4557,97 @@ class AkanAppHost {
|
|
|
4388
4557
|
this.#sendToBackend(message);
|
|
4389
4558
|
}
|
|
4390
4559
|
async#handleInvalidate(message) {
|
|
4560
|
+
if (shouldRestartDevHostByDevPlan(message)) {
|
|
4561
|
+
this.#recordDevHostRestartRequired(message);
|
|
4562
|
+
return;
|
|
4563
|
+
}
|
|
4391
4564
|
if (await this.#shouldRestartBackend(message)) {
|
|
4392
|
-
this.#scheduleBackendRestart(message
|
|
4565
|
+
this.#scheduleBackendRestart(backendRestartReasonFromMessage(message));
|
|
4393
4566
|
return;
|
|
4394
4567
|
}
|
|
4395
4568
|
this.#sendToBackend(message);
|
|
4396
4569
|
}
|
|
4570
|
+
#recordLastGood(message) {
|
|
4571
|
+
if (message.type === "pages-updated") {
|
|
4572
|
+
if (!shouldReplaceLastGoodMessage(this.#lastGoodFrontend.pages, message))
|
|
4573
|
+
return;
|
|
4574
|
+
this.#lastGoodFrontend.pages = message;
|
|
4575
|
+
this.logger.verbose(`[last-good] pages generation=${message.data.generation ?? "(unknown)"} buildId=${message.data.buildId}`);
|
|
4576
|
+
return;
|
|
4577
|
+
}
|
|
4578
|
+
if (!shouldReplaceLastGoodMessage(this.#lastGoodFrontend.css, message))
|
|
4579
|
+
return;
|
|
4580
|
+
this.#lastGoodFrontend.css = message;
|
|
4581
|
+
this.logger.verbose(`[last-good] css generation=${message.data.generation ?? "(unknown)"} assets=${Object.keys(message.data.cssAssets).length}`);
|
|
4582
|
+
}
|
|
4583
|
+
#recordDevHostRestartRequired(message) {
|
|
4584
|
+
const generation = message.devPlan?.generation ?? message.generation;
|
|
4585
|
+
const detail = `generation=${generation ?? "(unknown)"} files=${message.files.length}`;
|
|
4586
|
+
this.logger.warn(`[dev-host] config change requires a manual restart until controlled dev-host restart is implemented (${detail})`);
|
|
4587
|
+
if (typeof generation === "number") {
|
|
4588
|
+
const status = {
|
|
4589
|
+
generation,
|
|
4590
|
+
phase: "scan",
|
|
4591
|
+
ok: false,
|
|
4592
|
+
files: message.files,
|
|
4593
|
+
message: "Config change requires restarting `akan start` to apply."
|
|
4594
|
+
};
|
|
4595
|
+
this.#recordBuildStatus(status);
|
|
4596
|
+
this.#sendOrQueueBuildStatus(status);
|
|
4597
|
+
}
|
|
4598
|
+
}
|
|
4599
|
+
#recordBuildStatus(status) {
|
|
4600
|
+
const recovered = shouldMarkBuildPhaseRecovered(this.#buildStatusByPhase, status);
|
|
4601
|
+
this.#buildStatusByPhase.set(status.phase, status);
|
|
4602
|
+
const label = `[build-status] generation=${status.generation} phase=${status.phase} ok=${status.ok} files=${status.files.length}`;
|
|
4603
|
+
if (status.ok)
|
|
4604
|
+
this.logger.verbose(`${label}${recovered ? " recovered=1" : ""}`);
|
|
4605
|
+
else
|
|
4606
|
+
this.logger.warn(`${label}${status.message ? ` message=${status.message}` : ""}`);
|
|
4607
|
+
}
|
|
4608
|
+
#sendOrQueueBuildStatus(status) {
|
|
4609
|
+
if (!this.#backend || shouldQueueBuildStatusReplay(this.#backendReady, this.#pendingBuildStatusReplay.length)) {
|
|
4610
|
+
this.#pendingBuildStatusReplay.push(status);
|
|
4611
|
+
this.logger.verbose(`backend is not ready; will replay build-status generation=${status.generation} phase=${status.phase}`);
|
|
4612
|
+
return;
|
|
4613
|
+
}
|
|
4614
|
+
this.#sendToBackend({ type: "build-status", data: status });
|
|
4615
|
+
}
|
|
4397
4616
|
#replayBuilderState() {
|
|
4398
4617
|
if (!this.#backendReady)
|
|
4399
4618
|
return;
|
|
4400
|
-
if (this.#
|
|
4401
|
-
this.#sendToBackend(this.#
|
|
4402
|
-
if (this.#
|
|
4403
|
-
this.#sendToBackend(this.#
|
|
4619
|
+
if (this.#lastGoodFrontend.css)
|
|
4620
|
+
this.#sendToBackend(this.#lastGoodFrontend.css);
|
|
4621
|
+
if (this.#lastGoodFrontend.pages)
|
|
4622
|
+
this.#sendToBackend(this.#lastGoodFrontend.pages);
|
|
4623
|
+
const queuedStatuses = this.#pendingBuildStatusReplay.splice(0);
|
|
4624
|
+
for (const status of buildStatusReplaySequence(queuedStatuses, this.#buildStatusByPhase)) {
|
|
4625
|
+
this.#sendToBackend({ type: "build-status", data: status });
|
|
4626
|
+
}
|
|
4404
4627
|
}
|
|
4405
4628
|
async#shouldRestartBackend(message) {
|
|
4406
4629
|
if (message.kinds.length === 1 && message.kinds[0] === "css")
|
|
4407
4630
|
return false;
|
|
4408
|
-
if (
|
|
4631
|
+
if (message.devPlan) {
|
|
4632
|
+
const { generation, roles, actions, reasonByFile } = message.devPlan;
|
|
4633
|
+
this.logger.verbose(`[dev-plan] generation=${generation} roles=${roles.join(",") || "(none)"} actions=${actions.join(",") || "(none)"} reasons=${Object.keys(reasonByFile).length}`);
|
|
4634
|
+
const shouldRestart = shouldRestartBackendByDevPlan(message) ?? false;
|
|
4635
|
+
if (shouldRestart && message.kinds.includes("code"))
|
|
4636
|
+
await this.#backendGraph.refresh();
|
|
4637
|
+
return shouldRestart;
|
|
4638
|
+
}
|
|
4639
|
+
if (message.kinds.includes("code"))
|
|
4409
4640
|
await this.#backendGraph.refresh();
|
|
4410
|
-
|
|
4641
|
+
if (message.files.some((file) => this.#isBackendFile(file)))
|
|
4642
|
+
return true;
|
|
4643
|
+
if (!this.#backendGraph.lastRefreshSucceeded) {
|
|
4644
|
+
const fallbackFiles = message.files.filter((file) => isLegacyBackendFallbackFile(file, this.app.workspace.workspaceRoot));
|
|
4645
|
+
if (fallbackFiles.length > 0) {
|
|
4646
|
+
this.logger.warn(`[backend-graph] using path-role fallback for legacy invalidate; restart files=${fallbackFiles.length}`);
|
|
4647
|
+
return true;
|
|
4648
|
+
}
|
|
4649
|
+
}
|
|
4650
|
+
return false;
|
|
4411
4651
|
}
|
|
4412
4652
|
#isBackendFile(file) {
|
|
4413
4653
|
return this.#backendGraph.has(file);
|
|
@@ -4763,8 +5003,8 @@ Caused by: ${ApplicationBuildReporter.formatError(error.cause)}` : "";
|
|
|
4763
5003
|
}
|
|
4764
5004
|
}
|
|
4765
5005
|
// pkgs/@akanjs/devkit/applicationBuildRunner.ts
|
|
4766
|
-
import { mkdir as
|
|
4767
|
-
import
|
|
5006
|
+
import { mkdir as mkdir8, rm as rm3 } from "fs/promises";
|
|
5007
|
+
import path35 from "path";
|
|
4768
5008
|
|
|
4769
5009
|
// pkgs/@akanjs/devkit/frontendBuild/allRoutesBuilder.ts
|
|
4770
5010
|
import path20 from "path";
|
|
@@ -7232,9 +7472,292 @@ function getPageKeyBasePath(pageKey, basePaths) {
|
|
|
7232
7472
|
const firstPublicSegment = segments.find((segment) => segment !== "[lang]" && !/^\(.+\)$/.test(segment));
|
|
7233
7473
|
return firstPublicSegment && basePaths.includes(firstPublicSegment) ? firstPublicSegment : null;
|
|
7234
7474
|
}
|
|
7235
|
-
// pkgs/@akanjs/devkit/frontendBuild/
|
|
7236
|
-
import { mkdir as mkdir6 } from "fs/promises";
|
|
7475
|
+
// pkgs/@akanjs/devkit/frontendBuild/devChangePlanner.ts
|
|
7237
7476
|
import path25 from "path";
|
|
7477
|
+
var SOURCE_EXTS4 = new Set([".ts", ".tsx", ".js", ".jsx", ".mjs", ".cjs"]);
|
|
7478
|
+
var CONFIG_BASENAMES = new Set(["akan.config.ts", "bunfig.toml", "tsconfig.json", "package.json"]);
|
|
7479
|
+
var BARREL_FACETS = new Set(["common", "srvkit", "ui", "webkit"]);
|
|
7480
|
+
var CLIENT_SUFFIXES = [".Template.tsx", ".Unit.tsx", ".Util.tsx", ".View.tsx", ".Zone.tsx", ".store.ts"];
|
|
7481
|
+
var SHARED_SUFFIXES2 = [".constant.ts", ".dictionary.ts", ".signal.ts"];
|
|
7482
|
+
var SERVER_SUFFIXES2 = [".service.ts", ".document.ts"];
|
|
7483
|
+
var RUNTIME_METADATA_BASENAMES2 = new Set(["dict.ts", "sig.ts", "useClient.ts", "useServer.ts"]);
|
|
7484
|
+
|
|
7485
|
+
class DevChangePlanner {
|
|
7486
|
+
#workspaceRoot;
|
|
7487
|
+
constructor({ workspaceRoot }) {
|
|
7488
|
+
this.#workspaceRoot = path25.resolve(workspaceRoot);
|
|
7489
|
+
}
|
|
7490
|
+
plan({ generation, files, kinds, generatedFiles: generatedFiles2 = [] }) {
|
|
7491
|
+
const fileList = uniqueResolved([...files, ...generatedFiles2]);
|
|
7492
|
+
const generatedSet = new Set(generatedFiles2.map((file) => path25.resolve(file)));
|
|
7493
|
+
const kindSet = new Set(kinds);
|
|
7494
|
+
const roles = new Set;
|
|
7495
|
+
const actions = new Set;
|
|
7496
|
+
const reasonByFile = {};
|
|
7497
|
+
for (const kind of kindSet) {
|
|
7498
|
+
if (kind === "css") {
|
|
7499
|
+
roles.add("css");
|
|
7500
|
+
actions.add("rebuild-css");
|
|
7501
|
+
}
|
|
7502
|
+
if (kind === "config") {
|
|
7503
|
+
roles.add("config");
|
|
7504
|
+
actions.add("restart-dev-host");
|
|
7505
|
+
}
|
|
7506
|
+
}
|
|
7507
|
+
for (const file of fileList) {
|
|
7508
|
+
const reasons = new Set;
|
|
7509
|
+
const fileRoles = this.#rolesForFile(file, { isGenerated: generatedSet.has(path25.resolve(file)), reasons });
|
|
7510
|
+
for (const role of fileRoles)
|
|
7511
|
+
roles.add(role);
|
|
7512
|
+
if (reasons.size > 0)
|
|
7513
|
+
reasonByFile[path25.resolve(file)] = [...reasons].sort();
|
|
7514
|
+
}
|
|
7515
|
+
if (roles.has("barrel"))
|
|
7516
|
+
actions.add("sync-generated");
|
|
7517
|
+
if (roles.has("server") || roles.has("shared"))
|
|
7518
|
+
actions.add("restart-backend");
|
|
7519
|
+
if (roles.has("client") || roles.has("shared"))
|
|
7520
|
+
actions.add("rebuild-client");
|
|
7521
|
+
if (roles.has("css"))
|
|
7522
|
+
actions.add("rebuild-css");
|
|
7523
|
+
return {
|
|
7524
|
+
generation,
|
|
7525
|
+
files: fileList,
|
|
7526
|
+
generatedFiles: uniqueResolved(generatedFiles2),
|
|
7527
|
+
roles: [...roles].sort(),
|
|
7528
|
+
actions: [...actions].sort(),
|
|
7529
|
+
reasonByFile
|
|
7530
|
+
};
|
|
7531
|
+
}
|
|
7532
|
+
#rolesForFile(file, { isGenerated, reasons }) {
|
|
7533
|
+
const roles = new Set;
|
|
7534
|
+
const abs = path25.resolve(file);
|
|
7535
|
+
const base = path25.basename(abs);
|
|
7536
|
+
const ext = path25.extname(abs).toLowerCase();
|
|
7537
|
+
const isSource = SOURCE_EXTS4.has(ext);
|
|
7538
|
+
const rel = path25.relative(this.#workspaceRoot, abs);
|
|
7539
|
+
const parts = rel.split(path25.sep).filter(Boolean);
|
|
7540
|
+
if (CONFIG_BASENAMES.has(base)) {
|
|
7541
|
+
roles.add("config");
|
|
7542
|
+
reasons.add("config-file");
|
|
7543
|
+
}
|
|
7544
|
+
if (ext === ".css") {
|
|
7545
|
+
roles.add("css");
|
|
7546
|
+
reasons.add("css-file");
|
|
7547
|
+
}
|
|
7548
|
+
if (isGenerated || isSource && this.#isBarrelFacetChild(parts)) {
|
|
7549
|
+
roles.add("barrel");
|
|
7550
|
+
reasons.add(isGenerated ? "generated-index" : "barrel-facet-child");
|
|
7551
|
+
}
|
|
7552
|
+
if (isSource && this.#isServerBiased(abs, parts)) {
|
|
7553
|
+
roles.add("server");
|
|
7554
|
+
reasons.add("server-path");
|
|
7555
|
+
}
|
|
7556
|
+
if (isSource && this.#isClientBiased(abs, parts)) {
|
|
7557
|
+
roles.add("client");
|
|
7558
|
+
reasons.add("client-path");
|
|
7559
|
+
}
|
|
7560
|
+
if (isSource && this.#isSharedBiased(abs, parts)) {
|
|
7561
|
+
roles.add("shared");
|
|
7562
|
+
reasons.add("shared-path");
|
|
7563
|
+
}
|
|
7564
|
+
if (roles.has("server") && roles.has("client")) {
|
|
7565
|
+
roles.delete("server");
|
|
7566
|
+
roles.delete("client");
|
|
7567
|
+
roles.add("shared");
|
|
7568
|
+
reasons.add("server-client-overlap");
|
|
7569
|
+
}
|
|
7570
|
+
if (roles.size === 0 && SOURCE_EXTS4.has(ext) && this.#isWorkspaceSource(rel)) {
|
|
7571
|
+
roles.add("shared");
|
|
7572
|
+
reasons.add("workspace-source-fallback");
|
|
7573
|
+
}
|
|
7574
|
+
return roles;
|
|
7575
|
+
}
|
|
7576
|
+
#isServerBiased(abs, parts) {
|
|
7577
|
+
const base = path25.basename(abs);
|
|
7578
|
+
return parts.includes("srvkit") || SERVER_SUFFIXES2.some((suffix) => base.endsWith(suffix)) || base === "main.ts" || base === "server.ts";
|
|
7579
|
+
}
|
|
7580
|
+
#isClientBiased(abs, parts) {
|
|
7581
|
+
const base = path25.basename(abs);
|
|
7582
|
+
return parts.includes("ui") || parts.includes("webkit") || parts.includes("page") || CLIENT_SUFFIXES.some((suffix) => base.endsWith(suffix));
|
|
7583
|
+
}
|
|
7584
|
+
#isSharedBiased(abs, parts) {
|
|
7585
|
+
const base = path25.basename(abs);
|
|
7586
|
+
return parts.includes("common") || SHARED_SUFFIXES2.some((suffix) => base.endsWith(suffix)) || RUNTIME_METADATA_BASENAMES2.has(base);
|
|
7587
|
+
}
|
|
7588
|
+
#isBarrelFacetChild(parts) {
|
|
7589
|
+
if (parts.length < 4)
|
|
7590
|
+
return false;
|
|
7591
|
+
const [scope, , facet, child] = parts;
|
|
7592
|
+
if (scope !== "apps" && scope !== "libs")
|
|
7593
|
+
return false;
|
|
7594
|
+
if (!facet || !BARREL_FACETS.has(facet))
|
|
7595
|
+
return false;
|
|
7596
|
+
if (!child || child.startsWith(".") || child === "index.ts" || child === "index.tsx")
|
|
7597
|
+
return false;
|
|
7598
|
+
return true;
|
|
7599
|
+
}
|
|
7600
|
+
#isWorkspaceSource(rel) {
|
|
7601
|
+
if (!rel || rel.startsWith("..") || path25.isAbsolute(rel))
|
|
7602
|
+
return false;
|
|
7603
|
+
const [scope] = rel.split(path25.sep);
|
|
7604
|
+
return scope === "apps" || scope === "libs" || scope === "pkgs";
|
|
7605
|
+
}
|
|
7606
|
+
}
|
|
7607
|
+
var uniqueResolved = (files) => [...new Set(files.map((file) => path25.resolve(file)))].sort();
|
|
7608
|
+
// pkgs/@akanjs/devkit/frontendBuild/devGeneratedIndexSync.ts
|
|
7609
|
+
import { mkdir as mkdir6, readdir as readdir2, readFile, rm as rm2, stat as stat3, writeFile } from "fs/promises";
|
|
7610
|
+
import path26 from "path";
|
|
7611
|
+
var BARREL_FACETS2 = new Set(["common", "srvkit", "ui", "webkit"]);
|
|
7612
|
+
var FACET_SOURCE_FILE_RE = /\.(ts|tsx)$/;
|
|
7613
|
+
var FACET_EXCLUDED_FILE_RE = /(^index\.tsx?$|\.d\.ts$|\.(test|spec)\.(ts|tsx)$|\.css$|\.scss$|\.sass$)/;
|
|
7614
|
+
var MODULE_UI_TYPES = ["Template", "Unit", "Util", "View", "Zone"];
|
|
7615
|
+
var SERVICE_UI_TYPES = ["Util", "Zone"];
|
|
7616
|
+
var SCALAR_UI_TYPES = ["Template", "Unit"];
|
|
7617
|
+
|
|
7618
|
+
class DevGeneratedIndexSync {
|
|
7619
|
+
#workspaceRoot;
|
|
7620
|
+
constructor({ workspaceRoot }) {
|
|
7621
|
+
this.#workspaceRoot = path26.resolve(workspaceRoot);
|
|
7622
|
+
}
|
|
7623
|
+
async syncForBatch(files) {
|
|
7624
|
+
const indexPaths = new Set;
|
|
7625
|
+
const errors = [];
|
|
7626
|
+
for (const file of files) {
|
|
7627
|
+
const facetIndex = this.#facetIndexFor(file);
|
|
7628
|
+
if (facetIndex)
|
|
7629
|
+
indexPaths.add(facetIndex);
|
|
7630
|
+
const moduleIndex = await this.#moduleIndexForDirectoryEvent(file).catch((err) => {
|
|
7631
|
+
errors.push(`[generated-index] module detection failed for ${file}: ${formatError(err)}`);
|
|
7632
|
+
return null;
|
|
7633
|
+
});
|
|
7634
|
+
if (moduleIndex)
|
|
7635
|
+
indexPaths.add(moduleIndex);
|
|
7636
|
+
}
|
|
7637
|
+
const changedFiles = [];
|
|
7638
|
+
for (const indexPath of [...indexPaths].sort()) {
|
|
7639
|
+
try {
|
|
7640
|
+
const changed = await this.#syncIndex(indexPath);
|
|
7641
|
+
if (changed)
|
|
7642
|
+
changedFiles.push(indexPath);
|
|
7643
|
+
} catch (err) {
|
|
7644
|
+
errors.push(`[generated-index] sync failed for ${indexPath}: ${formatError(err)}`);
|
|
7645
|
+
}
|
|
7646
|
+
}
|
|
7647
|
+
return { changedFiles, errors };
|
|
7648
|
+
}
|
|
7649
|
+
#facetIndexFor(file) {
|
|
7650
|
+
const abs = path26.resolve(file);
|
|
7651
|
+
const rel = path26.relative(this.#workspaceRoot, abs);
|
|
7652
|
+
if (rel.startsWith("..") || path26.isAbsolute(rel))
|
|
7653
|
+
return null;
|
|
7654
|
+
const parts = rel.split(path26.sep).filter(Boolean);
|
|
7655
|
+
if (parts.length < 4)
|
|
7656
|
+
return null;
|
|
7657
|
+
const [scope, project, facet, child] = parts;
|
|
7658
|
+
if (scope !== "apps" && scope !== "libs" || !project || !facet || !BARREL_FACETS2.has(facet))
|
|
7659
|
+
return null;
|
|
7660
|
+
if (!child || child.startsWith(".") || child === "index.ts" || child === "index.tsx")
|
|
7661
|
+
return null;
|
|
7662
|
+
return path26.join(this.#workspaceRoot, scope, project, facet, "index.ts");
|
|
7663
|
+
}
|
|
7664
|
+
async#moduleIndexForDirectoryEvent(file) {
|
|
7665
|
+
const abs = path26.resolve(file);
|
|
7666
|
+
const rel = path26.relative(this.#workspaceRoot, abs);
|
|
7667
|
+
if (rel.startsWith("..") || path26.isAbsolute(rel))
|
|
7668
|
+
return null;
|
|
7669
|
+
const parts = rel.split(path26.sep).filter(Boolean);
|
|
7670
|
+
if (parts.length !== 4 && parts.length !== 5)
|
|
7671
|
+
return null;
|
|
7672
|
+
const [scope, project, libSegment, moduleSegment, scalarSegment] = parts;
|
|
7673
|
+
if (scope !== "apps" && scope !== "libs" || !project || libSegment !== "lib")
|
|
7674
|
+
return null;
|
|
7675
|
+
const isExistingDirectory = await stat3(abs).then((s) => s.isDirectory()).catch(() => false);
|
|
7676
|
+
const looksLikeDeletedDirectory = !path26.extname(abs);
|
|
7677
|
+
if (!isExistingDirectory && !looksLikeDeletedDirectory)
|
|
7678
|
+
return null;
|
|
7679
|
+
if (moduleSegment === "__scalar" && scalarSegment) {
|
|
7680
|
+
return path26.join(this.#workspaceRoot, scope, project, "lib", "__scalar", scalarSegment, "index.ts");
|
|
7681
|
+
}
|
|
7682
|
+
if (!moduleSegment || moduleSegment === "__scalar")
|
|
7683
|
+
return null;
|
|
7684
|
+
return path26.join(this.#workspaceRoot, scope, project, "lib", moduleSegment, "index.ts");
|
|
7685
|
+
}
|
|
7686
|
+
async#syncIndex(indexPath) {
|
|
7687
|
+
const dir = path26.dirname(indexPath);
|
|
7688
|
+
const content = await this.#contentForIndex(indexPath);
|
|
7689
|
+
if (content === null) {
|
|
7690
|
+
if (!await exists(indexPath))
|
|
7691
|
+
return false;
|
|
7692
|
+
await rm2(indexPath, { force: true });
|
|
7693
|
+
return true;
|
|
7694
|
+
}
|
|
7695
|
+
const current = await readFile(indexPath, "utf8").catch(() => null);
|
|
7696
|
+
if (current === content)
|
|
7697
|
+
return false;
|
|
7698
|
+
await mkdir6(dir, { recursive: true });
|
|
7699
|
+
await writeFile(indexPath, content);
|
|
7700
|
+
return true;
|
|
7701
|
+
}
|
|
7702
|
+
async#contentForIndex(indexPath) {
|
|
7703
|
+
const parts = path26.relative(this.#workspaceRoot, indexPath).split(path26.sep).filter(Boolean);
|
|
7704
|
+
const facet = parts.at(-2);
|
|
7705
|
+
if (facet && BARREL_FACETS2.has(facet))
|
|
7706
|
+
return this.#facetContent(path26.dirname(indexPath));
|
|
7707
|
+
return this.#moduleContent(path26.dirname(indexPath));
|
|
7708
|
+
}
|
|
7709
|
+
async#facetContent(dir) {
|
|
7710
|
+
const entries = await readdir2(dir, { withFileTypes: true }).catch(() => []);
|
|
7711
|
+
const exportNames = entries.flatMap((entry) => {
|
|
7712
|
+
const name = entry.name;
|
|
7713
|
+
if (name.startsWith("."))
|
|
7714
|
+
return [];
|
|
7715
|
+
if (entry.isDirectory())
|
|
7716
|
+
return [name];
|
|
7717
|
+
if (!entry.isFile())
|
|
7718
|
+
return [];
|
|
7719
|
+
if (!FACET_SOURCE_FILE_RE.test(name) || FACET_EXCLUDED_FILE_RE.test(name))
|
|
7720
|
+
return [];
|
|
7721
|
+
return [name.replace(FACET_SOURCE_FILE_RE, "")];
|
|
7722
|
+
}).sort();
|
|
7723
|
+
if (exportNames.length === 0)
|
|
7724
|
+
return null;
|
|
7725
|
+
return `${exportNames.map((name) => `export * from "./${name}";`).join(`
|
|
7726
|
+
`)}
|
|
7727
|
+
`;
|
|
7728
|
+
}
|
|
7729
|
+
async#moduleContent(dir) {
|
|
7730
|
+
const rel = path26.relative(this.#workspaceRoot, dir);
|
|
7731
|
+
const parts = rel.split(path26.sep).filter(Boolean);
|
|
7732
|
+
const moduleSegment = parts.at(-1);
|
|
7733
|
+
if (!moduleSegment)
|
|
7734
|
+
return null;
|
|
7735
|
+
const isScalar = parts.at(-2) === "__scalar";
|
|
7736
|
+
const rawModel = moduleSegment.startsWith("_") ? moduleSegment.slice(1) : moduleSegment;
|
|
7737
|
+
if (!rawModel)
|
|
7738
|
+
return null;
|
|
7739
|
+
const modelName = capitalize3(rawModel);
|
|
7740
|
+
const allowedTypes = isScalar ? SCALAR_UI_TYPES : moduleSegment.startsWith("_") ? SERVICE_UI_TYPES : MODULE_UI_TYPES;
|
|
7741
|
+
const fileTypes = [];
|
|
7742
|
+
for (const type of allowedTypes) {
|
|
7743
|
+
if (await exists(path26.join(dir, `${modelName}.${type}.tsx`)))
|
|
7744
|
+
fileTypes.push(type);
|
|
7745
|
+
}
|
|
7746
|
+
if (fileTypes.length === 0)
|
|
7747
|
+
return null;
|
|
7748
|
+
return `
|
|
7749
|
+
${fileTypes.map((type) => `import * as ${type} from "./${modelName}.${type}";`).join(`
|
|
7750
|
+
`)}
|
|
7751
|
+
|
|
7752
|
+
export const ${modelName} = { ${fileTypes.join(", ")} };`;
|
|
7753
|
+
}
|
|
7754
|
+
}
|
|
7755
|
+
var exists = async (file) => stat3(file).then(() => true).catch(() => false);
|
|
7756
|
+
var capitalize3 = (value) => `${value.charAt(0).toUpperCase()}${value.slice(1)}`;
|
|
7757
|
+
var formatError = (err) => err instanceof Error ? err.message : String(err);
|
|
7758
|
+
// pkgs/@akanjs/devkit/frontendBuild/fontOptimizer.ts
|
|
7759
|
+
import { mkdir as mkdir7 } from "fs/promises";
|
|
7760
|
+
import path27 from "path";
|
|
7238
7761
|
import {
|
|
7239
7762
|
generateFontFace,
|
|
7240
7763
|
getMetricsForFamily,
|
|
@@ -7258,7 +7781,7 @@ class FontOptimizer {
|
|
|
7258
7781
|
constructor(app, command = "start") {
|
|
7259
7782
|
this.#app = app;
|
|
7260
7783
|
this.#command = command;
|
|
7261
|
-
this.#artifactRoot =
|
|
7784
|
+
this.#artifactRoot = path27.join(command === "build" ? app.dist.cwdPath : app.cwdPath, ".akan/artifact");
|
|
7262
7785
|
}
|
|
7263
7786
|
async optimize() {
|
|
7264
7787
|
const fonts = await this.discoverFonts();
|
|
@@ -7277,7 +7800,7 @@ class FontOptimizer {
|
|
|
7277
7800
|
const pageKeys = await this.#app.getPageKeys();
|
|
7278
7801
|
const fonts = [];
|
|
7279
7802
|
await Promise.all(pageKeys.map(async (key) => {
|
|
7280
|
-
const filePath =
|
|
7803
|
+
const filePath = path27.resolve(this.#app.cwdPath, "page", key);
|
|
7281
7804
|
const file = Bun.file(filePath);
|
|
7282
7805
|
if (!await file.exists())
|
|
7283
7806
|
return;
|
|
@@ -7293,8 +7816,8 @@ class FontOptimizer {
|
|
|
7293
7816
|
this.#app.logger.warn(`[font] source not found: ${face.src}`);
|
|
7294
7817
|
continue;
|
|
7295
7818
|
}
|
|
7296
|
-
const outputPath =
|
|
7297
|
-
await
|
|
7819
|
+
const outputPath = path27.join(this.#artifactRoot, face.optimizedSrc.replace(/^\/_akan\//, ""));
|
|
7820
|
+
await mkdir7(path27.dirname(outputPath), { recursive: true });
|
|
7298
7821
|
const sourceBuffer = Buffer.from(await Bun.file(sourcePath).arrayBuffer());
|
|
7299
7822
|
const outputBuffer = font.subset === false ? await this.#convertToWoff2(sourceBuffer, sourcePath) : await subsetFont(sourceBuffer, await this.#getSubsetText(font), { targetFormat: "woff2" });
|
|
7300
7823
|
await Bun.write(outputPath, outputBuffer);
|
|
@@ -7441,8 +7964,8 @@ class FontOptimizer {
|
|
|
7441
7964
|
return null;
|
|
7442
7965
|
const rel = src.replace(/^\//, "");
|
|
7443
7966
|
const candidates = [
|
|
7444
|
-
this.#command === "build" ?
|
|
7445
|
-
|
|
7967
|
+
this.#command === "build" ? path27.join(this.#app.dist.cwdPath, "public", rel) : null,
|
|
7968
|
+
path27.join(this.#app.cwdPath, "public", rel),
|
|
7446
7969
|
this.#resolveWorkspacePublicPath(rel)
|
|
7447
7970
|
].filter(Boolean);
|
|
7448
7971
|
for (const candidate of candidates) {
|
|
@@ -7470,7 +7993,7 @@ class FontOptimizer {
|
|
|
7470
7993
|
return "woff2";
|
|
7471
7994
|
if (signature === "OTTO")
|
|
7472
7995
|
return "otf";
|
|
7473
|
-
const ext =
|
|
7996
|
+
const ext = path27.extname(sourcePath).slice(1).toLowerCase();
|
|
7474
7997
|
if (ext === "otf" || ext === "woff" || ext === "woff2")
|
|
7475
7998
|
return ext;
|
|
7476
7999
|
return "ttf";
|
|
@@ -7479,7 +8002,7 @@ class FontOptimizer {
|
|
|
7479
8002
|
const [root, dep, ...rest] = rel.split("/");
|
|
7480
8003
|
if (root !== "libs" || !dep || rest.length === 0)
|
|
7481
8004
|
return null;
|
|
7482
|
-
return
|
|
8005
|
+
return path27.join(this.#app.workspace.workspaceRoot, "libs", dep, "public", ...rest);
|
|
7483
8006
|
}
|
|
7484
8007
|
async#getSubsetText(font) {
|
|
7485
8008
|
const parts = new Set;
|
|
@@ -7489,7 +8012,7 @@ class FontOptimizer {
|
|
|
7489
8012
|
if (font.subsetText)
|
|
7490
8013
|
parts.add(font.subsetText);
|
|
7491
8014
|
for (const filePath of font.subsetFiles ?? []) {
|
|
7492
|
-
const abs =
|
|
8015
|
+
const abs = path27.isAbsolute(filePath) ? filePath : path27.join(this.#app.cwdPath, filePath);
|
|
7493
8016
|
const file = Bun.file(abs);
|
|
7494
8017
|
if (await file.exists())
|
|
7495
8018
|
parts.add(await file.text());
|
|
@@ -7508,7 +8031,7 @@ class FontOptimizer {
|
|
|
7508
8031
|
return "";
|
|
7509
8032
|
}
|
|
7510
8033
|
async#collectAutoSubsetText() {
|
|
7511
|
-
const roots = ["page", "ui"].map((dir) =>
|
|
8034
|
+
const roots = ["page", "ui"].map((dir) => path27.join(this.#app.cwdPath, dir));
|
|
7512
8035
|
const glob = new Bun.Glob("**/*.{ts,tsx,js,jsx,html,md}");
|
|
7513
8036
|
const parts = [];
|
|
7514
8037
|
await Promise.all(roots.map(async (root) => {
|
|
@@ -7622,43 +8145,43 @@ ${declarations.map(([prop, value]) => ` ${prop}: ${value};`).join(`
|
|
|
7622
8145
|
}
|
|
7623
8146
|
}
|
|
7624
8147
|
// pkgs/@akanjs/devkit/frontendBuild/hmrChangeClassifier.ts
|
|
7625
|
-
import
|
|
7626
|
-
var
|
|
8148
|
+
import path28 from "path";
|
|
8149
|
+
var SOURCE_EXTS5 = new Set([".ts", ".tsx", ".js", ".jsx", ".mjs", ".cjs"]);
|
|
7627
8150
|
var CSS_EXTS = new Set([".css"]);
|
|
7628
|
-
var
|
|
8151
|
+
var CONFIG_BASENAMES2 = new Set(["akan.config.ts", "bunfig.toml", "tsconfig.json", "package.json"]);
|
|
7629
8152
|
|
|
7630
8153
|
class HmrChangeClassifier {
|
|
7631
8154
|
classify(abs) {
|
|
7632
8155
|
if (this.#isUninteresting(abs))
|
|
7633
8156
|
return "ignore";
|
|
7634
|
-
const base =
|
|
7635
|
-
if (
|
|
8157
|
+
const base = path28.basename(abs);
|
|
8158
|
+
if (CONFIG_BASENAMES2.has(base))
|
|
7636
8159
|
return "config";
|
|
7637
|
-
const ext =
|
|
8160
|
+
const ext = path28.extname(abs).toLowerCase();
|
|
7638
8161
|
if (CSS_EXTS.has(ext))
|
|
7639
8162
|
return "css";
|
|
7640
|
-
if (
|
|
8163
|
+
if (SOURCE_EXTS5.has(ext))
|
|
7641
8164
|
return "code";
|
|
7642
8165
|
return "ignore";
|
|
7643
8166
|
}
|
|
7644
8167
|
#isUninteresting(abs) {
|
|
7645
|
-
const base =
|
|
8168
|
+
const base = path28.basename(abs);
|
|
7646
8169
|
if (!base)
|
|
7647
8170
|
return true;
|
|
7648
8171
|
if (base.startsWith("."))
|
|
7649
8172
|
return true;
|
|
7650
8173
|
if (base.endsWith("~") || base.endsWith(".swp") || base.endsWith(".swx") || base.endsWith(".tmp"))
|
|
7651
8174
|
return true;
|
|
7652
|
-
if (abs.includes(`${
|
|
8175
|
+
if (abs.includes(`${path28.sep}node_modules${path28.sep}`))
|
|
7653
8176
|
return true;
|
|
7654
|
-
if (abs.includes(`${
|
|
8177
|
+
if (abs.includes(`${path28.sep}.akan${path28.sep}`))
|
|
7655
8178
|
return true;
|
|
7656
8179
|
return false;
|
|
7657
8180
|
}
|
|
7658
8181
|
}
|
|
7659
8182
|
// pkgs/@akanjs/devkit/frontendBuild/hmrWatcher.ts
|
|
7660
8183
|
import fs4 from "fs";
|
|
7661
|
-
import
|
|
8184
|
+
import path29 from "path";
|
|
7662
8185
|
class HmrWatcher {
|
|
7663
8186
|
#roots;
|
|
7664
8187
|
#debounceMs;
|
|
@@ -7671,7 +8194,7 @@ class HmrWatcher {
|
|
|
7671
8194
|
#stopped = false;
|
|
7672
8195
|
#flushing = false;
|
|
7673
8196
|
constructor(opts) {
|
|
7674
|
-
this.#roots = [...new Set(opts.roots.map((r) =>
|
|
8197
|
+
this.#roots = [...new Set(opts.roots.map((r) => path29.resolve(r)))];
|
|
7675
8198
|
this.#debounceMs = opts.debounceMs ?? 80;
|
|
7676
8199
|
this.#onBatch = opts.onBatch;
|
|
7677
8200
|
this.#logger = opts.logger;
|
|
@@ -7682,7 +8205,7 @@ class HmrWatcher {
|
|
|
7682
8205
|
const w = fs4.watch(root, { recursive: true, persistent: false }, (_event, filename) => {
|
|
7683
8206
|
if (!filename)
|
|
7684
8207
|
return;
|
|
7685
|
-
const abs =
|
|
8208
|
+
const abs = path29.resolve(root, filename.toString());
|
|
7686
8209
|
this.#queue(abs);
|
|
7687
8210
|
});
|
|
7688
8211
|
this.#watchers.push(w);
|
|
@@ -7740,10 +8263,10 @@ class HmrWatcher {
|
|
|
7740
8263
|
}
|
|
7741
8264
|
}
|
|
7742
8265
|
// pkgs/@akanjs/devkit/frontendBuild/pagesBundleBuilder.ts
|
|
7743
|
-
import
|
|
8266
|
+
import path31 from "path";
|
|
7744
8267
|
|
|
7745
8268
|
// pkgs/@akanjs/devkit/transforms/externalizeFrameworkPlugin.ts
|
|
7746
|
-
import
|
|
8269
|
+
import path30 from "path";
|
|
7747
8270
|
var DEFAULT_INCLUDE = ["akanjs/", "@apps/", "@libs/"];
|
|
7748
8271
|
var DEFAULT_EXCLUDE_EXACT = new Set(["akanjs/webkit", "@akanjs/cli", "@akanjs/devkit"]);
|
|
7749
8272
|
var DEFAULT_EXCLUDE_PREFIX = ["@akanjs/cli/", "@akanjs/devkit/"];
|
|
@@ -7786,7 +8309,7 @@ async function createExternalizeFrameworkPlugin(options) {
|
|
|
7786
8309
|
const replPath = repl?.endsWith("/*") ? repl.slice(0, -1) : repl ?? "";
|
|
7787
8310
|
if (!replPath)
|
|
7788
8311
|
continue;
|
|
7789
|
-
const candidate =
|
|
8312
|
+
const candidate = path30.resolve(workspaceRoot, replPath + suffix);
|
|
7790
8313
|
const hit = await firstExisting(candidate);
|
|
7791
8314
|
if (hit)
|
|
7792
8315
|
return hit;
|
|
@@ -7798,8 +8321,8 @@ async function createExternalizeFrameworkPlugin(options) {
|
|
|
7798
8321
|
if (spec === pkg)
|
|
7799
8322
|
continue;
|
|
7800
8323
|
const suffix = spec.slice(pkg.length + 1);
|
|
7801
|
-
const pkgDir =
|
|
7802
|
-
const candidate =
|
|
8324
|
+
const pkgDir = path30.dirname(path30.resolve(workspaceRoot, entryFile));
|
|
8325
|
+
const candidate = path30.join(pkgDir, suffix);
|
|
7803
8326
|
const hit = await firstExisting(candidate);
|
|
7804
8327
|
if (hit)
|
|
7805
8328
|
return hit;
|
|
@@ -7849,7 +8372,7 @@ async function firstExisting(basePath2) {
|
|
|
7849
8372
|
return candidate;
|
|
7850
8373
|
}
|
|
7851
8374
|
for (const ext of CANDIDATE_EXTS3) {
|
|
7852
|
-
const candidate =
|
|
8375
|
+
const candidate = path30.join(basePath2, `index${ext}`);
|
|
7853
8376
|
if (await Bun.file(candidate).exists())
|
|
7854
8377
|
return candidate;
|
|
7855
8378
|
}
|
|
@@ -7940,11 +8463,11 @@ class PagesBundleBuilder {
|
|
|
7940
8463
|
const entryArtifact = result.outputs.find((a) => a.kind === "entry-point");
|
|
7941
8464
|
if (!entryArtifact)
|
|
7942
8465
|
throw new Error("[PagesBundleBuilder] Bun.build emitted no entry-point artifact");
|
|
7943
|
-
const bundlePath =
|
|
8466
|
+
const bundlePath = path31.resolve(entryArtifact.path);
|
|
7944
8467
|
const buildId = Date.now();
|
|
7945
8468
|
const outputBytes = result.outputs.reduce((sum, output) => sum + output.size, 0);
|
|
7946
8469
|
const chunkCount = result.outputs.filter((output) => output.kind === "chunk").length;
|
|
7947
|
-
this.#app.verbose(`[PagesBundleBuilder] ${
|
|
8470
|
+
this.#app.verbose(`[PagesBundleBuilder] ${path31.basename(bundlePath)} emitted in ${Date.now() - this.#started}ms splitting=${this.#splitting} entry=${entryArtifact.size} bytes outputs=${result.outputs.length} chunks=${chunkCount} total=${outputBytes} bytes`);
|
|
7948
8471
|
return {
|
|
7949
8472
|
bundlePath,
|
|
7950
8473
|
buildId,
|
|
@@ -8026,11 +8549,11 @@ function loaderFor4(absPath) {
|
|
|
8026
8549
|
}
|
|
8027
8550
|
// pkgs/@akanjs/devkit/frontendBuild/precompressArtifacts.ts
|
|
8028
8551
|
import fs5 from "fs";
|
|
8029
|
-
import
|
|
8552
|
+
import path32 from "path";
|
|
8030
8553
|
var COMPRESSIBLE_EXTS = new Set([".css", ".html", ".js", ".json", ".svg"]);
|
|
8031
8554
|
var MIN_COMPRESS_BYTES = 1024;
|
|
8032
8555
|
async function precompressArtifacts(app) {
|
|
8033
|
-
const roots = [
|
|
8556
|
+
const roots = [path32.join(app.dist.cwdPath, ".akan/artifact/client")];
|
|
8034
8557
|
const result = { files: 0, inputBytes: 0, outputBytes: 0 };
|
|
8035
8558
|
await Promise.all(roots.map((root) => precompressRoot(root, result)));
|
|
8036
8559
|
if (result.files > 0) {
|
|
@@ -8056,7 +8579,7 @@ async function precompressRoot(root, result) {
|
|
|
8056
8579
|
async function shouldPrecompress(filePath) {
|
|
8057
8580
|
if (filePath.endsWith(".gz"))
|
|
8058
8581
|
return false;
|
|
8059
|
-
if (!COMPRESSIBLE_EXTS.has(
|
|
8582
|
+
if (!COMPRESSIBLE_EXTS.has(path32.extname(filePath).toLowerCase()))
|
|
8060
8583
|
return false;
|
|
8061
8584
|
const file = Bun.file(filePath);
|
|
8062
8585
|
if (!await file.exists())
|
|
@@ -8074,7 +8597,7 @@ function formatBytes(bytes) {
|
|
|
8074
8597
|
return `${(bytes / 1024 / 1024).toFixed(1)}MB`;
|
|
8075
8598
|
}
|
|
8076
8599
|
// pkgs/@akanjs/devkit/frontendBuild/ssrBaseArtifactBuilder.ts
|
|
8077
|
-
import
|
|
8600
|
+
import path33 from "path";
|
|
8078
8601
|
import { optimize } from "@tailwindcss/node";
|
|
8079
8602
|
function prepareCssAsset(command, basePath2, cssText) {
|
|
8080
8603
|
return optimize(cssText, { file: `${basePath2 || "root"}.css`, minify: command === "build" }).code;
|
|
@@ -8090,7 +8613,7 @@ class SsrBaseArtifactBuilder {
|
|
|
8090
8613
|
this.#app = app;
|
|
8091
8614
|
this.#command = command;
|
|
8092
8615
|
this.#artifactDir = `${command === "build" ? app.dist.cwdPath : app.cwdPath}/.akan/artifact`;
|
|
8093
|
-
this.#absArtifactDir =
|
|
8616
|
+
this.#absArtifactDir = path33.resolve(this.#artifactDir);
|
|
8094
8617
|
}
|
|
8095
8618
|
async build() {
|
|
8096
8619
|
const akanConfig2 = await this.#app.getConfig();
|
|
@@ -8112,7 +8635,7 @@ class SsrBaseArtifactBuilder {
|
|
|
8112
8635
|
rscRuntimeSsrManifest,
|
|
8113
8636
|
vendorMap,
|
|
8114
8637
|
cssAssets,
|
|
8115
|
-
pagesBundlePath: this.#command === "build" ?
|
|
8638
|
+
pagesBundlePath: this.#command === "build" ? path33.relative(this.#absArtifactDir, pagesBundle.bundlePath) : pagesBundle.bundlePath,
|
|
8116
8639
|
pagesBundleBuildId: pagesBundle.buildId,
|
|
8117
8640
|
domains: [...akanConfig2.domains],
|
|
8118
8641
|
subRoutes: Object.fromEntries(Array.from(akanConfig2.subRoutes.entries()).map(([basePath2, domains]) => [basePath2, [...domains]])),
|
|
@@ -8121,7 +8644,7 @@ class SsrBaseArtifactBuilder {
|
|
|
8121
8644
|
i18n: akanConfig2.i18n,
|
|
8122
8645
|
imageConfig: akanConfig2.images
|
|
8123
8646
|
};
|
|
8124
|
-
await Bun.write(
|
|
8647
|
+
await Bun.write(path33.join(this.#absArtifactDir, "base-artifact.json"), `${JSON.stringify(artifact, null, 2)}
|
|
8125
8648
|
`);
|
|
8126
8649
|
this.#app.verbose(`[base-artifact] complete in ${Date.now() - this.#started}ms`);
|
|
8127
8650
|
return { artifact, seedIndex, cssCompiler, optimizedFonts };
|
|
@@ -8169,15 +8692,15 @@ class SsrBaseArtifactBuilder {
|
|
|
8169
8692
|
async#resolveAkanServerPath() {
|
|
8170
8693
|
const candidates = [];
|
|
8171
8694
|
try {
|
|
8172
|
-
candidates.push(
|
|
8695
|
+
candidates.push(path33.dirname(Bun.resolveSync("akanjs/server", this.#app.workspace.workspaceRoot)));
|
|
8173
8696
|
} catch {}
|
|
8174
|
-
candidates.push(
|
|
8697
|
+
candidates.push(path33.join(this.#app.workspace.workspaceRoot, "pkgs/akanjs/server"), path33.join(this.#app.workspace.workspaceRoot, "node_modules/akanjs/server"));
|
|
8175
8698
|
try {
|
|
8176
|
-
candidates.push(
|
|
8699
|
+
candidates.push(path33.dirname(Bun.resolveSync("akanjs/server", path33.dirname(Bun.main))));
|
|
8177
8700
|
} catch {}
|
|
8178
|
-
candidates.push(
|
|
8701
|
+
candidates.push(path33.join(path33.dirname(Bun.main), "node_modules/akanjs/server"), path33.join(path33.dirname(Bun.main), "../../akanjs/server"), path33.resolve(import.meta.dir, "../../server"), path33.resolve(import.meta.dir, "../server"));
|
|
8179
8702
|
for (const candidate of candidates) {
|
|
8180
|
-
if (await Bun.file(
|
|
8703
|
+
if (await Bun.file(path33.join(candidate, "rscClient.tsx")).exists())
|
|
8181
8704
|
return candidate;
|
|
8182
8705
|
}
|
|
8183
8706
|
throw new Error(`[base-artifact] failed to locate akanjs/server; looked in: ${candidates.join(", ")}`);
|
|
@@ -8206,14 +8729,14 @@ ${preparedCssText}`).toString(36);
|
|
|
8206
8729
|
`styles/${cssAssetName}-${cssHash}.css`,
|
|
8207
8730
|
`/_akan/styles/${cssAssetName}-${cssHash}.css`
|
|
8208
8731
|
];
|
|
8209
|
-
await Bun.write(
|
|
8732
|
+
await Bun.write(path33.join(this.#absArtifactDir, cssRelPath), preparedCssText);
|
|
8210
8733
|
this.#app.verbose(`[base-artifact] wrote ${preparedCssText.length} bytes of CSS for ${basePath2} -> ${cssRelPath}`);
|
|
8211
8734
|
return [basePath2, { cssUrl, cssRelPath }];
|
|
8212
8735
|
}
|
|
8213
8736
|
}
|
|
8214
8737
|
// pkgs/@akanjs/devkit/frontendBuild/watchRootResolver.ts
|
|
8215
8738
|
import fs6 from "fs";
|
|
8216
|
-
import
|
|
8739
|
+
import path34 from "path";
|
|
8217
8740
|
|
|
8218
8741
|
class WatchRootResolver {
|
|
8219
8742
|
#app;
|
|
@@ -8223,15 +8746,15 @@ class WatchRootResolver {
|
|
|
8223
8746
|
async resolve() {
|
|
8224
8747
|
const tsconfig = await this.#app.getTsConfig();
|
|
8225
8748
|
const set = new Set;
|
|
8226
|
-
set.add(
|
|
8749
|
+
set.add(path34.resolve(`${this.#app.cwdPath}/page`));
|
|
8227
8750
|
for (const targets of Object.values(tsconfig.compilerOptions.paths ?? {})) {
|
|
8228
8751
|
for (const target of targets) {
|
|
8229
8752
|
if (!target)
|
|
8230
8753
|
continue;
|
|
8231
|
-
if (
|
|
8754
|
+
if (path34.isAbsolute(target))
|
|
8232
8755
|
continue;
|
|
8233
8756
|
const cleaned = target.replace(/\/?\*+.*$/, "").replace(/\/[^/]+\.[^/]+$/, "");
|
|
8234
|
-
const resolved =
|
|
8757
|
+
const resolved = path34.resolve(this.#app.workspace.workspaceRoot, cleaned);
|
|
8235
8758
|
if (fs6.existsSync(resolved))
|
|
8236
8759
|
set.add(resolved);
|
|
8237
8760
|
}
|
|
@@ -8298,7 +8821,7 @@ class ApplicationBuildRunner {
|
|
|
8298
8821
|
phases: this.#phases,
|
|
8299
8822
|
durationMs: Date.now() - this.#startedAt,
|
|
8300
8823
|
outputDir: this.#app.dist.cwdPath,
|
|
8301
|
-
artifactDir:
|
|
8824
|
+
artifactDir: path35.join(this.#app.dist.cwdPath, ".akan/artifact")
|
|
8302
8825
|
};
|
|
8303
8826
|
}
|
|
8304
8827
|
async typecheck(options = {}) {
|
|
@@ -8306,7 +8829,7 @@ class ApplicationBuildRunner {
|
|
|
8306
8829
|
await this.#app.getPageKeys({ refresh: true });
|
|
8307
8830
|
const { typecheckDir, tsconfigPath } = await this.#writeTypecheckTsconfig({ incremental });
|
|
8308
8831
|
if (clean)
|
|
8309
|
-
await
|
|
8832
|
+
await rm3(path35.join(typecheckDir, "tsconfig.tsbuildinfo"), { force: true });
|
|
8310
8833
|
await this.#checkProjectInChildProcess(tsconfigPath);
|
|
8311
8834
|
}
|
|
8312
8835
|
async#runPhase(id, label, task, summarize, options = {}) {
|
|
@@ -8378,7 +8901,7 @@ class ApplicationBuildRunner {
|
|
|
8378
8901
|
};
|
|
8379
8902
|
}
|
|
8380
8903
|
async#writeConsoleShim() {
|
|
8381
|
-
await Bun.write(
|
|
8904
|
+
await Bun.write(path35.join(this.#app.dist.cwdPath, "console.js"), `import { cnst, db, dict, option, server, sig, srv } from "./server.js";
|
|
8382
8905
|
import { assertAkanConsoleAllowed, startAkanConsole } from "./console-runtime.js";
|
|
8383
8906
|
|
|
8384
8907
|
const run = async () => {
|
|
@@ -8401,14 +8924,14 @@ void run().catch((error) => {
|
|
|
8401
8924
|
try {
|
|
8402
8925
|
return Bun.resolveSync("akanjs/server/rsc-worker", import.meta.dir);
|
|
8403
8926
|
} catch {
|
|
8404
|
-
return
|
|
8927
|
+
return path35.join(this.#app.workspace.workspaceRoot, "pkgs/akanjs/server/rscWorker.tsx");
|
|
8405
8928
|
}
|
|
8406
8929
|
}
|
|
8407
8930
|
#resolveConsoleRuntimeBuildEntry() {
|
|
8408
8931
|
try {
|
|
8409
|
-
return
|
|
8932
|
+
return path35.join(path35.dirname(Bun.resolveSync("akanjs/server", import.meta.dir)), "console.ts");
|
|
8410
8933
|
} catch {
|
|
8411
|
-
return
|
|
8934
|
+
return path35.join(this.#app.workspace.workspaceRoot, "pkgs/akanjs/server/console.ts");
|
|
8412
8935
|
}
|
|
8413
8936
|
}
|
|
8414
8937
|
async#buildCsr() {
|
|
@@ -8425,8 +8948,8 @@ void run().catch((error) => {
|
|
|
8425
8948
|
return { base, allRoutes };
|
|
8426
8949
|
}
|
|
8427
8950
|
async#writeTypecheckTsconfig({ incremental = true } = {}) {
|
|
8428
|
-
const typecheckDir =
|
|
8429
|
-
await
|
|
8951
|
+
const typecheckDir = path35.join(this.#app.cwdPath, ".akan", "typecheck");
|
|
8952
|
+
await mkdir8(typecheckDir, { recursive: true });
|
|
8430
8953
|
const tsconfig = {
|
|
8431
8954
|
extends: "../../tsconfig.json",
|
|
8432
8955
|
compilerOptions: {
|
|
@@ -8444,7 +8967,7 @@ void run().catch((error) => {
|
|
|
8444
8967
|
],
|
|
8445
8968
|
references: []
|
|
8446
8969
|
};
|
|
8447
|
-
const tsconfigPath =
|
|
8970
|
+
const tsconfigPath = path35.join(typecheckDir, "tsconfig.json");
|
|
8448
8971
|
await Bun.write(tsconfigPath, `${JSON.stringify(tsconfig, null, 2)}
|
|
8449
8972
|
`);
|
|
8450
8973
|
return { typecheckDir, tsconfigPath };
|
|
@@ -8470,10 +8993,10 @@ void run().catch((error) => {
|
|
|
8470
8993
|
}
|
|
8471
8994
|
async#resolveTypecheckWorkerEntry() {
|
|
8472
8995
|
const candidates = [
|
|
8473
|
-
|
|
8474
|
-
|
|
8475
|
-
|
|
8476
|
-
|
|
8996
|
+
path35.join(this.#app.workspace.workspaceRoot, "pkgs/@akanjs/devkit/typecheck/typecheck.proc.ts"),
|
|
8997
|
+
path35.join(this.#app.workspace.workspaceRoot, "node_modules/@akanjs/devkit/typecheck/typecheck.proc.ts"),
|
|
8998
|
+
path35.join(import.meta.dir, "typecheck.proc.js"),
|
|
8999
|
+
path35.join(import.meta.dir, "typecheck.proc.ts")
|
|
8477
9000
|
];
|
|
8478
9001
|
for (const candidate of candidates)
|
|
8479
9002
|
if (await Bun.file(candidate).exists())
|
|
@@ -8506,7 +9029,7 @@ void run().catch((error) => {
|
|
|
8506
9029
|
}
|
|
8507
9030
|
}
|
|
8508
9031
|
// pkgs/@akanjs/devkit/applicationReleasePackager.ts
|
|
8509
|
-
import { cp, mkdir as
|
|
9032
|
+
import { cp, mkdir as mkdir9, rm as rm4 } from "fs/promises";
|
|
8510
9033
|
|
|
8511
9034
|
// pkgs/@akanjs/devkit/uploadRelease.ts
|
|
8512
9035
|
import { HttpClient as HttpClient2, Logger as Logger9 } from "akanjs/common";
|
|
@@ -8612,16 +9135,16 @@ class ApplicationReleasePackager {
|
|
|
8612
9135
|
const platformVersion = akanConfig2.mobile.version;
|
|
8613
9136
|
const buildRoot = `${this.#app.workspace.workspaceRoot}/releases/builds/${this.#app.name}`;
|
|
8614
9137
|
if (await FileSys.dirExists(buildRoot))
|
|
8615
|
-
await
|
|
8616
|
-
await
|
|
9138
|
+
await rm4(buildRoot, { recursive: true, force: true });
|
|
9139
|
+
await mkdir9(buildRoot, { recursive: true });
|
|
8617
9140
|
if (rebuild || !await FileSys.dirExists(`${this.#app.dist.cwdPath}/backend`))
|
|
8618
9141
|
await this.#build();
|
|
8619
9142
|
const buildVersion = `${platformVersion}-${buildNum}`;
|
|
8620
9143
|
const buildPath = `${buildRoot}/${buildVersion}`;
|
|
8621
|
-
await
|
|
9144
|
+
await mkdir9(buildPath, { recursive: true });
|
|
8622
9145
|
await cp(`${this.#app.dist.cwdPath}/backend`, `${buildPath}/backend`, { recursive: true });
|
|
8623
9146
|
await cp(this.#app.dist.cwdPath, buildRoot, { recursive: true });
|
|
8624
|
-
await
|
|
9147
|
+
await rm4(`${buildRoot}/frontend/.next`, { recursive: true, force: true });
|
|
8625
9148
|
await this.#app.workspace.spawn("tar", [
|
|
8626
9149
|
"-zcf",
|
|
8627
9150
|
`${this.#app.workspace.workspaceRoot}/releases/builds/${this.#app.name}-release.tar.gz`,
|
|
@@ -8641,7 +9164,7 @@ class ApplicationReleasePackager {
|
|
|
8641
9164
|
`${this.#app.workspace.workspaceRoot}/releases/builds/${this.#app.name}-appBuild.zip`,
|
|
8642
9165
|
"./csr"
|
|
8643
9166
|
]);
|
|
8644
|
-
await
|
|
9167
|
+
await rm4("./csr", { recursive: true, force: true });
|
|
8645
9168
|
}
|
|
8646
9169
|
async#writeSourceArchive({ readme }) {
|
|
8647
9170
|
const sourceRoot = `${this.#app.workspace.workspaceRoot}/releases/sources/${this.#app.name}`;
|
|
@@ -8649,13 +9172,13 @@ class ApplicationReleasePackager {
|
|
|
8649
9172
|
await cp(this.#app.dist.cwdPath, `${sourceRoot}/apps/${this.#app.name}`, { recursive: true });
|
|
8650
9173
|
const libDeps = ["social", "shared", "platform", "util"];
|
|
8651
9174
|
await Promise.all(libDeps.map((lib) => cp(`${this.#app.workspace.cwdPath}/libs/${lib}`, `${sourceRoot}/libs/${lib}`, { recursive: true })));
|
|
8652
|
-
await Promise.all([".next", "ios", "android", "public/libs"].map(async (
|
|
8653
|
-
const targetPath = `${sourceRoot}/apps/${this.#app.name}/${
|
|
9175
|
+
await Promise.all([".next", "ios", "android", "public/libs"].map(async (path36) => {
|
|
9176
|
+
const targetPath = `${sourceRoot}/apps/${this.#app.name}/${path36}`;
|
|
8654
9177
|
if (await FileSys.dirExists(targetPath))
|
|
8655
|
-
await
|
|
9178
|
+
await rm4(targetPath, { recursive: true, force: true });
|
|
8656
9179
|
}));
|
|
8657
9180
|
const syncPaths = [".husky", ".gitignore", "package.json"];
|
|
8658
|
-
await Promise.all(syncPaths.map((
|
|
9181
|
+
await Promise.all(syncPaths.map((path36) => cp(`${this.#app.workspace.cwdPath}/${path36}`, `${sourceRoot}/${path36}`, { recursive: true })));
|
|
8659
9182
|
await this.#writeSourceTsconfig(sourceRoot, libDeps);
|
|
8660
9183
|
await Bun.write(`${sourceRoot}/README.md`, readme);
|
|
8661
9184
|
await this.#app.workspace.spawn("tar", [
|
|
@@ -8671,11 +9194,11 @@ class ApplicationReleasePackager {
|
|
|
8671
9194
|
const maxRetry = 3;
|
|
8672
9195
|
for (let i = 0;i < maxRetry; i++) {
|
|
8673
9196
|
try {
|
|
8674
|
-
await
|
|
9197
|
+
await rm4(sourceRoot, { recursive: true, force: true });
|
|
8675
9198
|
} catch {}
|
|
8676
9199
|
}
|
|
8677
9200
|
}
|
|
8678
|
-
await
|
|
9201
|
+
await mkdir9(sourceRoot, { recursive: true });
|
|
8679
9202
|
}
|
|
8680
9203
|
async#writeSourceTsconfig(sourceRoot, libDeps) {
|
|
8681
9204
|
const tsconfig = await this.#app.workspace.readJson("tsconfig.json");
|
|
@@ -8746,20 +9269,20 @@ class ApplicationReleasePackager {
|
|
|
8746
9269
|
}
|
|
8747
9270
|
}
|
|
8748
9271
|
// pkgs/@akanjs/devkit/applicationTestPreload.ts
|
|
8749
|
-
import
|
|
9272
|
+
import path36 from "path";
|
|
8750
9273
|
var SIGNAL_TEST_PRELOAD_PATH = "test/signalTest.preload.ts";
|
|
8751
9274
|
async function resolveSignalTestPreloadPath(target) {
|
|
8752
9275
|
const candidates = [];
|
|
8753
9276
|
const addResolvedPackageCandidate = (basePath2) => {
|
|
8754
9277
|
try {
|
|
8755
|
-
candidates.push(
|
|
9278
|
+
candidates.push(path36.join(path36.dirname(Bun.resolveSync("akanjs/package.json", basePath2)), SIGNAL_TEST_PRELOAD_PATH));
|
|
8756
9279
|
} catch {}
|
|
8757
9280
|
};
|
|
8758
9281
|
addResolvedPackageCandidate(target.cwdPath);
|
|
8759
9282
|
addResolvedPackageCandidate(process.cwd());
|
|
8760
|
-
addResolvedPackageCandidate(
|
|
9283
|
+
addResolvedPackageCandidate(path36.dirname(Bun.main));
|
|
8761
9284
|
addResolvedPackageCandidate(import.meta.dir);
|
|
8762
|
-
candidates.push(
|
|
9285
|
+
candidates.push(path36.join(target.cwdPath, "../../node_modules/akanjs", SIGNAL_TEST_PRELOAD_PATH), path36.join(target.cwdPath, "../../pkgs/akanjs", SIGNAL_TEST_PRELOAD_PATH), path36.join(process.cwd(), "node_modules/akanjs", SIGNAL_TEST_PRELOAD_PATH), path36.join(process.cwd(), "pkgs/akanjs", SIGNAL_TEST_PRELOAD_PATH), path36.join(path36.dirname(Bun.main), "../../akanjs", SIGNAL_TEST_PRELOAD_PATH), path36.resolve(import.meta.dir, "../../akanjs", SIGNAL_TEST_PRELOAD_PATH));
|
|
8763
9286
|
for (const candidate of [...new Set(candidates)]) {
|
|
8764
9287
|
if (await Bun.file(candidate).exists())
|
|
8765
9288
|
return candidate;
|
|
@@ -8768,8 +9291,8 @@ async function resolveSignalTestPreloadPath(target) {
|
|
|
8768
9291
|
}
|
|
8769
9292
|
// pkgs/@akanjs/devkit/builder.ts
|
|
8770
9293
|
import { existsSync as existsSync2 } from "fs";
|
|
8771
|
-
import { mkdir as
|
|
8772
|
-
import
|
|
9294
|
+
import { mkdir as mkdir10 } from "fs/promises";
|
|
9295
|
+
import path37 from "path";
|
|
8773
9296
|
var SKIP_ENTRY_DIR_SET = new Set(["node_modules", "dist", "build", ".git", ".next"]);
|
|
8774
9297
|
var assetExtensions = [".css", ".md", ".js", ".png", ".ico", ".svg", ".json", ".template"];
|
|
8775
9298
|
var assetLoader = Object.fromEntries(assetExtensions.map((ext) => [ext, "file"]));
|
|
@@ -8786,14 +9309,14 @@ class Builder {
|
|
|
8786
9309
|
#globEntrypoints(cwd, pattern) {
|
|
8787
9310
|
const glob = new Bun.Glob(pattern);
|
|
8788
9311
|
return Array.from(glob.scanSync({ cwd, onlyFiles: true })).filter((relativePath) => {
|
|
8789
|
-
const segments = relativePath.split(
|
|
9312
|
+
const segments = relativePath.split(path37.sep);
|
|
8790
9313
|
return !segments.some((segment) => SKIP_ENTRY_DIR_SET.has(segment));
|
|
8791
|
-
}).map((rel) =>
|
|
9314
|
+
}).map((rel) => path37.join(cwd, rel));
|
|
8792
9315
|
}
|
|
8793
9316
|
#globFiles(cwd, pattern = "**/*.*") {
|
|
8794
9317
|
const glob = new Bun.Glob(pattern);
|
|
8795
9318
|
return Array.from(glob.scanSync({ cwd, onlyFiles: true })).filter((relativePath) => {
|
|
8796
|
-
const segments = relativePath.split(
|
|
9319
|
+
const segments = relativePath.split(path37.sep);
|
|
8797
9320
|
return !segments.some((segment) => SKIP_ENTRY_DIR_SET.has(segment));
|
|
8798
9321
|
});
|
|
8799
9322
|
}
|
|
@@ -8801,17 +9324,17 @@ class Builder {
|
|
|
8801
9324
|
const out = [];
|
|
8802
9325
|
for (const p of additionalEntryPoints) {
|
|
8803
9326
|
if (p.includes("*")) {
|
|
8804
|
-
const rel = p.startsWith(`${cwd}/`) || p.startsWith(`${cwd}${
|
|
9327
|
+
const rel = p.startsWith(`${cwd}/`) || p.startsWith(`${cwd}${path37.sep}`) ? p.slice(cwd.length + 1) : p;
|
|
8805
9328
|
out.push(...this.#globEntrypoints(cwd, rel));
|
|
8806
9329
|
} else
|
|
8807
|
-
out.push(
|
|
9330
|
+
out.push(path37.isAbsolute(p) ? p : path37.join(cwd, p));
|
|
8808
9331
|
}
|
|
8809
9332
|
return out;
|
|
8810
9333
|
}
|
|
8811
9334
|
#getBuildOptions({ bundle = false, additionalEntryPoints = [] } = {}) {
|
|
8812
9335
|
const cwd = this.#executor.cwdPath;
|
|
8813
9336
|
const entrypoints = [
|
|
8814
|
-
...bundle ? [
|
|
9337
|
+
...bundle ? [path37.join(cwd, "index.ts")] : this.#globEntrypoints(cwd, "**/*.{ts,tsx}"),
|
|
8815
9338
|
...this.#resolveAdditionalEntrypoints(cwd, additionalEntryPoints)
|
|
8816
9339
|
];
|
|
8817
9340
|
return {
|
|
@@ -8832,9 +9355,9 @@ class Builder {
|
|
|
8832
9355
|
for (const relativePath of this.#globFiles(cwd)) {
|
|
8833
9356
|
if (relativePath === "package.json")
|
|
8834
9357
|
continue;
|
|
8835
|
-
const sourcePath =
|
|
8836
|
-
const targetPath =
|
|
8837
|
-
await
|
|
9358
|
+
const sourcePath = path37.join(cwd, relativePath);
|
|
9359
|
+
const targetPath = path37.join(this.#distExecutor.cwdPath, relativePath);
|
|
9360
|
+
await mkdir10(path37.dirname(targetPath), { recursive: true });
|
|
8838
9361
|
await Bun.write(targetPath, Bun.file(sourcePath));
|
|
8839
9362
|
}
|
|
8840
9363
|
}
|
|
@@ -8845,13 +9368,13 @@ class Builder {
|
|
|
8845
9368
|
return withoutFormatDir;
|
|
8846
9369
|
if (!hasDotSlash && withoutFormatDir === publishedPath)
|
|
8847
9370
|
return publishedPath;
|
|
8848
|
-
const parsed =
|
|
9371
|
+
const parsed = path37.posix.parse(withoutFormatDir);
|
|
8849
9372
|
if (![".js", ".mjs", ".cjs"].includes(parsed.ext))
|
|
8850
9373
|
return withoutFormatDir;
|
|
8851
|
-
const withoutExt =
|
|
9374
|
+
const withoutExt = path37.posix.join(parsed.dir, parsed.name);
|
|
8852
9375
|
const sourcePath = withoutExt.startsWith("./") ? withoutExt.slice(2) : withoutExt;
|
|
8853
9376
|
const sourceCandidates = [`${sourcePath}.ts`, `${sourcePath}.tsx`];
|
|
8854
|
-
const matchedSource = sourceCandidates.find((candidate) => existsSync2(
|
|
9377
|
+
const matchedSource = sourceCandidates.find((candidate) => existsSync2(path37.join(this.#executor.cwdPath, candidate)));
|
|
8855
9378
|
if (!matchedSource)
|
|
8856
9379
|
return withoutFormatDir;
|
|
8857
9380
|
return hasDotSlash ? `./${matchedSource}` : matchedSource;
|
|
@@ -8900,10 +9423,10 @@ class Builder {
|
|
|
8900
9423
|
}
|
|
8901
9424
|
}
|
|
8902
9425
|
// pkgs/@akanjs/devkit/capacitorApp.ts
|
|
8903
|
-
import { cp as cp2, mkdir as
|
|
8904
|
-
import
|
|
9426
|
+
import { cp as cp2, mkdir as mkdir11, rm as rm5 } from "fs/promises";
|
|
9427
|
+
import path38 from "path";
|
|
8905
9428
|
import { MobileProject } from "@trapezedev/project";
|
|
8906
|
-
import { capitalize as
|
|
9429
|
+
import { capitalize as capitalize4 } from "akanjs/common";
|
|
8907
9430
|
|
|
8908
9431
|
// pkgs/@akanjs/devkit/fileEditor.ts
|
|
8909
9432
|
class FileEditor {
|
|
@@ -9096,10 +9619,10 @@ class CapacitorApp {
|
|
|
9096
9619
|
constructor(app, target) {
|
|
9097
9620
|
this.app = app;
|
|
9098
9621
|
this.target = target;
|
|
9099
|
-
this.targetRootPath =
|
|
9100
|
-
this.targetRoot =
|
|
9101
|
-
this.targetWebRoot =
|
|
9102
|
-
this.targetAssetRoot =
|
|
9622
|
+
this.targetRootPath = path38.posix.join(".akan", "mobile", this.target.name);
|
|
9623
|
+
this.targetRoot = path38.join(this.app.cwdPath, this.targetRootPath);
|
|
9624
|
+
this.targetWebRoot = path38.join(this.targetRoot, "www");
|
|
9625
|
+
this.targetAssetRoot = path38.join(this.targetRoot, "assets");
|
|
9103
9626
|
this.project = new MobileProject(this.app.cwdPath, {
|
|
9104
9627
|
android: { path: this.androidRootPath },
|
|
9105
9628
|
ios: { path: this.iosProjectPath }
|
|
@@ -9111,13 +9634,13 @@ class CapacitorApp {
|
|
|
9111
9634
|
env = "debug",
|
|
9112
9635
|
regenerate = false
|
|
9113
9636
|
} = {}) {
|
|
9114
|
-
await
|
|
9637
|
+
await mkdir11(this.targetRoot, { recursive: true });
|
|
9115
9638
|
await this.#writeCapacitorConfig();
|
|
9116
9639
|
if (regenerate) {
|
|
9117
9640
|
if (!platform || platform === "ios")
|
|
9118
|
-
await
|
|
9641
|
+
await rm5(path38.join(this.app.cwdPath, this.iosRootPath), { recursive: true, force: true });
|
|
9119
9642
|
if (!platform || platform === "android")
|
|
9120
|
-
await
|
|
9643
|
+
await rm5(path38.join(this.app.cwdPath, this.androidRootPath), { recursive: true, force: true });
|
|
9121
9644
|
}
|
|
9122
9645
|
const project = this.project;
|
|
9123
9646
|
await this.project.load();
|
|
@@ -9181,7 +9704,7 @@ class CapacitorApp {
|
|
|
9181
9704
|
await this.#spawnMobile("npx", ["cap", "sync", "android"], { operation, env });
|
|
9182
9705
|
}
|
|
9183
9706
|
async#updateAndroidBuildTypes() {
|
|
9184
|
-
const appGradle = await FileEditor.create(
|
|
9707
|
+
const appGradle = await FileEditor.create(path38.join(this.app.cwdPath, this.androidRootPath, "app/build.gradle"));
|
|
9185
9708
|
const buildTypesBlock = `
|
|
9186
9709
|
debug {
|
|
9187
9710
|
applicationIdSuffix ".debug"
|
|
@@ -9224,7 +9747,7 @@ class CapacitorApp {
|
|
|
9224
9747
|
const gradleCommand = isWindows ? "gradlew.bat" : "./gradlew";
|
|
9225
9748
|
await this.app.spawn(gradleCommand, [assembleType === "apk" ? "assembleRelease" : "bundleRelease"], {
|
|
9226
9749
|
stdio: "inherit",
|
|
9227
|
-
cwd:
|
|
9750
|
+
cwd: path38.join(this.app.cwdPath, this.androidRootPath),
|
|
9228
9751
|
env: await this.#commandEnv("release", env)
|
|
9229
9752
|
});
|
|
9230
9753
|
}
|
|
@@ -9232,10 +9755,10 @@ class CapacitorApp {
|
|
|
9232
9755
|
await this.#spawnMobile("npx", ["cap", "open", "android"], { operation: "local", env: "local" });
|
|
9233
9756
|
}
|
|
9234
9757
|
async#ensureAndroidAssetsDir() {
|
|
9235
|
-
await
|
|
9758
|
+
await mkdir11(path38.join(this.app.cwdPath, this.androidAssetsPath), { recursive: true });
|
|
9236
9759
|
}
|
|
9237
9760
|
async#ensureAndroidDebugKeystore() {
|
|
9238
|
-
const keystorePath =
|
|
9761
|
+
const keystorePath = path38.join(this.app.cwdPath, this.androidRootPath, "app/debug.keystore");
|
|
9239
9762
|
if (await Bun.file(keystorePath).exists())
|
|
9240
9763
|
return;
|
|
9241
9764
|
await this.#spawn("keytool", [
|
|
@@ -9281,12 +9804,12 @@ class CapacitorApp {
|
|
|
9281
9804
|
await this.#prepareAndroid({ operation: "release", env: "main" });
|
|
9282
9805
|
}
|
|
9283
9806
|
async prepareWww() {
|
|
9284
|
-
const htmlSource =
|
|
9807
|
+
const htmlSource = path38.join(this.app.dist.cwdPath, "csr", targetHtmlFilename(this.target));
|
|
9285
9808
|
if (!await Bun.file(htmlSource).exists())
|
|
9286
9809
|
throw new Error(`CSR html for mobile target '${this.target.name}' not found: ${htmlSource}`);
|
|
9287
|
-
await
|
|
9288
|
-
await
|
|
9289
|
-
await Bun.write(
|
|
9810
|
+
await rm5(this.targetWebRoot, { recursive: true, force: true });
|
|
9811
|
+
await mkdir11(this.targetWebRoot, { recursive: true });
|
|
9812
|
+
await Bun.write(path38.join(this.targetWebRoot, "index.html"), this.#injectMobileTargetMeta(await Bun.file(htmlSource).text()));
|
|
9290
9813
|
}
|
|
9291
9814
|
#injectMobileTargetMeta(html) {
|
|
9292
9815
|
const basePath2 = this.target.basePath?.replace(/^\/+|\/+$/g, "") ?? "";
|
|
@@ -9297,8 +9820,8 @@ class CapacitorApp {
|
|
|
9297
9820
|
</head>`);
|
|
9298
9821
|
}
|
|
9299
9822
|
async#writeCapacitorConfig() {
|
|
9300
|
-
await
|
|
9301
|
-
const appInfoPath =
|
|
9823
|
+
await mkdir11(this.targetRoot, { recursive: true });
|
|
9824
|
+
const appInfoPath = path38.relative(this.app.cwdPath, path38.join(this.app.cwdPath, "akan.app.json")).split(path38.sep).join("/");
|
|
9302
9825
|
const content = `import type { AppScanResult } from "akanjs";
|
|
9303
9826
|
import { withBase } from "${process.env.USE_AKANJS_PKGS === "true" ? "../../pkgs/" : ""}akanjs/capacitor.base.config";
|
|
9304
9827
|
import appInfo from "${appInfoPath.startsWith(".") ? appInfoPath : `./${appInfoPath}`}";
|
|
@@ -9319,18 +9842,18 @@ export default withBase(
|
|
|
9319
9842
|
appInfo as AppScanResult,
|
|
9320
9843
|
);
|
|
9321
9844
|
`;
|
|
9322
|
-
await Bun.write(
|
|
9845
|
+
await Bun.write(path38.join(this.app.cwdPath, "capacitor.config.ts"), content);
|
|
9323
9846
|
}
|
|
9324
9847
|
async#prepareTargetAssets() {
|
|
9325
9848
|
if (!this.target.assets)
|
|
9326
9849
|
return;
|
|
9327
|
-
await
|
|
9850
|
+
await mkdir11(this.targetAssetRoot, { recursive: true });
|
|
9328
9851
|
if (this.target.assets.icon)
|
|
9329
|
-
await cp2(
|
|
9852
|
+
await cp2(path38.join(this.app.cwdPath, this.target.assets.icon), path38.join(this.targetAssetRoot, "icon.png"), {
|
|
9330
9853
|
force: true
|
|
9331
9854
|
});
|
|
9332
9855
|
if (this.target.assets.splash)
|
|
9333
|
-
await cp2(
|
|
9856
|
+
await cp2(path38.join(this.app.cwdPath, this.target.assets.splash), path38.join(this.targetAssetRoot, "splash.png"), {
|
|
9334
9857
|
force: true
|
|
9335
9858
|
});
|
|
9336
9859
|
}
|
|
@@ -9338,11 +9861,11 @@ export default withBase(
|
|
|
9338
9861
|
const files = this.target.files?.[platform];
|
|
9339
9862
|
if (!files)
|
|
9340
9863
|
return;
|
|
9341
|
-
const platformRoot =
|
|
9864
|
+
const platformRoot = path38.join(this.app.cwdPath, platform === "ios" ? this.iosRootPath : this.androidRootPath);
|
|
9342
9865
|
await Promise.all(Object.entries(files).map(async ([to, from]) => {
|
|
9343
|
-
const targetPath =
|
|
9344
|
-
await
|
|
9345
|
-
await cp2(
|
|
9866
|
+
const targetPath = path38.join(platformRoot, to);
|
|
9867
|
+
await mkdir11(path38.dirname(targetPath), { recursive: true });
|
|
9868
|
+
await cp2(path38.join(this.app.cwdPath, from), targetPath, { force: true });
|
|
9346
9869
|
}));
|
|
9347
9870
|
}
|
|
9348
9871
|
async#generateAssets({ operation, env }) {
|
|
@@ -9352,7 +9875,7 @@ export default withBase(
|
|
|
9352
9875
|
"@capacitor/assets",
|
|
9353
9876
|
"generate",
|
|
9354
9877
|
"--assetPath",
|
|
9355
|
-
|
|
9878
|
+
path38.posix.join(this.targetRootPath, "assets"),
|
|
9356
9879
|
"--iosProject",
|
|
9357
9880
|
this.iosProjectPath,
|
|
9358
9881
|
"--androidProject",
|
|
@@ -9454,7 +9977,7 @@ export default withBase(
|
|
|
9454
9977
|
this.#setPermissionsInAndroid(["POST_NOTIFICATIONS"]);
|
|
9455
9978
|
}
|
|
9456
9979
|
async#setPermissionInIos(permissions) {
|
|
9457
|
-
const updateNs = Object.fromEntries(Object.entries(permissions).map(([key, value]) => [`NS${
|
|
9980
|
+
const updateNs = Object.fromEntries(Object.entries(permissions).map(([key, value]) => [`NS${capitalize4(key)}`, value]));
|
|
9458
9981
|
await Promise.all([
|
|
9459
9982
|
this.project.ios.updateInfoPlist(this.iosTargetName, "Debug", updateNs),
|
|
9460
9983
|
this.project.ios.updateInfoPlist(this.iosTargetName, "Release", updateNs)
|
|
@@ -9539,15 +10062,15 @@ var Pkg = createInternalArgToken("Pkg");
|
|
|
9539
10062
|
var Module = createInternalArgToken("Module");
|
|
9540
10063
|
var Workspace = createInternalArgToken("Workspace");
|
|
9541
10064
|
// pkgs/@akanjs/devkit/commandDecorators/command.ts
|
|
9542
|
-
import
|
|
10065
|
+
import path39 from "path";
|
|
9543
10066
|
import { confirm, input as input2, select as select2 } from "@inquirer/prompts";
|
|
9544
10067
|
import { Logger as Logger10 } from "akanjs/common";
|
|
9545
10068
|
import chalk6 from "chalk";
|
|
9546
10069
|
import { program } from "commander";
|
|
9547
10070
|
|
|
9548
10071
|
// pkgs/@akanjs/devkit/commandDecorators/dependencyBuilder.ts
|
|
9549
|
-
var
|
|
9550
|
-
var createDependencyKey = (refName, kind) => `${refName}${
|
|
10072
|
+
var capitalize5 = (value) => value.slice(0, 1).toUpperCase() + value.slice(1);
|
|
10073
|
+
var createDependencyKey = (refName, kind) => `${refName}${capitalize5(kind)}`;
|
|
9551
10074
|
|
|
9552
10075
|
class CommandContainer {
|
|
9553
10076
|
static #instances = new Map;
|
|
@@ -10039,7 +10562,7 @@ var runCommands = async (...commands) => {
|
|
|
10039
10562
|
process.exit(1);
|
|
10040
10563
|
});
|
|
10041
10564
|
const __dirname2 = getDirname(import.meta.url);
|
|
10042
|
-
const packageJsonCandidates = [`${
|
|
10565
|
+
const packageJsonCandidates = [`${path39.dirname(Bun.main)}/package.json`, `${__dirname2}/../package.json`];
|
|
10043
10566
|
let cliPackageJson = null;
|
|
10044
10567
|
for (const packageJsonPath of packageJsonCandidates) {
|
|
10045
10568
|
if (!await FileSys.fileExists(packageJsonPath))
|
|
@@ -10317,8 +10840,8 @@ var scanModuleSpecifiers = (source, filePath, includeExports) => {
|
|
|
10317
10840
|
return importSpecifiers;
|
|
10318
10841
|
};
|
|
10319
10842
|
var parseTsConfig = (tsConfigPath = "./tsconfig.json") => {
|
|
10320
|
-
const configFile = ts7.readConfigFile(tsConfigPath, (
|
|
10321
|
-
return ts7.sys.readFile(
|
|
10843
|
+
const configFile = ts7.readConfigFile(tsConfigPath, (path40) => {
|
|
10844
|
+
return ts7.sys.readFile(path40);
|
|
10322
10845
|
});
|
|
10323
10846
|
return ts7.parseJsonConfigFileContent(configFile.config, ts7.sys, realpathSync(tsConfigPath).replace(/[^/\\]+$/, ""));
|
|
10324
10847
|
};
|
|
@@ -10560,7 +11083,6 @@ import { jsxDEV as jsxDEV2, Fragment as Fragment2 } from "react/jsx-dev-runtime"
|
|
|
10560
11083
|
"use client";
|
|
10561
11084
|
// pkgs/@akanjs/devkit/incrementalBuilder/incrementalBuilder.proc.ts
|
|
10562
11085
|
import { Logger as Logger11 } from "akanjs/common";
|
|
10563
|
-
|
|
10564
11086
|
class IncrementalBuilder {
|
|
10565
11087
|
#logger = new Logger11("IncrementalBuilder");
|
|
10566
11088
|
#app;
|
|
@@ -10569,6 +11091,8 @@ class IncrementalBuilder {
|
|
|
10569
11091
|
#cssCompiler;
|
|
10570
11092
|
#optimizedFonts;
|
|
10571
11093
|
#discovery;
|
|
11094
|
+
#changePlanner;
|
|
11095
|
+
#generatedIndexSync;
|
|
10572
11096
|
#generation = 0;
|
|
10573
11097
|
#workQueue = Promise.resolve();
|
|
10574
11098
|
#cssRebuildQueue = Promise.resolve();
|
|
@@ -10581,6 +11105,8 @@ class IncrementalBuilder {
|
|
|
10581
11105
|
this.#cssCompiler = options.cssCompiler;
|
|
10582
11106
|
this.#optimizedFonts = options.optimizedFonts;
|
|
10583
11107
|
this.#discovery = options.discovery;
|
|
11108
|
+
this.#changePlanner = new DevChangePlanner({ workspaceRoot: options.app.workspace.workspaceRoot });
|
|
11109
|
+
this.#generatedIndexSync = new DevGeneratedIndexSync({ workspaceRoot: options.app.workspace.workspaceRoot });
|
|
10584
11110
|
}
|
|
10585
11111
|
async handleBuildRoute(msg) {
|
|
10586
11112
|
return this.#enqueueWork(`build-route:${msg.routeId}`, async () => this.#handleBuildRoute(msg));
|
|
@@ -10596,6 +11122,7 @@ class IncrementalBuilder {
|
|
|
10596
11122
|
discovery: this.#discovery
|
|
10597
11123
|
}).build();
|
|
10598
11124
|
this.#logger.verbose(`build-route ok routeId=${msg.routeId} newEntries=${delta.newEntries.length}`);
|
|
11125
|
+
this.#sendBuildStatus("route", { generation: msg.generation, ok: true, files: msg.seeds });
|
|
10599
11126
|
return {
|
|
10600
11127
|
type: "build-route-res",
|
|
10601
11128
|
id: msg.id,
|
|
@@ -10614,9 +11141,24 @@ class IncrementalBuilder {
|
|
|
10614
11141
|
} catch (err) {
|
|
10615
11142
|
const errMsg = err instanceof Error ? err.message : String(err);
|
|
10616
11143
|
this.#logger.error(`build-route failed routeId=${msg.routeId}: ${errMsg}`);
|
|
11144
|
+
this.#sendBuildStatus("route", { generation: msg.generation, ok: false, files: msg.seeds, message: errMsg });
|
|
10617
11145
|
return { type: "build-route-res", id: msg.id, ok: false, error: errMsg };
|
|
10618
11146
|
}
|
|
10619
11147
|
}
|
|
11148
|
+
#sendBuildStatus(phase, { generation, ok, files, message }) {
|
|
11149
|
+
if (typeof generation !== "number")
|
|
11150
|
+
return;
|
|
11151
|
+
process.send?.({
|
|
11152
|
+
type: "build-status",
|
|
11153
|
+
data: {
|
|
11154
|
+
generation,
|
|
11155
|
+
phase,
|
|
11156
|
+
ok,
|
|
11157
|
+
files: files ?? [],
|
|
11158
|
+
message
|
|
11159
|
+
}
|
|
11160
|
+
});
|
|
11161
|
+
}
|
|
10620
11162
|
async#enqueueWork(label, fn) {
|
|
10621
11163
|
const started = Date.now();
|
|
10622
11164
|
const run = this.#workQueue.then(fn, fn);
|
|
@@ -10632,10 +11174,10 @@ class IncrementalBuilder {
|
|
|
10632
11174
|
}
|
|
10633
11175
|
}
|
|
10634
11176
|
batchTouchesPagesTree(appDir, batch) {
|
|
10635
|
-
const absAppDir =
|
|
11177
|
+
const absAppDir = path40.resolve(appDir);
|
|
10636
11178
|
for (const f of batch.files) {
|
|
10637
|
-
const abs =
|
|
10638
|
-
if (!abs.startsWith(`${absAppDir}${
|
|
11179
|
+
const abs = path40.resolve(f);
|
|
11180
|
+
if (!abs.startsWith(`${absAppDir}${path40.sep}`) && abs !== absAppDir)
|
|
10639
11181
|
continue;
|
|
10640
11182
|
if (/\.(tsx|ts|jsx|js)$/.test(abs))
|
|
10641
11183
|
return true;
|
|
@@ -10643,15 +11185,15 @@ class IncrementalBuilder {
|
|
|
10643
11185
|
return false;
|
|
10644
11186
|
}
|
|
10645
11187
|
async batchMayChangePageKeys(appDir, batch) {
|
|
10646
|
-
const absAppDir =
|
|
10647
|
-
const pageKeys = new Set((await this.#app.getPageKeys()).map((key) =>
|
|
11188
|
+
const absAppDir = path40.resolve(appDir);
|
|
11189
|
+
const pageKeys = new Set((await this.#app.getPageKeys()).map((key) => path40.normalize(key)));
|
|
10648
11190
|
for (const f of batch.files) {
|
|
10649
|
-
const abs =
|
|
10650
|
-
if (!abs.startsWith(`${absAppDir}${
|
|
11191
|
+
const abs = path40.resolve(f);
|
|
11192
|
+
if (!abs.startsWith(`${absAppDir}${path40.sep}`) && abs !== absAppDir)
|
|
10651
11193
|
continue;
|
|
10652
11194
|
if (!/\.(tsx|ts|jsx|js)$/.test(abs))
|
|
10653
11195
|
continue;
|
|
10654
|
-
const rel =
|
|
11196
|
+
const rel = path40.normalize(path40.relative(absAppDir, abs));
|
|
10655
11197
|
if (!await Bun.file(abs).exists() || !pageKeys.has(rel))
|
|
10656
11198
|
return true;
|
|
10657
11199
|
}
|
|
@@ -10679,7 +11221,7 @@ class IncrementalBuilder {
|
|
|
10679
11221
|
${cssText}`).toString(36);
|
|
10680
11222
|
const cssRelPath = `styles/${cssAssetName}-${cssHash}.css`;
|
|
10681
11223
|
const cssUrl = `/_akan/styles/${cssAssetName}-${cssHash}.css`;
|
|
10682
|
-
await Bun.write(
|
|
11224
|
+
await Bun.write(path40.join(artifactDir, cssRelPath), cssText);
|
|
10683
11225
|
cssAssetEntries.push([basePath2, { cssUrl, cssRelPath }]);
|
|
10684
11226
|
cssBase64ByUrl[cssUrl] = Buffer.from(new TextEncoder().encode(cssText)).toString("base64");
|
|
10685
11227
|
})()
|
|
@@ -10719,9 +11261,17 @@ ${cssText}`).toString(36);
|
|
|
10719
11261
|
generation: next.generation,
|
|
10720
11262
|
changedFiles: next.changedFiles
|
|
10721
11263
|
});
|
|
11264
|
+
this.#sendBuildStatus("css", { generation: next.generation, ok: true, files: next.changedFiles });
|
|
10722
11265
|
this.#logger.verbose(`css-rebuild checked (${Date.now() - started}ms)`);
|
|
10723
11266
|
}).catch((err) => {
|
|
10724
|
-
|
|
11267
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
11268
|
+
this.#logger.error(`css-rebuild failed: ${message}`);
|
|
11269
|
+
this.#sendBuildStatus("css", {
|
|
11270
|
+
generation: next.generation,
|
|
11271
|
+
ok: false,
|
|
11272
|
+
files: next.changedFiles,
|
|
11273
|
+
message
|
|
11274
|
+
});
|
|
10725
11275
|
});
|
|
10726
11276
|
}, 150);
|
|
10727
11277
|
}
|
|
@@ -10739,10 +11289,10 @@ ${cssText}`).toString(36);
|
|
|
10739
11289
|
if (changedFiles.length === 0)
|
|
10740
11290
|
return false;
|
|
10741
11291
|
return changedFiles.some((file) => {
|
|
10742
|
-
const normalized =
|
|
11292
|
+
const normalized = path40.resolve(file);
|
|
10743
11293
|
if (/\.(woff2?|ttf|otf)$/i.test(normalized))
|
|
10744
11294
|
return true;
|
|
10745
|
-
return this.#optimizedFonts.files.some((fontFile) =>
|
|
11295
|
+
return this.#optimizedFonts.files.some((fontFile) => path40.resolve(fontFile) === normalized);
|
|
10746
11296
|
});
|
|
10747
11297
|
}
|
|
10748
11298
|
async installWatcher() {
|
|
@@ -10759,38 +11309,58 @@ ${cssText}`).toString(36);
|
|
|
10759
11309
|
this.#logger.verbose(`watching ${roots.length} roots`);
|
|
10760
11310
|
}
|
|
10761
11311
|
async#handleWatchBatch(appDir, artifactDir, batch) {
|
|
10762
|
-
const
|
|
10763
|
-
if (
|
|
11312
|
+
const rawKinds = new Set(batch.kinds);
|
|
11313
|
+
if (rawKinds.size === 0)
|
|
10764
11314
|
return;
|
|
10765
11315
|
const generation = ++this.#generation;
|
|
10766
|
-
|
|
11316
|
+
const indexSync = await this.#generatedIndexSync.syncForBatch(batch.files);
|
|
11317
|
+
const { files, kinds, expandedBatch, event, hasSyncErrors } = prepareDevWatchBatch({
|
|
11318
|
+
generation,
|
|
11319
|
+
batch,
|
|
11320
|
+
indexSync,
|
|
11321
|
+
changePlanner: this.#changePlanner
|
|
11322
|
+
});
|
|
11323
|
+
const devPlan = event.devPlan;
|
|
11324
|
+
this.#logger.verbose(`[hmr] batch generation=${generation} kinds=${kinds.join(",")} files=${files.length} generated=${indexSync.changedFiles.length} roles=${devPlan.roles.join(",") || "(none)"} actions=${devPlan.actions.join(",") || "(none)"}`);
|
|
11325
|
+
for (const error of indexSync.errors)
|
|
11326
|
+
this.#logger.error(error);
|
|
10767
11327
|
if (kinds.includes("code")) {
|
|
10768
11328
|
const started = Date.now();
|
|
10769
11329
|
if (kinds.includes("config"))
|
|
10770
11330
|
this.#discovery = await GraphClientEntryDiscovery.create(this.#app);
|
|
10771
11331
|
else
|
|
10772
|
-
this.#discovery.invalidate?.(
|
|
11332
|
+
this.#discovery.invalidate?.(files);
|
|
10773
11333
|
this.#logger.verbose(`client-entry-discovery ${kinds.includes("config") ? "refreshed" : "invalidated"} (${Date.now() - started}ms)`);
|
|
10774
11334
|
}
|
|
10775
|
-
if (
|
|
11335
|
+
if (hasSyncErrors) {
|
|
11336
|
+
this.#sendBuildStatus("barrel", { generation, ok: false, files, message: indexSync.errors.join(`
|
|
11337
|
+
`) });
|
|
11338
|
+
process.send?.(event);
|
|
11339
|
+
return;
|
|
11340
|
+
}
|
|
11341
|
+
if (indexSync.changedFiles.length > 0)
|
|
11342
|
+
this.#sendBuildStatus("barrel", { generation, ok: true, files });
|
|
11343
|
+
if (kinds.includes("code") && await this.batchMayChangePageKeys(appDir, expandedBatch)) {
|
|
10776
11344
|
const started = Date.now();
|
|
10777
11345
|
await this.#app.getPageKeys({ refresh: true });
|
|
10778
11346
|
this.#logger.verbose(`pageKeys updated, app pageKeys are refreshed (${Date.now() - started}ms)`);
|
|
10779
|
-
} else if (kinds.includes("code") && this.batchTouchesPagesTree(appDir,
|
|
11347
|
+
} else if (kinds.includes("code") && this.batchTouchesPagesTree(appDir, expandedBatch)) {
|
|
10780
11348
|
this.#logger.verbose("pageKeys refresh skipped; changed page source cannot add/remove a route key");
|
|
10781
11349
|
}
|
|
10782
11350
|
if (kinds.includes("code") && this.#shouldRebuildCsr()) {
|
|
10783
11351
|
try {
|
|
10784
11352
|
const started = Date.now();
|
|
10785
11353
|
await new CsrArtifactBuilder(this.#app).build();
|
|
11354
|
+
this.#sendBuildStatus("csr", { generation, ok: true, files });
|
|
10786
11355
|
this.#logger.verbose(`csr-rebundle ok (${Date.now() - started}ms)`);
|
|
10787
11356
|
} catch (err) {
|
|
10788
|
-
|
|
11357
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
11358
|
+
this.#logger.error(`csr-rebundle failed: ${message}`);
|
|
11359
|
+
this.#sendBuildStatus("csr", { generation, ok: false, files, message });
|
|
10789
11360
|
}
|
|
10790
11361
|
} else if (kinds.includes("code")) {
|
|
10791
11362
|
this.#logger.verbose(`csr-rebundle skipped; set AKAN_DEV_CSR_REBUILD=1 to enable per-save CSR rebuilds`);
|
|
10792
11363
|
}
|
|
10793
|
-
const event = { type: "invalidate", kinds, files: batch.files, generation };
|
|
10794
11364
|
process.send?.(event);
|
|
10795
11365
|
if (kinds.includes("code")) {
|
|
10796
11366
|
try {
|
|
@@ -10798,15 +11368,18 @@ ${cssText}`).toString(36);
|
|
|
10798
11368
|
const next = await new PagesBundleBuilder(this.#app).build();
|
|
10799
11369
|
process.send?.({
|
|
10800
11370
|
type: "pages-updated",
|
|
10801
|
-
data: { bundlePath: next.bundlePath, buildId: next.buildId, generation, changedFiles:
|
|
11371
|
+
data: { bundlePath: next.bundlePath, buildId: next.buildId, generation, changedFiles: files }
|
|
10802
11372
|
});
|
|
11373
|
+
this.#sendBuildStatus("pages", { generation, ok: true, files });
|
|
10803
11374
|
this.#logger.verbose(`pages-rebundle ok buildId=${next.buildId} (${Date.now() - started}ms)`);
|
|
10804
11375
|
} catch (err) {
|
|
10805
|
-
|
|
11376
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
11377
|
+
this.#logger.error(`pages-rebundle failed: ${message}`);
|
|
11378
|
+
this.#sendBuildStatus("pages", { generation, ok: false, files, message });
|
|
10806
11379
|
}
|
|
10807
11380
|
}
|
|
10808
11381
|
if (kinds.includes("code") || kinds.includes("css")) {
|
|
10809
|
-
this.scheduleCssRebuild(artifactDir, { refresh: true, generation, changedFiles:
|
|
11382
|
+
this.scheduleCssRebuild(artifactDir, { refresh: true, generation, changedFiles: files });
|
|
10810
11383
|
this.#logger.verbose(`css-rebuild scheduled generation=${generation}`);
|
|
10811
11384
|
}
|
|
10812
11385
|
}
|