@akanjs/devkit 2.3.2 → 2.3.3-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/CHANGELOG.md CHANGED
@@ -1,5 +1,23 @@
1
1
  # @akanjs/devkit
2
2
 
3
+ ## 2.3.2
4
+
5
+ ### Patch Changes
6
+
7
+ - d6db24d: Fix dev runtime refresh for client components, dictionaries, and signal metadata while keeping regenerated server page bundles aligned with live app signal definitions.
8
+ - 1a48756: Add RSC partial navigation patch handling and supporting SSR build updates, plus benchmark harness improvements for validating production behavior.
9
+ - Updated dependencies [940d6db]
10
+ - Updated dependencies [d6db24d]
11
+ - Updated dependencies [dc60773]
12
+ - Updated dependencies [ffe68ec]
13
+ - Updated dependencies [1a48756]
14
+ - Updated dependencies [1a48756]
15
+ - Updated dependencies [1a48756]
16
+ - Updated dependencies [1a48756]
17
+ - Updated dependencies [1a48756]
18
+ - Updated dependencies [4fc2673]
19
+ - akanjs@2.4.0
20
+
3
21
  ## 2.2.12
4
22
 
5
23
  ### Patch Changes
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@akanjs/devkit",
3
- "version": "2.3.2",
3
+ "version": "2.3.3-rc.1",
4
4
  "sourceType": "module",
5
5
  "type": "module",
