@akanjs/devkit 2.1.0-rc.9 → 2.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/akanConfig/akanConfig.test.ts +36 -0
- package/akanConfig/akanConfig.ts +22 -2
- package/applicationBuildRunner.test.ts +10 -0
- package/applicationBuildRunner.ts +15 -3
- package/applicationTestPreload.ts +40 -0
- package/devkitUtils.test.ts +21 -0
- package/index.ts +1 -0
- package/package.json +2 -2
|
@@ -132,6 +132,7 @@ describe("AkanAppConfig", () => {
|
|
|
132
132
|
react: "19.0.0",
|
|
133
133
|
"react-dom": "19.0.0",
|
|
134
134
|
"react-server-dom-webpack": "19.0.0",
|
|
135
|
+
croner: akanPackageJson.peerDependencies?.croner,
|
|
135
136
|
sharp: "1.0.0",
|
|
136
137
|
"@external/runtime": "2.0.0",
|
|
137
138
|
},
|
|
@@ -168,10 +169,45 @@ describe("AkanAppConfig", () => {
|
|
|
168
169
|
react: runtimeDependencies.react,
|
|
169
170
|
"react-dom": runtimeDependencies["react-dom"],
|
|
170
171
|
"react-server-dom-webpack": runtimeDependencies["react-server-dom-webpack"],
|
|
172
|
+
croner: runtimeDependencies.croner,
|
|
171
173
|
sharp: runtimeDependencies.sharp,
|
|
172
174
|
});
|
|
173
175
|
});
|
|
174
176
|
|
|
177
|
+
test("adds backend runtime packages by database mode", () => {
|
|
178
|
+
const runtimeDependencies = { ...akanPackageJson.dependencies, ...akanPackageJson.peerDependencies };
|
|
179
|
+
const singleConfig = new AkanAppConfig(app, [], packageJson, { defaultDatabaseMode: "single" }, baseDevEnv);
|
|
180
|
+
const multipleConfig = new AkanAppConfig(app, [], packageJson, { defaultDatabaseMode: "multiple" }, baseDevEnv);
|
|
181
|
+
const clusterConfig = new AkanAppConfig(app, [], packageJson, { defaultDatabaseMode: "cluster" }, baseDevEnv);
|
|
182
|
+
|
|
183
|
+
expect(singleConfig.getProductionPackageJson().dependencies).toMatchObject({
|
|
184
|
+
croner: runtimeDependencies.croner,
|
|
185
|
+
});
|
|
186
|
+
expect(singleConfig.getProductionPackageJson().dependencies).not.toHaveProperty("ioredis");
|
|
187
|
+
expect(singleConfig.getProductionPackageJson().dependencies).not.toHaveProperty("bullmq");
|
|
188
|
+
expect(singleConfig.getProductionPackageJson().dependencies).not.toHaveProperty("@libsql/client");
|
|
189
|
+
expect(singleConfig.getProductionPackageJson().dependencies).not.toHaveProperty("postgres");
|
|
190
|
+
expect(singleConfig.getProductionPackageJson().dependencies).not.toHaveProperty("protobufjs");
|
|
191
|
+
|
|
192
|
+
expect(multipleConfig.getProductionPackageJson().dependencies).toMatchObject({
|
|
193
|
+
"@libsql/client": runtimeDependencies["@libsql/client"],
|
|
194
|
+
bullmq: runtimeDependencies.bullmq,
|
|
195
|
+
croner: runtimeDependencies.croner,
|
|
196
|
+
ioredis: runtimeDependencies.ioredis,
|
|
197
|
+
protobufjs: runtimeDependencies.protobufjs,
|
|
198
|
+
});
|
|
199
|
+
expect(multipleConfig.getProductionPackageJson().dependencies).not.toHaveProperty("postgres");
|
|
200
|
+
|
|
201
|
+
expect(clusterConfig.getProductionPackageJson().dependencies).toMatchObject({
|
|
202
|
+
bullmq: runtimeDependencies.bullmq,
|
|
203
|
+
croner: runtimeDependencies.croner,
|
|
204
|
+
ioredis: runtimeDependencies.ioredis,
|
|
205
|
+
postgres: runtimeDependencies.postgres,
|
|
206
|
+
protobufjs: runtimeDependencies.protobufjs,
|
|
207
|
+
});
|
|
208
|
+
expect(clusterConfig.getProductionPackageJson().dependencies).not.toHaveProperty("@libsql/client");
|
|
209
|
+
});
|
|
210
|
+
|
|
175
211
|
test("normalizes multiple mobile targets and validates base paths", () => {
|
|
176
212
|
const config = new AkanAppConfig(
|
|
177
213
|
app,
|
package/akanConfig/akanConfig.ts
CHANGED
|
@@ -50,7 +50,18 @@ const DEFAULT_OPTIMIZE_IMPORTS = [
|
|
|
50
50
|
const WORKSPACE_BARREL_FACETS = ["ui", "webkit", "common", "client", "server"] as const;
|
|
51
51
|
const SSR_RUNTIME_PACKAGES = ["react", "react-dom", "react-server-dom-webpack"] as const;
|
|
52
52
|
const NATIVE_RUNTIME_PACKAGES = ["sharp"] as const;
|
|
53
|
-
const
|
|
53
|
+
const DEFAULT_BACKEND_RUNTIME_PACKAGES = ["croner"] as const;
|
|
54
|
+
const DATABASE_MODE_RUNTIME_PACKAGES = {
|
|
55
|
+
single: [],
|
|
56
|
+
multiple: ["@libsql/client", "bullmq", "ioredis", "protobufjs"],
|
|
57
|
+
cluster: ["bullmq", "ioredis", "postgres", "protobufjs"],
|
|
58
|
+
} satisfies Record<DatabaseMode, readonly string[]>;
|
|
59
|
+
const AKAN_RUNTIME_PACKAGES = new Set<string>([
|
|
60
|
+
...SSR_RUNTIME_PACKAGES,
|
|
61
|
+
...NATIVE_RUNTIME_PACKAGES,
|
|
62
|
+
...DEFAULT_BACKEND_RUNTIME_PACKAGES,
|
|
63
|
+
...Object.values(DATABASE_MODE_RUNTIME_PACKAGES).flat(),
|
|
64
|
+
]);
|
|
54
65
|
const DEFAULT_AKAN_IMAGE_CONFIG: AkanImageConfig = {
|
|
55
66
|
deviceSizes: [640, 750, 828, 1080, 1200, 1920, 2048, 3840],
|
|
56
67
|
imageSizes: [32, 48, 64, 96, 128, 256, 384],
|
|
@@ -272,6 +283,15 @@ CMD [${command.map((c) => `"${c}"`).join(",")}]`;
|
|
|
272
283
|
if (AKAN_RUNTIME_PACKAGES.has(lib))
|
|
273
284
|
return akanPackageJson.dependencies?.[lib] ?? akanPackageJson.peerDependencies?.[lib];
|
|
274
285
|
}
|
|
286
|
+
#getProductionRuntimePackages() {
|
|
287
|
+
return [
|
|
288
|
+
...this.externalLibs,
|
|
289
|
+
...SSR_RUNTIME_PACKAGES,
|
|
290
|
+
...NATIVE_RUNTIME_PACKAGES,
|
|
291
|
+
...DEFAULT_BACKEND_RUNTIME_PACKAGES,
|
|
292
|
+
...DATABASE_MODE_RUNTIME_PACKAGES[this.defaultDatabaseMode],
|
|
293
|
+
];
|
|
294
|
+
}
|
|
275
295
|
getProductionPackageJson(data: Partial<PackageJson> = {}): PackageJson {
|
|
276
296
|
return {
|
|
277
297
|
name: this.app.name,
|
|
@@ -279,7 +299,7 @@ CMD [${command.map((c) => `"${c}"`).join(",")}]`;
|
|
|
279
299
|
version: "1.0.0",
|
|
280
300
|
main: "./main.js",
|
|
281
301
|
dependencies: Object.fromEntries(
|
|
282
|
-
[...new Set(
|
|
302
|
+
[...new Set(this.#getProductionRuntimePackages())].map((lib) => {
|
|
283
303
|
const version = this.#resolveProductionDependencyVersion(lib);
|
|
284
304
|
if (!version) throw new Error(`Dependency ${lib} not found in package.json`);
|
|
285
305
|
return [lib, version];
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { describe, expect, test } from "bun:test";
|
|
2
|
+
import { AKAN_OPTIONAL_BACKEND_EXTERNALS } from "./applicationBuildRunner";
|
|
3
|
+
|
|
4
|
+
describe("ApplicationBuildRunner", () => {
|
|
5
|
+
test("externalizes Akan optional backend dependencies", () => {
|
|
6
|
+
expect(AKAN_OPTIONAL_BACKEND_EXTERNALS).toEqual(
|
|
7
|
+
expect.arrayContaining(["@libsql/client", "bullmq", "croner", "ioredis", "postgres", "protobufjs"]),
|
|
8
|
+
);
|
|
9
|
+
});
|
|
10
|
+
});
|
|
@@ -47,10 +47,21 @@ const SSR_RENDER_EXTERNALS = [
|
|
|
47
47
|
"react/jsx-dev-runtime",
|
|
48
48
|
"react-dom",
|
|
49
49
|
"react-dom/server.browser",
|
|
50
|
+
"react-server-dom-webpack",
|
|
51
|
+
"react-server-dom-webpack/server.node",
|
|
50
52
|
"react-server-dom-webpack/client.node",
|
|
51
53
|
"react-server-dom-webpack/client.browser",
|
|
52
54
|
] as const;
|
|
53
55
|
|
|
56
|
+
export const AKAN_OPTIONAL_BACKEND_EXTERNALS = [
|
|
57
|
+
"@libsql/client",
|
|
58
|
+
"bullmq",
|
|
59
|
+
"croner",
|
|
60
|
+
"ioredis",
|
|
61
|
+
"postgres",
|
|
62
|
+
"protobufjs",
|
|
63
|
+
] as const;
|
|
64
|
+
|
|
54
65
|
export class ApplicationBuildRunner {
|
|
55
66
|
#app: App;
|
|
56
67
|
#fast: boolean;
|
|
@@ -161,7 +172,9 @@ export class ApplicationBuildRunner {
|
|
|
161
172
|
|
|
162
173
|
async #buildBackend() {
|
|
163
174
|
const akanConfig = await this.#app.getConfig();
|
|
164
|
-
const backendExternals = [
|
|
175
|
+
const backendExternals = [
|
|
176
|
+
...new Set([...akanConfig.externalLibs, ...SSR_RENDER_EXTERNALS, ...AKAN_OPTIONAL_BACKEND_EXTERNALS]),
|
|
177
|
+
];
|
|
165
178
|
const backendEntryPoints = [`${this.#app.cwdPath}/main.ts`, `${this.#app.cwdPath}/server.ts`];
|
|
166
179
|
for (const entrypoint of backendEntryPoints) {
|
|
167
180
|
if (!(await Bun.file(entrypoint).exists())) throw new Error(`Backend entrypoint not found: ${entrypoint}`);
|
|
@@ -183,8 +196,7 @@ export class ApplicationBuildRunner {
|
|
|
183
196
|
conditions: ["react-server"],
|
|
184
197
|
// `akan build` must embed production react-server-dom regardless of the shell's NODE_ENV.
|
|
185
198
|
define: { "process.env.NODE_ENV": JSON.stringify("production") },
|
|
186
|
-
plugins:
|
|
187
|
-
akanConfig.externalLibs.length > 0 ? [this.#createExternalSpecifiersPlugin(akanConfig.externalLibs)] : [],
|
|
199
|
+
plugins: backendExternals.length > 0 ? [this.#createExternalSpecifiersPlugin(backendExternals)] : [],
|
|
188
200
|
});
|
|
189
201
|
return {
|
|
190
202
|
entrypoints: backendEntryPoints.length + 1,
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import path from "node:path";
|
|
2
|
+
|
|
3
|
+
export const SIGNAL_TEST_PRELOAD_PATH = "test/signalTest.preload.ts";
|
|
4
|
+
|
|
5
|
+
export interface SignalTestPreloadTarget {
|
|
6
|
+
cwdPath: string;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export async function resolveSignalTestPreloadPath(target: SignalTestPreloadTarget) {
|
|
10
|
+
const candidates: string[] = [];
|
|
11
|
+
const addResolvedPackageCandidate = (basePath: string) => {
|
|
12
|
+
try {
|
|
13
|
+
candidates.push(
|
|
14
|
+
path.join(path.dirname(Bun.resolveSync("akanjs/package.json", basePath)), SIGNAL_TEST_PRELOAD_PATH),
|
|
15
|
+
);
|
|
16
|
+
} catch {
|
|
17
|
+
// Source workspaces and published installs can resolve Akan packages from different roots.
|
|
18
|
+
}
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
addResolvedPackageCandidate(target.cwdPath);
|
|
22
|
+
addResolvedPackageCandidate(process.cwd());
|
|
23
|
+
addResolvedPackageCandidate(path.dirname(Bun.main));
|
|
24
|
+
addResolvedPackageCandidate(import.meta.dir);
|
|
25
|
+
|
|
26
|
+
candidates.push(
|
|
27
|
+
path.join(target.cwdPath, "../../node_modules/akanjs", SIGNAL_TEST_PRELOAD_PATH),
|
|
28
|
+
path.join(target.cwdPath, "../../pkgs/akanjs", SIGNAL_TEST_PRELOAD_PATH),
|
|
29
|
+
path.join(process.cwd(), "node_modules/akanjs", SIGNAL_TEST_PRELOAD_PATH),
|
|
30
|
+
path.join(process.cwd(), "pkgs/akanjs", SIGNAL_TEST_PRELOAD_PATH),
|
|
31
|
+
path.join(path.dirname(Bun.main), "../../akanjs", SIGNAL_TEST_PRELOAD_PATH),
|
|
32
|
+
path.resolve(import.meta.dir, "../../akanjs", SIGNAL_TEST_PRELOAD_PATH),
|
|
33
|
+
);
|
|
34
|
+
|
|
35
|
+
for (const candidate of [...new Set(candidates)]) {
|
|
36
|
+
if (await Bun.file(candidate).exists()) return candidate;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
throw new Error(`Failed to locate ${SIGNAL_TEST_PRELOAD_PATH} from ${target.cwdPath}`);
|
|
40
|
+
}
|
package/devkitUtils.test.ts
CHANGED
|
@@ -3,6 +3,7 @@ import { mkdir, mkdtemp, rm, writeFile } from "node:fs/promises";
|
|
|
3
3
|
import os from "node:os";
|
|
4
4
|
import path from "node:path";
|
|
5
5
|
import { ApplicationBuildReporter } from "./applicationBuildReporter";
|
|
6
|
+
import { resolveSignalTestPreloadPath } from "./applicationTestPreload";
|
|
6
7
|
import { TypeScriptDependencyScanner } from "./dependencyScanner";
|
|
7
8
|
import { extractDependencies } from "./extractDeps";
|
|
8
9
|
import { getModelFileData } from "./getModelFileData";
|
|
@@ -84,6 +85,26 @@ describe("extractDependencies", () => {
|
|
|
84
85
|
});
|
|
85
86
|
});
|
|
86
87
|
|
|
88
|
+
describe("resolveSignalTestPreloadPath", () => {
|
|
89
|
+
test("resolves the preload file from an installed akanjs package", async () => {
|
|
90
|
+
const root = await makeTempRoot();
|
|
91
|
+
const libDir = path.join(root, "libs/shared");
|
|
92
|
+
await write(
|
|
93
|
+
path.join(root, "node_modules/akanjs/package.json"),
|
|
94
|
+
JSON.stringify({
|
|
95
|
+
name: "akanjs",
|
|
96
|
+
version: "0.0.0",
|
|
97
|
+
exports: { "./package.json": "./package.json" },
|
|
98
|
+
}),
|
|
99
|
+
);
|
|
100
|
+
await write(path.join(root, "node_modules/akanjs/test/signalTest.preload.ts"), "export {};\n");
|
|
101
|
+
|
|
102
|
+
await expect(resolveSignalTestPreloadPath({ cwdPath: libDir })).resolves.toContain(
|
|
103
|
+
"node_modules/akanjs/test/signalTest.preload.ts",
|
|
104
|
+
);
|
|
105
|
+
});
|
|
106
|
+
});
|
|
107
|
+
|
|
87
108
|
describe("TypeScriptDependencyScanner", () => {
|
|
88
109
|
test("separates monorepo package, lib, runtime, and type-only dependencies", async () => {
|
|
89
110
|
const root = await makeTempRoot();
|
package/index.ts
CHANGED
|
@@ -4,6 +4,7 @@ export * from "./akanConfig";
|
|
|
4
4
|
export * from "./applicationBuildReporter";
|
|
5
5
|
export * from "./applicationBuildRunner";
|
|
6
6
|
export * from "./applicationReleasePackager";
|
|
7
|
+
export * from "./applicationTestPreload";
|
|
7
8
|
export * from "./artifact";
|
|
8
9
|
export * from "./auth";
|
|
9
10
|
export * from "./builder";
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@akanjs/devkit",
|
|
3
|
-
"version": "2.1.0
|
|
3
|
+
"version": "2.1.0",
|
|
4
4
|
"sourceType": "module",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"publishConfig": {
|
|
@@ -31,7 +31,7 @@
|
|
|
31
31
|
"@langchain/deepseek": "^1.0.26",
|
|
32
32
|
"@langchain/openai": "^1.4.6",
|
|
33
33
|
"@trapezedev/project": "^7.1.4",
|
|
34
|
-
"akanjs": "2.1.0
|
|
34
|
+
"akanjs": "2.1.0",
|
|
35
35
|
"chalk": "^5.6.2",
|
|
36
36
|
"commander": "^14.0.3",
|
|
37
37
|
"daisyui": "^5.5.20",
|