@akanjs/cli 2.3.5 → 2.3.6-rc.1
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 +814 -181
- package/index.js +787 -204
- package/package.json +2 -3
- package/templates/workspaceRoot/.cursor/rules/akan-page-routing.mdc.template +17 -0
- package/templates/workspaceRoot/.cursor/rules/akan-scan-conventions.mdc.template +19 -0
- package/templates/workspaceRoot/.cursor/rules/application-test-commands.mdc.template +16 -0
- package/templates/workspaceRoot/.cursor/rules/change-scope.mdc.template +12 -0
- package/templates/workspaceRoot/.cursor/rules/client-server-boundaries.mdc.template +17 -0
- package/templates/workspaceRoot/.cursor/rules/coding-style.mdc.template +24 -0
- package/templates/workspaceRoot/.cursor/rules/domain-module-conventions.mdc.template +17 -0
- package/templates/workspaceRoot/.cursor/rules/format-lint.mdc.template +12 -0
- package/templates/workspaceRoot/.cursor/rules/repo-overview.mdc.template +17 -0
- package/templates/workspaceRoot/.cursor/rules/scalar-modeling.mdc.template +14 -0
- package/templates/workspaceRoot/.cursor/rules/secrets-and-env.mdc.template +13 -0
- package/templates/workspaceRoot/.cursor/rules/service-signal-conventions.mdc.template +14 -0
- package/templates/workspaceRoot/.cursor/rules/typescript-imports.mdc.template +13 -0
- package/templates/workspaceRoot/AGENTS.md.template +12 -9
|
@@ -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,79 @@ 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
|
+
if (message.devPlan.actions.includes("restart-builder"))
|
|
4140
|
+
return false;
|
|
4141
|
+
return message.devPlan.actions.includes("restart-backend");
|
|
4142
|
+
};
|
|
4143
|
+
var shouldRestartBuilderByDevPlan = (message) => message.devPlan?.actions.includes("restart-builder") ?? false;
|
|
4144
|
+
var shouldRestartDevHostByDevPlan = (message) => message.devPlan?.actions.includes("restart-dev-host") ?? message.kinds.includes("config");
|
|
4145
|
+
var RESTART_ROLE_ORDER = ["server", "shared", "barrel", "config"];
|
|
4146
|
+
var generationValue = (generation) => generation ?? -1;
|
|
4147
|
+
var isLegacyBackendFallbackFile = (file, workspaceRoot) => {
|
|
4148
|
+
const abs = path9.resolve(file);
|
|
4149
|
+
const ext = path9.extname(abs).toLowerCase();
|
|
4150
|
+
if (!SOURCE_EXTS.has(ext))
|
|
4151
|
+
return false;
|
|
4152
|
+
const rel = path9.relative(path9.resolve(workspaceRoot), abs);
|
|
4153
|
+
if (!rel || rel.startsWith("..") || path9.isAbsolute(rel))
|
|
4154
|
+
return false;
|
|
4155
|
+
const parts = rel.split(path9.sep).filter(Boolean);
|
|
4156
|
+
const [scope] = parts;
|
|
4157
|
+
if (scope !== "apps" && scope !== "libs" && scope !== "pkgs")
|
|
4158
|
+
return false;
|
|
4159
|
+
const base = path9.basename(abs);
|
|
4160
|
+
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";
|
|
4161
|
+
};
|
|
4162
|
+
var shouldMarkBuildPhaseRecovered = (previousByPhase, status) => {
|
|
4163
|
+
const previous = previousByPhase.get(status.phase);
|
|
4164
|
+
return Boolean(previous && status.ok && !previous.ok && generationValue(status.generation) >= previous.generation);
|
|
4165
|
+
};
|
|
4166
|
+
var createBackendBuildStatus = ({
|
|
4167
|
+
generation,
|
|
4168
|
+
ok,
|
|
4169
|
+
files = [],
|
|
4170
|
+
message
|
|
4171
|
+
}) => ({
|
|
4172
|
+
generation,
|
|
4173
|
+
phase: "backend",
|
|
4174
|
+
ok,
|
|
4175
|
+
files,
|
|
4176
|
+
message
|
|
4177
|
+
});
|
|
4178
|
+
var backendRestartReasonFromMessage = (message) => {
|
|
4179
|
+
const roleSet = new Set;
|
|
4180
|
+
for (const role of message.devPlan?.roles ?? []) {
|
|
4181
|
+
if (role === "server" || role === "shared" || role === "barrel" || role === "config")
|
|
4182
|
+
roleSet.add(role);
|
|
4183
|
+
}
|
|
4184
|
+
return {
|
|
4185
|
+
generation: message.devPlan?.generation ?? message.generation,
|
|
4186
|
+
files: [...new Set(message.files)].sort(),
|
|
4187
|
+
roles: RESTART_ROLE_ORDER.filter((role) => roleSet.has(role))
|
|
4188
|
+
};
|
|
4189
|
+
};
|
|
4190
|
+
var mergeBackendRestartReasons = (current, next) => ({
|
|
4191
|
+
generation: generationValue(next.generation) >= generationValue(current?.generation) ? next.generation : current?.generation,
|
|
4192
|
+
files: [...new Set([...current?.files ?? [], ...next.files])].sort(),
|
|
4193
|
+
roles: RESTART_ROLE_ORDER.filter((role) => current?.roles.includes(role) || next.roles.includes(role))
|
|
4194
|
+
});
|
|
4195
|
+
var shouldReplaceLastGoodMessage = (current, next) => !current || generationValue(next.data.generation) >= generationValue(current.data.generation);
|
|
4196
|
+
var shouldQueueBuildStatusReplay = (backendReady, pendingReplayCount) => !backendReady || pendingReplayCount > 0;
|
|
4197
|
+
var buildStatusReplaySequence = (pendingReplay, latestByPhase) => [...pendingReplay, ...latestByPhase.values()];
|
|
4100
4198
|
|
|
4101
4199
|
class BackendImportGraph {
|
|
4102
4200
|
#app;
|
|
@@ -4107,6 +4205,7 @@ class BackendImportGraph {
|
|
|
4107
4205
|
#jsxTranspiler = new Bun.Transpiler({ loader: "jsx" });
|
|
4108
4206
|
#files = new Set;
|
|
4109
4207
|
#ready = false;
|
|
4208
|
+
#lastRefreshSucceeded = false;
|
|
4110
4209
|
constructor(app, logger) {
|
|
4111
4210
|
this.#app = app;
|
|
4112
4211
|
this.#logger = logger;
|
|
@@ -4114,6 +4213,9 @@ class BackendImportGraph {
|
|
|
4114
4213
|
get ready() {
|
|
4115
4214
|
return this.#ready;
|
|
4116
4215
|
}
|
|
4216
|
+
get lastRefreshSucceeded() {
|
|
4217
|
+
return this.#lastRefreshSucceeded;
|
|
4218
|
+
}
|
|
4117
4219
|
has(file) {
|
|
4118
4220
|
return this.#files.has(path9.resolve(file));
|
|
4119
4221
|
}
|
|
@@ -4122,10 +4224,12 @@ class BackendImportGraph {
|
|
|
4122
4224
|
const files = await this.#build();
|
|
4123
4225
|
this.#files = files;
|
|
4124
4226
|
this.#ready = true;
|
|
4227
|
+
this.#lastRefreshSucceeded = true;
|
|
4125
4228
|
this.#logger.verbose(`[backend-graph] scanned ${files.size} files`);
|
|
4126
4229
|
return true;
|
|
4127
4230
|
} catch (err) {
|
|
4128
4231
|
this.#ready = this.#files.size > 0;
|
|
4232
|
+
this.#lastRefreshSucceeded = false;
|
|
4129
4233
|
this.#logger.warn(`[backend-graph] scan failed; ${this.#ready ? "using previous graph" : "using fallback rules"}: ${err instanceof Error ? err.message : String(err)}`);
|
|
4130
4234
|
return this.#ready;
|
|
4131
4235
|
}
|
|
@@ -4212,9 +4316,13 @@ class AkanAppHost {
|
|
|
4212
4316
|
#restartTimer = null;
|
|
4213
4317
|
#backendRecoveryTimer = null;
|
|
4214
4318
|
#backendRecoveryAttempts = 0;
|
|
4215
|
-
#
|
|
4216
|
-
#
|
|
4217
|
-
#
|
|
4319
|
+
#backendLifecycleState = "stopped";
|
|
4320
|
+
#pendingRestartReason = null;
|
|
4321
|
+
#backendStartStatus = null;
|
|
4322
|
+
#backendBuildStatusGeneration = 0;
|
|
4323
|
+
#lastGoodFrontend = {};
|
|
4324
|
+
#buildStatusByPhase = new Map;
|
|
4325
|
+
#pendingBuildStatusReplay = [];
|
|
4218
4326
|
#builderMessageQueue = Promise.resolve();
|
|
4219
4327
|
#backendGraph;
|
|
4220
4328
|
constructor(app, { env, withInk = false }) {
|
|
@@ -4259,7 +4367,9 @@ class AkanAppHost {
|
|
|
4259
4367
|
return "localhost";
|
|
4260
4368
|
return await createTunnel(type, { app: this.app, environment });
|
|
4261
4369
|
}
|
|
4262
|
-
#startBackend() {
|
|
4370
|
+
#startBackend(startStatus = null) {
|
|
4371
|
+
this.#backendStartStatus = startStatus;
|
|
4372
|
+
this.#setBackendLifecycleState("starting");
|
|
4263
4373
|
this.#backendReady = false;
|
|
4264
4374
|
const backend = Bun.spawn(["bun", `apps/${this.app.name}/main.ts`], {
|
|
4265
4375
|
cwd: this.app.workspace.workspaceRoot,
|
|
@@ -4271,6 +4381,8 @@ class AkanAppHost {
|
|
|
4271
4381
|
if (msg.type === "backend-ready") {
|
|
4272
4382
|
this.#backendReady = true;
|
|
4273
4383
|
this.#backendRecoveryAttempts = 0;
|
|
4384
|
+
this.#setBackendLifecycleState("ready", `pid=${msg.pid}`);
|
|
4385
|
+
this.#recordBackendReadyStatus();
|
|
4274
4386
|
this.logger.verbose(`backend ready pid=${msg.pid}`);
|
|
4275
4387
|
this.#replayBuilderState();
|
|
4276
4388
|
return;
|
|
@@ -4293,9 +4405,53 @@ class AkanAppHost {
|
|
|
4293
4405
|
this.#backend = backend;
|
|
4294
4406
|
this.logger.verbose(`backend spawned pid=${backend.pid}`);
|
|
4295
4407
|
}
|
|
4408
|
+
#nextBackendBuildStatusGeneration(generation) {
|
|
4409
|
+
if (typeof generation === "number") {
|
|
4410
|
+
this.#backendBuildStatusGeneration = Math.max(this.#backendBuildStatusGeneration, generation);
|
|
4411
|
+
return generation;
|
|
4412
|
+
}
|
|
4413
|
+
this.#backendBuildStatusGeneration += 1;
|
|
4414
|
+
return this.#backendBuildStatusGeneration;
|
|
4415
|
+
}
|
|
4416
|
+
#recordBackendBuildStatus({
|
|
4417
|
+
generation,
|
|
4418
|
+
ok,
|
|
4419
|
+
files,
|
|
4420
|
+
message
|
|
4421
|
+
}) {
|
|
4422
|
+
const status = createBackendBuildStatus({
|
|
4423
|
+
generation: this.#nextBackendBuildStatusGeneration(generation),
|
|
4424
|
+
ok,
|
|
4425
|
+
files,
|
|
4426
|
+
message
|
|
4427
|
+
});
|
|
4428
|
+
this.#recordBuildStatus(status);
|
|
4429
|
+
return status;
|
|
4430
|
+
}
|
|
4431
|
+
#recordBackendReadyStatus() {
|
|
4432
|
+
const previous = this.#buildStatusByPhase.get("backend");
|
|
4433
|
+
const startStatus = this.#backendStartStatus;
|
|
4434
|
+
if (startStatus || previous?.ok === false) {
|
|
4435
|
+
const status = this.#recordBackendBuildStatus({
|
|
4436
|
+
generation: startStatus?.generation ?? previous?.generation,
|
|
4437
|
+
ok: true,
|
|
4438
|
+
files: startStatus?.files ?? previous?.files ?? [],
|
|
4439
|
+
message: "Backend ready"
|
|
4440
|
+
});
|
|
4441
|
+
this.#sendOrQueueBuildStatus(status);
|
|
4442
|
+
}
|
|
4443
|
+
this.#backendStartStatus = null;
|
|
4444
|
+
}
|
|
4445
|
+
#setBackendLifecycleState(next, detail) {
|
|
4446
|
+
if (this.#backendLifecycleState === next && !detail)
|
|
4447
|
+
return;
|
|
4448
|
+
const prev = this.#backendLifecycleState;
|
|
4449
|
+
this.#backendLifecycleState = next;
|
|
4450
|
+
this.logger.verbose(`[backend-lifecycle] ${prev} -> ${next}${detail ? ` ${detail}` : ""}`);
|
|
4451
|
+
}
|
|
4296
4452
|
#sendToBackend(message) {
|
|
4297
4453
|
if (!this.#backend || !this.#backendReady) {
|
|
4298
|
-
if (message.type === "css-updated" || message.type === "pages-updated") {
|
|
4454
|
+
if (message.type === "css-updated" || message.type === "pages-updated" || message.type === "build-status") {
|
|
4299
4455
|
this.logger.verbose(`backend is not ready; will replay ${message.type}`);
|
|
4300
4456
|
return;
|
|
4301
4457
|
}
|
|
@@ -4315,6 +4471,7 @@ class AkanAppHost {
|
|
|
4315
4471
|
const backend = this.#backend;
|
|
4316
4472
|
this.#plannedBackendStops.add(backend);
|
|
4317
4473
|
this.#backendReady = false;
|
|
4474
|
+
this.#setBackendLifecycleState("stopping", `pid=${backend.pid}`);
|
|
4318
4475
|
this.logger.verbose(`stopping backend pid=${backend.pid}`);
|
|
4319
4476
|
try {
|
|
4320
4477
|
backend.kill("SIGTERM");
|
|
@@ -4330,11 +4487,13 @@ class AkanAppHost {
|
|
|
4330
4487
|
} finally {
|
|
4331
4488
|
if (this.#backend === backend)
|
|
4332
4489
|
this.#backend = null;
|
|
4490
|
+
this.#setBackendLifecycleState("stopped", `pid=${backend.pid}`);
|
|
4333
4491
|
}
|
|
4334
4492
|
}
|
|
4335
|
-
#scheduleBackendRestart(
|
|
4336
|
-
|
|
4337
|
-
|
|
4493
|
+
#scheduleBackendRestart(reason) {
|
|
4494
|
+
this.#pendingRestartReason = mergeBackendRestartReasons(this.#pendingRestartReason, reason);
|
|
4495
|
+
const pending = this.#pendingRestartReason;
|
|
4496
|
+
this.#setBackendLifecycleState("restart-pending", `generation=${pending.generation ?? "(unknown)"} files=${pending.files.length} roles=${pending.roles.join(",") || "(none)"}`);
|
|
4338
4497
|
if (this.#backendRecoveryTimer) {
|
|
4339
4498
|
clearTimeout(this.#backendRecoveryTimer);
|
|
4340
4499
|
this.#backendRecoveryTimer = null;
|
|
@@ -4343,23 +4502,31 @@ class AkanAppHost {
|
|
|
4343
4502
|
clearTimeout(this.#restartTimer);
|
|
4344
4503
|
this.#restartTimer = setTimeout(() => {
|
|
4345
4504
|
this.#restartTimer = null;
|
|
4346
|
-
const
|
|
4347
|
-
this.#
|
|
4348
|
-
|
|
4505
|
+
const next = this.#pendingRestartReason;
|
|
4506
|
+
this.#pendingRestartReason = null;
|
|
4507
|
+
if (next)
|
|
4508
|
+
this.#restartBackend(next);
|
|
4349
4509
|
}, BACKEND_RESTART_DEBOUNCE_MS);
|
|
4350
4510
|
}
|
|
4351
|
-
async#restartBackend(
|
|
4352
|
-
this.logger.verbose(`[backend-reload] restarting backend
|
|
4511
|
+
async#restartBackend(reason) {
|
|
4512
|
+
this.logger.verbose(`[backend-reload] restarting backend generation=${reason.generation ?? "(unknown)"} files=${reason.files.length} roles=${reason.roles.join(",") || "(none)"}`);
|
|
4353
4513
|
this.#backendRecoveryAttempts = 0;
|
|
4354
4514
|
await Promise.all([this.#stopBackend(), this.#backendGraph.refresh()]);
|
|
4355
|
-
this.#startBackend();
|
|
4515
|
+
this.#startBackend({ generation: reason.generation, files: reason.files });
|
|
4356
4516
|
}
|
|
4357
4517
|
#scheduleBackendRecovery(reason) {
|
|
4358
4518
|
if (this.#backendRecoveryTimer || this.#backend)
|
|
4359
4519
|
return;
|
|
4520
|
+
this.#setBackendLifecycleState("recovering", reason);
|
|
4360
4521
|
const attempt = this.#backendRecoveryAttempts;
|
|
4361
4522
|
const delay = Math.min(BACKEND_RECOVERY_BASE_DELAY_MS * 2 ** attempt, BACKEND_RECOVERY_MAX_DELAY_MS);
|
|
4362
4523
|
this.#backendRecoveryAttempts = attempt + 1;
|
|
4524
|
+
const failureStatus = this.#recordBackendBuildStatus({
|
|
4525
|
+
ok: false,
|
|
4526
|
+
files: [],
|
|
4527
|
+
message: `Backend exited unexpectedly (${reason}); restarting in ${delay}ms`
|
|
4528
|
+
});
|
|
4529
|
+
this.#sendOrQueueBuildStatus(failureStatus);
|
|
4363
4530
|
this.logger.warn(`[backend-recovery] backend exited unexpectedly (${reason}); restarting in ${delay}ms (attempt ${this.#backendRecoveryAttempts})`);
|
|
4364
4531
|
this.#backendRecoveryTimer = setTimeout(() => {
|
|
4365
4532
|
this.#backendRecoveryTimer = null;
|
|
@@ -4367,7 +4534,7 @@ class AkanAppHost {
|
|
|
4367
4534
|
return;
|
|
4368
4535
|
this.#backendGraph.refresh().finally(() => {
|
|
4369
4536
|
if (!this.#backend)
|
|
4370
|
-
this.#startBackend();
|
|
4537
|
+
this.#startBackend({ generation: failureStatus.generation, files: failureStatus.files });
|
|
4371
4538
|
});
|
|
4372
4539
|
}, delay);
|
|
4373
4540
|
}
|
|
@@ -4377,10 +4544,15 @@ class AkanAppHost {
|
|
|
4377
4544
|
});
|
|
4378
4545
|
}
|
|
4379
4546
|
async#handleBuilderMessage(message) {
|
|
4547
|
+
if (message.type === "build-status") {
|
|
4548
|
+
this.#recordBuildStatus(message.data);
|
|
4549
|
+
this.#sendOrQueueBuildStatus(message.data);
|
|
4550
|
+
return;
|
|
4551
|
+
}
|
|
4380
4552
|
if (message.type === "pages-updated")
|
|
4381
|
-
this.#
|
|
4553
|
+
this.#recordLastGood(message);
|
|
4382
4554
|
if (message.type === "css-updated")
|
|
4383
|
-
this.#
|
|
4555
|
+
this.#recordLastGood(message);
|
|
4384
4556
|
if (message.type === "invalidate") {
|
|
4385
4557
|
await this.#handleInvalidate(message);
|
|
4386
4558
|
return;
|
|
@@ -4388,26 +4560,140 @@ class AkanAppHost {
|
|
|
4388
4560
|
this.#sendToBackend(message);
|
|
4389
4561
|
}
|
|
4390
4562
|
async#handleInvalidate(message) {
|
|
4563
|
+
if (shouldRestartBuilderByDevPlan(message)) {
|
|
4564
|
+
try {
|
|
4565
|
+
await this.#restartDevChildren(message);
|
|
4566
|
+
} catch (err) {
|
|
4567
|
+
this.#recordDevHostRestartFailure(message, err);
|
|
4568
|
+
}
|
|
4569
|
+
return;
|
|
4570
|
+
}
|
|
4571
|
+
if (shouldRestartDevHostByDevPlan(message)) {
|
|
4572
|
+
this.#recordDevHostRestartRequired(message);
|
|
4573
|
+
return;
|
|
4574
|
+
}
|
|
4391
4575
|
if (await this.#shouldRestartBackend(message)) {
|
|
4392
|
-
this.#scheduleBackendRestart(message
|
|
4576
|
+
this.#scheduleBackendRestart(backendRestartReasonFromMessage(message));
|
|
4393
4577
|
return;
|
|
4394
4578
|
}
|
|
4395
4579
|
this.#sendToBackend(message);
|
|
4396
4580
|
}
|
|
4581
|
+
async#restartDevChildren(message) {
|
|
4582
|
+
const generation = message.devPlan?.generation ?? message.generation;
|
|
4583
|
+
this.logger.warn(`[dev-host] recycling builder/backend for runtime metadata generation=${generation ?? "(unknown)"} files=${message.files.length}`);
|
|
4584
|
+
if (this.#restartTimer) {
|
|
4585
|
+
clearTimeout(this.#restartTimer);
|
|
4586
|
+
this.#restartTimer = null;
|
|
4587
|
+
}
|
|
4588
|
+
if (this.#backendRecoveryTimer) {
|
|
4589
|
+
clearTimeout(this.#backendRecoveryTimer);
|
|
4590
|
+
this.#backendRecoveryTimer = null;
|
|
4591
|
+
}
|
|
4592
|
+
this.#pendingRestartReason = null;
|
|
4593
|
+
this.#lastGoodFrontend = {};
|
|
4594
|
+
this.#buildStatusByPhase.clear();
|
|
4595
|
+
this.#pendingBuildStatusReplay = [];
|
|
4596
|
+
await this.#stopBackend();
|
|
4597
|
+
this.#stopBuilder();
|
|
4598
|
+
await this.#backendGraph.refresh();
|
|
4599
|
+
await this.#startBuilder();
|
|
4600
|
+
this.#startBackend({ generation, files: message.files });
|
|
4601
|
+
}
|
|
4602
|
+
#recordLastGood(message) {
|
|
4603
|
+
if (message.type === "pages-updated") {
|
|
4604
|
+
if (!shouldReplaceLastGoodMessage(this.#lastGoodFrontend.pages, message))
|
|
4605
|
+
return;
|
|
4606
|
+
this.#lastGoodFrontend.pages = message;
|
|
4607
|
+
this.logger.verbose(`[last-good] pages generation=${message.data.generation ?? "(unknown)"} buildId=${message.data.buildId}`);
|
|
4608
|
+
return;
|
|
4609
|
+
}
|
|
4610
|
+
if (!shouldReplaceLastGoodMessage(this.#lastGoodFrontend.css, message))
|
|
4611
|
+
return;
|
|
4612
|
+
this.#lastGoodFrontend.css = message;
|
|
4613
|
+
this.logger.verbose(`[last-good] css generation=${message.data.generation ?? "(unknown)"} assets=${Object.keys(message.data.cssAssets).length}`);
|
|
4614
|
+
}
|
|
4615
|
+
#recordDevHostRestartRequired(message) {
|
|
4616
|
+
const generation = message.devPlan?.generation ?? message.generation;
|
|
4617
|
+
const detail = `generation=${generation ?? "(unknown)"} files=${message.files.length}`;
|
|
4618
|
+
this.logger.warn(`[dev-host] config change requires a manual restart until controlled dev-host restart is implemented (${detail})`);
|
|
4619
|
+
if (typeof generation === "number") {
|
|
4620
|
+
const status = {
|
|
4621
|
+
generation,
|
|
4622
|
+
phase: "scan",
|
|
4623
|
+
ok: false,
|
|
4624
|
+
files: message.files,
|
|
4625
|
+
message: "Config change requires restarting `akan start` to apply."
|
|
4626
|
+
};
|
|
4627
|
+
this.#recordBuildStatus(status);
|
|
4628
|
+
this.#sendOrQueueBuildStatus(status);
|
|
4629
|
+
}
|
|
4630
|
+
}
|
|
4631
|
+
#recordDevHostRestartFailure(message, err) {
|
|
4632
|
+
const generation = message.devPlan?.generation ?? message.generation ?? this.#nextBackendBuildStatusGeneration();
|
|
4633
|
+
const detail = err instanceof Error ? err.message : String(err);
|
|
4634
|
+
this.logger.warn(`[dev-host] runtime metadata restart failed generation=${generation}: ${detail}`);
|
|
4635
|
+
const status = {
|
|
4636
|
+
generation,
|
|
4637
|
+
phase: "scan",
|
|
4638
|
+
ok: false,
|
|
4639
|
+
files: message.files,
|
|
4640
|
+
message: `Runtime metadata change requires restarting \`akan start\` to apply: ${detail}`
|
|
4641
|
+
};
|
|
4642
|
+
this.#recordBuildStatus(status);
|
|
4643
|
+
this.#sendOrQueueBuildStatus(status);
|
|
4644
|
+
}
|
|
4645
|
+
#recordBuildStatus(status) {
|
|
4646
|
+
const recovered = shouldMarkBuildPhaseRecovered(this.#buildStatusByPhase, status);
|
|
4647
|
+
this.#buildStatusByPhase.set(status.phase, status);
|
|
4648
|
+
const label = `[build-status] generation=${status.generation} phase=${status.phase} ok=${status.ok} files=${status.files.length}`;
|
|
4649
|
+
if (status.ok)
|
|
4650
|
+
this.logger.verbose(`${label}${recovered ? " recovered=1" : ""}`);
|
|
4651
|
+
else
|
|
4652
|
+
this.logger.warn(`${label}${status.message ? ` message=${status.message}` : ""}`);
|
|
4653
|
+
}
|
|
4654
|
+
#sendOrQueueBuildStatus(status) {
|
|
4655
|
+
if (!this.#backend || shouldQueueBuildStatusReplay(this.#backendReady, this.#pendingBuildStatusReplay.length)) {
|
|
4656
|
+
this.#pendingBuildStatusReplay.push(status);
|
|
4657
|
+
this.logger.verbose(`backend is not ready; will replay build-status generation=${status.generation} phase=${status.phase}`);
|
|
4658
|
+
return;
|
|
4659
|
+
}
|
|
4660
|
+
this.#sendToBackend({ type: "build-status", data: status });
|
|
4661
|
+
}
|
|
4397
4662
|
#replayBuilderState() {
|
|
4398
4663
|
if (!this.#backendReady)
|
|
4399
4664
|
return;
|
|
4400
|
-
if (this.#
|
|
4401
|
-
this.#sendToBackend(this.#
|
|
4402
|
-
if (this.#
|
|
4403
|
-
this.#sendToBackend(this.#
|
|
4665
|
+
if (this.#lastGoodFrontend.css)
|
|
4666
|
+
this.#sendToBackend(this.#lastGoodFrontend.css);
|
|
4667
|
+
if (this.#lastGoodFrontend.pages)
|
|
4668
|
+
this.#sendToBackend(this.#lastGoodFrontend.pages);
|
|
4669
|
+
const queuedStatuses = this.#pendingBuildStatusReplay.splice(0);
|
|
4670
|
+
for (const status of buildStatusReplaySequence(queuedStatuses, this.#buildStatusByPhase)) {
|
|
4671
|
+
this.#sendToBackend({ type: "build-status", data: status });
|
|
4672
|
+
}
|
|
4404
4673
|
}
|
|
4405
4674
|
async#shouldRestartBackend(message) {
|
|
4406
4675
|
if (message.kinds.length === 1 && message.kinds[0] === "css")
|
|
4407
4676
|
return false;
|
|
4408
|
-
if (
|
|
4677
|
+
if (message.devPlan) {
|
|
4678
|
+
const { generation, roles, actions, reasonByFile } = message.devPlan;
|
|
4679
|
+
this.logger.verbose(`[dev-plan] generation=${generation} roles=${roles.join(",") || "(none)"} actions=${actions.join(",") || "(none)"} reasons=${Object.keys(reasonByFile).length}`);
|
|
4680
|
+
const shouldRestart = shouldRestartBackendByDevPlan(message) ?? false;
|
|
4681
|
+
if (shouldRestart && message.kinds.includes("code"))
|
|
4682
|
+
await this.#backendGraph.refresh();
|
|
4683
|
+
return shouldRestart;
|
|
4684
|
+
}
|
|
4685
|
+
if (message.kinds.includes("code"))
|
|
4409
4686
|
await this.#backendGraph.refresh();
|
|
4410
|
-
|
|
4687
|
+
if (message.files.some((file) => this.#isBackendFile(file)))
|
|
4688
|
+
return true;
|
|
4689
|
+
if (!this.#backendGraph.lastRefreshSucceeded) {
|
|
4690
|
+
const fallbackFiles = message.files.filter((file) => isLegacyBackendFallbackFile(file, this.app.workspace.workspaceRoot));
|
|
4691
|
+
if (fallbackFiles.length > 0) {
|
|
4692
|
+
this.logger.warn(`[backend-graph] using path-role fallback for legacy invalidate; restart files=${fallbackFiles.length}`);
|
|
4693
|
+
return true;
|
|
4694
|
+
}
|
|
4695
|
+
}
|
|
4696
|
+
return false;
|
|
4411
4697
|
}
|
|
4412
4698
|
#isBackendFile(file) {
|
|
4413
4699
|
return this.#backendGraph.has(file);
|
|
@@ -4763,8 +5049,8 @@ Caused by: ${ApplicationBuildReporter.formatError(error.cause)}` : "";
|
|
|
4763
5049
|
}
|
|
4764
5050
|
}
|
|
4765
5051
|
// pkgs/@akanjs/devkit/applicationBuildRunner.ts
|
|
4766
|
-
import { mkdir as
|
|
4767
|
-
import
|
|
5052
|
+
import { mkdir as mkdir8, rm as rm3 } from "fs/promises";
|
|
5053
|
+
import path35 from "path";
|
|
4768
5054
|
|
|
4769
5055
|
// pkgs/@akanjs/devkit/frontendBuild/allRoutesBuilder.ts
|
|
4770
5056
|
import path20 from "path";
|
|
@@ -7232,9 +7518,306 @@ function getPageKeyBasePath(pageKey, basePaths) {
|
|
|
7232
7518
|
const firstPublicSegment = segments.find((segment) => segment !== "[lang]" && !/^\(.+\)$/.test(segment));
|
|
7233
7519
|
return firstPublicSegment && basePaths.includes(firstPublicSegment) ? firstPublicSegment : null;
|
|
7234
7520
|
}
|
|
7235
|
-
// pkgs/@akanjs/devkit/frontendBuild/
|
|
7236
|
-
import { mkdir as mkdir6 } from "fs/promises";
|
|
7521
|
+
// pkgs/@akanjs/devkit/frontendBuild/devChangePlanner.ts
|
|
7237
7522
|
import path25 from "path";
|
|
7523
|
+
var SOURCE_EXTS4 = new Set([".ts", ".tsx", ".js", ".jsx", ".mjs", ".cjs"]);
|
|
7524
|
+
var CONFIG_BASENAMES = new Set(["akan.config.ts", "bunfig.toml", "tsconfig.json", "package.json"]);
|
|
7525
|
+
var BARREL_FACETS = new Set(["common", "srvkit", "ui", "webkit"]);
|
|
7526
|
+
var CLIENT_SUFFIXES = [".Template.tsx", ".Unit.tsx", ".Util.tsx", ".View.tsx", ".Zone.tsx", ".store.ts"];
|
|
7527
|
+
var SHARED_SUFFIXES2 = [".constant.ts", ".dictionary.ts", ".signal.ts"];
|
|
7528
|
+
var SERVER_SUFFIXES2 = [".service.ts", ".document.ts"];
|
|
7529
|
+
var RUNTIME_METADATA_BASENAMES2 = new Set(["dict.ts", "sig.ts", "useClient.ts", "useServer.ts"]);
|
|
7530
|
+
|
|
7531
|
+
class DevChangePlanner {
|
|
7532
|
+
#workspaceRoot;
|
|
7533
|
+
constructor({ workspaceRoot }) {
|
|
7534
|
+
this.#workspaceRoot = path25.resolve(workspaceRoot);
|
|
7535
|
+
}
|
|
7536
|
+
plan({ generation, files, kinds, generatedFiles: generatedFiles2 = [] }) {
|
|
7537
|
+
const fileList = uniqueResolved([...files, ...generatedFiles2]);
|
|
7538
|
+
const generatedSet = new Set(generatedFiles2.map((file) => path25.resolve(file)));
|
|
7539
|
+
const kindSet = new Set(kinds);
|
|
7540
|
+
const roles = new Set;
|
|
7541
|
+
const actions = new Set;
|
|
7542
|
+
const reasonByFile = {};
|
|
7543
|
+
for (const kind of kindSet) {
|
|
7544
|
+
if (kind === "css") {
|
|
7545
|
+
roles.add("css");
|
|
7546
|
+
actions.add("rebuild-css");
|
|
7547
|
+
}
|
|
7548
|
+
if (kind === "config") {
|
|
7549
|
+
roles.add("config");
|
|
7550
|
+
actions.add("restart-dev-host");
|
|
7551
|
+
}
|
|
7552
|
+
}
|
|
7553
|
+
for (const file of fileList) {
|
|
7554
|
+
const reasons = new Set;
|
|
7555
|
+
const fileRoles = this.#rolesForFile(file, { isGenerated: generatedSet.has(path25.resolve(file)), reasons });
|
|
7556
|
+
for (const role of fileRoles)
|
|
7557
|
+
roles.add(role);
|
|
7558
|
+
if (reasons.has("runtime-metadata"))
|
|
7559
|
+
actions.add("restart-builder");
|
|
7560
|
+
if (reasons.size > 0)
|
|
7561
|
+
reasonByFile[path25.resolve(file)] = [...reasons].sort();
|
|
7562
|
+
}
|
|
7563
|
+
if (roles.has("barrel"))
|
|
7564
|
+
actions.add("sync-generated");
|
|
7565
|
+
if (roles.has("server") || roles.has("shared"))
|
|
7566
|
+
actions.add("restart-backend");
|
|
7567
|
+
if (roles.has("client") || roles.has("shared"))
|
|
7568
|
+
actions.add("rebuild-client");
|
|
7569
|
+
if (roles.has("css"))
|
|
7570
|
+
actions.add("rebuild-css");
|
|
7571
|
+
return {
|
|
7572
|
+
generation,
|
|
7573
|
+
files: fileList,
|
|
7574
|
+
generatedFiles: uniqueResolved(generatedFiles2),
|
|
7575
|
+
roles: [...roles].sort(),
|
|
7576
|
+
actions: [...actions].sort(),
|
|
7577
|
+
reasonByFile
|
|
7578
|
+
};
|
|
7579
|
+
}
|
|
7580
|
+
#rolesForFile(file, { isGenerated, reasons }) {
|
|
7581
|
+
const roles = new Set;
|
|
7582
|
+
const abs = path25.resolve(file);
|
|
7583
|
+
const base = path25.basename(abs);
|
|
7584
|
+
const ext = path25.extname(abs).toLowerCase();
|
|
7585
|
+
const isSource = SOURCE_EXTS4.has(ext);
|
|
7586
|
+
const rel = path25.relative(this.#workspaceRoot, abs);
|
|
7587
|
+
const parts = rel.split(path25.sep).filter(Boolean);
|
|
7588
|
+
if (CONFIG_BASENAMES.has(base)) {
|
|
7589
|
+
roles.add("config");
|
|
7590
|
+
reasons.add("config-file");
|
|
7591
|
+
}
|
|
7592
|
+
if (ext === ".css") {
|
|
7593
|
+
roles.add("css");
|
|
7594
|
+
reasons.add("css-file");
|
|
7595
|
+
}
|
|
7596
|
+
if (isGenerated || isSource && this.#isBarrelFacetChild(parts)) {
|
|
7597
|
+
roles.add("barrel");
|
|
7598
|
+
reasons.add(isGenerated ? "generated-index" : "barrel-facet-child");
|
|
7599
|
+
}
|
|
7600
|
+
if (isSource && this.#isServerBiased(abs, parts)) {
|
|
7601
|
+
roles.add("server");
|
|
7602
|
+
reasons.add("server-path");
|
|
7603
|
+
}
|
|
7604
|
+
if (isSource && this.#isClientBiased(abs, parts)) {
|
|
7605
|
+
roles.add("client");
|
|
7606
|
+
reasons.add("client-path");
|
|
7607
|
+
}
|
|
7608
|
+
if (isSource && this.#isSharedBiased(abs, parts)) {
|
|
7609
|
+
roles.add("shared");
|
|
7610
|
+
reasons.add("shared-path");
|
|
7611
|
+
}
|
|
7612
|
+
if (isSource && this.#isRuntimeMetadataFile(parts, base)) {
|
|
7613
|
+
reasons.add("runtime-metadata");
|
|
7614
|
+
}
|
|
7615
|
+
if (roles.has("server") && roles.has("client")) {
|
|
7616
|
+
roles.delete("server");
|
|
7617
|
+
roles.delete("client");
|
|
7618
|
+
roles.add("shared");
|
|
7619
|
+
reasons.add("server-client-overlap");
|
|
7620
|
+
}
|
|
7621
|
+
if (roles.size === 0 && SOURCE_EXTS4.has(ext) && this.#isWorkspaceSource(rel)) {
|
|
7622
|
+
roles.add("shared");
|
|
7623
|
+
reasons.add("workspace-source-fallback");
|
|
7624
|
+
}
|
|
7625
|
+
return roles;
|
|
7626
|
+
}
|
|
7627
|
+
#isServerBiased(abs, parts) {
|
|
7628
|
+
const base = path25.basename(abs);
|
|
7629
|
+
return parts.includes("srvkit") || SERVER_SUFFIXES2.some((suffix) => base.endsWith(suffix)) || base === "main.ts" || base === "server.ts";
|
|
7630
|
+
}
|
|
7631
|
+
#isClientBiased(abs, parts) {
|
|
7632
|
+
const base = path25.basename(abs);
|
|
7633
|
+
return parts.includes("ui") || parts.includes("webkit") || parts.includes("page") || CLIENT_SUFFIXES.some((suffix) => base.endsWith(suffix));
|
|
7634
|
+
}
|
|
7635
|
+
#isSharedBiased(abs, parts) {
|
|
7636
|
+
const base = path25.basename(abs);
|
|
7637
|
+
return parts.includes("common") || SHARED_SUFFIXES2.some((suffix) => base.endsWith(suffix)) || RUNTIME_METADATA_BASENAMES2.has(base);
|
|
7638
|
+
}
|
|
7639
|
+
#isRuntimeMetadataFile(parts, base) {
|
|
7640
|
+
const parent = parts.at(-2);
|
|
7641
|
+
if (parent === "lib" && RUNTIME_METADATA_BASENAMES2.has(base))
|
|
7642
|
+
return true;
|
|
7643
|
+
const libIndex = parts.lastIndexOf("lib");
|
|
7644
|
+
if (libIndex < 0 || parts.length <= libIndex + 1)
|
|
7645
|
+
return false;
|
|
7646
|
+
return base.endsWith(".dictionary.ts") || base.endsWith(".signal.ts");
|
|
7647
|
+
}
|
|
7648
|
+
#isBarrelFacetChild(parts) {
|
|
7649
|
+
if (parts.length < 4)
|
|
7650
|
+
return false;
|
|
7651
|
+
const [scope, , facet, child] = parts;
|
|
7652
|
+
if (scope !== "apps" && scope !== "libs")
|
|
7653
|
+
return false;
|
|
7654
|
+
if (!facet || !BARREL_FACETS.has(facet))
|
|
7655
|
+
return false;
|
|
7656
|
+
if (!child || child.startsWith(".") || child === "index.ts" || child === "index.tsx")
|
|
7657
|
+
return false;
|
|
7658
|
+
return true;
|
|
7659
|
+
}
|
|
7660
|
+
#isWorkspaceSource(rel) {
|
|
7661
|
+
if (!rel || rel.startsWith("..") || path25.isAbsolute(rel))
|
|
7662
|
+
return false;
|
|
7663
|
+
const [scope] = rel.split(path25.sep);
|
|
7664
|
+
return scope === "apps" || scope === "libs" || scope === "pkgs";
|
|
7665
|
+
}
|
|
7666
|
+
}
|
|
7667
|
+
var uniqueResolved = (files) => [...new Set(files.map((file) => path25.resolve(file)))].sort();
|
|
7668
|
+
// pkgs/@akanjs/devkit/frontendBuild/devGeneratedIndexSync.ts
|
|
7669
|
+
import { mkdir as mkdir6, readdir as readdir2, readFile, rm as rm2, stat as stat3, writeFile } from "fs/promises";
|
|
7670
|
+
import path26 from "path";
|
|
7671
|
+
var BARREL_FACETS2 = new Set(["common", "srvkit", "ui", "webkit"]);
|
|
7672
|
+
var FACET_SOURCE_FILE_RE = /\.(ts|tsx)$/;
|
|
7673
|
+
var FACET_EXCLUDED_FILE_RE = /(^index\.tsx?$|\.d\.ts$|\.(test|spec)\.(ts|tsx)$|\.css$|\.scss$|\.sass$)/;
|
|
7674
|
+
var MODULE_UI_TYPES = ["Template", "Unit", "Util", "View", "Zone"];
|
|
7675
|
+
var SERVICE_UI_TYPES = ["Util", "Zone"];
|
|
7676
|
+
var SCALAR_UI_TYPES = ["Template", "Unit"];
|
|
7677
|
+
|
|
7678
|
+
class DevGeneratedIndexSync {
|
|
7679
|
+
#workspaceRoot;
|
|
7680
|
+
constructor({ workspaceRoot }) {
|
|
7681
|
+
this.#workspaceRoot = path26.resolve(workspaceRoot);
|
|
7682
|
+
}
|
|
7683
|
+
async syncForBatch(files) {
|
|
7684
|
+
const indexPaths = new Set;
|
|
7685
|
+
const errors = [];
|
|
7686
|
+
for (const file of files) {
|
|
7687
|
+
const facetIndex = this.#facetIndexFor(file);
|
|
7688
|
+
if (facetIndex)
|
|
7689
|
+
indexPaths.add(facetIndex);
|
|
7690
|
+
const moduleIndex = await this.#moduleIndexForDirectoryEvent(file).catch((err) => {
|
|
7691
|
+
errors.push(`[generated-index] module detection failed for ${file}: ${formatError(err)}`);
|
|
7692
|
+
return null;
|
|
7693
|
+
});
|
|
7694
|
+
if (moduleIndex)
|
|
7695
|
+
indexPaths.add(moduleIndex);
|
|
7696
|
+
}
|
|
7697
|
+
const changedFiles = [];
|
|
7698
|
+
for (const indexPath of [...indexPaths].sort()) {
|
|
7699
|
+
try {
|
|
7700
|
+
const changed = await this.#syncIndex(indexPath);
|
|
7701
|
+
if (changed)
|
|
7702
|
+
changedFiles.push(indexPath);
|
|
7703
|
+
} catch (err) {
|
|
7704
|
+
errors.push(`[generated-index] sync failed for ${indexPath}: ${formatError(err)}`);
|
|
7705
|
+
}
|
|
7706
|
+
}
|
|
7707
|
+
return { changedFiles, errors };
|
|
7708
|
+
}
|
|
7709
|
+
#facetIndexFor(file) {
|
|
7710
|
+
const abs = path26.resolve(file);
|
|
7711
|
+
const rel = path26.relative(this.#workspaceRoot, abs);
|
|
7712
|
+
if (rel.startsWith("..") || path26.isAbsolute(rel))
|
|
7713
|
+
return null;
|
|
7714
|
+
const parts = rel.split(path26.sep).filter(Boolean);
|
|
7715
|
+
if (parts.length < 4)
|
|
7716
|
+
return null;
|
|
7717
|
+
const [scope, project, facet, child] = parts;
|
|
7718
|
+
if (scope !== "apps" && scope !== "libs" || !project || !facet || !BARREL_FACETS2.has(facet))
|
|
7719
|
+
return null;
|
|
7720
|
+
if (!child || child.startsWith(".") || child === "index.ts" || child === "index.tsx")
|
|
7721
|
+
return null;
|
|
7722
|
+
return path26.join(this.#workspaceRoot, scope, project, facet, "index.ts");
|
|
7723
|
+
}
|
|
7724
|
+
async#moduleIndexForDirectoryEvent(file) {
|
|
7725
|
+
const abs = path26.resolve(file);
|
|
7726
|
+
const rel = path26.relative(this.#workspaceRoot, abs);
|
|
7727
|
+
if (rel.startsWith("..") || path26.isAbsolute(rel))
|
|
7728
|
+
return null;
|
|
7729
|
+
const parts = rel.split(path26.sep).filter(Boolean);
|
|
7730
|
+
if (parts.length !== 4 && parts.length !== 5)
|
|
7731
|
+
return null;
|
|
7732
|
+
const [scope, project, libSegment, moduleSegment, scalarSegment] = parts;
|
|
7733
|
+
if (scope !== "apps" && scope !== "libs" || !project || libSegment !== "lib")
|
|
7734
|
+
return null;
|
|
7735
|
+
const isExistingDirectory = await stat3(abs).then((s) => s.isDirectory()).catch(() => false);
|
|
7736
|
+
const looksLikeDeletedDirectory = !path26.extname(abs);
|
|
7737
|
+
if (!isExistingDirectory && !looksLikeDeletedDirectory)
|
|
7738
|
+
return null;
|
|
7739
|
+
if (moduleSegment === "__scalar" && scalarSegment) {
|
|
7740
|
+
return path26.join(this.#workspaceRoot, scope, project, "lib", "__scalar", scalarSegment, "index.ts");
|
|
7741
|
+
}
|
|
7742
|
+
if (!moduleSegment || moduleSegment === "__scalar")
|
|
7743
|
+
return null;
|
|
7744
|
+
return path26.join(this.#workspaceRoot, scope, project, "lib", moduleSegment, "index.ts");
|
|
7745
|
+
}
|
|
7746
|
+
async#syncIndex(indexPath) {
|
|
7747
|
+
const dir = path26.dirname(indexPath);
|
|
7748
|
+
const content = await this.#contentForIndex(indexPath);
|
|
7749
|
+
if (content === null) {
|
|
7750
|
+
if (!await exists(indexPath))
|
|
7751
|
+
return false;
|
|
7752
|
+
await rm2(indexPath, { force: true });
|
|
7753
|
+
return true;
|
|
7754
|
+
}
|
|
7755
|
+
const current = await readFile(indexPath, "utf8").catch(() => null);
|
|
7756
|
+
if (current === content)
|
|
7757
|
+
return false;
|
|
7758
|
+
await mkdir6(dir, { recursive: true });
|
|
7759
|
+
await writeFile(indexPath, content);
|
|
7760
|
+
return true;
|
|
7761
|
+
}
|
|
7762
|
+
async#contentForIndex(indexPath) {
|
|
7763
|
+
const parts = path26.relative(this.#workspaceRoot, indexPath).split(path26.sep).filter(Boolean);
|
|
7764
|
+
const facet = parts.at(-2);
|
|
7765
|
+
if (facet && BARREL_FACETS2.has(facet))
|
|
7766
|
+
return this.#facetContent(path26.dirname(indexPath));
|
|
7767
|
+
return this.#moduleContent(path26.dirname(indexPath));
|
|
7768
|
+
}
|
|
7769
|
+
async#facetContent(dir) {
|
|
7770
|
+
const entries = await readdir2(dir, { withFileTypes: true }).catch(() => []);
|
|
7771
|
+
const exportNames = entries.flatMap((entry) => {
|
|
7772
|
+
const name = entry.name;
|
|
7773
|
+
if (name.startsWith("."))
|
|
7774
|
+
return [];
|
|
7775
|
+
if (entry.isDirectory())
|
|
7776
|
+
return [name];
|
|
7777
|
+
if (!entry.isFile())
|
|
7778
|
+
return [];
|
|
7779
|
+
if (!FACET_SOURCE_FILE_RE.test(name) || FACET_EXCLUDED_FILE_RE.test(name))
|
|
7780
|
+
return [];
|
|
7781
|
+
return [name.replace(FACET_SOURCE_FILE_RE, "")];
|
|
7782
|
+
}).sort();
|
|
7783
|
+
if (exportNames.length === 0)
|
|
7784
|
+
return null;
|
|
7785
|
+
return `${exportNames.map((name) => `export * from "./${name}";`).join(`
|
|
7786
|
+
`)}
|
|
7787
|
+
`;
|
|
7788
|
+
}
|
|
7789
|
+
async#moduleContent(dir) {
|
|
7790
|
+
const rel = path26.relative(this.#workspaceRoot, dir);
|
|
7791
|
+
const parts = rel.split(path26.sep).filter(Boolean);
|
|
7792
|
+
const moduleSegment = parts.at(-1);
|
|
7793
|
+
if (!moduleSegment)
|
|
7794
|
+
return null;
|
|
7795
|
+
const isScalar = parts.at(-2) === "__scalar";
|
|
7796
|
+
const rawModel = moduleSegment.startsWith("_") ? moduleSegment.slice(1) : moduleSegment;
|
|
7797
|
+
if (!rawModel)
|
|
7798
|
+
return null;
|
|
7799
|
+
const modelName = capitalize3(rawModel);
|
|
7800
|
+
const allowedTypes = isScalar ? SCALAR_UI_TYPES : moduleSegment.startsWith("_") ? SERVICE_UI_TYPES : MODULE_UI_TYPES;
|
|
7801
|
+
const fileTypes = [];
|
|
7802
|
+
for (const type of allowedTypes) {
|
|
7803
|
+
if (await exists(path26.join(dir, `${modelName}.${type}.tsx`)))
|
|
7804
|
+
fileTypes.push(type);
|
|
7805
|
+
}
|
|
7806
|
+
if (fileTypes.length === 0)
|
|
7807
|
+
return null;
|
|
7808
|
+
return `
|
|
7809
|
+
${fileTypes.map((type) => `import * as ${type} from "./${modelName}.${type}";`).join(`
|
|
7810
|
+
`)}
|
|
7811
|
+
|
|
7812
|
+
export const ${modelName} = { ${fileTypes.join(", ")} };`;
|
|
7813
|
+
}
|
|
7814
|
+
}
|
|
7815
|
+
var exists = async (file) => stat3(file).then(() => true).catch(() => false);
|
|
7816
|
+
var capitalize3 = (value) => `${value.charAt(0).toUpperCase()}${value.slice(1)}`;
|
|
7817
|
+
var formatError = (err) => err instanceof Error ? err.message : String(err);
|
|
7818
|
+
// pkgs/@akanjs/devkit/frontendBuild/fontOptimizer.ts
|
|
7819
|
+
import { mkdir as mkdir7 } from "fs/promises";
|
|
7820
|
+
import path27 from "path";
|
|
7238
7821
|
import {
|
|
7239
7822
|
generateFontFace,
|
|
7240
7823
|
getMetricsForFamily,
|
|
@@ -7258,7 +7841,7 @@ class FontOptimizer {
|
|
|
7258
7841
|
constructor(app, command = "start") {
|
|
7259
7842
|
this.#app = app;
|
|
7260
7843
|
this.#command = command;
|
|
7261
|
-
this.#artifactRoot =
|
|
7844
|
+
this.#artifactRoot = path27.join(command === "build" ? app.dist.cwdPath : app.cwdPath, ".akan/artifact");
|
|
7262
7845
|
}
|
|
7263
7846
|
async optimize() {
|
|
7264
7847
|
const fonts = await this.discoverFonts();
|
|
@@ -7277,7 +7860,7 @@ class FontOptimizer {
|
|
|
7277
7860
|
const pageKeys = await this.#app.getPageKeys();
|
|
7278
7861
|
const fonts = [];
|
|
7279
7862
|
await Promise.all(pageKeys.map(async (key) => {
|
|
7280
|
-
const filePath =
|
|
7863
|
+
const filePath = path27.resolve(this.#app.cwdPath, "page", key);
|
|
7281
7864
|
const file = Bun.file(filePath);
|
|
7282
7865
|
if (!await file.exists())
|
|
7283
7866
|
return;
|
|
@@ -7293,8 +7876,8 @@ class FontOptimizer {
|
|
|
7293
7876
|
this.#app.logger.warn(`[font] source not found: ${face.src}`);
|
|
7294
7877
|
continue;
|
|
7295
7878
|
}
|
|
7296
|
-
const outputPath =
|
|
7297
|
-
await
|
|
7879
|
+
const outputPath = path27.join(this.#artifactRoot, face.optimizedSrc.replace(/^\/_akan\//, ""));
|
|
7880
|
+
await mkdir7(path27.dirname(outputPath), { recursive: true });
|
|
7298
7881
|
const sourceBuffer = Buffer.from(await Bun.file(sourcePath).arrayBuffer());
|
|
7299
7882
|
const outputBuffer = font.subset === false ? await this.#convertToWoff2(sourceBuffer, sourcePath) : await subsetFont(sourceBuffer, await this.#getSubsetText(font), { targetFormat: "woff2" });
|
|
7300
7883
|
await Bun.write(outputPath, outputBuffer);
|
|
@@ -7441,8 +8024,8 @@ class FontOptimizer {
|
|
|
7441
8024
|
return null;
|
|
7442
8025
|
const rel = src.replace(/^\//, "");
|
|
7443
8026
|
const candidates = [
|
|
7444
|
-
this.#command === "build" ?
|
|
7445
|
-
|
|
8027
|
+
this.#command === "build" ? path27.join(this.#app.dist.cwdPath, "public", rel) : null,
|
|
8028
|
+
path27.join(this.#app.cwdPath, "public", rel),
|
|
7446
8029
|
this.#resolveWorkspacePublicPath(rel)
|
|
7447
8030
|
].filter(Boolean);
|
|
7448
8031
|
for (const candidate of candidates) {
|
|
@@ -7470,7 +8053,7 @@ class FontOptimizer {
|
|
|
7470
8053
|
return "woff2";
|
|
7471
8054
|
if (signature === "OTTO")
|
|
7472
8055
|
return "otf";
|
|
7473
|
-
const ext =
|
|
8056
|
+
const ext = path27.extname(sourcePath).slice(1).toLowerCase();
|
|
7474
8057
|
if (ext === "otf" || ext === "woff" || ext === "woff2")
|
|
7475
8058
|
return ext;
|
|
7476
8059
|
return "ttf";
|
|
@@ -7479,7 +8062,7 @@ class FontOptimizer {
|
|
|
7479
8062
|
const [root, dep, ...rest] = rel.split("/");
|
|
7480
8063
|
if (root !== "libs" || !dep || rest.length === 0)
|
|
7481
8064
|
return null;
|
|
7482
|
-
return
|
|
8065
|
+
return path27.join(this.#app.workspace.workspaceRoot, "libs", dep, "public", ...rest);
|
|
7483
8066
|
}
|
|
7484
8067
|
async#getSubsetText(font) {
|
|
7485
8068
|
const parts = new Set;
|
|
@@ -7489,7 +8072,7 @@ class FontOptimizer {
|
|
|
7489
8072
|
if (font.subsetText)
|
|
7490
8073
|
parts.add(font.subsetText);
|
|
7491
8074
|
for (const filePath of font.subsetFiles ?? []) {
|
|
7492
|
-
const abs =
|
|
8075
|
+
const abs = path27.isAbsolute(filePath) ? filePath : path27.join(this.#app.cwdPath, filePath);
|
|
7493
8076
|
const file = Bun.file(abs);
|
|
7494
8077
|
if (await file.exists())
|
|
7495
8078
|
parts.add(await file.text());
|
|
@@ -7508,7 +8091,7 @@ class FontOptimizer {
|
|
|
7508
8091
|
return "";
|
|
7509
8092
|
}
|
|
7510
8093
|
async#collectAutoSubsetText() {
|
|
7511
|
-
const roots = ["page", "ui"].map((dir) =>
|
|
8094
|
+
const roots = ["page", "ui"].map((dir) => path27.join(this.#app.cwdPath, dir));
|
|
7512
8095
|
const glob = new Bun.Glob("**/*.{ts,tsx,js,jsx,html,md}");
|
|
7513
8096
|
const parts = [];
|
|
7514
8097
|
await Promise.all(roots.map(async (root) => {
|
|
@@ -7622,43 +8205,43 @@ ${declarations.map(([prop, value]) => ` ${prop}: ${value};`).join(`
|
|
|
7622
8205
|
}
|
|
7623
8206
|
}
|
|
7624
8207
|
// pkgs/@akanjs/devkit/frontendBuild/hmrChangeClassifier.ts
|
|
7625
|
-
import
|
|
7626
|
-
var
|
|
8208
|
+
import path28 from "path";
|
|
8209
|
+
var SOURCE_EXTS5 = new Set([".ts", ".tsx", ".js", ".jsx", ".mjs", ".cjs"]);
|
|
7627
8210
|
var CSS_EXTS = new Set([".css"]);
|
|
7628
|
-
var
|
|
8211
|
+
var CONFIG_BASENAMES2 = new Set(["akan.config.ts", "bunfig.toml", "tsconfig.json", "package.json"]);
|
|
7629
8212
|
|
|
7630
8213
|
class HmrChangeClassifier {
|
|
7631
8214
|
classify(abs) {
|
|
7632
8215
|
if (this.#isUninteresting(abs))
|
|
7633
8216
|
return "ignore";
|
|
7634
|
-
const base =
|
|
7635
|
-
if (
|
|
8217
|
+
const base = path28.basename(abs);
|
|
8218
|
+
if (CONFIG_BASENAMES2.has(base))
|
|
7636
8219
|
return "config";
|
|
7637
|
-
const ext =
|
|
8220
|
+
const ext = path28.extname(abs).toLowerCase();
|
|
7638
8221
|
if (CSS_EXTS.has(ext))
|
|
7639
8222
|
return "css";
|
|
7640
|
-
if (
|
|
8223
|
+
if (SOURCE_EXTS5.has(ext))
|
|
7641
8224
|
return "code";
|
|
7642
8225
|
return "ignore";
|
|
7643
8226
|
}
|
|
7644
8227
|
#isUninteresting(abs) {
|
|
7645
|
-
const base =
|
|
8228
|
+
const base = path28.basename(abs);
|
|
7646
8229
|
if (!base)
|
|
7647
8230
|
return true;
|
|
7648
8231
|
if (base.startsWith("."))
|
|
7649
8232
|
return true;
|
|
7650
8233
|
if (base.endsWith("~") || base.endsWith(".swp") || base.endsWith(".swx") || base.endsWith(".tmp"))
|
|
7651
8234
|
return true;
|
|
7652
|
-
if (abs.includes(`${
|
|
8235
|
+
if (abs.includes(`${path28.sep}node_modules${path28.sep}`))
|
|
7653
8236
|
return true;
|
|
7654
|
-
if (abs.includes(`${
|
|
8237
|
+
if (abs.includes(`${path28.sep}.akan${path28.sep}`))
|
|
7655
8238
|
return true;
|
|
7656
8239
|
return false;
|
|
7657
8240
|
}
|
|
7658
8241
|
}
|
|
7659
8242
|
// pkgs/@akanjs/devkit/frontendBuild/hmrWatcher.ts
|
|
7660
8243
|
import fs4 from "fs";
|
|
7661
|
-
import
|
|
8244
|
+
import path29 from "path";
|
|
7662
8245
|
class HmrWatcher {
|
|
7663
8246
|
#roots;
|
|
7664
8247
|
#debounceMs;
|
|
@@ -7671,7 +8254,7 @@ class HmrWatcher {
|
|
|
7671
8254
|
#stopped = false;
|
|
7672
8255
|
#flushing = false;
|
|
7673
8256
|
constructor(opts) {
|
|
7674
|
-
this.#roots = [...new Set(opts.roots.map((r) =>
|
|
8257
|
+
this.#roots = [...new Set(opts.roots.map((r) => path29.resolve(r)))];
|
|
7675
8258
|
this.#debounceMs = opts.debounceMs ?? 80;
|
|
7676
8259
|
this.#onBatch = opts.onBatch;
|
|
7677
8260
|
this.#logger = opts.logger;
|
|
@@ -7682,7 +8265,7 @@ class HmrWatcher {
|
|
|
7682
8265
|
const w = fs4.watch(root, { recursive: true, persistent: false }, (_event, filename) => {
|
|
7683
8266
|
if (!filename)
|
|
7684
8267
|
return;
|
|
7685
|
-
const abs =
|
|
8268
|
+
const abs = path29.resolve(root, filename.toString());
|
|
7686
8269
|
this.#queue(abs);
|
|
7687
8270
|
});
|
|
7688
8271
|
this.#watchers.push(w);
|
|
@@ -7740,10 +8323,10 @@ class HmrWatcher {
|
|
|
7740
8323
|
}
|
|
7741
8324
|
}
|
|
7742
8325
|
// pkgs/@akanjs/devkit/frontendBuild/pagesBundleBuilder.ts
|
|
7743
|
-
import
|
|
8326
|
+
import path31 from "path";
|
|
7744
8327
|
|
|
7745
8328
|
// pkgs/@akanjs/devkit/transforms/externalizeFrameworkPlugin.ts
|
|
7746
|
-
import
|
|
8329
|
+
import path30 from "path";
|
|
7747
8330
|
var DEFAULT_INCLUDE = ["akanjs/", "@apps/", "@libs/"];
|
|
7748
8331
|
var DEFAULT_EXCLUDE_EXACT = new Set(["akanjs/webkit", "@akanjs/cli", "@akanjs/devkit"]);
|
|
7749
8332
|
var DEFAULT_EXCLUDE_PREFIX = ["@akanjs/cli/", "@akanjs/devkit/"];
|
|
@@ -7786,7 +8369,7 @@ async function createExternalizeFrameworkPlugin(options) {
|
|
|
7786
8369
|
const replPath = repl?.endsWith("/*") ? repl.slice(0, -1) : repl ?? "";
|
|
7787
8370
|
if (!replPath)
|
|
7788
8371
|
continue;
|
|
7789
|
-
const candidate =
|
|
8372
|
+
const candidate = path30.resolve(workspaceRoot, replPath + suffix);
|
|
7790
8373
|
const hit = await firstExisting(candidate);
|
|
7791
8374
|
if (hit)
|
|
7792
8375
|
return hit;
|
|
@@ -7798,8 +8381,8 @@ async function createExternalizeFrameworkPlugin(options) {
|
|
|
7798
8381
|
if (spec === pkg)
|
|
7799
8382
|
continue;
|
|
7800
8383
|
const suffix = spec.slice(pkg.length + 1);
|
|
7801
|
-
const pkgDir =
|
|
7802
|
-
const candidate =
|
|
8384
|
+
const pkgDir = path30.dirname(path30.resolve(workspaceRoot, entryFile));
|
|
8385
|
+
const candidate = path30.join(pkgDir, suffix);
|
|
7803
8386
|
const hit = await firstExisting(candidate);
|
|
7804
8387
|
if (hit)
|
|
7805
8388
|
return hit;
|
|
@@ -7849,7 +8432,7 @@ async function firstExisting(basePath2) {
|
|
|
7849
8432
|
return candidate;
|
|
7850
8433
|
}
|
|
7851
8434
|
for (const ext of CANDIDATE_EXTS3) {
|
|
7852
|
-
const candidate =
|
|
8435
|
+
const candidate = path30.join(basePath2, `index${ext}`);
|
|
7853
8436
|
if (await Bun.file(candidate).exists())
|
|
7854
8437
|
return candidate;
|
|
7855
8438
|
}
|
|
@@ -7940,11 +8523,11 @@ class PagesBundleBuilder {
|
|
|
7940
8523
|
const entryArtifact = result.outputs.find((a) => a.kind === "entry-point");
|
|
7941
8524
|
if (!entryArtifact)
|
|
7942
8525
|
throw new Error("[PagesBundleBuilder] Bun.build emitted no entry-point artifact");
|
|
7943
|
-
const bundlePath =
|
|
8526
|
+
const bundlePath = path31.resolve(entryArtifact.path);
|
|
7944
8527
|
const buildId = Date.now();
|
|
7945
8528
|
const outputBytes = result.outputs.reduce((sum, output) => sum + output.size, 0);
|
|
7946
8529
|
const chunkCount = result.outputs.filter((output) => output.kind === "chunk").length;
|
|
7947
|
-
this.#app.verbose(`[PagesBundleBuilder] ${
|
|
8530
|
+
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
8531
|
return {
|
|
7949
8532
|
bundlePath,
|
|
7950
8533
|
buildId,
|
|
@@ -8026,11 +8609,11 @@ function loaderFor4(absPath) {
|
|
|
8026
8609
|
}
|
|
8027
8610
|
// pkgs/@akanjs/devkit/frontendBuild/precompressArtifacts.ts
|
|
8028
8611
|
import fs5 from "fs";
|
|
8029
|
-
import
|
|
8612
|
+
import path32 from "path";
|
|
8030
8613
|
var COMPRESSIBLE_EXTS = new Set([".css", ".html", ".js", ".json", ".svg"]);
|
|
8031
8614
|
var MIN_COMPRESS_BYTES = 1024;
|
|
8032
8615
|
async function precompressArtifacts(app) {
|
|
8033
|
-
const roots = [
|
|
8616
|
+
const roots = [path32.join(app.dist.cwdPath, ".akan/artifact/client")];
|
|
8034
8617
|
const result = { files: 0, inputBytes: 0, outputBytes: 0 };
|
|
8035
8618
|
await Promise.all(roots.map((root) => precompressRoot(root, result)));
|
|
8036
8619
|
if (result.files > 0) {
|
|
@@ -8056,7 +8639,7 @@ async function precompressRoot(root, result) {
|
|
|
8056
8639
|
async function shouldPrecompress(filePath) {
|
|
8057
8640
|
if (filePath.endsWith(".gz"))
|
|
8058
8641
|
return false;
|
|
8059
|
-
if (!COMPRESSIBLE_EXTS.has(
|
|
8642
|
+
if (!COMPRESSIBLE_EXTS.has(path32.extname(filePath).toLowerCase()))
|
|
8060
8643
|
return false;
|
|
8061
8644
|
const file = Bun.file(filePath);
|
|
8062
8645
|
if (!await file.exists())
|
|
@@ -8074,7 +8657,7 @@ function formatBytes(bytes) {
|
|
|
8074
8657
|
return `${(bytes / 1024 / 1024).toFixed(1)}MB`;
|
|
8075
8658
|
}
|
|
8076
8659
|
// pkgs/@akanjs/devkit/frontendBuild/ssrBaseArtifactBuilder.ts
|
|
8077
|
-
import
|
|
8660
|
+
import path33 from "path";
|
|
8078
8661
|
import { optimize } from "@tailwindcss/node";
|
|
8079
8662
|
function prepareCssAsset(command, basePath2, cssText) {
|
|
8080
8663
|
return optimize(cssText, { file: `${basePath2 || "root"}.css`, minify: command === "build" }).code;
|
|
@@ -8090,7 +8673,7 @@ class SsrBaseArtifactBuilder {
|
|
|
8090
8673
|
this.#app = app;
|
|
8091
8674
|
this.#command = command;
|
|
8092
8675
|
this.#artifactDir = `${command === "build" ? app.dist.cwdPath : app.cwdPath}/.akan/artifact`;
|
|
8093
|
-
this.#absArtifactDir =
|
|
8676
|
+
this.#absArtifactDir = path33.resolve(this.#artifactDir);
|
|
8094
8677
|
}
|
|
8095
8678
|
async build() {
|
|
8096
8679
|
const akanConfig2 = await this.#app.getConfig();
|
|
@@ -8112,7 +8695,7 @@ class SsrBaseArtifactBuilder {
|
|
|
8112
8695
|
rscRuntimeSsrManifest,
|
|
8113
8696
|
vendorMap,
|
|
8114
8697
|
cssAssets,
|
|
8115
|
-
pagesBundlePath: this.#command === "build" ?
|
|
8698
|
+
pagesBundlePath: this.#command === "build" ? path33.relative(this.#absArtifactDir, pagesBundle.bundlePath) : pagesBundle.bundlePath,
|
|
8116
8699
|
pagesBundleBuildId: pagesBundle.buildId,
|
|
8117
8700
|
domains: [...akanConfig2.domains],
|
|
8118
8701
|
subRoutes: Object.fromEntries(Array.from(akanConfig2.subRoutes.entries()).map(([basePath2, domains]) => [basePath2, [...domains]])),
|
|
@@ -8121,7 +8704,7 @@ class SsrBaseArtifactBuilder {
|
|
|
8121
8704
|
i18n: akanConfig2.i18n,
|
|
8122
8705
|
imageConfig: akanConfig2.images
|
|
8123
8706
|
};
|
|
8124
|
-
await Bun.write(
|
|
8707
|
+
await Bun.write(path33.join(this.#absArtifactDir, "base-artifact.json"), `${JSON.stringify(artifact, null, 2)}
|
|
8125
8708
|
`);
|
|
8126
8709
|
this.#app.verbose(`[base-artifact] complete in ${Date.now() - this.#started}ms`);
|
|
8127
8710
|
return { artifact, seedIndex, cssCompiler, optimizedFonts };
|
|
@@ -8169,15 +8752,15 @@ class SsrBaseArtifactBuilder {
|
|
|
8169
8752
|
async#resolveAkanServerPath() {
|
|
8170
8753
|
const candidates = [];
|
|
8171
8754
|
try {
|
|
8172
|
-
candidates.push(
|
|
8755
|
+
candidates.push(path33.dirname(Bun.resolveSync("akanjs/server", this.#app.workspace.workspaceRoot)));
|
|
8173
8756
|
} catch {}
|
|
8174
|
-
candidates.push(
|
|
8757
|
+
candidates.push(path33.join(this.#app.workspace.workspaceRoot, "pkgs/akanjs/server"), path33.join(this.#app.workspace.workspaceRoot, "node_modules/akanjs/server"));
|
|
8175
8758
|
try {
|
|
8176
|
-
candidates.push(
|
|
8759
|
+
candidates.push(path33.dirname(Bun.resolveSync("akanjs/server", path33.dirname(Bun.main))));
|
|
8177
8760
|
} catch {}
|
|
8178
|
-
candidates.push(
|
|
8761
|
+
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
8762
|
for (const candidate of candidates) {
|
|
8180
|
-
if (await Bun.file(
|
|
8763
|
+
if (await Bun.file(path33.join(candidate, "rscClient.tsx")).exists())
|
|
8181
8764
|
return candidate;
|
|
8182
8765
|
}
|
|
8183
8766
|
throw new Error(`[base-artifact] failed to locate akanjs/server; looked in: ${candidates.join(", ")}`);
|
|
@@ -8206,14 +8789,14 @@ ${preparedCssText}`).toString(36);
|
|
|
8206
8789
|
`styles/${cssAssetName}-${cssHash}.css`,
|
|
8207
8790
|
`/_akan/styles/${cssAssetName}-${cssHash}.css`
|
|
8208
8791
|
];
|
|
8209
|
-
await Bun.write(
|
|
8792
|
+
await Bun.write(path33.join(this.#absArtifactDir, cssRelPath), preparedCssText);
|
|
8210
8793
|
this.#app.verbose(`[base-artifact] wrote ${preparedCssText.length} bytes of CSS for ${basePath2} -> ${cssRelPath}`);
|
|
8211
8794
|
return [basePath2, { cssUrl, cssRelPath }];
|
|
8212
8795
|
}
|
|
8213
8796
|
}
|
|
8214
8797
|
// pkgs/@akanjs/devkit/frontendBuild/watchRootResolver.ts
|
|
8215
8798
|
import fs6 from "fs";
|
|
8216
|
-
import
|
|
8799
|
+
import path34 from "path";
|
|
8217
8800
|
|
|
8218
8801
|
class WatchRootResolver {
|
|
8219
8802
|
#app;
|
|
@@ -8223,15 +8806,15 @@ class WatchRootResolver {
|
|
|
8223
8806
|
async resolve() {
|
|
8224
8807
|
const tsconfig = await this.#app.getTsConfig();
|
|
8225
8808
|
const set = new Set;
|
|
8226
|
-
set.add(
|
|
8809
|
+
set.add(path34.resolve(`${this.#app.cwdPath}/page`));
|
|
8227
8810
|
for (const targets of Object.values(tsconfig.compilerOptions.paths ?? {})) {
|
|
8228
8811
|
for (const target of targets) {
|
|
8229
8812
|
if (!target)
|
|
8230
8813
|
continue;
|
|
8231
|
-
if (
|
|
8814
|
+
if (path34.isAbsolute(target))
|
|
8232
8815
|
continue;
|
|
8233
8816
|
const cleaned = target.replace(/\/?\*+.*$/, "").replace(/\/[^/]+\.[^/]+$/, "");
|
|
8234
|
-
const resolved =
|
|
8817
|
+
const resolved = path34.resolve(this.#app.workspace.workspaceRoot, cleaned);
|
|
8235
8818
|
if (fs6.existsSync(resolved))
|
|
8236
8819
|
set.add(resolved);
|
|
8237
8820
|
}
|
|
@@ -8298,7 +8881,7 @@ class ApplicationBuildRunner {
|
|
|
8298
8881
|
phases: this.#phases,
|
|
8299
8882
|
durationMs: Date.now() - this.#startedAt,
|
|
8300
8883
|
outputDir: this.#app.dist.cwdPath,
|
|
8301
|
-
artifactDir:
|
|
8884
|
+
artifactDir: path35.join(this.#app.dist.cwdPath, ".akan/artifact")
|
|
8302
8885
|
};
|
|
8303
8886
|
}
|
|
8304
8887
|
async typecheck(options = {}) {
|
|
@@ -8306,7 +8889,7 @@ class ApplicationBuildRunner {
|
|
|
8306
8889
|
await this.#app.getPageKeys({ refresh: true });
|
|
8307
8890
|
const { typecheckDir, tsconfigPath } = await this.#writeTypecheckTsconfig({ incremental });
|
|
8308
8891
|
if (clean)
|
|
8309
|
-
await
|
|
8892
|
+
await rm3(path35.join(typecheckDir, "tsconfig.tsbuildinfo"), { force: true });
|
|
8310
8893
|
await this.#checkProjectInChildProcess(tsconfigPath);
|
|
8311
8894
|
}
|
|
8312
8895
|
async#runPhase(id, label, task, summarize, options = {}) {
|
|
@@ -8378,7 +8961,7 @@ class ApplicationBuildRunner {
|
|
|
8378
8961
|
};
|
|
8379
8962
|
}
|
|
8380
8963
|
async#writeConsoleShim() {
|
|
8381
|
-
await Bun.write(
|
|
8964
|
+
await Bun.write(path35.join(this.#app.dist.cwdPath, "console.js"), `import { cnst, db, dict, option, server, sig, srv } from "./server.js";
|
|
8382
8965
|
import { assertAkanConsoleAllowed, startAkanConsole } from "./console-runtime.js";
|
|
8383
8966
|
|
|
8384
8967
|
const run = async () => {
|
|
@@ -8401,14 +8984,14 @@ void run().catch((error) => {
|
|
|
8401
8984
|
try {
|
|
8402
8985
|
return Bun.resolveSync("akanjs/server/rsc-worker", import.meta.dir);
|
|
8403
8986
|
} catch {
|
|
8404
|
-
return
|
|
8987
|
+
return path35.join(this.#app.workspace.workspaceRoot, "pkgs/akanjs/server/rscWorker.tsx");
|
|
8405
8988
|
}
|
|
8406
8989
|
}
|
|
8407
8990
|
#resolveConsoleRuntimeBuildEntry() {
|
|
8408
8991
|
try {
|
|
8409
|
-
return
|
|
8992
|
+
return path35.join(path35.dirname(Bun.resolveSync("akanjs/server", import.meta.dir)), "console.ts");
|
|
8410
8993
|
} catch {
|
|
8411
|
-
return
|
|
8994
|
+
return path35.join(this.#app.workspace.workspaceRoot, "pkgs/akanjs/server/console.ts");
|
|
8412
8995
|
}
|
|
8413
8996
|
}
|
|
8414
8997
|
async#buildCsr() {
|
|
@@ -8425,8 +9008,8 @@ void run().catch((error) => {
|
|
|
8425
9008
|
return { base, allRoutes };
|
|
8426
9009
|
}
|
|
8427
9010
|
async#writeTypecheckTsconfig({ incremental = true } = {}) {
|
|
8428
|
-
const typecheckDir =
|
|
8429
|
-
await
|
|
9011
|
+
const typecheckDir = path35.join(this.#app.cwdPath, ".akan", "typecheck");
|
|
9012
|
+
await mkdir8(typecheckDir, { recursive: true });
|
|
8430
9013
|
const tsconfig = {
|
|
8431
9014
|
extends: "../../tsconfig.json",
|
|
8432
9015
|
compilerOptions: {
|
|
@@ -8444,7 +9027,7 @@ void run().catch((error) => {
|
|
|
8444
9027
|
],
|
|
8445
9028
|
references: []
|
|
8446
9029
|
};
|
|
8447
|
-
const tsconfigPath =
|
|
9030
|
+
const tsconfigPath = path35.join(typecheckDir, "tsconfig.json");
|
|
8448
9031
|
await Bun.write(tsconfigPath, `${JSON.stringify(tsconfig, null, 2)}
|
|
8449
9032
|
`);
|
|
8450
9033
|
return { typecheckDir, tsconfigPath };
|
|
@@ -8470,10 +9053,10 @@ void run().catch((error) => {
|
|
|
8470
9053
|
}
|
|
8471
9054
|
async#resolveTypecheckWorkerEntry() {
|
|
8472
9055
|
const candidates = [
|
|
8473
|
-
|
|
8474
|
-
|
|
8475
|
-
|
|
8476
|
-
|
|
9056
|
+
path35.join(this.#app.workspace.workspaceRoot, "pkgs/@akanjs/devkit/typecheck/typecheck.proc.ts"),
|
|
9057
|
+
path35.join(this.#app.workspace.workspaceRoot, "node_modules/@akanjs/devkit/typecheck/typecheck.proc.ts"),
|
|
9058
|
+
path35.join(import.meta.dir, "typecheck.proc.js"),
|
|
9059
|
+
path35.join(import.meta.dir, "typecheck.proc.ts")
|
|
8477
9060
|
];
|
|
8478
9061
|
for (const candidate of candidates)
|
|
8479
9062
|
if (await Bun.file(candidate).exists())
|
|
@@ -8506,7 +9089,7 @@ void run().catch((error) => {
|
|
|
8506
9089
|
}
|
|
8507
9090
|
}
|
|
8508
9091
|
// pkgs/@akanjs/devkit/applicationReleasePackager.ts
|
|
8509
|
-
import { cp, mkdir as
|
|
9092
|
+
import { cp, mkdir as mkdir9, rm as rm4 } from "fs/promises";
|
|
8510
9093
|
|
|
8511
9094
|
// pkgs/@akanjs/devkit/uploadRelease.ts
|
|
8512
9095
|
import { HttpClient as HttpClient2, Logger as Logger9 } from "akanjs/common";
|
|
@@ -8612,16 +9195,16 @@ class ApplicationReleasePackager {
|
|
|
8612
9195
|
const platformVersion = akanConfig2.mobile.version;
|
|
8613
9196
|
const buildRoot = `${this.#app.workspace.workspaceRoot}/releases/builds/${this.#app.name}`;
|
|
8614
9197
|
if (await FileSys.dirExists(buildRoot))
|
|
8615
|
-
await
|
|
8616
|
-
await
|
|
9198
|
+
await rm4(buildRoot, { recursive: true, force: true });
|
|
9199
|
+
await mkdir9(buildRoot, { recursive: true });
|
|
8617
9200
|
if (rebuild || !await FileSys.dirExists(`${this.#app.dist.cwdPath}/backend`))
|
|
8618
9201
|
await this.#build();
|
|
8619
9202
|
const buildVersion = `${platformVersion}-${buildNum}`;
|
|
8620
9203
|
const buildPath = `${buildRoot}/${buildVersion}`;
|
|
8621
|
-
await
|
|
9204
|
+
await mkdir9(buildPath, { recursive: true });
|
|
8622
9205
|
await cp(`${this.#app.dist.cwdPath}/backend`, `${buildPath}/backend`, { recursive: true });
|
|
8623
9206
|
await cp(this.#app.dist.cwdPath, buildRoot, { recursive: true });
|
|
8624
|
-
await
|
|
9207
|
+
await rm4(`${buildRoot}/frontend/.next`, { recursive: true, force: true });
|
|
8625
9208
|
await this.#app.workspace.spawn("tar", [
|
|
8626
9209
|
"-zcf",
|
|
8627
9210
|
`${this.#app.workspace.workspaceRoot}/releases/builds/${this.#app.name}-release.tar.gz`,
|
|
@@ -8641,7 +9224,7 @@ class ApplicationReleasePackager {
|
|
|
8641
9224
|
`${this.#app.workspace.workspaceRoot}/releases/builds/${this.#app.name}-appBuild.zip`,
|
|
8642
9225
|
"./csr"
|
|
8643
9226
|
]);
|
|
8644
|
-
await
|
|
9227
|
+
await rm4("./csr", { recursive: true, force: true });
|
|
8645
9228
|
}
|
|
8646
9229
|
async#writeSourceArchive({ readme }) {
|
|
8647
9230
|
const sourceRoot = `${this.#app.workspace.workspaceRoot}/releases/sources/${this.#app.name}`;
|
|
@@ -8649,13 +9232,13 @@ class ApplicationReleasePackager {
|
|
|
8649
9232
|
await cp(this.#app.dist.cwdPath, `${sourceRoot}/apps/${this.#app.name}`, { recursive: true });
|
|
8650
9233
|
const libDeps = ["social", "shared", "platform", "util"];
|
|
8651
9234
|
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}/${
|
|
9235
|
+
await Promise.all([".next", "ios", "android", "public/libs"].map(async (path36) => {
|
|
9236
|
+
const targetPath = `${sourceRoot}/apps/${this.#app.name}/${path36}`;
|
|
8654
9237
|
if (await FileSys.dirExists(targetPath))
|
|
8655
|
-
await
|
|
9238
|
+
await rm4(targetPath, { recursive: true, force: true });
|
|
8656
9239
|
}));
|
|
8657
9240
|
const syncPaths = [".husky", ".gitignore", "package.json"];
|
|
8658
|
-
await Promise.all(syncPaths.map((
|
|
9241
|
+
await Promise.all(syncPaths.map((path36) => cp(`${this.#app.workspace.cwdPath}/${path36}`, `${sourceRoot}/${path36}`, { recursive: true })));
|
|
8659
9242
|
await this.#writeSourceTsconfig(sourceRoot, libDeps);
|
|
8660
9243
|
await Bun.write(`${sourceRoot}/README.md`, readme);
|
|
8661
9244
|
await this.#app.workspace.spawn("tar", [
|
|
@@ -8671,11 +9254,11 @@ class ApplicationReleasePackager {
|
|
|
8671
9254
|
const maxRetry = 3;
|
|
8672
9255
|
for (let i = 0;i < maxRetry; i++) {
|
|
8673
9256
|
try {
|
|
8674
|
-
await
|
|
9257
|
+
await rm4(sourceRoot, { recursive: true, force: true });
|
|
8675
9258
|
} catch {}
|
|
8676
9259
|
}
|
|
8677
9260
|
}
|
|
8678
|
-
await
|
|
9261
|
+
await mkdir9(sourceRoot, { recursive: true });
|
|
8679
9262
|
}
|
|
8680
9263
|
async#writeSourceTsconfig(sourceRoot, libDeps) {
|
|
8681
9264
|
const tsconfig = await this.#app.workspace.readJson("tsconfig.json");
|
|
@@ -8746,20 +9329,20 @@ class ApplicationReleasePackager {
|
|
|
8746
9329
|
}
|
|
8747
9330
|
}
|
|
8748
9331
|
// pkgs/@akanjs/devkit/applicationTestPreload.ts
|
|
8749
|
-
import
|
|
9332
|
+
import path36 from "path";
|
|
8750
9333
|
var SIGNAL_TEST_PRELOAD_PATH = "test/signalTest.preload.ts";
|
|
8751
9334
|
async function resolveSignalTestPreloadPath(target) {
|
|
8752
9335
|
const candidates = [];
|
|
8753
9336
|
const addResolvedPackageCandidate = (basePath2) => {
|
|
8754
9337
|
try {
|
|
8755
|
-
candidates.push(
|
|
9338
|
+
candidates.push(path36.join(path36.dirname(Bun.resolveSync("akanjs/package.json", basePath2)), SIGNAL_TEST_PRELOAD_PATH));
|
|
8756
9339
|
} catch {}
|
|
8757
9340
|
};
|
|
8758
9341
|
addResolvedPackageCandidate(target.cwdPath);
|
|
8759
9342
|
addResolvedPackageCandidate(process.cwd());
|
|
8760
|
-
addResolvedPackageCandidate(
|
|
9343
|
+
addResolvedPackageCandidate(path36.dirname(Bun.main));
|
|
8761
9344
|
addResolvedPackageCandidate(import.meta.dir);
|
|
8762
|
-
candidates.push(
|
|
9345
|
+
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
9346
|
for (const candidate of [...new Set(candidates)]) {
|
|
8764
9347
|
if (await Bun.file(candidate).exists())
|
|
8765
9348
|
return candidate;
|
|
@@ -8768,8 +9351,8 @@ async function resolveSignalTestPreloadPath(target) {
|
|
|
8768
9351
|
}
|
|
8769
9352
|
// pkgs/@akanjs/devkit/builder.ts
|
|
8770
9353
|
import { existsSync as existsSync2 } from "fs";
|
|
8771
|
-
import { mkdir as
|
|
8772
|
-
import
|
|
9354
|
+
import { mkdir as mkdir10 } from "fs/promises";
|
|
9355
|
+
import path37 from "path";
|
|
8773
9356
|
var SKIP_ENTRY_DIR_SET = new Set(["node_modules", "dist", "build", ".git", ".next"]);
|
|
8774
9357
|
var assetExtensions = [".css", ".md", ".js", ".png", ".ico", ".svg", ".json", ".template"];
|
|
8775
9358
|
var assetLoader = Object.fromEntries(assetExtensions.map((ext) => [ext, "file"]));
|
|
@@ -8786,14 +9369,14 @@ class Builder {
|
|
|
8786
9369
|
#globEntrypoints(cwd, pattern) {
|
|
8787
9370
|
const glob = new Bun.Glob(pattern);
|
|
8788
9371
|
return Array.from(glob.scanSync({ cwd, onlyFiles: true })).filter((relativePath) => {
|
|
8789
|
-
const segments = relativePath.split(
|
|
9372
|
+
const segments = relativePath.split(path37.sep);
|
|
8790
9373
|
return !segments.some((segment) => SKIP_ENTRY_DIR_SET.has(segment));
|
|
8791
|
-
}).map((rel) =>
|
|
9374
|
+
}).map((rel) => path37.join(cwd, rel));
|
|
8792
9375
|
}
|
|
8793
9376
|
#globFiles(cwd, pattern = "**/*.*") {
|
|
8794
9377
|
const glob = new Bun.Glob(pattern);
|
|
8795
9378
|
return Array.from(glob.scanSync({ cwd, onlyFiles: true })).filter((relativePath) => {
|
|
8796
|
-
const segments = relativePath.split(
|
|
9379
|
+
const segments = relativePath.split(path37.sep);
|
|
8797
9380
|
return !segments.some((segment) => SKIP_ENTRY_DIR_SET.has(segment));
|
|
8798
9381
|
});
|
|
8799
9382
|
}
|
|
@@ -8801,17 +9384,17 @@ class Builder {
|
|
|
8801
9384
|
const out = [];
|
|
8802
9385
|
for (const p of additionalEntryPoints) {
|
|
8803
9386
|
if (p.includes("*")) {
|
|
8804
|
-
const rel = p.startsWith(`${cwd}/`) || p.startsWith(`${cwd}${
|
|
9387
|
+
const rel = p.startsWith(`${cwd}/`) || p.startsWith(`${cwd}${path37.sep}`) ? p.slice(cwd.length + 1) : p;
|
|
8805
9388
|
out.push(...this.#globEntrypoints(cwd, rel));
|
|
8806
9389
|
} else
|
|
8807
|
-
out.push(
|
|
9390
|
+
out.push(path37.isAbsolute(p) ? p : path37.join(cwd, p));
|
|
8808
9391
|
}
|
|
8809
9392
|
return out;
|
|
8810
9393
|
}
|
|
8811
9394
|
#getBuildOptions({ bundle = false, additionalEntryPoints = [] } = {}) {
|
|
8812
9395
|
const cwd = this.#executor.cwdPath;
|
|
8813
9396
|
const entrypoints = [
|
|
8814
|
-
...bundle ? [
|
|
9397
|
+
...bundle ? [path37.join(cwd, "index.ts")] : this.#globEntrypoints(cwd, "**/*.{ts,tsx}"),
|
|
8815
9398
|
...this.#resolveAdditionalEntrypoints(cwd, additionalEntryPoints)
|
|
8816
9399
|
];
|
|
8817
9400
|
return {
|
|
@@ -8832,9 +9415,9 @@ class Builder {
|
|
|
8832
9415
|
for (const relativePath of this.#globFiles(cwd)) {
|
|
8833
9416
|
if (relativePath === "package.json")
|
|
8834
9417
|
continue;
|
|
8835
|
-
const sourcePath =
|
|
8836
|
-
const targetPath =
|
|
8837
|
-
await
|
|
9418
|
+
const sourcePath = path37.join(cwd, relativePath);
|
|
9419
|
+
const targetPath = path37.join(this.#distExecutor.cwdPath, relativePath);
|
|
9420
|
+
await mkdir10(path37.dirname(targetPath), { recursive: true });
|
|
8838
9421
|
await Bun.write(targetPath, Bun.file(sourcePath));
|
|
8839
9422
|
}
|
|
8840
9423
|
}
|
|
@@ -8845,13 +9428,13 @@ class Builder {
|
|
|
8845
9428
|
return withoutFormatDir;
|
|
8846
9429
|
if (!hasDotSlash && withoutFormatDir === publishedPath)
|
|
8847
9430
|
return publishedPath;
|
|
8848
|
-
const parsed =
|
|
9431
|
+
const parsed = path37.posix.parse(withoutFormatDir);
|
|
8849
9432
|
if (![".js", ".mjs", ".cjs"].includes(parsed.ext))
|
|
8850
9433
|
return withoutFormatDir;
|
|
8851
|
-
const withoutExt =
|
|
9434
|
+
const withoutExt = path37.posix.join(parsed.dir, parsed.name);
|
|
8852
9435
|
const sourcePath = withoutExt.startsWith("./") ? withoutExt.slice(2) : withoutExt;
|
|
8853
9436
|
const sourceCandidates = [`${sourcePath}.ts`, `${sourcePath}.tsx`];
|
|
8854
|
-
const matchedSource = sourceCandidates.find((candidate) => existsSync2(
|
|
9437
|
+
const matchedSource = sourceCandidates.find((candidate) => existsSync2(path37.join(this.#executor.cwdPath, candidate)));
|
|
8855
9438
|
if (!matchedSource)
|
|
8856
9439
|
return withoutFormatDir;
|
|
8857
9440
|
return hasDotSlash ? `./${matchedSource}` : matchedSource;
|
|
@@ -8900,10 +9483,10 @@ class Builder {
|
|
|
8900
9483
|
}
|
|
8901
9484
|
}
|
|
8902
9485
|
// pkgs/@akanjs/devkit/capacitorApp.ts
|
|
8903
|
-
import { cp as cp2, mkdir as
|
|
8904
|
-
import
|
|
9486
|
+
import { cp as cp2, mkdir as mkdir11, rm as rm5 } from "fs/promises";
|
|
9487
|
+
import path38 from "path";
|
|
8905
9488
|
import { MobileProject } from "@trapezedev/project";
|
|
8906
|
-
import { capitalize as
|
|
9489
|
+
import { capitalize as capitalize4 } from "akanjs/common";
|
|
8907
9490
|
|
|
8908
9491
|
// pkgs/@akanjs/devkit/fileEditor.ts
|
|
8909
9492
|
class FileEditor {
|
|
@@ -9096,10 +9679,10 @@ class CapacitorApp {
|
|
|
9096
9679
|
constructor(app, target) {
|
|
9097
9680
|
this.app = app;
|
|
9098
9681
|
this.target = target;
|
|
9099
|
-
this.targetRootPath =
|
|
9100
|
-
this.targetRoot =
|
|
9101
|
-
this.targetWebRoot =
|
|
9102
|
-
this.targetAssetRoot =
|
|
9682
|
+
this.targetRootPath = path38.posix.join(".akan", "mobile", this.target.name);
|
|
9683
|
+
this.targetRoot = path38.join(this.app.cwdPath, this.targetRootPath);
|
|
9684
|
+
this.targetWebRoot = path38.join(this.targetRoot, "www");
|
|
9685
|
+
this.targetAssetRoot = path38.join(this.targetRoot, "assets");
|
|
9103
9686
|
this.project = new MobileProject(this.app.cwdPath, {
|
|
9104
9687
|
android: { path: this.androidRootPath },
|
|
9105
9688
|
ios: { path: this.iosProjectPath }
|
|
@@ -9111,13 +9694,13 @@ class CapacitorApp {
|
|
|
9111
9694
|
env = "debug",
|
|
9112
9695
|
regenerate = false
|
|
9113
9696
|
} = {}) {
|
|
9114
|
-
await
|
|
9697
|
+
await mkdir11(this.targetRoot, { recursive: true });
|
|
9115
9698
|
await this.#writeCapacitorConfig();
|
|
9116
9699
|
if (regenerate) {
|
|
9117
9700
|
if (!platform || platform === "ios")
|
|
9118
|
-
await
|
|
9701
|
+
await rm5(path38.join(this.app.cwdPath, this.iosRootPath), { recursive: true, force: true });
|
|
9119
9702
|
if (!platform || platform === "android")
|
|
9120
|
-
await
|
|
9703
|
+
await rm5(path38.join(this.app.cwdPath, this.androidRootPath), { recursive: true, force: true });
|
|
9121
9704
|
}
|
|
9122
9705
|
const project = this.project;
|
|
9123
9706
|
await this.project.load();
|
|
@@ -9181,7 +9764,7 @@ class CapacitorApp {
|
|
|
9181
9764
|
await this.#spawnMobile("npx", ["cap", "sync", "android"], { operation, env });
|
|
9182
9765
|
}
|
|
9183
9766
|
async#updateAndroidBuildTypes() {
|
|
9184
|
-
const appGradle = await FileEditor.create(
|
|
9767
|
+
const appGradle = await FileEditor.create(path38.join(this.app.cwdPath, this.androidRootPath, "app/build.gradle"));
|
|
9185
9768
|
const buildTypesBlock = `
|
|
9186
9769
|
debug {
|
|
9187
9770
|
applicationIdSuffix ".debug"
|
|
@@ -9224,7 +9807,7 @@ class CapacitorApp {
|
|
|
9224
9807
|
const gradleCommand = isWindows ? "gradlew.bat" : "./gradlew";
|
|
9225
9808
|
await this.app.spawn(gradleCommand, [assembleType === "apk" ? "assembleRelease" : "bundleRelease"], {
|
|
9226
9809
|
stdio: "inherit",
|
|
9227
|
-
cwd:
|
|
9810
|
+
cwd: path38.join(this.app.cwdPath, this.androidRootPath),
|
|
9228
9811
|
env: await this.#commandEnv("release", env)
|
|
9229
9812
|
});
|
|
9230
9813
|
}
|
|
@@ -9232,10 +9815,10 @@ class CapacitorApp {
|
|
|
9232
9815
|
await this.#spawnMobile("npx", ["cap", "open", "android"], { operation: "local", env: "local" });
|
|
9233
9816
|
}
|
|
9234
9817
|
async#ensureAndroidAssetsDir() {
|
|
9235
|
-
await
|
|
9818
|
+
await mkdir11(path38.join(this.app.cwdPath, this.androidAssetsPath), { recursive: true });
|
|
9236
9819
|
}
|
|
9237
9820
|
async#ensureAndroidDebugKeystore() {
|
|
9238
|
-
const keystorePath =
|
|
9821
|
+
const keystorePath = path38.join(this.app.cwdPath, this.androidRootPath, "app/debug.keystore");
|
|
9239
9822
|
if (await Bun.file(keystorePath).exists())
|
|
9240
9823
|
return;
|
|
9241
9824
|
await this.#spawn("keytool", [
|
|
@@ -9281,12 +9864,12 @@ class CapacitorApp {
|
|
|
9281
9864
|
await this.#prepareAndroid({ operation: "release", env: "main" });
|
|
9282
9865
|
}
|
|
9283
9866
|
async prepareWww() {
|
|
9284
|
-
const htmlSource =
|
|
9867
|
+
const htmlSource = path38.join(this.app.dist.cwdPath, "csr", targetHtmlFilename(this.target));
|
|
9285
9868
|
if (!await Bun.file(htmlSource).exists())
|
|
9286
9869
|
throw new Error(`CSR html for mobile target '${this.target.name}' not found: ${htmlSource}`);
|
|
9287
|
-
await
|
|
9288
|
-
await
|
|
9289
|
-
await Bun.write(
|
|
9870
|
+
await rm5(this.targetWebRoot, { recursive: true, force: true });
|
|
9871
|
+
await mkdir11(this.targetWebRoot, { recursive: true });
|
|
9872
|
+
await Bun.write(path38.join(this.targetWebRoot, "index.html"), this.#injectMobileTargetMeta(await Bun.file(htmlSource).text()));
|
|
9290
9873
|
}
|
|
9291
9874
|
#injectMobileTargetMeta(html) {
|
|
9292
9875
|
const basePath2 = this.target.basePath?.replace(/^\/+|\/+$/g, "") ?? "";
|
|
@@ -9297,8 +9880,8 @@ class CapacitorApp {
|
|
|
9297
9880
|
</head>`);
|
|
9298
9881
|
}
|
|
9299
9882
|
async#writeCapacitorConfig() {
|
|
9300
|
-
await
|
|
9301
|
-
const appInfoPath =
|
|
9883
|
+
await mkdir11(this.targetRoot, { recursive: true });
|
|
9884
|
+
const appInfoPath = path38.relative(this.app.cwdPath, path38.join(this.app.cwdPath, "akan.app.json")).split(path38.sep).join("/");
|
|
9302
9885
|
const content = `import type { AppScanResult } from "akanjs";
|
|
9303
9886
|
import { withBase } from "${process.env.USE_AKANJS_PKGS === "true" ? "../../pkgs/" : ""}akanjs/capacitor.base.config";
|
|
9304
9887
|
import appInfo from "${appInfoPath.startsWith(".") ? appInfoPath : `./${appInfoPath}`}";
|
|
@@ -9319,18 +9902,18 @@ export default withBase(
|
|
|
9319
9902
|
appInfo as AppScanResult,
|
|
9320
9903
|
);
|
|
9321
9904
|
`;
|
|
9322
|
-
await Bun.write(
|
|
9905
|
+
await Bun.write(path38.join(this.app.cwdPath, "capacitor.config.ts"), content);
|
|
9323
9906
|
}
|
|
9324
9907
|
async#prepareTargetAssets() {
|
|
9325
9908
|
if (!this.target.assets)
|
|
9326
9909
|
return;
|
|
9327
|
-
await
|
|
9910
|
+
await mkdir11(this.targetAssetRoot, { recursive: true });
|
|
9328
9911
|
if (this.target.assets.icon)
|
|
9329
|
-
await cp2(
|
|
9912
|
+
await cp2(path38.join(this.app.cwdPath, this.target.assets.icon), path38.join(this.targetAssetRoot, "icon.png"), {
|
|
9330
9913
|
force: true
|
|
9331
9914
|
});
|
|
9332
9915
|
if (this.target.assets.splash)
|
|
9333
|
-
await cp2(
|
|
9916
|
+
await cp2(path38.join(this.app.cwdPath, this.target.assets.splash), path38.join(this.targetAssetRoot, "splash.png"), {
|
|
9334
9917
|
force: true
|
|
9335
9918
|
});
|
|
9336
9919
|
}
|
|
@@ -9338,11 +9921,11 @@ export default withBase(
|
|
|
9338
9921
|
const files = this.target.files?.[platform];
|
|
9339
9922
|
if (!files)
|
|
9340
9923
|
return;
|
|
9341
|
-
const platformRoot =
|
|
9924
|
+
const platformRoot = path38.join(this.app.cwdPath, platform === "ios" ? this.iosRootPath : this.androidRootPath);
|
|
9342
9925
|
await Promise.all(Object.entries(files).map(async ([to, from]) => {
|
|
9343
|
-
const targetPath =
|
|
9344
|
-
await
|
|
9345
|
-
await cp2(
|
|
9926
|
+
const targetPath = path38.join(platformRoot, to);
|
|
9927
|
+
await mkdir11(path38.dirname(targetPath), { recursive: true });
|
|
9928
|
+
await cp2(path38.join(this.app.cwdPath, from), targetPath, { force: true });
|
|
9346
9929
|
}));
|
|
9347
9930
|
}
|
|
9348
9931
|
async#generateAssets({ operation, env }) {
|
|
@@ -9352,7 +9935,7 @@ export default withBase(
|
|
|
9352
9935
|
"@capacitor/assets",
|
|
9353
9936
|
"generate",
|
|
9354
9937
|
"--assetPath",
|
|
9355
|
-
|
|
9938
|
+
path38.posix.join(this.targetRootPath, "assets"),
|
|
9356
9939
|
"--iosProject",
|
|
9357
9940
|
this.iosProjectPath,
|
|
9358
9941
|
"--androidProject",
|
|
@@ -9454,7 +10037,7 @@ export default withBase(
|
|
|
9454
10037
|
this.#setPermissionsInAndroid(["POST_NOTIFICATIONS"]);
|
|
9455
10038
|
}
|
|
9456
10039
|
async#setPermissionInIos(permissions) {
|
|
9457
|
-
const updateNs = Object.fromEntries(Object.entries(permissions).map(([key, value]) => [`NS${
|
|
10040
|
+
const updateNs = Object.fromEntries(Object.entries(permissions).map(([key, value]) => [`NS${capitalize4(key)}`, value]));
|
|
9458
10041
|
await Promise.all([
|
|
9459
10042
|
this.project.ios.updateInfoPlist(this.iosTargetName, "Debug", updateNs),
|
|
9460
10043
|
this.project.ios.updateInfoPlist(this.iosTargetName, "Release", updateNs)
|
|
@@ -9539,15 +10122,15 @@ var Pkg = createInternalArgToken("Pkg");
|
|
|
9539
10122
|
var Module = createInternalArgToken("Module");
|
|
9540
10123
|
var Workspace = createInternalArgToken("Workspace");
|
|
9541
10124
|
// pkgs/@akanjs/devkit/commandDecorators/command.ts
|
|
9542
|
-
import
|
|
10125
|
+
import path39 from "path";
|
|
9543
10126
|
import { confirm, input as input2, select as select2 } from "@inquirer/prompts";
|
|
9544
10127
|
import { Logger as Logger10 } from "akanjs/common";
|
|
9545
10128
|
import chalk6 from "chalk";
|
|
9546
10129
|
import { program } from "commander";
|
|
9547
10130
|
|
|
9548
10131
|
// pkgs/@akanjs/devkit/commandDecorators/dependencyBuilder.ts
|
|
9549
|
-
var
|
|
9550
|
-
var createDependencyKey = (refName, kind) => `${refName}${
|
|
10132
|
+
var capitalize5 = (value) => value.slice(0, 1).toUpperCase() + value.slice(1);
|
|
10133
|
+
var createDependencyKey = (refName, kind) => `${refName}${capitalize5(kind)}`;
|
|
9551
10134
|
|
|
9552
10135
|
class CommandContainer {
|
|
9553
10136
|
static #instances = new Map;
|
|
@@ -10039,7 +10622,7 @@ var runCommands = async (...commands) => {
|
|
|
10039
10622
|
process.exit(1);
|
|
10040
10623
|
});
|
|
10041
10624
|
const __dirname2 = getDirname(import.meta.url);
|
|
10042
|
-
const packageJsonCandidates = [`${
|
|
10625
|
+
const packageJsonCandidates = [`${path39.dirname(Bun.main)}/package.json`, `${__dirname2}/../package.json`];
|
|
10043
10626
|
let cliPackageJson = null;
|
|
10044
10627
|
for (const packageJsonPath of packageJsonCandidates) {
|
|
10045
10628
|
if (!await FileSys.fileExists(packageJsonPath))
|
|
@@ -10317,8 +10900,8 @@ var scanModuleSpecifiers = (source, filePath, includeExports) => {
|
|
|
10317
10900
|
return importSpecifiers;
|
|
10318
10901
|
};
|
|
10319
10902
|
var parseTsConfig = (tsConfigPath = "./tsconfig.json") => {
|
|
10320
|
-
const configFile = ts7.readConfigFile(tsConfigPath, (
|
|
10321
|
-
return ts7.sys.readFile(
|
|
10903
|
+
const configFile = ts7.readConfigFile(tsConfigPath, (path40) => {
|
|
10904
|
+
return ts7.sys.readFile(path40);
|
|
10322
10905
|
});
|
|
10323
10906
|
return ts7.parseJsonConfigFileContent(configFile.config, ts7.sys, realpathSync(tsConfigPath).replace(/[^/\\]+$/, ""));
|
|
10324
10907
|
};
|
|
@@ -10560,7 +11143,6 @@ import { jsxDEV as jsxDEV2, Fragment as Fragment2 } from "react/jsx-dev-runtime"
|
|
|
10560
11143
|
"use client";
|
|
10561
11144
|
// pkgs/@akanjs/devkit/incrementalBuilder/incrementalBuilder.proc.ts
|
|
10562
11145
|
import { Logger as Logger11 } from "akanjs/common";
|
|
10563
|
-
|
|
10564
11146
|
class IncrementalBuilder {
|
|
10565
11147
|
#logger = new Logger11("IncrementalBuilder");
|
|
10566
11148
|
#app;
|
|
@@ -10569,6 +11151,8 @@ class IncrementalBuilder {
|
|
|
10569
11151
|
#cssCompiler;
|
|
10570
11152
|
#optimizedFonts;
|
|
10571
11153
|
#discovery;
|
|
11154
|
+
#changePlanner;
|
|
11155
|
+
#generatedIndexSync;
|
|
10572
11156
|
#generation = 0;
|
|
10573
11157
|
#workQueue = Promise.resolve();
|
|
10574
11158
|
#cssRebuildQueue = Promise.resolve();
|
|
@@ -10581,6 +11165,8 @@ class IncrementalBuilder {
|
|
|
10581
11165
|
this.#cssCompiler = options.cssCompiler;
|
|
10582
11166
|
this.#optimizedFonts = options.optimizedFonts;
|
|
10583
11167
|
this.#discovery = options.discovery;
|
|
11168
|
+
this.#changePlanner = new DevChangePlanner({ workspaceRoot: options.app.workspace.workspaceRoot });
|
|
11169
|
+
this.#generatedIndexSync = new DevGeneratedIndexSync({ workspaceRoot: options.app.workspace.workspaceRoot });
|
|
10584
11170
|
}
|
|
10585
11171
|
async handleBuildRoute(msg) {
|
|
10586
11172
|
return this.#enqueueWork(`build-route:${msg.routeId}`, async () => this.#handleBuildRoute(msg));
|
|
@@ -10596,6 +11182,7 @@ class IncrementalBuilder {
|
|
|
10596
11182
|
discovery: this.#discovery
|
|
10597
11183
|
}).build();
|
|
10598
11184
|
this.#logger.verbose(`build-route ok routeId=${msg.routeId} newEntries=${delta.newEntries.length}`);
|
|
11185
|
+
this.#sendBuildStatus("route", { generation: msg.generation, ok: true, files: msg.seeds });
|
|
10599
11186
|
return {
|
|
10600
11187
|
type: "build-route-res",
|
|
10601
11188
|
id: msg.id,
|
|
@@ -10614,9 +11201,24 @@ class IncrementalBuilder {
|
|
|
10614
11201
|
} catch (err) {
|
|
10615
11202
|
const errMsg = err instanceof Error ? err.message : String(err);
|
|
10616
11203
|
this.#logger.error(`build-route failed routeId=${msg.routeId}: ${errMsg}`);
|
|
11204
|
+
this.#sendBuildStatus("route", { generation: msg.generation, ok: false, files: msg.seeds, message: errMsg });
|
|
10617
11205
|
return { type: "build-route-res", id: msg.id, ok: false, error: errMsg };
|
|
10618
11206
|
}
|
|
10619
11207
|
}
|
|
11208
|
+
#sendBuildStatus(phase, { generation, ok, files, message }) {
|
|
11209
|
+
if (typeof generation !== "number")
|
|
11210
|
+
return;
|
|
11211
|
+
process.send?.({
|
|
11212
|
+
type: "build-status",
|
|
11213
|
+
data: {
|
|
11214
|
+
generation,
|
|
11215
|
+
phase,
|
|
11216
|
+
ok,
|
|
11217
|
+
files: files ?? [],
|
|
11218
|
+
message
|
|
11219
|
+
}
|
|
11220
|
+
});
|
|
11221
|
+
}
|
|
10620
11222
|
async#enqueueWork(label, fn) {
|
|
10621
11223
|
const started = Date.now();
|
|
10622
11224
|
const run = this.#workQueue.then(fn, fn);
|
|
@@ -10632,10 +11234,10 @@ class IncrementalBuilder {
|
|
|
10632
11234
|
}
|
|
10633
11235
|
}
|
|
10634
11236
|
batchTouchesPagesTree(appDir, batch) {
|
|
10635
|
-
const absAppDir =
|
|
11237
|
+
const absAppDir = path40.resolve(appDir);
|
|
10636
11238
|
for (const f of batch.files) {
|
|
10637
|
-
const abs =
|
|
10638
|
-
if (!abs.startsWith(`${absAppDir}${
|
|
11239
|
+
const abs = path40.resolve(f);
|
|
11240
|
+
if (!abs.startsWith(`${absAppDir}${path40.sep}`) && abs !== absAppDir)
|
|
10639
11241
|
continue;
|
|
10640
11242
|
if (/\.(tsx|ts|jsx|js)$/.test(abs))
|
|
10641
11243
|
return true;
|
|
@@ -10643,15 +11245,15 @@ class IncrementalBuilder {
|
|
|
10643
11245
|
return false;
|
|
10644
11246
|
}
|
|
10645
11247
|
async batchMayChangePageKeys(appDir, batch) {
|
|
10646
|
-
const absAppDir =
|
|
10647
|
-
const pageKeys = new Set((await this.#app.getPageKeys()).map((key) =>
|
|
11248
|
+
const absAppDir = path40.resolve(appDir);
|
|
11249
|
+
const pageKeys = new Set((await this.#app.getPageKeys()).map((key) => path40.normalize(key)));
|
|
10648
11250
|
for (const f of batch.files) {
|
|
10649
|
-
const abs =
|
|
10650
|
-
if (!abs.startsWith(`${absAppDir}${
|
|
11251
|
+
const abs = path40.resolve(f);
|
|
11252
|
+
if (!abs.startsWith(`${absAppDir}${path40.sep}`) && abs !== absAppDir)
|
|
10651
11253
|
continue;
|
|
10652
11254
|
if (!/\.(tsx|ts|jsx|js)$/.test(abs))
|
|
10653
11255
|
continue;
|
|
10654
|
-
const rel =
|
|
11256
|
+
const rel = path40.normalize(path40.relative(absAppDir, abs));
|
|
10655
11257
|
if (!await Bun.file(abs).exists() || !pageKeys.has(rel))
|
|
10656
11258
|
return true;
|
|
10657
11259
|
}
|
|
@@ -10679,7 +11281,7 @@ class IncrementalBuilder {
|
|
|
10679
11281
|
${cssText}`).toString(36);
|
|
10680
11282
|
const cssRelPath = `styles/${cssAssetName}-${cssHash}.css`;
|
|
10681
11283
|
const cssUrl = `/_akan/styles/${cssAssetName}-${cssHash}.css`;
|
|
10682
|
-
await Bun.write(
|
|
11284
|
+
await Bun.write(path40.join(artifactDir, cssRelPath), cssText);
|
|
10683
11285
|
cssAssetEntries.push([basePath2, { cssUrl, cssRelPath }]);
|
|
10684
11286
|
cssBase64ByUrl[cssUrl] = Buffer.from(new TextEncoder().encode(cssText)).toString("base64");
|
|
10685
11287
|
})()
|
|
@@ -10719,9 +11321,17 @@ ${cssText}`).toString(36);
|
|
|
10719
11321
|
generation: next.generation,
|
|
10720
11322
|
changedFiles: next.changedFiles
|
|
10721
11323
|
});
|
|
11324
|
+
this.#sendBuildStatus("css", { generation: next.generation, ok: true, files: next.changedFiles });
|
|
10722
11325
|
this.#logger.verbose(`css-rebuild checked (${Date.now() - started}ms)`);
|
|
10723
11326
|
}).catch((err) => {
|
|
10724
|
-
|
|
11327
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
11328
|
+
this.#logger.error(`css-rebuild failed: ${message}`);
|
|
11329
|
+
this.#sendBuildStatus("css", {
|
|
11330
|
+
generation: next.generation,
|
|
11331
|
+
ok: false,
|
|
11332
|
+
files: next.changedFiles,
|
|
11333
|
+
message
|
|
11334
|
+
});
|
|
10725
11335
|
});
|
|
10726
11336
|
}, 150);
|
|
10727
11337
|
}
|
|
@@ -10739,10 +11349,10 @@ ${cssText}`).toString(36);
|
|
|
10739
11349
|
if (changedFiles.length === 0)
|
|
10740
11350
|
return false;
|
|
10741
11351
|
return changedFiles.some((file) => {
|
|
10742
|
-
const normalized =
|
|
11352
|
+
const normalized = path40.resolve(file);
|
|
10743
11353
|
if (/\.(woff2?|ttf|otf)$/i.test(normalized))
|
|
10744
11354
|
return true;
|
|
10745
|
-
return this.#optimizedFonts.files.some((fontFile) =>
|
|
11355
|
+
return this.#optimizedFonts.files.some((fontFile) => path40.resolve(fontFile) === normalized);
|
|
10746
11356
|
});
|
|
10747
11357
|
}
|
|
10748
11358
|
async installWatcher() {
|
|
@@ -10759,38 +11369,58 @@ ${cssText}`).toString(36);
|
|
|
10759
11369
|
this.#logger.verbose(`watching ${roots.length} roots`);
|
|
10760
11370
|
}
|
|
10761
11371
|
async#handleWatchBatch(appDir, artifactDir, batch) {
|
|
10762
|
-
const
|
|
10763
|
-
if (
|
|
11372
|
+
const rawKinds = new Set(batch.kinds);
|
|
11373
|
+
if (rawKinds.size === 0)
|
|
10764
11374
|
return;
|
|
10765
11375
|
const generation = ++this.#generation;
|
|
10766
|
-
|
|
11376
|
+
const indexSync = await this.#generatedIndexSync.syncForBatch(batch.files);
|
|
11377
|
+
const { files, kinds, expandedBatch, event, hasSyncErrors } = prepareDevWatchBatch({
|
|
11378
|
+
generation,
|
|
11379
|
+
batch,
|
|
11380
|
+
indexSync,
|
|
11381
|
+
changePlanner: this.#changePlanner
|
|
11382
|
+
});
|
|
11383
|
+
const devPlan = event.devPlan;
|
|
11384
|
+
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)"}`);
|
|
11385
|
+
for (const error of indexSync.errors)
|
|
11386
|
+
this.#logger.error(error);
|
|
10767
11387
|
if (kinds.includes("code")) {
|
|
10768
11388
|
const started = Date.now();
|
|
10769
11389
|
if (kinds.includes("config"))
|
|
10770
11390
|
this.#discovery = await GraphClientEntryDiscovery.create(this.#app);
|
|
10771
11391
|
else
|
|
10772
|
-
this.#discovery.invalidate?.(
|
|
11392
|
+
this.#discovery.invalidate?.(files);
|
|
10773
11393
|
this.#logger.verbose(`client-entry-discovery ${kinds.includes("config") ? "refreshed" : "invalidated"} (${Date.now() - started}ms)`);
|
|
10774
11394
|
}
|
|
10775
|
-
if (
|
|
11395
|
+
if (hasSyncErrors) {
|
|
11396
|
+
this.#sendBuildStatus("barrel", { generation, ok: false, files, message: indexSync.errors.join(`
|
|
11397
|
+
`) });
|
|
11398
|
+
process.send?.(event);
|
|
11399
|
+
return;
|
|
11400
|
+
}
|
|
11401
|
+
if (indexSync.changedFiles.length > 0)
|
|
11402
|
+
this.#sendBuildStatus("barrel", { generation, ok: true, files });
|
|
11403
|
+
if (kinds.includes("code") && await this.batchMayChangePageKeys(appDir, expandedBatch)) {
|
|
10776
11404
|
const started = Date.now();
|
|
10777
11405
|
await this.#app.getPageKeys({ refresh: true });
|
|
10778
11406
|
this.#logger.verbose(`pageKeys updated, app pageKeys are refreshed (${Date.now() - started}ms)`);
|
|
10779
|
-
} else if (kinds.includes("code") && this.batchTouchesPagesTree(appDir,
|
|
11407
|
+
} else if (kinds.includes("code") && this.batchTouchesPagesTree(appDir, expandedBatch)) {
|
|
10780
11408
|
this.#logger.verbose("pageKeys refresh skipped; changed page source cannot add/remove a route key");
|
|
10781
11409
|
}
|
|
10782
11410
|
if (kinds.includes("code") && this.#shouldRebuildCsr()) {
|
|
10783
11411
|
try {
|
|
10784
11412
|
const started = Date.now();
|
|
10785
11413
|
await new CsrArtifactBuilder(this.#app).build();
|
|
11414
|
+
this.#sendBuildStatus("csr", { generation, ok: true, files });
|
|
10786
11415
|
this.#logger.verbose(`csr-rebundle ok (${Date.now() - started}ms)`);
|
|
10787
11416
|
} catch (err) {
|
|
10788
|
-
|
|
11417
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
11418
|
+
this.#logger.error(`csr-rebundle failed: ${message}`);
|
|
11419
|
+
this.#sendBuildStatus("csr", { generation, ok: false, files, message });
|
|
10789
11420
|
}
|
|
10790
11421
|
} else if (kinds.includes("code")) {
|
|
10791
11422
|
this.#logger.verbose(`csr-rebundle skipped; set AKAN_DEV_CSR_REBUILD=1 to enable per-save CSR rebuilds`);
|
|
10792
11423
|
}
|
|
10793
|
-
const event = { type: "invalidate", kinds, files: batch.files, generation };
|
|
10794
11424
|
process.send?.(event);
|
|
10795
11425
|
if (kinds.includes("code")) {
|
|
10796
11426
|
try {
|
|
@@ -10798,15 +11428,18 @@ ${cssText}`).toString(36);
|
|
|
10798
11428
|
const next = await new PagesBundleBuilder(this.#app).build();
|
|
10799
11429
|
process.send?.({
|
|
10800
11430
|
type: "pages-updated",
|
|
10801
|
-
data: { bundlePath: next.bundlePath, buildId: next.buildId, generation, changedFiles:
|
|
11431
|
+
data: { bundlePath: next.bundlePath, buildId: next.buildId, generation, changedFiles: files }
|
|
10802
11432
|
});
|
|
11433
|
+
this.#sendBuildStatus("pages", { generation, ok: true, files });
|
|
10803
11434
|
this.#logger.verbose(`pages-rebundle ok buildId=${next.buildId} (${Date.now() - started}ms)`);
|
|
10804
11435
|
} catch (err) {
|
|
10805
|
-
|
|
11436
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
11437
|
+
this.#logger.error(`pages-rebundle failed: ${message}`);
|
|
11438
|
+
this.#sendBuildStatus("pages", { generation, ok: false, files, message });
|
|
10806
11439
|
}
|
|
10807
11440
|
}
|
|
10808
11441
|
if (kinds.includes("code") || kinds.includes("css")) {
|
|
10809
|
-
this.scheduleCssRebuild(artifactDir, { refresh: true, generation, changedFiles:
|
|
11442
|
+
this.scheduleCssRebuild(artifactDir, { refresh: true, generation, changedFiles: files });
|
|
10810
11443
|
this.#logger.verbose(`css-rebuild scheduled generation=${generation}`);
|
|
10811
11444
|
}
|
|
10812
11445
|
}
|