6
6
  "publishConfig": {
@@ -32,7 +32,7 @@
32
32
  "@langchain/openai": "^1.4.6",
33
33
  "@tailwindcss/node": "^4.3.0",
34
34
  "@trapezedev/project": "^7.1.4",
35
- "akanjs": "2.3.2",
35
+ "akanjs": "2.3.3-rc.1",
36
36
  "chalk": "^5.6.2",
37
37
  "commander": "^14.0.3",
38
38
  "daisyui": "^5.5.20",
@@ -0,0 +1,182 @@
1
+ import { afterEach, describe, expect, test } from "bun:test";
2
+ import { mkdir, mkdtemp, rm, writeFile } from "node:fs/promises";
3
+ import os from "node:os";
4
+ import path from "node:path";
5
+ import { createExternalizeFrameworkPlugin } from "./externalizeFrameworkPlugin";
6
+
7
+ const tempRoots: string[] = [];
8
+
9
+ const makeTempRoot = async () => {
10
+ const root = await mkdtemp(path.join(os.tmpdir(), "akan-devkit-externalize-"));
11
+ tempRoots.push(root);
12
+ return root;
13
+ };
14
+
15
+ const write = async (filePath: string, content: string) => {
16
+ await mkdir(path.dirname(filePath), { recursive: true });
17
+ await writeFile(filePath, content);
18
+ };
19
+
20
+ afterEach(async () => {
21
+ await Promise.all(tempRoots.splice(0).map((root) => rm(root, { recursive: true, force: true })));
22
+ });
23
+
24
+ describe("createExternalizeFrameworkPlugin", () => {
25
+ test("bundles akanjs/server workspace sources into production pages artifacts", async () => {
26
+ const root = await makeTempRoot();
27
+ const entry = path.join(root, "entry.ts");
28
+ const outdir = path.join(root, "out");
29
+ await write(
30
+ entry,
31
+ [
32
+ 'import { optionMarker } from "akanjs/server";',
33
+ 'import { tryMarker } from "akanjs/server/decorators";',
34
+ 'import { jsx } from "react/jsx-runtime";',
35
+ "export const markers = [optionMarker, tryMarker, jsx];",
36
+ "",
37
+ ].join("\n"),
38
+ );
39
+ await write(path.join(root, "pkgs/akanjs/server/index.ts"), 'export const optionMarker = "akan-option";\n');
40
+ await write(path.join(root, "pkgs/akanjs/server/decorators.ts"), 'export const tryMarker = "akan-try";\n');
41
+
42
+ const plugin = await createExternalizeFrameworkPlugin({
43
+ app: {
44
+ workspace: { workspaceRoot: root },
45
+ getTsConfig: async () => ({
46
+ compilerOptions: {
47
+ paths: {
48
+ "akanjs": ["./pkgs/akanjs/index.ts"],
49
+ "akanjs/*": ["./pkgs/akanjs/*"],
50
+ "akanjs/server": ["./pkgs/akanjs/server/index.ts"],
51
+ "akanjs/server/*": ["./pkgs/akanjs/server/*"],
52
+ },
53
+ },
54
+ }),
55
+ } as never,
56
+ });
57
+
58
+ const result = await Bun.build({
59
+ entrypoints: [entry],
60
+ outdir,
61
+ target: "bun",
62
+ format: "esm",
63
+ plugins: [plugin],
64
+ });
65
+
66
+ expect(result.success).toBe(true);
67
+ const output = await result.outputs.find((artifact) => artifact.kind === "entry-point")?.text();
68
+ expect(output).toContain("akan-option");
69
+ expect(output).toContain("akan-try");
70
+ expect(output).not.toMatch(/from\s+["']akanjs\/server(?:\/decorators)?["']/);
71
+ expect(output).toContain("react/jsx-runtime");
72
+ });
73
+
74
+ test("externalizes optional backend dependencies reached through bundled akanjs/server sources", async () => {
75
+ const root = await makeTempRoot();
76
+ const entry = path.join(root, "entry.ts");
77
+ const outdir = path.join(root, "out");
78
+ await write(
79
+ entry,
80
+ [
81
+ 'import { optionMarker, redisLoader } from "akanjs/server";',
82
+ "export const marker = optionMarker;",
83
+ "export const loader = redisLoader;",
84
+ "",
85
+ ].join("\n"),
86
+ );
87
+ await write(
88
+ path.join(root, "pkgs/akanjs/server/index.ts"),
89
+ [
90
+ 'import { loadRedis } from "akanjs/service";',
91
+ 'export const optionMarker = "akan-option";',
92
+ "export const redisLoader = loadRedis;",
93
+ "",
94
+ ].join("\n"),
95
+ );
96
+ await write(path.join(root, "pkgs/akanjs/service/index.ts"), 'export * from "./predefinedAdaptor";\n');
97
+ await write(
98
+ path.join(root, "pkgs/akanjs/service/predefinedAdaptor/index.ts"),
99
+ 'export const loadRedis = async () => import("ioredis");\n',
100
+ );
101
+
102
+ const plugin = await createExternalizeFrameworkPlugin({
103
+ app: {
104
+ workspace: { workspaceRoot: root },
105
+ getTsConfig: async () => ({
106
+ compilerOptions: {
107
+ paths: {
108
+ "akanjs": ["./pkgs/akanjs/index.ts"],
109
+ "akanjs/*": ["./pkgs/akanjs/*"],
110
+ "akanjs/server": ["./pkgs/akanjs/server/index.ts"],
111
+ "akanjs/server/*": ["./pkgs/akanjs/server/*"],
112
+ },
113
+ },
114
+ }),
115
+ } as never,
116
+ });
117
+
118
+ const result = await Bun.build({
119
+ entrypoints: [entry],
120
+ outdir,
121
+ target: "bun",
122
+ format: "esm",
123
+ plugins: [plugin],
124
+ });
125
+
126
+ expect(result.success).toBe(true);
127
+ const output = await result.outputs.find((artifact) => artifact.kind === "entry-point")?.text();
128
+ expect(output).toContain("akan-option");
129
+ expect(output).toContain("ioredis");
130
+ expect(output).not.toMatch(/from\s+["']akanjs\/(?:server|service)["']/);
131
+ });
132
+
133
+ test("lets published akanjs package exports resolve when workspace paths are absent", async () => {
134
+ const root = await makeTempRoot();
135
+ const entry = path.join(root, "entry.ts");
136
+ const outdir = path.join(root, "out");
137
+ await write(
138
+ entry,
139
+ [
140
+ 'import { optionMarker } from "akanjs/server";',
141
+ 'import { jsx } from "react/jsx-runtime";',
142
+ "export const markers = [optionMarker, jsx];",
143
+ "",
144
+ ].join("\n"),
145
+ );
146
+ await write(
147
+ path.join(root, "node_modules/akanjs/package.json"),
148
+ JSON.stringify({
149
+ name: "akanjs",
150
+ type: "module",
151
+ exports: {
152
+ "./server": {
153
+ import: "./server/index.ts",
154
+ default: "./server/index.ts",
155
+ },
156
+ },
157
+ }),
158
+ );
159
+ await write(path.join(root, "node_modules/akanjs/server/index.ts"), 'export const optionMarker = "published-option";\n');
160
+
161
+ const plugin = await createExternalizeFrameworkPlugin({
162
+ app: {
163
+ workspace: { workspaceRoot: root },
164
+ getTsConfig: async () => ({ compilerOptions: { paths: {} } }),
165
+ } as never,
166
+ });
167
+
168
+ const result = await Bun.build({
169
+ entrypoints: [entry],
170
+ outdir,
171
+ target: "bun",
172
+ format: "esm",
173
+ plugins: [plugin],
174
+ });
175
+
176
+ expect(result.success).toBe(true);
177
+ const output = await result.outputs.find((artifact) => artifact.kind === "entry-point")?.text();
178
+ expect(output).toContain("published-option");
179
+ expect(output).not.toMatch(/from\s+["']akanjs\/server["']/);
180
+ expect(output).toContain("react/jsx-runtime");
181
+ });
182
+ });
@@ -28,7 +28,7 @@ import type { App } from "../commandDecorators";
28
28
  * inlined so transitive `"use client"` stubs work.
29
29
  * 2. Specifiers listed in `include` (workspace aliases like
30
30
  * `@apps/*`, `@libs/*`) are NEVER externalized.
31
- * 3. React / RSC runtime packages and framework host packages are
31
+ * 3. React / RSC runtime packages and build-time tooling packages are
32
32
  * externalized so the runtime supplies a single shared instance.
33
33
  * 4. Other bare npm packages are bundled so the Docker runtime does not
34
34
  * need to install every transitive page dependency separately.
@@ -60,8 +60,16 @@ const DEFAULT_INCLUDE = ["akanjs/", "@apps/", "@libs/"];
60
60
  // otherwise bundle them. These are runtime hosts or shared singleton
61
61
  // framework packages; ordinary npm dependencies intentionally fall through
62
62
  // to Bun's resolver and get bundled.
63
- const DEFAULT_EXCLUDE_EXACT = new Set<string>(["akanjs/webkit", "akanjs/server", "@akanjs/cli", "@akanjs/devkit"]);
64
- const DEFAULT_EXCLUDE_PREFIX = ["akanjs/server/", "@akanjs/cli/", "@akanjs/devkit/"];
63
+ const DEFAULT_EXCLUDE_EXACT = new Set<string>(["akanjs/webkit", "@akanjs/cli", "@akanjs/devkit"]);
64
+ const DEFAULT_EXCLUDE_PREFIX = ["@akanjs/cli/", "@akanjs/devkit/"];
65
+ const OPTIONAL_BACKEND_EXTERNAL_EXACT = new Set<string>([
66
+ "@libsql/client",
67
+ "bullmq",
68
+ "croner",
69
+ "ioredis",
70
+ "postgres",
71
+ "protobufjs",
72
+ ]);
65
73
  const RUNTIME_EXTERNAL_EXACT = new Set<string>([
66
74
  "react",
67
75
  "react-dom",
@@ -141,6 +149,7 @@ export async function createExternalizeFrameworkPlugin(options: ExternalizeFrame
141
149
  for (const prefix of DEFAULT_EXCLUDE_PREFIX) {
142
150
  if (spec.startsWith(prefix)) return { path: spec, external: true };
143
151
  }
152
+ if (OPTIONAL_BACKEND_EXTERNAL_EXACT.has(spec)) return { path: spec, external: true };
144
153
  if (RUNTIME_EXTERNAL_EXACT.has(spec)) return { path: spec, external: true };
145
154
  for (const prefix of RUNTIME_EXTERNAL_PREFIX) {
146
155
  if (spec.startsWith(prefix)) return { path: spec, external: true };