@akanjs/devkit 3.0.0-alpha.65 → 3.0.0-alpha.66
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/akanConfig/akanConfig.test.ts +105 -13
- package/akanConfig/akanConfig.ts +68 -35
- package/akanConfig/types.ts +4 -0
- package/applicationBuildRunner.ts +1 -1
- package/package.json +2 -2
- package/scanInfo.ts +8 -3
|
@@ -64,7 +64,7 @@ describe("AkanAppConfig", () => {
|
|
|
64
64
|
expect(config.barrelImports).toEqual(
|
|
65
65
|
expect.arrayContaining(["@apps/portal/ui", "@libs/shared/server", "akanjs/common", "akanjs/server"]),
|
|
66
66
|
);
|
|
67
|
-
expect(config.
|
|
67
|
+
expect(config.dockerfile).toContain("ENV AKAN_PUBLIC_APP_NAME=portal");
|
|
68
68
|
expect(process.env.AKAN_PUBLIC_DEFAULT_LOCALE).toBe("en");
|
|
69
69
|
});
|
|
70
70
|
|
|
@@ -128,29 +128,29 @@ describe("AkanAppConfig", () => {
|
|
|
128
128
|
});
|
|
129
129
|
expect(config.publicEnv).toEqual(["AKAN_PUBLIC_FEATURE"]);
|
|
130
130
|
expect(config.optimizeImports).toContain("custom-icons");
|
|
131
|
-
expect(config.
|
|
132
|
-
expect(config.
|
|
133
|
-
expect(config.
|
|
131
|
+
expect(config.dockerfile).toContain('CMD ["bun","server.js"]');
|
|
132
|
+
expect(config.dockerfile).toContain("FROM oven/bun:amd64 AS amd64");
|
|
133
|
+
expect(config.dockerfile).toContain('RUN if [ "$TARGETARCH" = "arm64"');
|
|
134
134
|
});
|
|
135
135
|
|
|
136
136
|
test("defaults both web surfaces on and keeps the image free of web env overrides", () => {
|
|
137
137
|
const config = new AkanAppConfig(app, [], packageJson, {}, baseDevEnv);
|
|
138
138
|
|
|
139
139
|
expect(config.web).toEqual({ ssr: true, csr: true });
|
|
140
|
-
expect(config.
|
|
141
|
-
expect(config.
|
|
140
|
+
expect(config.dockerfile).not.toContain("AKAN_SSR");
|
|
141
|
+
expect(config.dockerfile).not.toContain("AKAN_CSR");
|
|
142
142
|
});
|
|
143
143
|
|
|
144
144
|
test("bakes the disabled surface into the image env so the default matches what was built", () => {
|
|
145
145
|
const ssrOnly = new AkanAppConfig(app, [], packageJson, { web: { csr: false } }, baseDevEnv);
|
|
146
146
|
expect(ssrOnly.web).toEqual({ ssr: true, csr: false });
|
|
147
|
-
expect(ssrOnly.
|
|
148
|
-
expect(ssrOnly.
|
|
147
|
+
expect(ssrOnly.dockerfile).toContain("ENV AKAN_CSR=false");
|
|
148
|
+
expect(ssrOnly.dockerfile).not.toContain("ENV AKAN_SSR=false");
|
|
149
149
|
|
|
150
150
|
const apiOnly = new AkanAppConfig(app, [], packageJson, { web: false }, baseDevEnv);
|
|
151
151
|
expect(apiOnly.web).toEqual({ ssr: false, csr: false });
|
|
152
|
-
expect(apiOnly.
|
|
153
|
-
expect(apiOnly.
|
|
152
|
+
expect(apiOnly.dockerfile).toContain("ENV AKAN_SSR=false");
|
|
153
|
+
expect(apiOnly.dockerfile).toContain("ENV AKAN_CSR=false");
|
|
154
154
|
|
|
155
155
|
expect(new AkanAppConfig(app, [], packageJson, { web: true }, baseDevEnv).web).toEqual({ ssr: true, csr: true });
|
|
156
156
|
});
|
|
@@ -164,12 +164,31 @@ describe("AkanAppConfig", () => {
|
|
|
164
164
|
test("installs only ca-certificates and tzdata in the default image", () => {
|
|
165
165
|
const config = new AkanAppConfig(app, [], packageJson, {}, baseDevEnv);
|
|
166
166
|
|
|
167
|
-
expect(config.
|
|
167
|
+
expect(config.dockerfile).toContain(
|
|
168
168
|
"RUN apt-get update && apt-get upgrade -y && apt-get install -y --no-install-recommends ca-certificates tzdata && rm -rf /var/lib/apt/lists/*",
|
|
169
169
|
);
|
|
170
170
|
// The Chromium/ffmpeg toolchain moved to per-app `preRuns`; keeping it here paid for it in every image.
|
|
171
171
|
for (const dropped of ["libnss3", "ffmpeg", "build-essential", "redis", "xdg-utils"])
|
|
172
|
-
expect(config.
|
|
172
|
+
expect(config.dockerfile).not.toContain(dropped);
|
|
173
|
+
});
|
|
174
|
+
|
|
175
|
+
test("keeps a declared Dockerfile string verbatim", () => {
|
|
176
|
+
const dockerfile = "FROM oven/bun:1-slim\nCOPY . .\nCMD [\"bun\",\"main.js\"]";
|
|
177
|
+
const config = new AkanAppConfig(app, [], packageJson, { docker: dockerfile }, baseDevEnv);
|
|
178
|
+
|
|
179
|
+
expect(config.docker).toBe(dockerfile);
|
|
180
|
+
expect(config.dockerfile).toBe(dockerfile);
|
|
181
|
+
});
|
|
182
|
+
|
|
183
|
+
test("resolves the image parts, defaulting the base image and the command", () => {
|
|
184
|
+
const config = new AkanAppConfig(app, [], packageJson, { docker: { preRuns: ["echo hi"] } }, baseDevEnv);
|
|
185
|
+
|
|
186
|
+
expect(config.docker).toEqual({
|
|
187
|
+
image: "oven/bun:1-slim",
|
|
188
|
+
preRuns: ["echo hi"],
|
|
189
|
+
postRuns: [],
|
|
190
|
+
command: ["bun", "main.js"],
|
|
191
|
+
});
|
|
173
192
|
});
|
|
174
193
|
|
|
175
194
|
test("creates production package json and reports missing external versions", () => {
|
|
@@ -435,7 +454,7 @@ describe("AkanAppConfig lib externalLibs", () => {
|
|
|
435
454
|
{ externalLibs: ["@external/runtime"] },
|
|
436
455
|
baseDevEnv,
|
|
437
456
|
[],
|
|
438
|
-
["@external/runtime", "puppeteer"],
|
|
457
|
+
{ externalLibs: ["@external/runtime", "puppeteer"], docker: { preRuns: [], postRuns: [] } },
|
|
439
458
|
);
|
|
440
459
|
|
|
441
460
|
expect(config.externalLibs).toEqual(["@external/runtime", "puppeteer"]);
|
|
@@ -468,6 +487,70 @@ describe("AkanAppConfig lib externalLibs", () => {
|
|
|
468
487
|
});
|
|
469
488
|
});
|
|
470
489
|
|
|
490
|
+
|
|
491
|
+
describe("AkanAppConfig lib docker runs", () => {
|
|
492
|
+
const libDocker = (preRuns: string[], postRuns: string[] = []) => ({
|
|
493
|
+
externalLibs: [],
|
|
494
|
+
docker: { preRuns, postRuns },
|
|
495
|
+
});
|
|
496
|
+
|
|
497
|
+
test("runs lib steps before the app's own, deduped", () => {
|
|
498
|
+
const config = new AkanAppConfig(
|
|
499
|
+
app,
|
|
500
|
+
["shared"],
|
|
501
|
+
packageJson,
|
|
502
|
+
{ docker: { preRuns: ["apt-get install -y ffmpeg", "echo app"], postRuns: ["echo app-post"] } },
|
|
503
|
+
baseDevEnv,
|
|
504
|
+
[],
|
|
505
|
+
libDocker(["apt-get install -y ffmpeg", "echo lib"], ["echo lib-post"]),
|
|
506
|
+
);
|
|
507
|
+
|
|
508
|
+
expect(config.docker).toMatchObject({
|
|
509
|
+
preRuns: ["apt-get install -y ffmpeg", "echo lib", "echo app"],
|
|
510
|
+
postRuns: ["echo lib-post", "echo app-post"],
|
|
511
|
+
});
|
|
512
|
+
expect(config.dockerfile).toContain("RUN echo lib\nRUN echo app\n");
|
|
513
|
+
expect(config.dockerfile.match(/RUN apt-get install -y ffmpeg/g)).toHaveLength(1);
|
|
514
|
+
});
|
|
515
|
+
|
|
516
|
+
test("drops them when the app hands over a whole Dockerfile", () => {
|
|
517
|
+
const config = new AkanAppConfig(
|
|
518
|
+
app,
|
|
519
|
+
["shared"],
|
|
520
|
+
packageJson,
|
|
521
|
+
{ docker: "FROM scratch" },
|
|
522
|
+
baseDevEnv,
|
|
523
|
+
[],
|
|
524
|
+
libDocker(["echo lib"]),
|
|
525
|
+
);
|
|
526
|
+
|
|
527
|
+
expect(config.dockerfile).toBe("FROM scratch");
|
|
528
|
+
});
|
|
529
|
+
|
|
530
|
+
test("reads them off every workspace lib config on load", async () => {
|
|
531
|
+
const root = fs.mkdtempSync(path.join(os.tmpdir(), "akan-config-libdocker-"));
|
|
532
|
+
try {
|
|
533
|
+
fs.mkdirSync(path.join(root, "apps/extapp"), { recursive: true });
|
|
534
|
+
fs.mkdirSync(path.join(root, "libs/extlib"), { recursive: true });
|
|
535
|
+
fs.writeFileSync(path.join(root, "apps/extapp/akan.config.ts"), "export default {};\n");
|
|
536
|
+
fs.writeFileSync(
|
|
537
|
+
path.join(root, "libs/extlib/akan.config.ts"),
|
|
538
|
+
"export default { docker: { preRuns: ['echo from-lib'], postRuns: [{ arm64: 'echo arm-only' }] } };\n",
|
|
539
|
+
);
|
|
540
|
+
fs.writeFileSync(path.join(root, "package.json"), JSON.stringify({ name: "extrepo", version: "1.0.0" }));
|
|
541
|
+
fs.writeFileSync(path.join(root, ".env"), "AKAN_PUBLIC_REPO_NAME=extrepo\nAKAN_PUBLIC_SERVE_DOMAIN=ext.test\n");
|
|
542
|
+
|
|
543
|
+
const workspace = WorkspaceExecutor.fromRoot({ workspaceRoot: root, repoName: "extrepo" });
|
|
544
|
+
const config = await AppExecutor.from(workspace, "extapp").getConfig();
|
|
545
|
+
|
|
546
|
+
expect(config.dockerfile).toContain("RUN echo from-lib");
|
|
547
|
+
expect(config.dockerfile).toContain('RUN if [ "$TARGETARCH" = "arm64"');
|
|
548
|
+
} finally {
|
|
549
|
+
fs.rmSync(root, { recursive: true, force: true });
|
|
550
|
+
}
|
|
551
|
+
});
|
|
552
|
+
});
|
|
553
|
+
|
|
471
554
|
describe("AkanLibConfig", () => {
|
|
472
555
|
test("uses empty external libs by default and preserves explicit libs", () => {
|
|
473
556
|
const lib = { name: "shared" } as never;
|
|
@@ -478,6 +561,15 @@ describe("AkanLibConfig", () => {
|
|
|
478
561
|
};
|
|
479
562
|
expect(new AkanLibConfig(lib, config).externalLibs).toEqual(["firebase-admin"]);
|
|
480
563
|
});
|
|
564
|
+
|
|
565
|
+
test("defaults docker runs to empty lists and preserves declared ones", () => {
|
|
566
|
+
const lib = { name: "shared" } as never;
|
|
567
|
+
expect(new AkanLibConfig(lib, {}).docker).toEqual({ preRuns: [], postRuns: [] });
|
|
568
|
+
expect(new AkanLibConfig(lib, { docker: { preRuns: ["echo lib"] } }).docker).toEqual({
|
|
569
|
+
preRuns: ["echo lib"],
|
|
570
|
+
postRuns: [],
|
|
571
|
+
});
|
|
572
|
+
});
|
|
481
573
|
});
|
|
482
574
|
|
|
483
575
|
describe("AkanAppConfig.importConfigModule", () => {
|
package/akanConfig/akanConfig.ts
CHANGED
|
@@ -19,7 +19,10 @@ import {
|
|
|
19
19
|
type DatabaseMode,
|
|
20
20
|
type DeepPartial,
|
|
21
21
|
type DockerConfig,
|
|
22
|
+
type DockerOption,
|
|
23
|
+
type DockerRun,
|
|
22
24
|
type LibConfigResult,
|
|
25
|
+
type LibDockerConfig,
|
|
23
26
|
} from "./types";
|
|
24
27
|
|
|
25
28
|
const DEFAULT_BARREL_IMPORTS = ["akanjs/webkit", "akanjs/common", "akanjs/ui", "akanjs/server"];
|
|
@@ -51,6 +54,7 @@ const DEFAULT_OPTIMIZE_IMPORTS = [
|
|
|
51
54
|
"react-icons/*",
|
|
52
55
|
];
|
|
53
56
|
const WORKSPACE_BARREL_FACETS = ["ui", "webkit", "common", "client", "server"] as const;
|
|
57
|
+
const DEFAULT_DOCKER_IMAGE = "oven/bun:1-slim";
|
|
54
58
|
const SSR_RUNTIME_PACKAGES = ["react", "react-dom", "react-server-dom-webpack"] as const;
|
|
55
59
|
// The firebase client (push tokens) and the Capacitor toolchain — `@capacitor/cli` (`npx cap`),
|
|
56
60
|
// `@capacitor/assets` (`npx @capacitor/assets`) plus the `@capacitor/core`/`ios`/`android` runtime
|
|
@@ -165,13 +169,36 @@ const normalizeDeepLinks = (deepLinks: DeepPartial<AkanMobileTargetConfig["deepL
|
|
|
165
169
|
} satisfies AkanMobileTargetConfig["deepLinks"];
|
|
166
170
|
};
|
|
167
171
|
|
|
168
|
-
/** What `akan.config.ts` may write: the resolved shape made partial, with `web` in
|
|
169
|
-
type AppConfigDeclaration = Omit<DeepPartial<AppConfigResult>, "web"> & {
|
|
172
|
+
/** What `akan.config.ts` may write: the resolved shape made partial, with `docker` and `web` in their unions. */
|
|
173
|
+
type AppConfigDeclaration = Omit<DeepPartial<AppConfigResult>, "docker" | "web"> & {
|
|
174
|
+
docker?: DockerOption;
|
|
175
|
+
web?: AkanWebOption;
|
|
176
|
+
};
|
|
177
|
+
|
|
178
|
+
/** What the workspace's libs add to an app's build, read off each `libs/<lib>/akan.config.ts`. */
|
|
179
|
+
export interface LibContributions {
|
|
180
|
+
externalLibs: string[];
|
|
181
|
+
docker: LibDockerConfig;
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
const emptyLibContributions = (): LibContributions => ({
|
|
185
|
+
externalLibs: [],
|
|
186
|
+
docker: { preRuns: [], postRuns: [] },
|
|
187
|
+
});
|
|
188
|
+
|
|
189
|
+
/** First occurrence wins, so a step a lib and its app both declare becomes one layer. */
|
|
190
|
+
const dedupeDockerRuns = (runs: DockerRun[]): DockerRun[] => {
|
|
191
|
+
const byKey = new Map<string, DockerRun>();
|
|
192
|
+
for (const run of runs) byKey.set(typeof run === "string" ? run : JSON.stringify(run), run);
|
|
193
|
+
return [...byKey.values()];
|
|
194
|
+
};
|
|
170
195
|
|
|
171
196
|
export class AkanAppConfig implements AppConfigResult {
|
|
172
197
|
app: App;
|
|
173
198
|
rootPackageJson: PackageJson;
|
|
174
199
|
docker: DockerConfig;
|
|
200
|
+
/** The Dockerfile `akan build` writes: the declared string verbatim, or one assembled from the parts. */
|
|
201
|
+
dockerfile: string;
|
|
175
202
|
defaultDatabaseMode: DatabaseMode;
|
|
176
203
|
web: AkanWebConfig;
|
|
177
204
|
externalLibs: string[];
|
|
@@ -201,7 +228,7 @@ export class AkanAppConfig implements AppConfigResult {
|
|
|
201
228
|
config: AppConfigDeclaration,
|
|
202
229
|
baseDevEnv: BaseDevEnv,
|
|
203
230
|
plugins: AkanPlugin[] = [],
|
|
204
|
-
|
|
231
|
+
libContributions: LibContributions = emptyLibContributions(),
|
|
205
232
|
) {
|
|
206
233
|
this.app = app;
|
|
207
234
|
this.rootPackageJson = rootPackageJson;
|
|
@@ -210,7 +237,7 @@ export class AkanAppConfig implements AppConfigResult {
|
|
|
210
237
|
this.plugins = plugins;
|
|
211
238
|
this.#applyRoutes(config?.routes);
|
|
212
239
|
this.defaultDatabaseMode = config?.defaultDatabaseMode ?? "single";
|
|
213
|
-
this.externalLibs = [...new Set([...(config?.externalLibs ?? []), ...
|
|
240
|
+
this.externalLibs = [...new Set([...(config?.externalLibs ?? []), ...libContributions.externalLibs])];
|
|
214
241
|
this.barrelImports = [
|
|
215
242
|
...DEFAULT_BARREL_IMPORTS,
|
|
216
243
|
...WORKSPACE_BARREL_FACETS.map((facet) => `@apps/${app.name}/${facet}`),
|
|
@@ -228,7 +255,8 @@ export class AkanAppConfig implements AppConfigResult {
|
|
|
228
255
|
this.hasMobileConfig = Boolean(config.mobile);
|
|
229
256
|
this.mobile = this.#resolveMobileConfig(config.mobile);
|
|
230
257
|
this.web = this.#resolveWebConfig(config.web);
|
|
231
|
-
this.docker =
|
|
258
|
+
this.docker = AkanAppConfig.#resolveDocker(config.docker, libContributions.docker);
|
|
259
|
+
this.dockerfile = this.#makeDockerfile();
|
|
232
260
|
}
|
|
233
261
|
#resolveWebConfig(web: AkanWebOption | undefined): AkanWebConfig {
|
|
234
262
|
const resolved = typeof web === "object" ? { ssr: true, csr: web.csr } : { ssr: web ?? true, csr: web ?? true };
|
|
@@ -338,7 +366,7 @@ export class AkanAppConfig implements AppConfigResult {
|
|
|
338
366
|
this.branches.forEach((domain) => void domains.add(`${basePath}-${domain}.${serveDomain}`));
|
|
339
367
|
});
|
|
340
368
|
}
|
|
341
|
-
#getDockerRunScripts(runs:
|
|
369
|
+
#getDockerRunScripts(runs: DockerRun[]) {
|
|
342
370
|
return runs.map((run) => {
|
|
343
371
|
if (typeof run === "string") return `RUN ${run}`;
|
|
344
372
|
else
|
|
@@ -355,29 +383,29 @@ export class AkanAppConfig implements AppConfigResult {
|
|
|
355
383
|
if (typeof image === "string") return `FROM ${image}`;
|
|
356
384
|
else return archs.map((arch) => `FROM ${image[arch] ?? defaultImage} AS ${arch}`).join("\n");
|
|
357
385
|
}
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
const
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
const
|
|
386
|
+
/** A declared Dockerfile string is verbatim, so a lib's steps are dropped rather than silently unapplied. */
|
|
387
|
+
static #resolveDocker(docker: DockerOption | undefined, libDocker: LibDockerConfig): DockerConfig {
|
|
388
|
+
if (typeof docker === "string") return docker;
|
|
389
|
+
return {
|
|
390
|
+
image: docker?.image ?? DEFAULT_DOCKER_IMAGE,
|
|
391
|
+
preRuns: dedupeDockerRuns([...libDocker.preRuns, ...(docker?.preRuns ?? [])]),
|
|
392
|
+
postRuns: dedupeDockerRuns([...libDocker.postRuns, ...(docker?.postRuns ?? [])]),
|
|
393
|
+
command: docker?.command ?? ["bun", "main.js"],
|
|
394
|
+
};
|
|
395
|
+
}
|
|
396
|
+
#makeDockerfile(): string {
|
|
397
|
+
if (typeof this.docker === "string") return this.docker;
|
|
398
|
+
const { image, preRuns, postRuns, command } = this.docker;
|
|
399
|
+
const preRunScripts = this.#getDockerRunScripts(preRuns);
|
|
400
|
+
const postRunScripts = this.#getDockerRunScripts(postRuns);
|
|
401
|
+
const imageScript = this.#getDockerImageScript(image, DEFAULT_DOCKER_IMAGE);
|
|
374
402
|
// The image default matches what the build actually produced; a deployment narrows it further with its
|
|
375
403
|
// own env, and can never widen it past the artifacts that are in the image.
|
|
376
404
|
const webEnvLines = [
|
|
377
405
|
...(this.web.ssr ? [] : ["ENV AKAN_SSR=false"]),
|
|
378
406
|
...(this.web.csr ? [] : ["ENV AKAN_CSR=false"]),
|
|
379
407
|
].join("\n");
|
|
380
|
-
|
|
408
|
+
return `${imageScript}
|
|
381
409
|
RUN apt-get update && apt-get upgrade -y && apt-get install -y --no-install-recommends ca-certificates tzdata && rm -rf /var/lib/apt/lists/*
|
|
382
410
|
RUN ln -sf /usr/share/zoneinfo/Asia/Seoul /etc/localtime
|
|
383
411
|
ARG TARGETARCH
|
|
@@ -400,13 +428,6 @@ ENV AKAN_PUBLIC_LOCALES=${this.i18n.locales.join(",")}
|
|
|
400
428
|
ENV AKAN_PUBLIC_OPERATION_MODE=cloud
|
|
401
429
|
${webEnvLines}
|
|
402
430
|
CMD [${command.map((c) => `"${c}"`).join(",")}]`;
|
|
403
|
-
return {
|
|
404
|
-
content,
|
|
405
|
-
image: imageScript,
|
|
406
|
-
preRuns: docker.preRuns ?? [],
|
|
407
|
-
postRuns: docker.postRuns ?? [],
|
|
408
|
-
command,
|
|
409
|
-
};
|
|
410
431
|
}
|
|
411
432
|
static #importGeneration = 0;
|
|
412
433
|
/**
|
|
@@ -434,23 +455,33 @@ CMD [${command.map((c) => `"${c}"`).join(",")}]`;
|
|
|
434
455
|
]);
|
|
435
456
|
const resolved = typeof configImp === "function" ? configImp(app) : configImp;
|
|
436
457
|
const { plugins, ...config } = (resolved ?? {}) as AppConfigDeclaration & { plugins?: AkanPlugin[] };
|
|
437
|
-
const
|
|
438
|
-
return new AkanAppConfig(app, libs, rootPackageJson, config, baseDevEnv, plugins ?? [],
|
|
458
|
+
const libContributions = await AkanAppConfig.#collectLibContributions(app, libs, bustImportCache);
|
|
459
|
+
return new AkanAppConfig(app, libs, rootPackageJson, config, baseDevEnv, plugins ?? [], libContributions);
|
|
439
460
|
}
|
|
440
461
|
//* Every workspace lib is read, not just this app's lib deps: narrowing the set needs the dependency
|
|
441
462
|
//* scan, and the incremental page rebundle re-reads this config on every file change.
|
|
442
|
-
static async #
|
|
463
|
+
static async #collectLibContributions(
|
|
464
|
+
app: App,
|
|
465
|
+
libs: string[],
|
|
466
|
+
bustImportCache: boolean,
|
|
467
|
+
): Promise<LibContributions> {
|
|
443
468
|
const libConfigs = await Promise.all(
|
|
444
469
|
libs.map(async (libName) =>
|
|
445
470
|
LibExecutor.from(app, libName)
|
|
446
471
|
.getConfig({ refresh: bustImportCache })
|
|
447
472
|
.catch((error: unknown) => {
|
|
448
|
-
app.logger.warn(`Skipped libs/${libName}/akan.config.ts
|
|
473
|
+
app.logger.warn(`Skipped libs/${libName}/akan.config.ts contributions: ${String(error)}`);
|
|
449
474
|
return null;
|
|
450
475
|
}),
|
|
451
476
|
),
|
|
452
477
|
);
|
|
453
|
-
return
|
|
478
|
+
return {
|
|
479
|
+
externalLibs: libConfigs.flatMap((libConfig) => libConfig?.externalLibs ?? []),
|
|
480
|
+
docker: {
|
|
481
|
+
preRuns: libConfigs.flatMap((libConfig) => libConfig?.docker.preRuns ?? []),
|
|
482
|
+
postRuns: libConfigs.flatMap((libConfig) => libConfig?.docker.postRuns ?? []),
|
|
483
|
+
},
|
|
484
|
+
};
|
|
454
485
|
}
|
|
455
486
|
#resolveProductionDependencyVersion(lib: string) {
|
|
456
487
|
const rootVersion = this.rootPackageJson.dependencies?.[lib] ?? this.rootPackageJson.devDependencies?.[lib];
|
|
@@ -560,11 +591,13 @@ function mergeImageConfig(config: Partial<AkanImageConfig> = {}): AkanImageConfi
|
|
|
560
591
|
export class AkanLibConfig implements LibConfigResult {
|
|
561
592
|
lib: Lib;
|
|
562
593
|
externalLibs: string[];
|
|
594
|
+
docker: LibDockerConfig;
|
|
563
595
|
/** Live-only: plugins declared in this lib's `akan.config.ts` (never serialized). */
|
|
564
596
|
plugins: AkanPlugin[];
|
|
565
597
|
constructor(lib: Lib, config: DeepPartial<LibConfigResult>, plugins: AkanPlugin[] = []) {
|
|
566
598
|
this.lib = lib;
|
|
567
599
|
this.externalLibs = config?.externalLibs ?? [];
|
|
600
|
+
this.docker = { preRuns: config?.docker?.preRuns ?? [], postRuns: config?.docker?.postRuns ?? [] };
|
|
568
601
|
this.plugins = plugins;
|
|
569
602
|
}
|
|
570
603
|
static async from(lib: Lib, { bustImportCache = false }: { bustImportCache?: boolean } = {}) {
|
package/akanConfig/types.ts
CHANGED
|
@@ -19,10 +19,14 @@ export type {
|
|
|
19
19
|
DatabaseMode,
|
|
20
20
|
DeepPartial,
|
|
21
21
|
DockerConfig,
|
|
22
|
+
DockerImageConfig,
|
|
23
|
+
DockerOption,
|
|
24
|
+
DockerRun,
|
|
22
25
|
FileConventionScanResult,
|
|
23
26
|
LibConfig,
|
|
24
27
|
LibConfigInput,
|
|
25
28
|
LibConfigResult,
|
|
29
|
+
LibDockerConfig,
|
|
26
30
|
LibScanResult,
|
|
27
31
|
MobileEnv,
|
|
28
32
|
MobilePermission,
|
|
@@ -168,7 +168,7 @@ export class ApplicationBuildRunner {
|
|
|
168
168
|
const akanConfig = await this.#app.getConfig();
|
|
169
169
|
await Promise.all([
|
|
170
170
|
this.#app.dist.writeJson("package.json", akanConfig.getProductionPackageJson()),
|
|
171
|
-
this.#app.dist.writeFile(`${this.#app.dist.cwdPath}/Dockerfile`, akanConfig.
|
|
171
|
+
this.#app.dist.writeFile(`${this.#app.dist.cwdPath}/Dockerfile`, akanConfig.dockerfile),
|
|
172
172
|
]);
|
|
173
173
|
}
|
|
174
174
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@akanjs/devkit",
|
|
3
|
-
"version": "3.0.0-alpha.
|
|
3
|
+
"version": "3.0.0-alpha.66",
|
|
4
4
|
"sourceType": "module",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"publishConfig": {
|
|
@@ -45,7 +45,7 @@
|
|
|
45
45
|
"@langchain/openai": "^1.4.6",
|
|
46
46
|
"@tailwindcss/node": "^4.3.0",
|
|
47
47
|
"@trapezedev/project": "^7.1.4",
|
|
48
|
-
"akanjs": "3.0.0-alpha.
|
|
48
|
+
"akanjs": "3.0.0-alpha.66",
|
|
49
49
|
"chalk": "^5.6.2",
|
|
50
50
|
"commander": "^14.0.3",
|
|
51
51
|
"dayjs": "^1.11.20",
|
package/scanInfo.ts
CHANGED
|
@@ -9,6 +9,7 @@ import type {
|
|
|
9
9
|
PkgScanResult,
|
|
10
10
|
ScanResult,
|
|
11
11
|
} from "./akanConfig";
|
|
12
|
+
import { AkanAppConfig } from "./akanConfig";
|
|
12
13
|
import { AppExecutor, LibExecutor, PkgExecutor, WorkspaceExecutor } from "./executors";
|
|
13
14
|
import { isAllowedLibFacetRootFile, rootAllowedDirs, rootAllowedFiles } from "./workspaceLayout";
|
|
14
15
|
|
|
@@ -285,12 +286,11 @@ class ScanInfo {
|
|
|
285
286
|
}),
|
|
286
287
|
]);
|
|
287
288
|
const routes = exec.type === "lib" ? [] : await (exec as AppExecutor).getPageKeys();
|
|
288
|
-
const
|
|
289
|
+
const common = {
|
|
289
290
|
name: exec.name,
|
|
290
291
|
type: exec.type,
|
|
291
292
|
repoName: exec.workspace.repoName,
|
|
292
293
|
serveDomain: WorkspaceExecutor.getBaseDevEnv(path.join(exec.workspace.workspaceRoot, ".env")).serveDomain,
|
|
293
|
-
akanConfig,
|
|
294
294
|
files,
|
|
295
295
|
libDeps,
|
|
296
296
|
pkgDeps,
|
|
@@ -298,6 +298,11 @@ class ScanInfo {
|
|
|
298
298
|
devDependencies: npmDevDeps.filter((dep) => !isAkanFrameworkDependency(dep)),
|
|
299
299
|
routes,
|
|
300
300
|
};
|
|
301
|
+
//? `exec.type` and the resolved config class are correlated, which the union alone cannot express.
|
|
302
|
+
const scanResult: AppScanResult | LibScanResult =
|
|
303
|
+
akanConfig instanceof AkanAppConfig
|
|
304
|
+
? ({ ...common, akanConfig } satisfies AppScanResult)
|
|
305
|
+
: ({ ...common, akanConfig } satisfies LibScanResult);
|
|
301
306
|
return scanResult;
|
|
302
307
|
}
|
|
303
308
|
|
|
@@ -462,7 +467,7 @@ export class LibInfo extends ScanInfo {
|
|
|
462
467
|
LibInfo.libInfos.set(libName, await LibInfo.fromExecutor(libExecutor));
|
|
463
468
|
}),
|
|
464
469
|
);
|
|
465
|
-
const libInfo = new LibInfo(exec, scanResult);
|
|
470
|
+
const libInfo = new LibInfo(exec, scanResult as LibScanResult);
|
|
466
471
|
LibInfo.libInfos.set(exec.name, libInfo);
|
|
467
472
|
LibInfo.#sortedLibIndices = null;
|
|
468
473
|
return libInfo;
|