@akanjs/cli 3.0.0-beta.24 → 3.0.0-beta.26
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/.build-stamp +1 -1
- package/{CodeAgent-0dh9fzzg.js → CodeAgent-p65jja35.js} +2 -2
- package/{CodeTui-drwdvwg9.js → CodeTui-5xr8kxx4.js} +1 -1
- package/TunnelShare-cwh9ezsm.js +9 -0
- package/{agent.command-ttn09rx3.js → agent.command-rp505zxa.js} +7 -7
- package/akanCode.js +2 -2
- package/{application.command-z7a4bdrq.js → application.command-afajz1m5.js} +7 -7
- package/{applicationBuildRunner-n6xfexaa.js → applicationBuildRunner-412y0345.js} +4 -4
- package/buildBatch.proc.js +77 -23
- package/{cloud.command-j0fcz44z.js → cloud.command-yz0h8a2e.js} +12 -12
- package/cloud.script-zez2b8am.js +20 -0
- package/{code.command-1jgk14tc.js → code.command-jhrjttm1.js} +7 -7
- package/{context.command-4vpjmg22.js → context.command-xncdvv7n.js} +14 -14
- package/context.runner-pjy89k2s.js +21 -0
- package/{dependencyScanner-ye2e6pdz.js → dependencyScanner-06z2dmrr.js} +2 -2
- package/{fileSys-5ngz93ga.js → fileSys-wzzrntkw.js} +1 -1
- package/{guideline.command-7jk47fgq.js → guideline.command-d5qmxx29.js} +4 -4
- package/guidelines/conventions/conventions.instruction.md +7 -5
- package/incrementalBuilder.proc.js +544 -72
- package/{index-ga6hygxr.js → index-0n9q1f23.js} +3 -3
- package/{index-3t7g9e4r.js → index-2ahj896a.js} +1 -1
- package/{index-zabxte74.js → index-2wc41k5w.js} +1 -1
- package/{index-khpfpcsd.js → index-48k96p55.js} +27 -7
- package/{index-2gh9sndr.js → index-50rq2qms.js} +4 -4
- package/{index-5b6tavv3.js → index-52w0fvh8.js} +15 -1
- package/{index-zbc2q6a6.js → index-56xwsgyn.js} +3 -3
- package/{index-rmcw8yc8.js → index-58cyna2s.js} +2 -2
- package/{index-1db2cw0y.js → index-5mm3g24t.js} +2 -2
- package/{index-wp43xrjv.js → index-6dt6zft6.js} +4 -4
- package/{index-vj1cwwy5.js → index-7yxndzea.js} +7 -7
- package/{index-q44jbw48.js → index-860zjnr1.js} +1 -1
- package/{index-fzd22vjh.js → index-94s080qm.js} +3 -17
- package/index-cd9qt2t6.js +4110 -0
- package/{index-ymgtbmf1.js → index-cfj73ck1.js} +213 -62
- package/{index-yy4s1j72.js → index-d65fzxvd.js} +1 -1
- package/{index-m0ms3n1c.js → index-da72pfdv.js} +7 -7
- package/{index-4dn1p115.js → index-ggfhz9ab.js} +4 -4
- package/{index-9629w7ns.js → index-hzy4pe24.js} +3 -3
- package/{index-d4mfjgm0.js → index-mq74nzf8.js} +6 -6
- package/{index-mgfx3mfs.js → index-per2kqy9.js} +4 -4
- package/{index-aaz7n8s2.js → index-qm4fppfk.js} +4 -4
- package/{index-ya5qbmyg.js → index-t02g6dpn.js} +3 -3
- package/{index-hb8sgepf.js → index-tpdn30ya.js} +1 -1
- package/{index-rnt66vym.js → index-v4hnkqz9.js} +1 -1
- package/{index-ne50hc2m.js → index-wvebzf0y.js} +456 -1623
- package/{index-pp54p5zk.js → index-xfgdmm5d.js} +2 -2
- package/{index-tgjy8xf3.js → index-zazvn45w.js} +4 -4
- package/{index-0bq14vr1.js → index-zzvvc845.js} +1 -1
- package/index.js +22 -22
- package/{library.command-ccb1cp92.js → library.command-sv6g1h2g.js} +4 -4
- package/{localRegistry.command-gmj1k6q8.js → localRegistry.command-fm41vdtw.js} +11 -11
- package/{module.command-5k6h407d.js → module.command-ccdeder9.js} +6 -6
- package/{package.command-e6ntvpez.js → package.command-xmw605b1.js} +5 -5
- package/package.json +2 -2
- package/{page.command-v4f90wb8.js → page.command-ky7vafsn.js} +4 -4
- package/{primitive.command-g6km57q5.js → primitive.command-bh6j8nfs.js} +7 -7
- package/{prompter-ptaa3nk0.js → prompter-76k56b37.js} +2 -2
- package/{quality.command-de3p36pz.js → quality.command-rqqrpe98.js} +3 -3
- package/{repair.command-d3x1javq.js → repair.command-n9nsm9qe.js} +6 -6
- package/{scalar.command-7ezrac2b.js → scalar.command-t09gmck8.js} +5 -5
- package/{subspace.command-ksenprez.js → subspace.command-y1p56wyv.js} +6 -6
- package/{tunnel.command-54947jck.js → tunnel.command-4g95vhsn.js} +5 -5
- package/{workflow.command-z2p32nmf.js → workflow.command-b91ph8sx.js} +11 -11
- package/{workspace.command-tkn8g0bn.js → workspace.command-d8he10tv.js} +24 -24
- package/TunnelShare-pemaknmd.js +0 -9
- package/cloud.script-g2dcnr8c.js +0 -20
- package/context.runner-ehjs419v.js +0 -21
- package/index-fgq75haa.js +0 -2084
package/index-fgq75haa.js
DELETED
|
@@ -1,2084 +0,0 @@
|
|
|
1
|
-
// @bun
|
|
2
|
-
import {
|
|
3
|
-
SOURCE_EXTS
|
|
4
|
-
} from "./index-khpfpcsd.js";
|
|
5
|
-
import {
|
|
6
|
-
FileSys2
|
|
7
|
-
} from "./index-5b6tavv3.js";
|
|
8
|
-
|
|
9
|
-
// pkgs/@akanjs/devkit/frontendBuild/clientEntryDiscovery.ts
|
|
10
|
-
import path3 from "path";
|
|
11
|
-
|
|
12
|
-
// pkgs/@akanjs/devkit/transforms/barrelAnalyzer.ts
|
|
13
|
-
import path from "path";
|
|
14
|
-
import { Logger } from "akanjs/common";
|
|
15
|
-
|
|
16
|
-
// pkgs/@akanjs/devkit/transforms/moduleSyntax.ts
|
|
17
|
-
var loaderFor = (absPath) => {
|
|
18
|
-
if (absPath.endsWith(".tsx"))
|
|
19
|
-
return "tsx";
|
|
20
|
-
if (absPath.endsWith(".jsx"))
|
|
21
|
-
return "jsx";
|
|
22
|
-
if (absPath.endsWith(".ts"))
|
|
23
|
-
return "ts";
|
|
24
|
-
return "js";
|
|
25
|
-
};
|
|
26
|
-
var parseNamedList = (listBody) => {
|
|
27
|
-
const out = [];
|
|
28
|
-
for (const raw of listBody.split(",")) {
|
|
29
|
-
let rest = raw.trim();
|
|
30
|
-
if (!rest)
|
|
31
|
-
continue;
|
|
32
|
-
let isType = false;
|
|
33
|
-
if (rest.startsWith("type ")) {
|
|
34
|
-
isType = true;
|
|
35
|
-
rest = rest.slice(5).trim();
|
|
36
|
-
}
|
|
37
|
-
const asMatch = /^(\w+)\s+as\s+(\w+)$/.exec(rest);
|
|
38
|
-
if (asMatch) {
|
|
39
|
-
out.push({ imported: asMatch[1] ?? "", local: asMatch[2] ?? "", isType });
|
|
40
|
-
continue;
|
|
41
|
-
}
|
|
42
|
-
if (/^\w+$/.test(rest))
|
|
43
|
-
out.push({ imported: rest, local: rest, isType });
|
|
44
|
-
}
|
|
45
|
-
return out;
|
|
46
|
-
};
|
|
47
|
-
|
|
48
|
-
// pkgs/@akanjs/devkit/transforms/barrelAnalyzer.ts
|
|
49
|
-
var REEXPORT_RE = /(?:^|\n)\s*export\s+(?:type\s+)?(?:(\*)(?:\s+as\s+(\w+))?|\{\s*([^}]*?)\s*\})\s+from\s+(["'])([^"']+)\4;?/g;
|
|
50
|
-
var LOCAL_NAMED_RE = /(?:^|\n)\s*export\s+\{\s*([^}]*?)\s*\}(?!\s*from)/g;
|
|
51
|
-
var CANDIDATE_EXTS = [".ts", ".tsx", ".js", ".jsx", ".mjs", ".cjs"];
|
|
52
|
-
|
|
53
|
-
class BarrelAnalyzer {
|
|
54
|
-
#logger = new Logger("BarrelAnalyzer");
|
|
55
|
-
#opts;
|
|
56
|
-
#cache = new Map;
|
|
57
|
-
#tsTranspiler = new Bun.Transpiler({ loader: "ts" });
|
|
58
|
-
#tsxTranspiler = new Bun.Transpiler({ loader: "tsx" });
|
|
59
|
-
constructor(opts) {
|
|
60
|
-
this.#opts = opts;
|
|
61
|
-
}
|
|
62
|
-
analyze(pkgName) {
|
|
63
|
-
const cached = this.#cache.get(pkgName);
|
|
64
|
-
if (cached)
|
|
65
|
-
return cached;
|
|
66
|
-
const promise = this.#analyzeSafe(pkgName);
|
|
67
|
-
this.#cache.set(pkgName, promise);
|
|
68
|
-
return promise;
|
|
69
|
-
}
|
|
70
|
-
async#analyzeSafe(pkgName) {
|
|
71
|
-
try {
|
|
72
|
-
const pkg = await this.#opts.resolvePackage(pkgName);
|
|
73
|
-
if (!pkg)
|
|
74
|
-
return null;
|
|
75
|
-
const map = new Map;
|
|
76
|
-
await this.#walk(pkg.entryFile, pkg, map, new Set);
|
|
77
|
-
return map;
|
|
78
|
-
} catch (err) {
|
|
79
|
-
this.#logger.error(`analyze failed for ${pkgName}: ${err.message}`);
|
|
80
|
-
return null;
|
|
81
|
-
}
|
|
82
|
-
}
|
|
83
|
-
async#walk(absFile, pkg, map, visited) {
|
|
84
|
-
if (visited.has(absFile))
|
|
85
|
-
return;
|
|
86
|
-
visited.add(absFile);
|
|
87
|
-
const file = Bun.file(absFile);
|
|
88
|
-
if (!await file.exists())
|
|
89
|
-
return;
|
|
90
|
-
const source = await file.text();
|
|
91
|
-
const currentSubpath = this.#subpathFor(pkg, absFile);
|
|
92
|
-
if (!currentSubpath)
|
|
93
|
-
return;
|
|
94
|
-
const authoritative = this.#scanExports(source, absFile);
|
|
95
|
-
authoritative.delete("default");
|
|
96
|
-
const attributed = new Set;
|
|
97
|
-
const attribute = (listBody, subpath) => {
|
|
98
|
-
for (const item of parseNamedList(listBody)) {
|
|
99
|
-
if (item.isType || item.imported === "default" || !authoritative.has(item.local))
|
|
100
|
-
continue;
|
|
101
|
-
attributed.add(item.local);
|
|
102
|
-
if (!map.has(item.local))
|
|
103
|
-
map.set(item.local, { subpath, originalName: item.imported });
|
|
104
|
-
}
|
|
105
|
-
};
|
|
106
|
-
const starSpecs = [];
|
|
107
|
-
for (const m of source.matchAll(REEXPORT_RE)) {
|
|
108
|
-
const star = m[1];
|
|
109
|
-
const nsAs = m[2];
|
|
110
|
-
const namedList = m[3];
|
|
111
|
-
const spec = m[5] ?? "";
|
|
112
|
-
if (!isRelative(spec))
|
|
113
|
-
continue;
|
|
114
|
-
if (star) {
|
|
115
|
-
if (nsAs)
|
|
116
|
-
authoritative.delete(nsAs);
|
|
117
|
-
else
|
|
118
|
-
starSpecs.push(spec);
|
|
119
|
-
continue;
|
|
120
|
-
}
|
|
121
|
-
if (namedList !== undefined) {
|
|
122
|
-
const targetAbs = await this.#resolveRel(absFile, spec);
|
|
123
|
-
if (!targetAbs)
|
|
124
|
-
continue;
|
|
125
|
-
const targetSubpath = this.#subpathFor(pkg, targetAbs);
|
|
126
|
-
if (targetSubpath)
|
|
127
|
-
attribute(namedList, targetSubpath);
|
|
128
|
-
}
|
|
129
|
-
}
|
|
130
|
-
LOCAL_NAMED_RE.lastIndex = 0;
|
|
131
|
-
let n = LOCAL_NAMED_RE.exec(source);
|
|
132
|
-
while (n !== null) {
|
|
133
|
-
const body = n[1] ?? "";
|
|
134
|
-
n = LOCAL_NAMED_RE.exec(source);
|
|
135
|
-
attribute(body, currentSubpath);
|
|
136
|
-
}
|
|
137
|
-
for (const name of authoritative) {
|
|
138
|
-
if (attributed.has(name))
|
|
139
|
-
continue;
|
|
140
|
-
if (map.has(name))
|
|
141
|
-
continue;
|
|
142
|
-
map.set(name, { subpath: currentSubpath, originalName: name });
|
|
143
|
-
}
|
|
144
|
-
for (const spec of starSpecs) {
|
|
145
|
-
const targetAbs = await this.#resolveRel(absFile, spec);
|
|
146
|
-
if (targetAbs)
|
|
147
|
-
await this.#walk(targetAbs, pkg, map, visited);
|
|
148
|
-
}
|
|
149
|
-
}
|
|
150
|
-
#scanExports(source, absFile) {
|
|
151
|
-
try {
|
|
152
|
-
const transpiler = [".tsx", ".jsx"].includes(path.extname(absFile)) ? this.#tsxTranspiler : this.#tsTranspiler;
|
|
153
|
-
const { exports } = transpiler.scan(source);
|
|
154
|
-
return new Set(exports);
|
|
155
|
-
} catch (err) {
|
|
156
|
-
this.#logger.error(`scan failed: ${err.message}`);
|
|
157
|
-
return new Set;
|
|
158
|
-
}
|
|
159
|
-
}
|
|
160
|
-
#subpathFor(pkg, absFile) {
|
|
161
|
-
const rel = path.relative(pkg.pkgDir, absFile);
|
|
162
|
-
if (!rel || rel.startsWith("..") || path.isAbsolute(rel))
|
|
163
|
-
return null;
|
|
164
|
-
if (pkg.preserveFilePath)
|
|
165
|
-
return `${pkg.pkgName}/${rel.split(path.sep).join("/")}`;
|
|
166
|
-
const ext = CANDIDATE_EXTS.find((candidate) => rel.endsWith(candidate));
|
|
167
|
-
const parts = (ext ? rel.slice(0, -ext.length) : rel).split(path.sep);
|
|
168
|
-
if (parts[parts.length - 1] === "index")
|
|
169
|
-
parts.pop();
|
|
170
|
-
const tail = parts.join("/");
|
|
171
|
-
return tail === "" ? pkg.pkgName : `${pkg.pkgName}/${tail}`;
|
|
172
|
-
}
|
|
173
|
-
async#resolveRel(fromFile, relSpec) {
|
|
174
|
-
if (this.#opts.resolveRelative)
|
|
175
|
-
return this.#opts.resolveRelative(fromFile, relSpec);
|
|
176
|
-
return defaultResolveRelative(fromFile, relSpec);
|
|
177
|
-
}
|
|
178
|
-
}
|
|
179
|
-
var isRelative = (spec) => {
|
|
180
|
-
return spec.startsWith("./") || spec.startsWith("../") || spec === "." || spec === "..";
|
|
181
|
-
};
|
|
182
|
-
var defaultResolveRelative = async (fromFile, relSpec) => {
|
|
183
|
-
const baseDir = path.dirname(fromFile);
|
|
184
|
-
const joined = path.resolve(baseDir, relSpec);
|
|
185
|
-
if (path.extname(joined)) {
|
|
186
|
-
if (await Bun.file(joined).exists())
|
|
187
|
-
return joined;
|
|
188
|
-
return null;
|
|
189
|
-
}
|
|
190
|
-
for (const ext of CANDIDATE_EXTS) {
|
|
191
|
-
const cand = joined + ext;
|
|
192
|
-
if (await Bun.file(cand).exists())
|
|
193
|
-
return cand;
|
|
194
|
-
}
|
|
195
|
-
for (const ext of CANDIDATE_EXTS) {
|
|
196
|
-
const cand = path.join(joined, `index${ext}`);
|
|
197
|
-
if (await Bun.file(cand).exists())
|
|
198
|
-
return cand;
|
|
199
|
-
}
|
|
200
|
-
return null;
|
|
201
|
-
};
|
|
202
|
-
|
|
203
|
-
// pkgs/@akanjs/devkit/transforms/barrelImportsPlugin.ts
|
|
204
|
-
import path2 from "path";
|
|
205
|
-
import ts from "typescript";
|
|
206
|
-
var createBarrelImportsPlugin = async (app, { skipPath = defaultSkipPath, pipeAfter } = {}) => {
|
|
207
|
-
const akanConfig = await app.getConfig();
|
|
208
|
-
const barrels = [...new Set(akanConfig.barrelImports)].filter(Boolean);
|
|
209
|
-
const analyzer = new BarrelAnalyzer({
|
|
210
|
-
resolvePackage: await createTsconfigPackageResolver(app)
|
|
211
|
-
});
|
|
212
|
-
return {
|
|
213
|
-
name: "barrel-imports",
|
|
214
|
-
setup(build) {
|
|
215
|
-
build.onLoad({
|
|
216
|
-
filter: /^(?:(?!.*[\\/]node_modules[\\/]).*|.*[\\/]node_modules[\\/]akanjs[\\/].*)\.(tsx|ts|jsx|js)(\?v=\d+)?$/
|
|
217
|
-
}, async (args) => {
|
|
218
|
-
const realPath = args.path.replace(/\?v=\d+$/, "");
|
|
219
|
-
const loader = loaderFor(realPath);
|
|
220
|
-
let source = await Bun.file(realPath).text();
|
|
221
|
-
if (skipPath(realPath))
|
|
222
|
-
return { contents: source, loader };
|
|
223
|
-
if (!MACRO_ATTR_RE.test(source) && barrels.length > 0) {
|
|
224
|
-
const rewritten = await rewriteBarrelImports(source, barrels, analyzer);
|
|
225
|
-
if (rewritten !== null)
|
|
226
|
-
source = rewritten;
|
|
227
|
-
}
|
|
228
|
-
if (pipeAfter) {
|
|
229
|
-
const piped = await pipeAfter(source, { path: realPath });
|
|
230
|
-
if (piped !== null)
|
|
231
|
-
source = piped;
|
|
232
|
-
}
|
|
233
|
-
return { contents: source, loader };
|
|
234
|
-
});
|
|
235
|
-
}
|
|
236
|
-
};
|
|
237
|
-
};
|
|
238
|
-
var createTsconfigPackageResolver = async (app) => {
|
|
239
|
-
const tsconfig = await app.getTsConfig();
|
|
240
|
-
const tsconfigPaths = tsconfig.compilerOptions.paths ?? {};
|
|
241
|
-
const wildcardEntries = Object.entries(tsconfigPaths).filter(([k]) => k.endsWith("/*")).map(([k, v]) => ({ prefix: k.slice(0, -1), replacements: v })).sort((a, b) => b.prefix.length - a.prefix.length);
|
|
242
|
-
return async (pkgName) => {
|
|
243
|
-
const exact = tsconfigPaths[pkgName];
|
|
244
|
-
if (exact && exact.length > 0) {
|
|
245
|
-
const raw = exact[0];
|
|
246
|
-
if (!raw)
|
|
247
|
-
return null;
|
|
248
|
-
const entryFile = path2.resolve(app.workspace.workspaceRoot, raw);
|
|
249
|
-
if (!await Bun.file(entryFile).exists())
|
|
250
|
-
return null;
|
|
251
|
-
const parsed = path2.parse(entryFile);
|
|
252
|
-
const lastSlash = pkgName.lastIndexOf("/");
|
|
253
|
-
if (parsed.name !== "index" && lastSlash > 0 && pkgName.slice(lastSlash + 1) === parsed.name)
|
|
254
|
-
return { pkgName: pkgName.slice(0, lastSlash), entryFile, pkgDir: parsed.dir };
|
|
255
|
-
return { pkgName, entryFile, pkgDir: path2.dirname(entryFile) };
|
|
256
|
-
}
|
|
257
|
-
for (const { prefix, replacements } of wildcardEntries) {
|
|
258
|
-
if (!pkgName.startsWith(prefix))
|
|
259
|
-
continue;
|
|
260
|
-
const suffix = pkgName.slice(prefix.length);
|
|
261
|
-
for (const repl of replacements) {
|
|
262
|
-
if (!repl)
|
|
263
|
-
continue;
|
|
264
|
-
const replPath = repl.endsWith("/*") ? repl.slice(0, -1) : repl;
|
|
265
|
-
const candidate = path2.resolve(app.workspace.workspaceRoot, replPath + suffix);
|
|
266
|
-
for (const ext of CANDIDATE_EXTS2) {
|
|
267
|
-
const file = `${candidate}${ext}`;
|
|
268
|
-
if (await Bun.file(file).exists()) {
|
|
269
|
-
const lastSlash = pkgName.lastIndexOf("/");
|
|
270
|
-
const parentSpec = lastSlash > 0 ? pkgName.slice(0, lastSlash) : pkgName;
|
|
271
|
-
return { pkgName: parentSpec, entryFile: file, pkgDir: path2.dirname(file) };
|
|
272
|
-
}
|
|
273
|
-
}
|
|
274
|
-
for (const ext of CANDIDATE_EXTS2) {
|
|
275
|
-
const file = path2.join(candidate, `index${ext}`);
|
|
276
|
-
if (await Bun.file(file).exists())
|
|
277
|
-
return { pkgName, entryFile: file, pkgDir: candidate };
|
|
278
|
-
}
|
|
279
|
-
}
|
|
280
|
-
return null;
|
|
281
|
-
}
|
|
282
|
-
const exported = await resolveNodePackageExport(app.workspace.workspaceRoot, pkgName);
|
|
283
|
-
if (exported)
|
|
284
|
-
return exported;
|
|
285
|
-
const pkgJsonPath = path2.join(app.workspace.workspaceRoot, "node_modules", pkgName, "package.json");
|
|
286
|
-
if (!await Bun.file(pkgJsonPath).exists())
|
|
287
|
-
return null;
|
|
288
|
-
try {
|
|
289
|
-
const pkgJson = JSON.parse(await Bun.file(pkgJsonPath).text());
|
|
290
|
-
const rel = pkgJson.module ?? pkgJson.main ?? "index.js";
|
|
291
|
-
const entryFile = path2.resolve(path2.dirname(pkgJsonPath), rel);
|
|
292
|
-
if (!await Bun.file(entryFile).exists())
|
|
293
|
-
return null;
|
|
294
|
-
return { pkgName, entryFile, pkgDir: path2.dirname(pkgJsonPath) };
|
|
295
|
-
} catch {
|
|
296
|
-
return null;
|
|
297
|
-
}
|
|
298
|
-
};
|
|
299
|
-
};
|
|
300
|
-
var CANDIDATE_EXTS2 = [".ts", ".tsx", ".js", ".jsx", ".mjs", ".cjs"];
|
|
301
|
-
var NODE_MODULES_RE = /[\\/]node_modules[\\/]/;
|
|
302
|
-
var AKANJS_NODE_MODULE_RE = /[\\/]node_modules[\\/]akanjs[\\/]/;
|
|
303
|
-
var defaultSkipPath = (absPath) => NODE_MODULES_RE.test(absPath) && !AKANJS_NODE_MODULE_RE.test(absPath);
|
|
304
|
-
var resolveNodePackageExport = async (workspaceRoot, specifier) => {
|
|
305
|
-
const packageName = getPackageName(specifier);
|
|
306
|
-
if (!packageName)
|
|
307
|
-
return null;
|
|
308
|
-
const pkgJsonPath = path2.join(workspaceRoot, "node_modules", packageName, "package.json");
|
|
309
|
-
if (!await Bun.file(pkgJsonPath).exists())
|
|
310
|
-
return null;
|
|
311
|
-
try {
|
|
312
|
-
const pkgDir = path2.dirname(pkgJsonPath);
|
|
313
|
-
const pkgJson = JSON.parse(await Bun.file(pkgJsonPath).text());
|
|
314
|
-
const subpath = specifier === packageName ? "." : `.${specifier.slice(packageName.length)}`;
|
|
315
|
-
const exported = resolvePackageExport(pkgJson.exports, subpath);
|
|
316
|
-
const rel = exported ?? (subpath === "." ? pkgJson.module ?? pkgJson.main ?? "index.js" : null);
|
|
317
|
-
if (!rel?.startsWith("."))
|
|
318
|
-
return null;
|
|
319
|
-
const entryFile = await resolveFileCandidate(path2.resolve(pkgDir, rel));
|
|
320
|
-
if (!entryFile)
|
|
321
|
-
return null;
|
|
322
|
-
return { pkgName: specifier, entryFile, pkgDir: path2.dirname(entryFile), preserveFilePath: true };
|
|
323
|
-
} catch {
|
|
324
|
-
return null;
|
|
325
|
-
}
|
|
326
|
-
};
|
|
327
|
-
var resolveExportValue = (value) => {
|
|
328
|
-
if (!value)
|
|
329
|
-
return null;
|
|
330
|
-
if (typeof value === "string")
|
|
331
|
-
return value;
|
|
332
|
-
if (Array.isArray(value)) {
|
|
333
|
-
for (const item of value) {
|
|
334
|
-
const resolved = resolveExportValue(item);
|
|
335
|
-
if (resolved)
|
|
336
|
-
return resolved;
|
|
337
|
-
}
|
|
338
|
-
return null;
|
|
339
|
-
}
|
|
340
|
-
for (const condition of ["source", "import", "default", "types"]) {
|
|
341
|
-
const resolved = resolveExportValue(value[condition]);
|
|
342
|
-
if (resolved)
|
|
343
|
-
return resolved;
|
|
344
|
-
}
|
|
345
|
-
return null;
|
|
346
|
-
};
|
|
347
|
-
var resolvePackageExport = (exportsMap, subpath) => {
|
|
348
|
-
if (!exportsMap)
|
|
349
|
-
return null;
|
|
350
|
-
const exact = resolveExportValue(exportsMap[subpath]);
|
|
351
|
-
if (exact)
|
|
352
|
-
return exact;
|
|
353
|
-
for (const [key, value] of Object.entries(exportsMap)) {
|
|
354
|
-
const starIdx = key.indexOf("*");
|
|
355
|
-
if (starIdx === -1)
|
|
356
|
-
continue;
|
|
357
|
-
const prefix = key.slice(0, starIdx);
|
|
358
|
-
const suffix = key.slice(starIdx + 1);
|
|
359
|
-
if (!subpath.startsWith(prefix) || !subpath.endsWith(suffix))
|
|
360
|
-
continue;
|
|
361
|
-
const wildcard = subpath.slice(prefix.length, subpath.length - suffix.length);
|
|
362
|
-
const resolved = resolveExportValue(value);
|
|
363
|
-
if (resolved)
|
|
364
|
-
return resolved.replace("*", wildcard);
|
|
365
|
-
}
|
|
366
|
-
return null;
|
|
367
|
-
};
|
|
368
|
-
var getPackageName = (specifier) => {
|
|
369
|
-
const parts = specifier.split("/");
|
|
370
|
-
if (!parts[0])
|
|
371
|
-
return null;
|
|
372
|
-
if (specifier.startsWith("@"))
|
|
373
|
-
return parts.length >= 2 ? `${parts[0]}/${parts[1]}` : null;
|
|
374
|
-
return parts[0];
|
|
375
|
-
};
|
|
376
|
-
var resolveFileCandidate = async (candidate) => {
|
|
377
|
-
if (await Bun.file(candidate).exists())
|
|
378
|
-
return candidate;
|
|
379
|
-
if (path2.extname(candidate))
|
|
380
|
-
return null;
|
|
381
|
-
for (const ext of CANDIDATE_EXTS2) {
|
|
382
|
-
const file = `${candidate}${ext}`;
|
|
383
|
-
if (await Bun.file(file).exists())
|
|
384
|
-
return file;
|
|
385
|
-
}
|
|
386
|
-
for (const ext of CANDIDATE_EXTS2) {
|
|
387
|
-
const file = path2.join(candidate, `index${ext}`);
|
|
388
|
-
if (await Bun.file(file).exists())
|
|
389
|
-
return file;
|
|
390
|
-
}
|
|
391
|
-
return null;
|
|
392
|
-
};
|
|
393
|
-
var MACRO_ATTR_RE = /with\s*\{\s*type\s*:\s*["']macro["']\s*\}/;
|
|
394
|
-
var rewriteBarrelImports = async (source, barrels, analyzer) => {
|
|
395
|
-
if (!barrels.some((barrel) => source.includes(barrel)))
|
|
396
|
-
return null;
|
|
397
|
-
const statements = findImportStatements(source);
|
|
398
|
-
if (statements.length === 0)
|
|
399
|
-
return null;
|
|
400
|
-
let changed = false;
|
|
401
|
-
let out = source;
|
|
402
|
-
for (let i = statements.length - 1;i >= 0; i--) {
|
|
403
|
-
const stmt = statements[i];
|
|
404
|
-
if (!stmt)
|
|
405
|
-
continue;
|
|
406
|
-
if (!barrels.includes(stmt.specifier))
|
|
407
|
-
continue;
|
|
408
|
-
const map = await analyzer.analyze(stmt.specifier);
|
|
409
|
-
if (!map || map.size === 0)
|
|
410
|
-
continue;
|
|
411
|
-
const replacement = rewriteSingleStatement(stmt, map);
|
|
412
|
-
if (replacement === null)
|
|
413
|
-
continue;
|
|
414
|
-
out = out.slice(0, stmt.start) + replacement + out.slice(stmt.end);
|
|
415
|
-
changed = true;
|
|
416
|
-
}
|
|
417
|
-
return changed ? out : null;
|
|
418
|
-
};
|
|
419
|
-
var findImportStatements = (source) => {
|
|
420
|
-
const statements = [];
|
|
421
|
-
const sourceFile = ts.createSourceFile("barrel-imports.tsx", source, ts.ScriptTarget.Latest, false, ts.ScriptKind.TSX);
|
|
422
|
-
for (const statement of sourceFile.statements) {
|
|
423
|
-
if (!ts.isImportDeclaration(statement))
|
|
424
|
-
continue;
|
|
425
|
-
if (!ts.isStringLiteral(statement.moduleSpecifier))
|
|
426
|
-
continue;
|
|
427
|
-
const importClause = statement.importClause;
|
|
428
|
-
if (!importClause)
|
|
429
|
-
continue;
|
|
430
|
-
statements.push({
|
|
431
|
-
start: statement.getStart(sourceFile),
|
|
432
|
-
end: statement.end,
|
|
433
|
-
clause: source.slice(importClause.getStart(sourceFile), importClause.end).trim(),
|
|
434
|
-
specifier: statement.moduleSpecifier.text
|
|
435
|
-
});
|
|
436
|
-
}
|
|
437
|
-
return statements;
|
|
438
|
-
};
|
|
439
|
-
var parseImportClause = (clause) => {
|
|
440
|
-
let rest = clause.trim();
|
|
441
|
-
let typeOnly = false;
|
|
442
|
-
if (rest.startsWith("type ")) {
|
|
443
|
-
typeOnly = true;
|
|
444
|
-
rest = rest.slice(5).trim();
|
|
445
|
-
}
|
|
446
|
-
const parsed = { typeOnly };
|
|
447
|
-
const commaMatch = /^(\w+)\s*,\s*(.+)$/.exec(rest);
|
|
448
|
-
if (commaMatch) {
|
|
449
|
-
parsed.defaultImport = commaMatch[1];
|
|
450
|
-
rest = commaMatch[2] ?? "";
|
|
451
|
-
} else if (/^\w+$/.test(rest)) {
|
|
452
|
-
parsed.defaultImport = rest;
|
|
453
|
-
return parsed;
|
|
454
|
-
}
|
|
455
|
-
if (rest.startsWith("*")) {
|
|
456
|
-
const ns = /^\*\s+as\s+(\w+)$/.exec(rest);
|
|
457
|
-
if (!ns)
|
|
458
|
-
return null;
|
|
459
|
-
parsed.namespaceImport = ns[1];
|
|
460
|
-
return parsed;
|
|
461
|
-
}
|
|
462
|
-
if (rest.startsWith("{")) {
|
|
463
|
-
const close = rest.indexOf("}");
|
|
464
|
-
if (close === -1)
|
|
465
|
-
return null;
|
|
466
|
-
parsed.named = parseNamedList(rest.slice(1, close));
|
|
467
|
-
}
|
|
468
|
-
return parsed;
|
|
469
|
-
};
|
|
470
|
-
var rewriteSingleStatement = (stmt, map) => {
|
|
471
|
-
const clause = parseImportClause(stmt.clause);
|
|
472
|
-
if (!clause)
|
|
473
|
-
return null;
|
|
474
|
-
if (clause.namespaceImport)
|
|
475
|
-
return null;
|
|
476
|
-
if (clause.typeOnly && !clause.defaultImport)
|
|
477
|
-
return null;
|
|
478
|
-
if (!clause.named?.length)
|
|
479
|
-
return null;
|
|
480
|
-
const remaining = [];
|
|
481
|
-
const rewrites = new Map;
|
|
482
|
-
for (const item of clause.named) {
|
|
483
|
-
if (item.isType) {
|
|
484
|
-
remaining.push(item);
|
|
485
|
-
continue;
|
|
486
|
-
}
|
|
487
|
-
const target = map.get(item.imported);
|
|
488
|
-
if (!target) {
|
|
489
|
-
remaining.push(item);
|
|
490
|
-
continue;
|
|
491
|
-
}
|
|
492
|
-
const list = rewrites.get(target.subpath) ?? [];
|
|
493
|
-
list.push({ imported: target.originalName, local: item.local, isType: false });
|
|
494
|
-
rewrites.set(target.subpath, list);
|
|
495
|
-
}
|
|
496
|
-
if (rewrites.size === 0)
|
|
497
|
-
return null;
|
|
498
|
-
const lines = [];
|
|
499
|
-
if (shouldPreserveBarrelSideEffects(stmt.specifier))
|
|
500
|
-
lines.push(`import "${stmt.specifier}";`);
|
|
501
|
-
if (clause.defaultImport || remaining.length > 0) {
|
|
502
|
-
const parts = [];
|
|
503
|
-
if (clause.defaultImport)
|
|
504
|
-
parts.push(clause.defaultImport);
|
|
505
|
-
if (remaining.length > 0)
|
|
506
|
-
parts.push(`{ ${remaining.map(serializeNamedItem).join(", ")} }`);
|
|
507
|
-
lines.push(`import ${parts.join(", ")} from "${stmt.specifier}";`);
|
|
508
|
-
}
|
|
509
|
-
for (const [subpath, items] of rewrites) {
|
|
510
|
-
lines.push(`import { ${items.map(serializeNamedItem).join(", ")} } from "${subpath}";`);
|
|
511
|
-
}
|
|
512
|
-
return lines.join(`
|
|
513
|
-
`);
|
|
514
|
-
};
|
|
515
|
-
var shouldPreserveBarrelSideEffects = (specifier) => /^@(apps|libs)\/[^/]+\/client$/.test(specifier);
|
|
516
|
-
var serializeNamedItem = (item) => {
|
|
517
|
-
const prefix = item.isType ? "type " : "";
|
|
518
|
-
if (item.imported === item.local)
|
|
519
|
-
return `${prefix}${item.imported}`;
|
|
520
|
-
return `${prefix}${item.imported} as ${item.local}`;
|
|
521
|
-
};
|
|
522
|
-
|
|
523
|
-
// pkgs/@akanjs/devkit/frontendBuild/clientEntryDiscovery.ts
|
|
524
|
-
var USE_CLIENT_RE = /^\s*(?:\/\*[\s\S]*?\*\/\s*|\/\/[^\n]*\n\s*)*["']use client["']/;
|
|
525
|
-
var NODE_MODULES_RE2 = /[\\/]node_modules[\\/]/;
|
|
526
|
-
var AKANJS_NODE_MODULE_RE2 = /[\\/]node_modules[\\/]akanjs[\\/]/;
|
|
527
|
-
var NON_SOURCE_EXT_RE = /\.(css|scss|sass|less|json|svg|png|jpe?g|webp|gif|avif|ico|woff2?|ttf|otf|mp3|mp4|wav)$/i;
|
|
528
|
-
var shouldSkipNodeModule = (absPath) => NODE_MODULES_RE2.test(absPath) && !AKANJS_NODE_MODULE_RE2.test(absPath);
|
|
529
|
-
|
|
530
|
-
class GraphClientEntryDiscovery {
|
|
531
|
-
#akanConfig;
|
|
532
|
-
#resolvePackage;
|
|
533
|
-
#analyzer;
|
|
534
|
-
#tsTranspiler = new Bun.Transpiler({ loader: "tsx" });
|
|
535
|
-
#fileExistsCache = new Map;
|
|
536
|
-
#factsCache = new Map;
|
|
537
|
-
#resolvedFileCache = new Map;
|
|
538
|
-
#resolvedSpecifierCache = new Map;
|
|
539
|
-
#reachableEntriesCache = new Map;
|
|
540
|
-
#missingFiles = new Set;
|
|
541
|
-
#unresolvedPaths = new Set;
|
|
542
|
-
#unresolvedSpecifiers = new Set;
|
|
543
|
-
constructor(akanConfig, resolvePackage) {
|
|
544
|
-
this.#akanConfig = akanConfig;
|
|
545
|
-
this.#resolvePackage = resolvePackage;
|
|
546
|
-
this.#analyzer = new BarrelAnalyzer({ resolvePackage });
|
|
547
|
-
}
|
|
548
|
-
static async create(app) {
|
|
549
|
-
return new GraphClientEntryDiscovery(await app.getConfig(), await createTsconfigPackageResolver(app));
|
|
550
|
-
}
|
|
551
|
-
async discover(seeds) {
|
|
552
|
-
const entries = new Set;
|
|
553
|
-
for (const seed of seeds) {
|
|
554
|
-
for (const entry of await this.#discoverFromFile(seed, new Set))
|
|
555
|
-
entries.add(entry);
|
|
556
|
-
}
|
|
557
|
-
return Array.from(entries).sort();
|
|
558
|
-
}
|
|
559
|
-
invalidate(files) {
|
|
560
|
-
for (const file of files) {
|
|
561
|
-
const absPath = path3.resolve(file);
|
|
562
|
-
this.#factsCache.delete(absPath);
|
|
563
|
-
this.#fileExistsCache.delete(absPath);
|
|
564
|
-
this.#reachableEntriesCache.delete(absPath);
|
|
565
|
-
}
|
|
566
|
-
if (files.length === 0)
|
|
567
|
-
return;
|
|
568
|
-
this.#reachableEntriesCache.clear();
|
|
569
|
-
this.#forgetMissing();
|
|
570
|
-
}
|
|
571
|
-
#forgetMissing() {
|
|
572
|
-
for (const key of this.#missingFiles)
|
|
573
|
-
this.#fileExistsCache.delete(key);
|
|
574
|
-
for (const key of this.#unresolvedPaths)
|
|
575
|
-
this.#resolvedFileCache.delete(key);
|
|
576
|
-
for (const key of this.#unresolvedSpecifiers)
|
|
577
|
-
this.#resolvedSpecifierCache.delete(key);
|
|
578
|
-
this.#missingFiles.clear();
|
|
579
|
-
this.#unresolvedPaths.clear();
|
|
580
|
-
this.#unresolvedSpecifiers.clear();
|
|
581
|
-
}
|
|
582
|
-
async#fileExists(p) {
|
|
583
|
-
const absPath = path3.resolve(p);
|
|
584
|
-
let cached = this.#fileExistsCache.get(absPath);
|
|
585
|
-
if (!cached) {
|
|
586
|
-
cached = Bun.file(absPath).exists().then((exists) => {
|
|
587
|
-
if (!exists)
|
|
588
|
-
this.#missingFiles.add(absPath);
|
|
589
|
-
return exists;
|
|
590
|
-
});
|
|
591
|
-
this.#fileExistsCache.set(absPath, cached);
|
|
592
|
-
}
|
|
593
|
-
return cached;
|
|
594
|
-
}
|
|
595
|
-
#facts(file) {
|
|
596
|
-
const absPath = path3.resolve(file);
|
|
597
|
-
let cached = this.#factsCache.get(absPath);
|
|
598
|
-
if (!cached) {
|
|
599
|
-
cached = (async () => {
|
|
600
|
-
const content = await Bun.file(absPath).text().catch(() => null);
|
|
601
|
-
if (content === null)
|
|
602
|
-
return null;
|
|
603
|
-
if (USE_CLIENT_RE.test(content))
|
|
604
|
-
return { isClientEntry: true, imports: [] };
|
|
605
|
-
return { isClientEntry: false, imports: this.#scanImports(await this.#rewrite(content)) };
|
|
606
|
-
})();
|
|
607
|
-
this.#factsCache.set(absPath, cached);
|
|
608
|
-
}
|
|
609
|
-
return cached;
|
|
610
|
-
}
|
|
611
|
-
async#rewrite(content) {
|
|
612
|
-
if (this.#akanConfig.barrelImports.length === 0)
|
|
613
|
-
return content;
|
|
614
|
-
try {
|
|
615
|
-
return await rewriteBarrelImports(content, this.#akanConfig.barrelImports, this.#analyzer) ?? content;
|
|
616
|
-
} catch {
|
|
617
|
-
return content;
|
|
618
|
-
}
|
|
619
|
-
}
|
|
620
|
-
#scanImports(source) {
|
|
621
|
-
try {
|
|
622
|
-
return this.#tsTranspiler.scanImports(source);
|
|
623
|
-
} catch {
|
|
624
|
-
return [];
|
|
625
|
-
}
|
|
626
|
-
}
|
|
627
|
-
async#resolveFileCandidate(absPathNoExt) {
|
|
628
|
-
const cacheKey = path3.resolve(absPathNoExt);
|
|
629
|
-
let cached = this.#resolvedFileCache.get(cacheKey);
|
|
630
|
-
if (cached)
|
|
631
|
-
return cached;
|
|
632
|
-
cached = (async () => {
|
|
633
|
-
if (await this.#fileExists(cacheKey))
|
|
634
|
-
return cacheKey;
|
|
635
|
-
for (const ext of SOURCE_EXTS) {
|
|
636
|
-
const f = `${cacheKey}${ext}`;
|
|
637
|
-
if (await this.#fileExists(f))
|
|
638
|
-
return f;
|
|
639
|
-
}
|
|
640
|
-
for (const ext of SOURCE_EXTS) {
|
|
641
|
-
const f = path3.join(cacheKey, `index${ext}`);
|
|
642
|
-
if (await this.#fileExists(f))
|
|
643
|
-
return f;
|
|
644
|
-
}
|
|
645
|
-
this.#unresolvedPaths.add(cacheKey);
|
|
646
|
-
return null;
|
|
647
|
-
})();
|
|
648
|
-
this.#resolvedFileCache.set(cacheKey, cached);
|
|
649
|
-
return cached;
|
|
650
|
-
}
|
|
651
|
-
async#resolveSpecifier(spec, importerDir) {
|
|
652
|
-
const cacheKey = `${importerDir}\x00${spec}`;
|
|
653
|
-
let cached = this.#resolvedSpecifierCache.get(cacheKey);
|
|
654
|
-
if (cached)
|
|
655
|
-
return cached;
|
|
656
|
-
cached = (async () => {
|
|
657
|
-
if (spec.startsWith(".") || spec.startsWith("/")) {
|
|
658
|
-
const abs = spec.startsWith("/") ? spec : path3.resolve(importerDir, spec);
|
|
659
|
-
return this.#resolveFileCandidate(abs);
|
|
660
|
-
}
|
|
661
|
-
const pkg = await this.#resolvePackage(spec);
|
|
662
|
-
if (pkg)
|
|
663
|
-
return pkg.entryFile;
|
|
664
|
-
this.#unresolvedSpecifiers.add(cacheKey);
|
|
665
|
-
return null;
|
|
666
|
-
})();
|
|
667
|
-
this.#resolvedSpecifierCache.set(cacheKey, cached);
|
|
668
|
-
return cached;
|
|
669
|
-
}
|
|
670
|
-
async#discoverFromFile(file, visiting) {
|
|
671
|
-
const absPath = path3.resolve(file);
|
|
672
|
-
const cached = this.#reachableEntriesCache.get(absPath);
|
|
673
|
-
if (cached)
|
|
674
|
-
return new Set(cached);
|
|
675
|
-
if (visiting.has(absPath) || shouldSkipNodeModule(absPath))
|
|
676
|
-
return new Set;
|
|
677
|
-
visiting.add(absPath);
|
|
678
|
-
const entries = new Set;
|
|
679
|
-
const facts = await this.#facts(absPath);
|
|
680
|
-
if (!facts)
|
|
681
|
-
return this.#finishDiscovery(absPath, visiting, entries);
|
|
682
|
-
if (facts.isClientEntry) {
|
|
683
|
-
entries.add(absPath);
|
|
684
|
-
return this.#finishDiscovery(absPath, visiting, entries);
|
|
685
|
-
}
|
|
686
|
-
const importerDir = path3.dirname(absPath);
|
|
687
|
-
for (const imp of facts.imports) {
|
|
688
|
-
const spec = imp.path;
|
|
689
|
-
if (!spec)
|
|
690
|
-
continue;
|
|
691
|
-
if (NON_SOURCE_EXT_RE.test(spec))
|
|
692
|
-
continue;
|
|
693
|
-
const resolved = await this.#resolveSpecifier(spec, importerDir);
|
|
694
|
-
if (!resolved)
|
|
695
|
-
continue;
|
|
696
|
-
if (shouldSkipNodeModule(resolved))
|
|
697
|
-
continue;
|
|
698
|
-
for (const entry of await this.#discoverFromFile(resolved, visiting))
|
|
699
|
-
entries.add(entry);
|
|
700
|
-
}
|
|
701
|
-
return this.#finishDiscovery(absPath, visiting, entries);
|
|
702
|
-
}
|
|
703
|
-
#finishDiscovery(absPath, visiting, entries) {
|
|
704
|
-
visiting.delete(absPath);
|
|
705
|
-
this.#reachableEntriesCache.set(absPath, entries);
|
|
706
|
-
return new Set(entries);
|
|
707
|
-
}
|
|
708
|
-
}
|
|
709
|
-
|
|
710
|
-
// pkgs/@akanjs/devkit/frontendBuild/routeClientBuilder.ts
|
|
711
|
-
import { mkdir } from "fs/promises";
|
|
712
|
-
import path6 from "path";
|
|
713
|
-
|
|
714
|
-
// pkgs/@akanjs/devkit/transforms/rscUseClientTransform.ts
|
|
715
|
-
import path4 from "path";
|
|
716
|
-
import ts2 from "typescript";
|
|
717
|
-
var USE_CLIENT_RE2 = /^\s*(?:\/\*[\s\S]*?\*\/\s*|\/\/[^\n]*\n\s*)*["']use client["']/;
|
|
718
|
-
var IMPLICIT_ROOT_LAYOUT_RE = /[/\\]\.akan[/\\]generated[/\\](?:implicit-root-layout|root-layouts[/\\].*__root_layout)\.(tsx|ts|jsx|js)$/;
|
|
719
|
-
var STAR_REEXPORT_HINT_RE = /\bexport\s*\*\s*from\b/;
|
|
720
|
-
function toClientReferencePath(absPath, workspaceRoot) {
|
|
721
|
-
return path4.relative(path4.resolve(workspaceRoot), path4.resolve(absPath)).split(path4.sep).join("/");
|
|
722
|
-
}
|
|
723
|
-
var starReexportSpecifiers = (source, filePath) => ts2.createSourceFile(filePath, source, ts2.ScriptTarget.Latest).statements.filter((statement) => ts2.isExportDeclaration(statement) && !statement.exportClause && !statement.isTypeOnly && !!statement.moduleSpecifier && ts2.isStringLiteral(statement.moduleSpecifier)).map((statement) => statement.moduleSpecifier.text);
|
|
724
|
-
var scanUseClientExports = (source, filePath, workspaceRoot) => {
|
|
725
|
-
const stars = USE_CLIENT_RE2.test(source) && STAR_REEXPORT_HINT_RE.test(source) ? starReexportSpecifiers(source, filePath) : [];
|
|
726
|
-
if (stars.length) {
|
|
727
|
-
const file = workspaceRoot ? toClientReferencePath(filePath, workspaceRoot) : filePath;
|
|
728
|
-
const listed = stars.slice(0, 3).map((specifier) => `\`export * from ${JSON.stringify(specifier)}\``);
|
|
729
|
-
const written = stars.length > 3 ? `${listed.join(", ")} (and ${stars.length - 3} more)` : listed.join(", ");
|
|
730
|
-
throw new Error(`${file} is a "use client" module, so it cannot ${written}: the server sees only the names a client module ` + `declares, and a star re-export declares none. Re-export them by name \u2014 \`export { \u2026 } from ${JSON.stringify(stars[0])}\`.`);
|
|
731
|
-
}
|
|
732
|
-
return new Bun.Transpiler({ loader: loaderFor(filePath) }).scan(source).exports;
|
|
733
|
-
};
|
|
734
|
-
function transformUseClient(source, args) {
|
|
735
|
-
if (!USE_CLIENT_RE2.test(source))
|
|
736
|
-
return null;
|
|
737
|
-
if (IMPLICIT_ROOT_LAYOUT_RE.test(args.path))
|
|
738
|
-
return null;
|
|
739
|
-
const exports = scanUseClientExports(source, args.path, args.workspaceRoot);
|
|
740
|
-
if (exports.length === 0)
|
|
741
|
-
return null;
|
|
742
|
-
const referencePath = args.workspaceRoot ? toClientReferencePath(args.path, args.workspaceRoot) : args.path;
|
|
743
|
-
const filePathLit = JSON.stringify(referencePath);
|
|
744
|
-
const lines = [`import { registerClientReference } from "react-server-dom-webpack/server.node";`];
|
|
745
|
-
for (const name of exports) {
|
|
746
|
-
const nameLit = JSON.stringify(name);
|
|
747
|
-
const errMsg = JSON.stringify(`Attempted to call '${name}' from '${referencePath}' on the server, but it is a client-only export.`);
|
|
748
|
-
const proxy = `() => { throw new Error(${errMsg}); }`;
|
|
749
|
-
const binding = name === "default" ? `export default registerClientReference(${proxy}, ${filePathLit}, ${nameLit});` : `export const ${name} = registerClientReference(${proxy}, ${filePathLit}, ${nameLit});`;
|
|
750
|
-
lines.push(binding);
|
|
751
|
-
}
|
|
752
|
-
return lines.join(`
|
|
753
|
-
`);
|
|
754
|
-
}
|
|
755
|
-
|
|
756
|
-
// pkgs/@akanjs/devkit/frontendBuild/clientEntriesBundler.ts
|
|
757
|
-
import fs from "fs";
|
|
758
|
-
import path5 from "path";
|
|
759
|
-
|
|
760
|
-
// pkgs/@akanjs/devkit/frontendBuild/bundleDefine.ts
|
|
761
|
-
var bundleDefine = (app, command, renderEnv) => {
|
|
762
|
-
const nodeEnv = command === "build" ? "production" : "development";
|
|
763
|
-
return {
|
|
764
|
-
"process.env.NODE_ENV": JSON.stringify(nodeEnv),
|
|
765
|
-
"process.env.AKAN_PUBLIC_RENDER_ENV": JSON.stringify(renderEnv),
|
|
766
|
-
...Object.fromEntries(Object.entries(app.getPublicEnv()).map(([key, value]) => [`process.env.${key}`, JSON.stringify(value)]))
|
|
767
|
-
};
|
|
768
|
-
};
|
|
769
|
-
|
|
770
|
-
// pkgs/@akanjs/devkit/frontendBuild/clientBuildTypes.ts
|
|
771
|
-
var CLIENT_BUNDLE_NAMING = {
|
|
772
|
-
entry: "[name]-[hash].[ext]",
|
|
773
|
-
chunk: "chunks/[hash].[ext]",
|
|
774
|
-
asset: "assets/[hash].[ext]"
|
|
775
|
-
};
|
|
776
|
-
|
|
777
|
-
// pkgs/@akanjs/devkit/frontendBuild/clientEntriesBundler.ts
|
|
778
|
-
class ClientEntriesBundler {
|
|
779
|
-
#app;
|
|
780
|
-
#entries;
|
|
781
|
-
#plugins;
|
|
782
|
-
#external;
|
|
783
|
-
#externalSubpaths;
|
|
784
|
-
#externalAliases;
|
|
785
|
-
#command;
|
|
786
|
-
#target;
|
|
787
|
-
#outputSubdir;
|
|
788
|
-
#reactFastRefresh;
|
|
789
|
-
#outdir;
|
|
790
|
-
#servePrefix;
|
|
791
|
-
#manifest = {};
|
|
792
|
-
#ssrManifest = { moduleLoading: null, moduleMap: {} };
|
|
793
|
-
#entryUrlsByAbsPath = new Map;
|
|
794
|
-
#entryOutputAbsByAbsPath = new Map;
|
|
795
|
-
#entryDepsByAbsPath = new Map;
|
|
796
|
-
#clientReferenceIdByAbsPath = new Map;
|
|
797
|
-
#opaqueEntries = null;
|
|
798
|
-
#metafileOutputsByAbs = new Map;
|
|
799
|
-
constructor(options) {
|
|
800
|
-
this.#app = options.app;
|
|
801
|
-
this.#entries = options.entries;
|
|
802
|
-
this.#plugins = options.plugins ?? [];
|
|
803
|
-
this.#external = options.external ?? [];
|
|
804
|
-
this.#externalSubpaths = options.externalSubpaths ?? [];
|
|
805
|
-
this.#externalAliases = options.externalAliases ?? {};
|
|
806
|
-
this.#command = options.command ?? "start";
|
|
807
|
-
this.#target = options.target ?? "browser";
|
|
808
|
-
this.#outputSubdir = options.outputSubdir ?? "client";
|
|
809
|
-
this.#reactFastRefresh = options.reactFastRefresh ?? false;
|
|
810
|
-
const artifactDir = `${this.#command === "build" ? this.#app.dist.cwdPath : this.#app.cwdPath}/.akan/artifact`;
|
|
811
|
-
this.#outdir = `${artifactDir}/${this.#outputSubdir}`;
|
|
812
|
-
this.#servePrefix = `/_akan/${this.#outputSubdir}`;
|
|
813
|
-
}
|
|
814
|
-
async bundle() {
|
|
815
|
-
const akanConfig = await this.#app.getConfig();
|
|
816
|
-
this.#opaqueEntries = await this.#createOpaqueEntryAliases();
|
|
817
|
-
const result = await Bun.build({
|
|
818
|
-
entrypoints: this.#opaqueEntries.entries,
|
|
819
|
-
outdir: this.#outdir,
|
|
820
|
-
splitting: true,
|
|
821
|
-
target: this.#target,
|
|
822
|
-
format: "esm",
|
|
823
|
-
naming: CLIENT_BUNDLE_NAMING,
|
|
824
|
-
metafile: true,
|
|
825
|
-
define: bundleDefine(this.#app, this.#command, "ssr"),
|
|
826
|
-
minify: this.#command === "build",
|
|
827
|
-
optimizeImports: akanConfig.optimizeImports,
|
|
828
|
-
reactFastRefresh: this.#command === "start" && this.#outputSubdir === "client" && this.#reactFastRefresh,
|
|
829
|
-
plugins: this.#external.length > 0 || this.#externalSubpaths.length > 0 || Object.keys(this.#externalAliases).length > 0 ? [...this.#plugins, this.#createExternalSpecifiersPlugin()] : this.#plugins
|
|
830
|
-
});
|
|
831
|
-
if (!result.success)
|
|
832
|
-
throw new AggregateError(result.logs, "[ClientEntriesBundler] Bun.build failed");
|
|
833
|
-
await this.#rewriteExternalImportSpecifiers(result.outputs);
|
|
834
|
-
const metafile = result.metafile;
|
|
835
|
-
if (!metafile)
|
|
836
|
-
throw new Error("[ClientEntriesBundler] metafile is missing");
|
|
837
|
-
this.#metafileOutputsByAbs = new Map(Object.entries(metafile.outputs).map(([k, v]) => [this.#absFromOutdir(k), v]));
|
|
838
|
-
for (const artifact of result.outputs.filter((a) => a.kind === "entry-point"))
|
|
839
|
-
this.#addEntryArtifact(artifact.path);
|
|
840
|
-
return {
|
|
841
|
-
manifest: this.#manifest,
|
|
842
|
-
ssrManifest: this.#ssrManifest,
|
|
843
|
-
entryUrlsByAbsPath: this.#entryUrlsByAbsPath,
|
|
844
|
-
entryOutputAbsByAbsPath: this.#entryOutputAbsByAbsPath,
|
|
845
|
-
entryDepsByAbsPath: this.#entryDepsByAbsPath,
|
|
846
|
-
clientReferenceIdByAbsPath: this.#clientReferenceIdByAbsPath
|
|
847
|
-
};
|
|
848
|
-
}
|
|
849
|
-
async#createOpaqueEntryAliases() {
|
|
850
|
-
const aliasDir = path5.join(this.#app.cwdPath, ".akan", "generated", "client-entry-alias", this.#outputSubdir);
|
|
851
|
-
fs.mkdirSync(aliasDir, { recursive: true });
|
|
852
|
-
const originalByAlias = new Map;
|
|
853
|
-
const aliasedEntries = await Promise.all(this.#entries.map(async (entry) => {
|
|
854
|
-
const absEntry = path5.resolve(entry);
|
|
855
|
-
const hash = Bun.hash(`${this.#app.name}
|
|
856
|
-
${this.#outputSubdir}
|
|
857
|
-
${absEntry}`).toString(36);
|
|
858
|
-
const aliasPath = path5.join(aliasDir, `${hash}.tsx`);
|
|
859
|
-
await Bun.write(aliasPath, this.#createOpaqueEntryAliasSource(absEntry, await this.#scanEntryExportNames(absEntry)));
|
|
860
|
-
originalByAlias.set(path5.resolve(aliasPath), absEntry);
|
|
861
|
-
return aliasPath;
|
|
862
|
-
}));
|
|
863
|
-
return { entries: aliasedEntries, originalByAlias, aliasDir };
|
|
864
|
-
}
|
|
865
|
-
#createOpaqueEntryAliasSource(absEntry, exportNames) {
|
|
866
|
-
const entryLit = JSON.stringify(path5.resolve(absEntry));
|
|
867
|
-
if (exportNames.length === 0)
|
|
868
|
-
return `export * from ${entryLit};
|
|
869
|
-
`;
|
|
870
|
-
const namedExports = exportNames.filter((name) => name !== "default");
|
|
871
|
-
const lines = namedExports.length > 0 ? [`export { ${namedExports.join(", ")} } from ${entryLit};`] : [];
|
|
872
|
-
if (exportNames.includes("default"))
|
|
873
|
-
lines.push(`export { default } from ${entryLit};`);
|
|
874
|
-
return `${lines.join(`
|
|
875
|
-
`)}
|
|
876
|
-
`;
|
|
877
|
-
}
|
|
878
|
-
async#scanEntryExportNames(absEntry) {
|
|
879
|
-
return scanUseClientExports(await Bun.file(absEntry).text(), absEntry, this.#app.workspace.workspaceRoot);
|
|
880
|
-
}
|
|
881
|
-
#createExternalSpecifiersPlugin() {
|
|
882
|
-
const set = new Set(this.#external);
|
|
883
|
-
const subpathSet = new Set(this.#externalSubpaths);
|
|
884
|
-
const aliases = this.#externalAliases;
|
|
885
|
-
const specifiers = [...new Set([...this.#external, ...this.#externalSubpaths, ...Object.keys(aliases)])];
|
|
886
|
-
const escaped = specifiers.map((s) => s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&"));
|
|
887
|
-
const filter = new RegExp(`^(${escaped.join("|")})(?:/.*)?$`);
|
|
888
|
-
return {
|
|
889
|
-
name: "akan-externalize-specifiers",
|
|
890
|
-
setup(build) {
|
|
891
|
-
build.onResolve({ filter }, (args) => {
|
|
892
|
-
const alias = aliases[args.path];
|
|
893
|
-
if (alias)
|
|
894
|
-
return { path: alias, external: true };
|
|
895
|
-
if (!ClientEntriesBundler.#matchesExternalSpecifier(args.path, set, subpathSet))
|
|
896
|
-
return;
|
|
897
|
-
return { path: args.path, external: true };
|
|
898
|
-
});
|
|
899
|
-
}
|
|
900
|
-
};
|
|
901
|
-
}
|
|
902
|
-
static #matchesExternalSpecifier(specifier, exactExternals, subpathExternals) {
|
|
903
|
-
if (exactExternals.has(specifier) || subpathExternals.has(specifier))
|
|
904
|
-
return true;
|
|
905
|
-
for (const external of subpathExternals)
|
|
906
|
-
if (specifier.startsWith(`${external}/`))
|
|
907
|
-
return true;
|
|
908
|
-
return false;
|
|
909
|
-
}
|
|
910
|
-
async#rewriteExternalImportSpecifiers(outputs) {
|
|
911
|
-
if (Object.keys(this.#externalAliases).length === 0)
|
|
912
|
-
return;
|
|
913
|
-
await Promise.all(outputs.filter((output) => output.path.endsWith(".js")).map(async (output) => {
|
|
914
|
-
const source = await Bun.file(output.path).text();
|
|
915
|
-
const rewritten = ClientEntriesBundler.rewriteExternalImportSpecifiers(source, this.#externalAliases);
|
|
916
|
-
if (rewritten !== source)
|
|
917
|
-
await Bun.write(output.path, rewritten);
|
|
918
|
-
}));
|
|
919
|
-
}
|
|
920
|
-
static rewriteExternalImportSpecifiers(source, aliases) {
|
|
921
|
-
let rewritten = source;
|
|
922
|
-
for (const [specifier, alias] of Object.entries(aliases)) {
|
|
923
|
-
if (!alias)
|
|
924
|
-
continue;
|
|
925
|
-
const escaped = specifier.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
926
|
-
rewritten = rewritten.replace(new RegExp(`(\\bfrom\\s*["'])${escaped}(["'])`, "g"), (_match, prefix, suffix) => {
|
|
927
|
-
return `${prefix}${alias}${suffix}`;
|
|
928
|
-
}).replace(new RegExp(`(\\bimport\\s*["'])${escaped}(["'])`, "g"), (_match, prefix, suffix) => {
|
|
929
|
-
return `${prefix}${alias}${suffix}`;
|
|
930
|
-
});
|
|
931
|
-
}
|
|
932
|
-
return rewritten;
|
|
933
|
-
}
|
|
934
|
-
#toServeUrl(absOutPath) {
|
|
935
|
-
const rel = path5.relative(this.#outdir, absOutPath).split(path5.sep).join("/");
|
|
936
|
-
return `${this.#servePrefix}/${rel}`;
|
|
937
|
-
}
|
|
938
|
-
#absFromOutdir(p) {
|
|
939
|
-
return path5.isAbsolute(p) ? p : path5.resolve(this.#outdir, p);
|
|
940
|
-
}
|
|
941
|
-
#absFromEntryPoint(p) {
|
|
942
|
-
if (path5.isAbsolute(p))
|
|
943
|
-
return path5.resolve(p);
|
|
944
|
-
const candidates = [path5.resolve(process.cwd(), p), path5.resolve(this.#app.cwdPath, p)];
|
|
945
|
-
return candidates.find((candidate) => fs.existsSync(candidate)) ?? candidates[0];
|
|
946
|
-
}
|
|
947
|
-
#collectChunkUrls(absOutPath, visited = new Set) {
|
|
948
|
-
if (visited.has(absOutPath))
|
|
949
|
-
return [];
|
|
950
|
-
visited.add(absOutPath);
|
|
951
|
-
const info = this.#metafileOutputsByAbs.get(absOutPath);
|
|
952
|
-
if (!info)
|
|
953
|
-
return [this.#toServeUrl(absOutPath)];
|
|
954
|
-
const urls = [this.#toServeUrl(absOutPath)];
|
|
955
|
-
for (const imp of info.imports) {
|
|
956
|
-
if (imp.kind === "dynamic-import")
|
|
957
|
-
continue;
|
|
958
|
-
const absImp = this.#absFromOutdir(imp.path);
|
|
959
|
-
if (!absImp.endsWith(".js"))
|
|
960
|
-
continue;
|
|
961
|
-
urls.push(...this.#collectChunkUrls(absImp, visited));
|
|
962
|
-
}
|
|
963
|
-
return urls;
|
|
964
|
-
}
|
|
965
|
-
#collectClientDeps(absOutPath, visited = new Set) {
|
|
966
|
-
if (visited.has(absOutPath))
|
|
967
|
-
return new Set;
|
|
968
|
-
visited.add(absOutPath);
|
|
969
|
-
const deps = new Set;
|
|
970
|
-
const info = this.#metafileOutputsByAbs.get(absOutPath);
|
|
971
|
-
if (!info)
|
|
972
|
-
return deps;
|
|
973
|
-
if (!this.#opaqueEntries)
|
|
974
|
-
throw new Error("[ClientEntriesBundler] opaque entries are missing");
|
|
975
|
-
for (const input of Object.keys(info.inputs ?? {})) {
|
|
976
|
-
const absInput = this.#absFromEntryPoint(input);
|
|
977
|
-
if (!absInput.includes("node_modules") && !absInput.startsWith(this.#opaqueEntries.aliasDir))
|
|
978
|
-
deps.add(absInput);
|
|
979
|
-
}
|
|
980
|
-
for (const imp of info.imports) {
|
|
981
|
-
if (imp.kind === "dynamic-import")
|
|
982
|
-
continue;
|
|
983
|
-
const absImp = this.#absFromOutdir(imp.path);
|
|
984
|
-
if (!absImp.endsWith(".js"))
|
|
985
|
-
continue;
|
|
986
|
-
for (const dep of this.#collectClientDeps(absImp, visited))
|
|
987
|
-
deps.add(dep);
|
|
988
|
-
}
|
|
989
|
-
return deps;
|
|
990
|
-
}
|
|
991
|
-
#addEntryArtifact(absOut) {
|
|
992
|
-
if (!this.#opaqueEntries)
|
|
993
|
-
throw new Error("[ClientEntriesBundler] opaque entries are missing");
|
|
994
|
-
const info = this.#metafileOutputsByAbs.get(absOut);
|
|
995
|
-
const buildEntryPointAbs = info?.entryPoint ? this.#absFromEntryPoint(info.entryPoint) : null;
|
|
996
|
-
const entryPointAbs = buildEntryPointAbs ? this.#opaqueEntries.originalByAlias.get(buildEntryPointAbs) ?? buildEntryPointAbs : null;
|
|
997
|
-
if (!entryPointAbs)
|
|
998
|
-
return;
|
|
999
|
-
const chunkUrls = this.#collectChunkUrls(absOut);
|
|
1000
|
-
const entryUrl = chunkUrls[0] ?? this.#toServeUrl(absOut);
|
|
1001
|
-
this.#entryUrlsByAbsPath.set(entryPointAbs, entryUrl);
|
|
1002
|
-
this.#entryOutputAbsByAbsPath.set(entryPointAbs, absOut);
|
|
1003
|
-
this.#entryDepsByAbsPath.set(entryPointAbs, [...this.#collectClientDeps(absOut)].sort());
|
|
1004
|
-
const clientReferenceId = toClientReferencePath(entryPointAbs, this.#app.workspace.workspaceRoot);
|
|
1005
|
-
this.#clientReferenceIdByAbsPath.set(entryPointAbs, clientReferenceId);
|
|
1006
|
-
const flatChunks = [];
|
|
1007
|
-
for (const url of chunkUrls)
|
|
1008
|
-
flatChunks.push(url, url);
|
|
1009
|
-
const exportNames = info?.exports && info.exports.length > 0 ? info.exports : ["default"];
|
|
1010
|
-
const ssrEntriesByName = {};
|
|
1011
|
-
for (const name of exportNames) {
|
|
1012
|
-
const key = `${clientReferenceId}#${name}`;
|
|
1013
|
-
this.#manifest[key] = { id: entryUrl, chunks: flatChunks, name, async: true };
|
|
1014
|
-
ssrEntriesByName[name] = { id: absOut, chunks: [absOut, absOut], name, async: true };
|
|
1015
|
-
}
|
|
1016
|
-
this.#ssrManifest.moduleMap[entryUrl] = ssrEntriesByName;
|
|
1017
|
-
}
|
|
1018
|
-
}
|
|
1019
|
-
|
|
1020
|
-
// pkgs/@akanjs/devkit/frontendBuild/vendorSpecifiers.ts
|
|
1021
|
-
var VENDOR_SPECIFIERS = [
|
|
1022
|
-
"react",
|
|
1023
|
-
"react-dom",
|
|
1024
|
-
"react-dom/client",
|
|
1025
|
-
"react/jsx-runtime",
|
|
1026
|
-
"react/jsx-dev-runtime",
|
|
1027
|
-
"react-refresh/runtime",
|
|
1028
|
-
"scheduler",
|
|
1029
|
-
"react-server-dom-webpack/client.browser",
|
|
1030
|
-
"akanjs/store",
|
|
1031
|
-
"akanjs/base",
|
|
1032
|
-
"akanjs/common",
|
|
1033
|
-
"akanjs/constant",
|
|
1034
|
-
"akanjs/fetch"
|
|
1035
|
-
];
|
|
1036
|
-
|
|
1037
|
-
// pkgs/@akanjs/devkit/frontendBuild/routeClientBuilder.ts
|
|
1038
|
-
var SSR_CLIENT_ALIAS_EXTERNALS = [
|
|
1039
|
-
"react",
|
|
1040
|
-
"react-dom",
|
|
1041
|
-
"react-dom/client",
|
|
1042
|
-
"react/jsx-runtime",
|
|
1043
|
-
"react/jsx-dev-runtime"
|
|
1044
|
-
];
|
|
1045
|
-
var SSR_CLIENT_EXTERNALS = [...SSR_CLIENT_ALIAS_EXTERNALS, "akanjs/fetch"];
|
|
1046
|
-
|
|
1047
|
-
class RouteClientBuilder {
|
|
1048
|
-
#app;
|
|
1049
|
-
#seeds;
|
|
1050
|
-
#graphSeeds;
|
|
1051
|
-
#knownEntries;
|
|
1052
|
-
#command;
|
|
1053
|
-
#discovery;
|
|
1054
|
-
#entries;
|
|
1055
|
-
constructor(options) {
|
|
1056
|
-
this.#app = options.app;
|
|
1057
|
-
this.#seeds = options.seeds;
|
|
1058
|
-
this.#graphSeeds = options.graphSeeds;
|
|
1059
|
-
this.#knownEntries = options.knownEntries ?? new Set;
|
|
1060
|
-
this.#command = options.command ?? "start";
|
|
1061
|
-
this.#discovery = options.discovery;
|
|
1062
|
-
this.#entries = options.entries;
|
|
1063
|
-
}
|
|
1064
|
-
async build() {
|
|
1065
|
-
const discovered = this.#entries ?? await (await this.#getDiscovery()).discover(this.#seeds);
|
|
1066
|
-
if (discovered.every((e) => this.#knownEntries.has(e)))
|
|
1067
|
-
return this.#emptyResult(discovered);
|
|
1068
|
-
const entries = this.#graphSeeds ? [...new Set([...discovered, ...await (await this.#getDiscovery()).discover(this.#graphSeeds)])].sort() : discovered.filter((e) => !this.#knownEntries.has(e));
|
|
1069
|
-
const bootstrapEntries = await this.#createBootstrapEntries(entries);
|
|
1070
|
-
const browserBundle = await this.#buildBrowserBundle(bootstrapEntries);
|
|
1071
|
-
const ssrBundle = await this.#buildSsrBundle(bootstrapEntries);
|
|
1072
|
-
const acceptedEntries = new Set(entries);
|
|
1073
|
-
const routeEntries = new Set(discovered);
|
|
1074
|
-
const manifestDelta = {};
|
|
1075
|
-
const ssrModuleMap = {};
|
|
1076
|
-
const clientDeps = new Set;
|
|
1077
|
-
const clientDepsByEntry = {};
|
|
1078
|
-
for (const [key, row] of Object.entries(browserBundle.manifest)) {
|
|
1079
|
-
const manifestEntry = RouteClientBuilder.resolveOriginalManifestEntry(key, bootstrapEntries.originalByBuildEntry, browserBundle.clientReferenceIdByAbsPath, this.#app.workspace.workspaceRoot);
|
|
1080
|
-
if (!manifestEntry)
|
|
1081
|
-
continue;
|
|
1082
|
-
if (!acceptedEntries.has(manifestEntry.originalEntry))
|
|
1083
|
-
continue;
|
|
1084
|
-
manifestDelta[manifestEntry.key] = row;
|
|
1085
|
-
const ssrOutput = ssrBundle.entryOutputAbsByAbsPath.get(manifestEntry.buildEntry);
|
|
1086
|
-
if (!ssrOutput)
|
|
1087
|
-
continue;
|
|
1088
|
-
ssrModuleMap[row.id] ??= {};
|
|
1089
|
-
ssrModuleMap[row.id][row.name] = { id: ssrOutput, chunks: [ssrOutput, ssrOutput], name: row.name, async: true };
|
|
1090
|
-
}
|
|
1091
|
-
for (const entry of bootstrapEntries.buildEntries) {
|
|
1092
|
-
const buildEntry = path6.resolve(entry);
|
|
1093
|
-
const originalEntry = path6.resolve(bootstrapEntries.originalByBuildEntry.get(buildEntry) ?? buildEntry);
|
|
1094
|
-
if (!acceptedEntries.has(originalEntry))
|
|
1095
|
-
continue;
|
|
1096
|
-
const deps = new Set([originalEntry]);
|
|
1097
|
-
for (const dep of browserBundle.entryDepsByAbsPath.get(buildEntry) ?? [])
|
|
1098
|
-
deps.add(path6.resolve(dep));
|
|
1099
|
-
const sortedDeps = [...deps].sort();
|
|
1100
|
-
clientDepsByEntry[originalEntry] = sortedDeps;
|
|
1101
|
-
if (routeEntries.has(originalEntry))
|
|
1102
|
-
for (const dep of sortedDeps)
|
|
1103
|
-
clientDeps.add(dep);
|
|
1104
|
-
}
|
|
1105
|
-
return {
|
|
1106
|
-
manifestDelta,
|
|
1107
|
-
ssrManifestDelta: { moduleLoading: null, moduleMap: ssrModuleMap },
|
|
1108
|
-
newEntries: entries.filter((e) => !this.#knownEntries.has(e)),
|
|
1109
|
-
discoveredEntries: discovered,
|
|
1110
|
-
clientDeps: [...clientDeps].sort(),
|
|
1111
|
-
clientDepsByEntry
|
|
1112
|
-
};
|
|
1113
|
-
}
|
|
1114
|
-
async#getDiscovery() {
|
|
1115
|
-
this.#discovery ??= await GraphClientEntryDiscovery.create(this.#app);
|
|
1116
|
-
return this.#discovery;
|
|
1117
|
-
}
|
|
1118
|
-
#emptyResult(discoveredEntries = []) {
|
|
1119
|
-
return {
|
|
1120
|
-
manifestDelta: {},
|
|
1121
|
-
ssrManifestDelta: { moduleLoading: null, moduleMap: {} },
|
|
1122
|
-
newEntries: [],
|
|
1123
|
-
discoveredEntries,
|
|
1124
|
-
clientDeps: [],
|
|
1125
|
-
clientDepsByEntry: {}
|
|
1126
|
-
};
|
|
1127
|
-
}
|
|
1128
|
-
async#buildBrowserBundle(bootstrapEntries) {
|
|
1129
|
-
const reactFastRefresh = process.env.AKAN_REACT_FAST_REFRESH !== "0";
|
|
1130
|
-
return new ClientEntriesBundler({
|
|
1131
|
-
app: this.#app,
|
|
1132
|
-
entries: bootstrapEntries.buildEntries,
|
|
1133
|
-
plugins: [
|
|
1134
|
-
await createBarrelImportsPlugin(this.#app, {
|
|
1135
|
-
pipeAfter: reactFastRefresh ? RouteClientBuilder.normalizeNamedDefaultFunctionForFastRefresh : undefined
|
|
1136
|
-
})
|
|
1137
|
-
],
|
|
1138
|
-
external: VENDOR_SPECIFIERS,
|
|
1139
|
-
command: this.#command,
|
|
1140
|
-
reactFastRefresh
|
|
1141
|
-
}).bundle();
|
|
1142
|
-
}
|
|
1143
|
-
async#buildSsrBundle(bootstrapEntries) {
|
|
1144
|
-
return new ClientEntriesBundler({
|
|
1145
|
-
app: this.#app,
|
|
1146
|
-
entries: bootstrapEntries.buildEntries,
|
|
1147
|
-
plugins: [await createBarrelImportsPlugin(this.#app)],
|
|
1148
|
-
...RouteClientBuilder.resolveSsrClientBundleOptions(this.#command),
|
|
1149
|
-
outputSubdir: "client-ssr",
|
|
1150
|
-
command: this.#command
|
|
1151
|
-
}).bundle();
|
|
1152
|
-
}
|
|
1153
|
-
async#createBootstrapEntries(entries) {
|
|
1154
|
-
if (!await Bun.file(path6.join(this.#app.cwdPath, "lib", "st.ts")).exists()) {
|
|
1155
|
-
return { buildEntries: entries, originalByBuildEntry: new Map };
|
|
1156
|
-
}
|
|
1157
|
-
const outdir = path6.join(this.#app.cwdPath, ".akan", "generated", "client-entry-bootstrap");
|
|
1158
|
-
await mkdir(outdir, { recursive: true });
|
|
1159
|
-
const originalByBuildEntry = new Map;
|
|
1160
|
-
const buildEntries = await Promise.all(entries.map(async (entry) => {
|
|
1161
|
-
const absEntry = path6.resolve(entry);
|
|
1162
|
-
const hash = Bun.hash(`${this.#app.name}
|
|
1163
|
-
${absEntry}`).toString(36);
|
|
1164
|
-
const base = path6.basename(absEntry).replace(/[^A-Za-z0-9._-]/g, "_");
|
|
1165
|
-
const wrapperEntry = path6.join(outdir, `${base}-${hash}.tsx`);
|
|
1166
|
-
const exportNames = await this.#scanExportNames(absEntry);
|
|
1167
|
-
await Bun.write(wrapperEntry, RouteClientBuilder.createStoreBootstrapEntrySource({
|
|
1168
|
-
appName: this.#app.name,
|
|
1169
|
-
originalEntry: absEntry,
|
|
1170
|
-
exportNames
|
|
1171
|
-
}));
|
|
1172
|
-
originalByBuildEntry.set(path6.resolve(wrapperEntry), absEntry);
|
|
1173
|
-
return wrapperEntry;
|
|
1174
|
-
}));
|
|
1175
|
-
return { buildEntries, originalByBuildEntry };
|
|
1176
|
-
}
|
|
1177
|
-
async#scanExportNames(absEntry) {
|
|
1178
|
-
return scanUseClientExports(await Bun.file(absEntry).text(), absEntry, this.#app.workspace.workspaceRoot);
|
|
1179
|
-
}
|
|
1180
|
-
static normalizeNamedDefaultFunctionForFastRefresh(source) {
|
|
1181
|
-
let changed = false;
|
|
1182
|
-
const defaultNames = [];
|
|
1183
|
-
const next = source.replace(/(^|\n)(\s*)export\s+default\s+(async\s+)?function\s+([A-Za-z_$][\w$]*)(?=\s*(?:<|\())/g, (match, lineStart, indent, asyncKeyword, name) => {
|
|
1184
|
-
changed = true;
|
|
1185
|
-
defaultNames.push(name);
|
|
1186
|
-
return `${lineStart}${indent}${asyncKeyword ?? ""}function ${name}`;
|
|
1187
|
-
});
|
|
1188
|
-
if (!changed)
|
|
1189
|
-
return null;
|
|
1190
|
-
return `${next}
|
|
1191
|
-
${defaultNames.map((name) => `export default ${name};`).join(`
|
|
1192
|
-
`)}
|
|
1193
|
-
`;
|
|
1194
|
-
}
|
|
1195
|
-
static resolveSsrClientRuntimeAliases() {
|
|
1196
|
-
const serverEntry = RouteClientBuilder.resolveAkanServerEntry();
|
|
1197
|
-
return { [Bun.resolveSync("akanjs/fetch", serverEntry)]: "akanjs/fetch" };
|
|
1198
|
-
}
|
|
1199
|
-
static resolveSsrClientBundleOptions(command) {
|
|
1200
|
-
if (command === "start") {
|
|
1201
|
-
return {
|
|
1202
|
-
target: "bun",
|
|
1203
|
-
external: SSR_CLIENT_EXTERNALS,
|
|
1204
|
-
externalSubpaths: ["akanjs/fetch"],
|
|
1205
|
-
externalAliases: RouteClientBuilder.resolveSsrClientRuntimeAliases()
|
|
1206
|
-
};
|
|
1207
|
-
}
|
|
1208
|
-
return { target: "bun", external: SSR_CLIENT_ALIAS_EXTERNALS };
|
|
1209
|
-
}
|
|
1210
|
-
static resolveAkanServerEntry() {
|
|
1211
|
-
try {
|
|
1212
|
-
return Bun.resolveSync("akanjs/server", import.meta.dir);
|
|
1213
|
-
} catch {
|
|
1214
|
-
return path6.resolve(import.meta.dir, "../../../server/index.ts");
|
|
1215
|
-
}
|
|
1216
|
-
}
|
|
1217
|
-
static createStoreBootstrapEntrySource(args) {
|
|
1218
|
-
const originalEntry = JSON.stringify(path6.resolve(args.originalEntry));
|
|
1219
|
-
const namedExports = args.exportNames.filter((name) => name !== "default");
|
|
1220
|
-
const lines = [
|
|
1221
|
-
`import ${JSON.stringify(`@apps/${args.appName}/client`)};`,
|
|
1222
|
-
...namedExports.length > 0 ? [`export { ${namedExports.join(", ")} } from ${originalEntry};`] : []
|
|
1223
|
-
];
|
|
1224
|
-
if (args.exportNames.includes("default"))
|
|
1225
|
-
lines.push(`export { default } from ${originalEntry};`);
|
|
1226
|
-
return `${lines.join(`
|
|
1227
|
-
`)}
|
|
1228
|
-
`;
|
|
1229
|
-
}
|
|
1230
|
-
static resolveOriginalManifestEntry(manifestKey, originalByBuildEntry, clientReferenceIdByBuildEntry = new Map, workspaceRoot = process.cwd()) {
|
|
1231
|
-
const hashIdx = manifestKey.lastIndexOf("#");
|
|
1232
|
-
if (hashIdx < 0)
|
|
1233
|
-
return null;
|
|
1234
|
-
const buildReferenceId = manifestKey.slice(0, hashIdx);
|
|
1235
|
-
const name = manifestKey.slice(hashIdx + 1);
|
|
1236
|
-
const buildEntry = [...clientReferenceIdByBuildEntry.entries()].find(([, referenceId]) => referenceId === buildReferenceId)?.[0] ?? buildReferenceId;
|
|
1237
|
-
const originalEntry = originalByBuildEntry.get(buildEntry) ?? buildEntry;
|
|
1238
|
-
const originalReferenceId = toClientReferencePath(originalEntry, workspaceRoot);
|
|
1239
|
-
return { buildEntry, originalEntry, name, key: `${originalReferenceId}#${name}` };
|
|
1240
|
-
}
|
|
1241
|
-
}
|
|
1242
|
-
|
|
1243
|
-
// pkgs/@akanjs/devkit/frontendBuild/autoImportSync.ts
|
|
1244
|
-
import { readdir, readFile, stat } from "fs/promises";
|
|
1245
|
-
import path7 from "path";
|
|
1246
|
-
import ts3 from "typescript";
|
|
1247
|
-
var TEST_FILE_RE = /\.(test|spec)\.(ts|tsx)$/;
|
|
1248
|
-
var AKAN_BASE = {
|
|
1249
|
-
names: ["Int", "Float", "ID", "Any", "Upload", "enumOf", "dayjs"],
|
|
1250
|
-
specifier: "akanjs/base",
|
|
1251
|
-
kind: "named"
|
|
1252
|
-
};
|
|
1253
|
-
var AKAN_BASE_TYPES = { names: ["Dayjs"], specifier: "akanjs/base", kind: "named", typeOnly: true };
|
|
1254
|
-
var CNST_NS = { names: ["cnst"], specifier: "../cnst", kind: "namespace" };
|
|
1255
|
-
var DB_NS = { names: ["db"], specifier: "../db", kind: "namespace" };
|
|
1256
|
-
var SRV_NS = { names: ["srv"], specifier: "../srv", kind: "namespace" };
|
|
1257
|
-
var ERR_DICT = { names: ["Err"], specifier: "../dict", kind: "named" };
|
|
1258
|
-
var RULES = {
|
|
1259
|
-
constant: [AKAN_BASE, AKAN_BASE_TYPES, { names: ["via"], specifier: "akanjs/constant", kind: "named" }],
|
|
1260
|
-
document: [
|
|
1261
|
-
AKAN_BASE,
|
|
1262
|
-
AKAN_BASE_TYPES,
|
|
1263
|
-
CNST_NS,
|
|
1264
|
-
DB_NS,
|
|
1265
|
-
ERR_DICT,
|
|
1266
|
-
{
|
|
1267
|
-
names: ["by", "from", "into", "SchemaOf", "DataInputOf", "DocumentUpdateHelper", "documentQueryHelper", "Mdl"],
|
|
1268
|
-
specifier: "akanjs/document",
|
|
1269
|
-
kind: "named"
|
|
1270
|
-
}
|
|
1271
|
-
],
|
|
1272
|
-
service: [
|
|
1273
|
-
AKAN_BASE,
|
|
1274
|
-
AKAN_BASE_TYPES,
|
|
1275
|
-
DB_NS,
|
|
1276
|
-
SRV_NS,
|
|
1277
|
-
CNST_NS,
|
|
1278
|
-
ERR_DICT,
|
|
1279
|
-
{ names: ["serve"], specifier: "akanjs/service", kind: "named" },
|
|
1280
|
-
{
|
|
1281
|
-
names: ["DataInputOf", "ListQueryOption", "DatabaseRegistry", "getFilterInfoByKey"],
|
|
1282
|
-
specifier: "akanjs/document",
|
|
1283
|
-
kind: "named"
|
|
1284
|
-
}
|
|
1285
|
-
],
|
|
1286
|
-
signal: [
|
|
1287
|
-
AKAN_BASE,
|
|
1288
|
-
AKAN_BASE_TYPES,
|
|
1289
|
-
SRV_NS,
|
|
1290
|
-
CNST_NS,
|
|
1291
|
-
ERR_DICT,
|
|
1292
|
-
{
|
|
1293
|
-
names: ["endpoint", "internal", "slice", "Public", "Req", "Ws", "None", "Res"],
|
|
1294
|
-
specifier: "akanjs/signal",
|
|
1295
|
-
kind: "named"
|
|
1296
|
-
}
|
|
1297
|
-
],
|
|
1298
|
-
dictionary: [
|
|
1299
|
-
{
|
|
1300
|
-
names: ["modelDictionary", "scalarDictionary", "serviceDictionary"],
|
|
1301
|
-
specifier: "akanjs/dictionary",
|
|
1302
|
-
kind: "named"
|
|
1303
|
-
}
|
|
1304
|
-
],
|
|
1305
|
-
srvkit: [
|
|
1306
|
-
AKAN_BASE,
|
|
1307
|
-
AKAN_BASE_TYPES,
|
|
1308
|
-
{ names: ["Logger", "HttpClient", "sleep"], specifier: "akanjs/common", kind: "named" },
|
|
1309
|
-
{ names: ["adapt"], specifier: "akanjs/service", kind: "named" },
|
|
1310
|
-
{ names: ["SignalContext", "Guard", "InternalArg", "Middleware"], specifier: "akanjs/signal", kind: "named" }
|
|
1311
|
-
],
|
|
1312
|
-
common: [AKAN_BASE, AKAN_BASE_TYPES],
|
|
1313
|
-
store: [
|
|
1314
|
-
CNST_NS,
|
|
1315
|
-
{ names: ["store"], specifier: "akanjs/store", kind: "named" },
|
|
1316
|
-
{ names: ["fetch", "usePage", "sig"], specifier: "../useClient", kind: "named" },
|
|
1317
|
-
{ names: ["st"], specifier: "../st", kind: "named" },
|
|
1318
|
-
{ names: ["RootStore"], specifier: "../st", kind: "named", typeOnly: true }
|
|
1319
|
-
],
|
|
1320
|
-
client: [
|
|
1321
|
-
{
|
|
1322
|
-
names: ["cnst", "fetch", "st", "usePage"],
|
|
1323
|
-
specifier: (ctx) => `@${ctx.scope}/${ctx.project}/client`,
|
|
1324
|
-
kind: "named"
|
|
1325
|
-
}
|
|
1326
|
-
]
|
|
1327
|
-
};
|
|
1328
|
-
var PASCAL_CASE_RE = /^[A-Z][A-Za-z0-9]*$/;
|
|
1329
|
-
var DOMAIN_ROLE_KINDS = {
|
|
1330
|
-
constant: ["constant"],
|
|
1331
|
-
dictionary: ["constant", "document", "signal"],
|
|
1332
|
-
common: ["constant"]
|
|
1333
|
-
};
|
|
1334
|
-
var LIB_BARRELS = {
|
|
1335
|
-
Err: { barrel: "dict", kind: "named" },
|
|
1336
|
-
db: { barrel: "db", kind: "namespace" },
|
|
1337
|
-
cnst: { barrel: "cnst", kind: "namespace" },
|
|
1338
|
-
srv: { barrel: "srv", kind: "namespace" }
|
|
1339
|
-
};
|
|
1340
|
-
var GLOBAL_NAMES = new Set(["fetch"]);
|
|
1341
|
-
var DOMAIN_DENYLIST = new Set([
|
|
1342
|
-
"Map",
|
|
1343
|
-
"Set",
|
|
1344
|
-
"WeakMap",
|
|
1345
|
-
"WeakSet",
|
|
1346
|
-
"Date",
|
|
1347
|
-
"Promise",
|
|
1348
|
-
"Array",
|
|
1349
|
-
"Object",
|
|
1350
|
-
"Error",
|
|
1351
|
-
"TypeError",
|
|
1352
|
-
"RangeError",
|
|
1353
|
-
"RegExp",
|
|
1354
|
-
"Symbol",
|
|
1355
|
-
"Proxy",
|
|
1356
|
-
"Reflect",
|
|
1357
|
-
"JSON",
|
|
1358
|
-
"Math",
|
|
1359
|
-
"Number",
|
|
1360
|
-
"BigInt",
|
|
1361
|
-
"Function",
|
|
1362
|
-
"Boolean",
|
|
1363
|
-
"String",
|
|
1364
|
-
"Record",
|
|
1365
|
-
"Partial",
|
|
1366
|
-
"Required",
|
|
1367
|
-
"Readonly",
|
|
1368
|
-
"Pick",
|
|
1369
|
-
"Omit",
|
|
1370
|
-
"Exclude",
|
|
1371
|
-
"Extract",
|
|
1372
|
-
"NonNullable",
|
|
1373
|
-
"ReturnType",
|
|
1374
|
-
"Parameters",
|
|
1375
|
-
"Awaited",
|
|
1376
|
-
"InstanceType"
|
|
1377
|
-
]);
|
|
1378
|
-
|
|
1379
|
-
class AutoImportSync {
|
|
1380
|
-
#workspaceRoot;
|
|
1381
|
-
#domainCache = new Map;
|
|
1382
|
-
constructor({ workspaceRoot }) {
|
|
1383
|
-
this.#workspaceRoot = path7.resolve(workspaceRoot);
|
|
1384
|
-
}
|
|
1385
|
-
async syncForBatch(files) {
|
|
1386
|
-
const changedFiles = [];
|
|
1387
|
-
const errors = [];
|
|
1388
|
-
const seen = new Set;
|
|
1389
|
-
for (const file of files) {
|
|
1390
|
-
const pkgRoot = this.#domainPkgRootOf(file);
|
|
1391
|
-
if (pkgRoot)
|
|
1392
|
-
this.#domainCache.delete(pkgRoot);
|
|
1393
|
-
}
|
|
1394
|
-
for (const file of files) {
|
|
1395
|
-
const abs = path7.resolve(file);
|
|
1396
|
-
if (seen.has(abs))
|
|
1397
|
-
continue;
|
|
1398
|
-
seen.add(abs);
|
|
1399
|
-
const ctx = this.#contextFor(abs);
|
|
1400
|
-
if (!ctx)
|
|
1401
|
-
continue;
|
|
1402
|
-
try {
|
|
1403
|
-
const changed = await this.#syncFile(abs, ctx);
|
|
1404
|
-
if (changed)
|
|
1405
|
-
changedFiles.push(abs);
|
|
1406
|
-
} catch (err) {
|
|
1407
|
-
errors.push(`[auto-import] sync failed for ${file}: ${formatError(err)}`);
|
|
1408
|
-
}
|
|
1409
|
-
}
|
|
1410
|
-
return { changedFiles, errors };
|
|
1411
|
-
}
|
|
1412
|
-
#contextFor(abs) {
|
|
1413
|
-
const rel = path7.relative(this.#workspaceRoot, abs);
|
|
1414
|
-
if (rel.startsWith("..") || path7.isAbsolute(rel))
|
|
1415
|
-
return null;
|
|
1416
|
-
const base = path7.basename(abs);
|
|
1417
|
-
if (TEST_FILE_RE.test(base) || base === "index.ts" || base === "index.tsx" || base.endsWith(".d.ts"))
|
|
1418
|
-
return null;
|
|
1419
|
-
const parts = rel.split(path7.sep).filter(Boolean);
|
|
1420
|
-
const [scope, project, facet] = parts;
|
|
1421
|
-
if (scope !== "apps" && scope !== "libs" || !project || !facet)
|
|
1422
|
-
return null;
|
|
1423
|
-
if (facet === "lib" && parts[3] === "__lib")
|
|
1424
|
-
return null;
|
|
1425
|
-
const role = roleFor(facet, base);
|
|
1426
|
-
if (!role)
|
|
1427
|
-
return null;
|
|
1428
|
-
return { role, scope, project };
|
|
1429
|
-
}
|
|
1430
|
-
async#syncFile(abs, ctx) {
|
|
1431
|
-
const stats = await stat(abs).catch(() => null);
|
|
1432
|
-
if (!stats?.isFile())
|
|
1433
|
-
return false;
|
|
1434
|
-
const source = await readFile(abs, "utf8");
|
|
1435
|
-
const resolveExtra = await this.#extraResolverFor(abs, ctx);
|
|
1436
|
-
const next = transformSource(source, abs, ctx, resolveExtra);
|
|
1437
|
-
if (next === null || next === source)
|
|
1438
|
-
return false;
|
|
1439
|
-
await FileSys2.writeTextAtomic(abs, next);
|
|
1440
|
-
return true;
|
|
1441
|
-
}
|
|
1442
|
-
async#extraResolverFor(abs, ctx) {
|
|
1443
|
-
const resolvers = [];
|
|
1444
|
-
if (ctx.role === "srvkit" || ctx.role === "common")
|
|
1445
|
-
resolvers.push(await this.#libBarrelResolver(abs, ctx));
|
|
1446
|
-
const kinds = DOMAIN_ROLE_KINDS[ctx.role];
|
|
1447
|
-
if (kinds)
|
|
1448
|
-
resolvers.push(await this.#domainResolver(abs, ctx, kinds));
|
|
1449
|
-
if (resolvers.length === 0)
|
|
1450
|
-
return;
|
|
1451
|
-
return (symbol) => {
|
|
1452
|
-
for (const resolve of resolvers) {
|
|
1453
|
-
const target = resolve(symbol);
|
|
1454
|
-
if (target)
|
|
1455
|
-
return target;
|
|
1456
|
-
}
|
|
1457
|
-
return null;
|
|
1458
|
-
};
|
|
1459
|
-
}
|
|
1460
|
-
async#domainResolver(abs, ctx, kinds) {
|
|
1461
|
-
const index = await this.#domainIndex(path7.join(this.#workspaceRoot, ctx.scope, ctx.project));
|
|
1462
|
-
const fileDir = path7.dirname(abs);
|
|
1463
|
-
return (symbol) => {
|
|
1464
|
-
if (!PASCAL_CASE_RE.test(symbol) || DOMAIN_DENYLIST.has(symbol))
|
|
1465
|
-
return null;
|
|
1466
|
-
const entries = (index.get(symbol) ?? []).filter((entry) => kinds.includes(entry.kind));
|
|
1467
|
-
if (entries.length !== 1)
|
|
1468
|
-
return null;
|
|
1469
|
-
return { specifier: relativeSpecifier(fileDir, entries[0].file), kind: "named" };
|
|
1470
|
-
};
|
|
1471
|
-
}
|
|
1472
|
-
async#libBarrelResolver(abs, ctx) {
|
|
1473
|
-
const fileDir = path7.dirname(abs);
|
|
1474
|
-
const libDir = path7.join(this.#workspaceRoot, ctx.scope, ctx.project, "lib");
|
|
1475
|
-
const available = new Map;
|
|
1476
|
-
for (const [symbol, { barrel, kind }] of Object.entries(LIB_BARRELS)) {
|
|
1477
|
-
if (await fileExists(path7.join(libDir, `${barrel}.ts`)))
|
|
1478
|
-
available.set(symbol, { specifier: relativeSpecifier(fileDir, path7.join(libDir, barrel)), kind });
|
|
1479
|
-
}
|
|
1480
|
-
return (symbol) => available.get(symbol) ?? null;
|
|
1481
|
-
}
|
|
1482
|
-
async#domainIndex(pkgRoot) {
|
|
1483
|
-
const cached = this.#domainCache.get(pkgRoot);
|
|
1484
|
-
if (cached)
|
|
1485
|
-
return cached;
|
|
1486
|
-
const index = await buildDomainIndex(path7.join(pkgRoot, "lib"));
|
|
1487
|
-
this.#domainCache.set(pkgRoot, index);
|
|
1488
|
-
return index;
|
|
1489
|
-
}
|
|
1490
|
-
#domainPkgRootOf(file) {
|
|
1491
|
-
const rel = path7.relative(this.#workspaceRoot, path7.resolve(file));
|
|
1492
|
-
if (rel.startsWith("..") || path7.isAbsolute(rel))
|
|
1493
|
-
return null;
|
|
1494
|
-
const [scope, project, facet] = rel.split(path7.sep).filter(Boolean);
|
|
1495
|
-
if (scope !== "apps" && scope !== "libs" || !project || facet !== "lib")
|
|
1496
|
-
return null;
|
|
1497
|
-
if (!domainKindOf(path7.basename(file)))
|
|
1498
|
-
return null;
|
|
1499
|
-
return path7.join(this.#workspaceRoot, scope, project);
|
|
1500
|
-
}
|
|
1501
|
-
}
|
|
1502
|
-
var roleFor = (facet, base) => {
|
|
1503
|
-
if (facet === "lib") {
|
|
1504
|
-
if (base.endsWith(".constant.ts"))
|
|
1505
|
-
return "constant";
|
|
1506
|
-
if (base.endsWith(".document.ts"))
|
|
1507
|
-
return "document";
|
|
1508
|
-
if (base.endsWith(".service.ts"))
|
|
1509
|
-
return "service";
|
|
1510
|
-
if (base.endsWith(".signal.ts"))
|
|
1511
|
-
return "signal";
|
|
1512
|
-
if (base.endsWith(".dictionary.ts"))
|
|
1513
|
-
return "dictionary";
|
|
1514
|
-
if (base.endsWith(".store.ts"))
|
|
1515
|
-
return "store";
|
|
1516
|
-
if (base.endsWith(".tsx"))
|
|
1517
|
-
return "client";
|
|
1518
|
-
return null;
|
|
1519
|
-
}
|
|
1520
|
-
if (facet === "ui" || facet === "page")
|
|
1521
|
-
return base.endsWith(".tsx") ? "client" : null;
|
|
1522
|
-
if (facet === "webkit")
|
|
1523
|
-
return base.endsWith(".ts") || base.endsWith(".tsx") ? "client" : null;
|
|
1524
|
-
if (facet === "srvkit")
|
|
1525
|
-
return base.endsWith(".ts") ? "srvkit" : null;
|
|
1526
|
-
if (facet === "common")
|
|
1527
|
-
return base.endsWith(".ts") || base.endsWith(".tsx") ? "common" : null;
|
|
1528
|
-
return null;
|
|
1529
|
-
};
|
|
1530
|
-
var targetFor = (symbol, ctx) => {
|
|
1531
|
-
for (const rule of RULES[ctx.role]) {
|
|
1532
|
-
if (!rule.names.includes(symbol))
|
|
1533
|
-
continue;
|
|
1534
|
-
const specifier = typeof rule.specifier === "function" ? rule.specifier(ctx) : rule.specifier;
|
|
1535
|
-
return { specifier, kind: rule.kind, typeOnly: rule.typeOnly };
|
|
1536
|
-
}
|
|
1537
|
-
return null;
|
|
1538
|
-
};
|
|
1539
|
-
var transformSource = (source, fileName, ctx, resolveExtra) => {
|
|
1540
|
-
const sf = ts3.createSourceFile(fileName, source, ts3.ScriptTarget.Latest, true, scriptKindFor(fileName));
|
|
1541
|
-
const bound = collectBoundNames(sf);
|
|
1542
|
-
const used = collectUsedReferences(sf);
|
|
1543
|
-
const bareGlobals = collectBareGlobalReferences(sf);
|
|
1544
|
-
const namedBySpecifier = new Map;
|
|
1545
|
-
const namespaceImports = [];
|
|
1546
|
-
for (const name of used) {
|
|
1547
|
-
if (bound.has(name) || bareGlobals.has(name))
|
|
1548
|
-
continue;
|
|
1549
|
-
const target = targetFor(name, ctx) ?? resolveExtra?.(name) ?? null;
|
|
1550
|
-
if (!target)
|
|
1551
|
-
continue;
|
|
1552
|
-
if (target.kind === "namespace")
|
|
1553
|
-
namespaceImports.push({ name, specifier: target.specifier });
|
|
1554
|
-
else {
|
|
1555
|
-
const names = namedBySpecifier.get(target.specifier) ?? new Map;
|
|
1556
|
-
names.set(name, target.typeOnly ?? false);
|
|
1557
|
-
namedBySpecifier.set(target.specifier, names);
|
|
1558
|
-
}
|
|
1559
|
-
}
|
|
1560
|
-
if (namedBySpecifier.size === 0 && namespaceImports.length === 0)
|
|
1561
|
-
return null;
|
|
1562
|
-
const importDecls = sf.statements.filter(ts3.isImportDeclaration);
|
|
1563
|
-
const anchor = importDecls.at(-1) ?? null;
|
|
1564
|
-
const namedImportsBySpecifier = collectExistingNamedImports(importDecls);
|
|
1565
|
-
const edits = [];
|
|
1566
|
-
const newStatements = [];
|
|
1567
|
-
for (const [specifier, names] of namedBySpecifier) {
|
|
1568
|
-
const existing = namedImportsBySpecifier.get(specifier);
|
|
1569
|
-
if (existing) {
|
|
1570
|
-
const merged = new Map(existing.names);
|
|
1571
|
-
for (const [name, isType] of names)
|
|
1572
|
-
if (!merged.has(name))
|
|
1573
|
-
merged.set(name, isType);
|
|
1574
|
-
edits.push({
|
|
1575
|
-
start: existing.decl.getStart(sf),
|
|
1576
|
-
end: existing.decl.getEnd(),
|
|
1577
|
-
text: formatNamedImport(specifier, merged)
|
|
1578
|
-
});
|
|
1579
|
-
} else
|
|
1580
|
-
newStatements.push(formatNamedImport(specifier, names));
|
|
1581
|
-
}
|
|
1582
|
-
for (const ns of namespaceImports)
|
|
1583
|
-
newStatements.push(`import * as ${ns.name} from "${ns.specifier}";`);
|
|
1584
|
-
if (newStatements.length > 0) {
|
|
1585
|
-
const anchorEdit = anchor ? edits.find((edit) => edit.start === anchor.getStart(sf)) : undefined;
|
|
1586
|
-
if (anchorEdit)
|
|
1587
|
-
anchorEdit.text = `${anchorEdit.text}
|
|
1588
|
-
${newStatements.join(`
|
|
1589
|
-
`)}`;
|
|
1590
|
-
else if (anchor)
|
|
1591
|
-
edits.push({ start: anchor.getEnd(), end: anchor.getEnd(), text: `
|
|
1592
|
-
${newStatements.join(`
|
|
1593
|
-
`)}` });
|
|
1594
|
-
else {
|
|
1595
|
-
const prologueEnd = directivePrologueEnd(sf);
|
|
1596
|
-
if (prologueEnd >= 0)
|
|
1597
|
-
edits.push({ start: prologueEnd, end: prologueEnd, text: `
|
|
1598
|
-
${newStatements.join(`
|
|
1599
|
-
`)}` });
|
|
1600
|
-
else
|
|
1601
|
-
edits.push({ start: 0, end: 0, text: `${newStatements.join(`
|
|
1602
|
-
`)}
|
|
1603
|
-
|
|
1604
|
-
` });
|
|
1605
|
-
}
|
|
1606
|
-
}
|
|
1607
|
-
if (edits.length === 0)
|
|
1608
|
-
return null;
|
|
1609
|
-
edits.sort((a, b) => b.start - a.start);
|
|
1610
|
-
let out = source;
|
|
1611
|
-
for (const edit of edits)
|
|
1612
|
-
out = out.slice(0, edit.start) + edit.text + out.slice(edit.end);
|
|
1613
|
-
return out === source ? null : out;
|
|
1614
|
-
};
|
|
1615
|
-
var collectBoundNames = (sf) => {
|
|
1616
|
-
const names = new Set;
|
|
1617
|
-
const visit = (node) => {
|
|
1618
|
-
if (ts3.isImportClause(node)) {
|
|
1619
|
-
if (node.name)
|
|
1620
|
-
names.add(node.name.text);
|
|
1621
|
-
const bindings = node.namedBindings;
|
|
1622
|
-
if (bindings && ts3.isNamespaceImport(bindings))
|
|
1623
|
-
names.add(bindings.name.text);
|
|
1624
|
-
if (bindings && ts3.isNamedImports(bindings))
|
|
1625
|
-
for (const el of bindings.elements)
|
|
1626
|
-
names.add(el.name.text);
|
|
1627
|
-
}
|
|
1628
|
-
if ((ts3.isVariableDeclaration(node) || ts3.isFunctionDeclaration(node) || ts3.isClassDeclaration(node) || ts3.isParameter(node) || ts3.isBindingElement(node) || ts3.isEnumDeclaration(node) || ts3.isTypeAliasDeclaration(node) || ts3.isInterfaceDeclaration(node)) && node.name && ts3.isIdentifier(node.name))
|
|
1629
|
-
names.add(node.name.text);
|
|
1630
|
-
ts3.forEachChild(node, visit);
|
|
1631
|
-
};
|
|
1632
|
-
ts3.forEachChild(sf, visit);
|
|
1633
|
-
return names;
|
|
1634
|
-
};
|
|
1635
|
-
var collectUsedReferences = (sf) => {
|
|
1636
|
-
const used = new Set;
|
|
1637
|
-
const visit = (node) => {
|
|
1638
|
-
if (ts3.isIdentifier(node) && isReferencePosition(node))
|
|
1639
|
-
used.add(node.text);
|
|
1640
|
-
ts3.forEachChild(node, visit);
|
|
1641
|
-
};
|
|
1642
|
-
ts3.forEachChild(sf, visit);
|
|
1643
|
-
return used;
|
|
1644
|
-
};
|
|
1645
|
-
var collectBareGlobalReferences = (sf) => {
|
|
1646
|
-
const bare = new Set;
|
|
1647
|
-
const visit = (node) => {
|
|
1648
|
-
if (ts3.isIdentifier(node) && GLOBAL_NAMES.has(node.text) && isReferencePosition(node)) {
|
|
1649
|
-
const parent = node.parent;
|
|
1650
|
-
if (!(parent && ts3.isPropertyAccessExpression(parent) && parent.expression === node))
|
|
1651
|
-
bare.add(node.text);
|
|
1652
|
-
}
|
|
1653
|
-
ts3.forEachChild(node, visit);
|
|
1654
|
-
};
|
|
1655
|
-
ts3.forEachChild(sf, visit);
|
|
1656
|
-
return bare;
|
|
1657
|
-
};
|
|
1658
|
-
var isReferencePosition = (node) => {
|
|
1659
|
-
const parent = node.parent;
|
|
1660
|
-
if (!parent)
|
|
1661
|
-
return true;
|
|
1662
|
-
if (ts3.isPropertyAccessExpression(parent) && parent.name === node)
|
|
1663
|
-
return false;
|
|
1664
|
-
if (ts3.isQualifiedName(parent) && parent.right === node)
|
|
1665
|
-
return false;
|
|
1666
|
-
if (ts3.isPropertyAssignment(parent) && parent.name === node)
|
|
1667
|
-
return false;
|
|
1668
|
-
if (ts3.isPropertySignature(parent) && parent.name === node)
|
|
1669
|
-
return false;
|
|
1670
|
-
if (ts3.isBindingElement(parent) && parent.propertyName === node)
|
|
1671
|
-
return false;
|
|
1672
|
-
if (ts3.isImportSpecifier(parent) || ts3.isExportSpecifier(parent))
|
|
1673
|
-
return false;
|
|
1674
|
-
return true;
|
|
1675
|
-
};
|
|
1676
|
-
var collectExistingNamedImports = (importDecls) => {
|
|
1677
|
-
const map = new Map;
|
|
1678
|
-
for (const decl of importDecls) {
|
|
1679
|
-
const clause = decl.importClause;
|
|
1680
|
-
const bindings = clause?.namedBindings;
|
|
1681
|
-
if (!clause || !bindings || !ts3.isNamedImports(bindings))
|
|
1682
|
-
continue;
|
|
1683
|
-
if (!ts3.isStringLiteral(decl.moduleSpecifier))
|
|
1684
|
-
continue;
|
|
1685
|
-
const specifier = decl.moduleSpecifier.text;
|
|
1686
|
-
if (map.has(specifier))
|
|
1687
|
-
continue;
|
|
1688
|
-
const names = new Map;
|
|
1689
|
-
for (const el of bindings.elements)
|
|
1690
|
-
names.set(el.name.text, clause.isTypeOnly || el.isTypeOnly);
|
|
1691
|
-
map.set(specifier, { decl, names });
|
|
1692
|
-
}
|
|
1693
|
-
return map;
|
|
1694
|
-
};
|
|
1695
|
-
var formatNamedImport = (specifier, names) => {
|
|
1696
|
-
const entries = [...names.entries()].sort((a, b) => compareNames(a[0], b[0]));
|
|
1697
|
-
if (entries.every(([, isType]) => isType))
|
|
1698
|
-
return `import type { ${entries.map(([name]) => name).join(", ")} } from "${specifier}";`;
|
|
1699
|
-
const parts = entries.map(([name, isType]) => isType ? `type ${name}` : name);
|
|
1700
|
-
return `import { ${parts.join(", ")} } from "${specifier}";`;
|
|
1701
|
-
};
|
|
1702
|
-
var directivePrologueEnd = (sf) => {
|
|
1703
|
-
let end = -1;
|
|
1704
|
-
for (const stmt of sf.statements) {
|
|
1705
|
-
if (ts3.isExpressionStatement(stmt) && ts3.isStringLiteral(stmt.expression))
|
|
1706
|
-
end = stmt.getEnd();
|
|
1707
|
-
else
|
|
1708
|
-
break;
|
|
1709
|
-
}
|
|
1710
|
-
return end;
|
|
1711
|
-
};
|
|
1712
|
-
var scriptKindFor = (fileName) => fileName.endsWith(".tsx") ? ts3.ScriptKind.TSX : ts3.ScriptKind.TS;
|
|
1713
|
-
var compareNames = (a, b) => {
|
|
1714
|
-
const [la, lb] = [a.toLowerCase(), b.toLowerCase()];
|
|
1715
|
-
if (la !== lb)
|
|
1716
|
-
return la < lb ? -1 : 1;
|
|
1717
|
-
return a < b ? -1 : a > b ? 1 : 0;
|
|
1718
|
-
};
|
|
1719
|
-
var formatError = (err) => err instanceof Error ? err.message : String(err);
|
|
1720
|
-
var fileExists = async (file) => stat(file).then((s) => s.isFile()).catch(() => false);
|
|
1721
|
-
var domainKindOf = (base) => {
|
|
1722
|
-
if (base.endsWith(".constant.ts"))
|
|
1723
|
-
return "constant";
|
|
1724
|
-
if (base.endsWith(".document.ts"))
|
|
1725
|
-
return "document";
|
|
1726
|
-
if (base.endsWith(".signal.ts"))
|
|
1727
|
-
return "signal";
|
|
1728
|
-
return null;
|
|
1729
|
-
};
|
|
1730
|
-
var buildDomainIndex = async (libDir) => {
|
|
1731
|
-
const index = new Map;
|
|
1732
|
-
for (const file of await collectDomainFiles(libDir)) {
|
|
1733
|
-
const kind = domainKindOf(path7.basename(file));
|
|
1734
|
-
if (!kind)
|
|
1735
|
-
continue;
|
|
1736
|
-
const src = await readFile(file, "utf8").catch(() => null);
|
|
1737
|
-
if (src === null)
|
|
1738
|
-
continue;
|
|
1739
|
-
for (const name of exportedPascalNames(src, file)) {
|
|
1740
|
-
const entries = index.get(name) ?? [];
|
|
1741
|
-
entries.push({ file, kind });
|
|
1742
|
-
index.set(name, entries);
|
|
1743
|
-
}
|
|
1744
|
-
}
|
|
1745
|
-
return index;
|
|
1746
|
-
};
|
|
1747
|
-
var collectDomainFiles = async (dir) => {
|
|
1748
|
-
const entries = await readdir(dir, { withFileTypes: true }).catch(() => []);
|
|
1749
|
-
const files = [];
|
|
1750
|
-
for (const entry of entries) {
|
|
1751
|
-
if (entry.name.startsWith(".") || entry.name === "node_modules")
|
|
1752
|
-
continue;
|
|
1753
|
-
const full = path7.join(dir, entry.name);
|
|
1754
|
-
if (entry.isDirectory())
|
|
1755
|
-
files.push(...await collectDomainFiles(full));
|
|
1756
|
-
else if (domainKindOf(entry.name) && !TEST_FILE_RE.test(entry.name))
|
|
1757
|
-
files.push(full);
|
|
1758
|
-
}
|
|
1759
|
-
return files;
|
|
1760
|
-
};
|
|
1761
|
-
var exportedPascalNames = (source, fileName) => {
|
|
1762
|
-
const sf = ts3.createSourceFile(fileName, source, ts3.ScriptTarget.Latest, true);
|
|
1763
|
-
const isExported = (node) => ts3.canHaveModifiers(node) && (ts3.getModifiers(node) ?? []).some((m) => m.kind === ts3.SyntaxKind.ExportKeyword);
|
|
1764
|
-
const names = [];
|
|
1765
|
-
for (const stmt of sf.statements) {
|
|
1766
|
-
if ((ts3.isClassDeclaration(stmt) || ts3.isFunctionDeclaration(stmt) || ts3.isEnumDeclaration(stmt)) && stmt.name) {
|
|
1767
|
-
if (isExported(stmt))
|
|
1768
|
-
names.push(stmt.name.text);
|
|
1769
|
-
} else if (ts3.isVariableStatement(stmt) && isExported(stmt)) {
|
|
1770
|
-
for (const decl of stmt.declarationList.declarations)
|
|
1771
|
-
if (ts3.isIdentifier(decl.name))
|
|
1772
|
-
names.push(decl.name.text);
|
|
1773
|
-
} else if (ts3.isExportDeclaration(stmt) && stmt.exportClause && ts3.isNamedExports(stmt.exportClause)) {
|
|
1774
|
-
for (const el of stmt.exportClause.elements)
|
|
1775
|
-
names.push(el.name.text);
|
|
1776
|
-
}
|
|
1777
|
-
}
|
|
1778
|
-
return names.filter((name) => PASCAL_CASE_RE.test(name));
|
|
1779
|
-
};
|
|
1780
|
-
var relativeSpecifier = (fromDir, toFile) => {
|
|
1781
|
-
const rel = path7.relative(fromDir, toFile).replace(/\.tsx?$/, "").split(path7.sep).join("/");
|
|
1782
|
-
return rel.startsWith(".") ? rel : `./${rel}`;
|
|
1783
|
-
};
|
|
1784
|
-
|
|
1785
|
-
// pkgs/@akanjs/devkit/frontendBuild/devChangePlanner.ts
|
|
1786
|
-
import path8 from "path";
|
|
1787
|
-
var CONFIG_BASENAMES = new Set(["akan.config.ts", "bunfig.toml", "tsconfig.json", "package.json"]);
|
|
1788
|
-
var BARREL_FACETS = new Set(["common", "srvkit", "ui", "webkit", "plugin"]);
|
|
1789
|
-
var CLIENT_SUFFIXES = [".Template.tsx", ".Unit.tsx", ".Util.tsx", ".View.tsx", ".Zone.tsx", ".store.ts"];
|
|
1790
|
-
var SHARED_SUFFIXES = [".constant.ts", ".dictionary.ts", ".signal.ts"];
|
|
1791
|
-
var SERVER_SUFFIXES = [".service.ts", ".document.ts"];
|
|
1792
|
-
var RUNTIME_METADATA_BASENAMES = new Set(["dict.ts", "sig.ts", "useClient.ts", "useServer.ts"]);
|
|
1793
|
-
|
|
1794
|
-
class DevChangePlanner {
|
|
1795
|
-
#workspaceRoot;
|
|
1796
|
-
constructor({ workspaceRoot }) {
|
|
1797
|
-
this.#workspaceRoot = path8.resolve(workspaceRoot);
|
|
1798
|
-
}
|
|
1799
|
-
plan({ generation, files, kinds, generatedFiles = [] }) {
|
|
1800
|
-
const fileList = uniqueResolved([...files, ...generatedFiles]);
|
|
1801
|
-
const generatedSet = new Set(generatedFiles.map((file) => path8.resolve(file)));
|
|
1802
|
-
const kindSet = new Set(kinds);
|
|
1803
|
-
const roles = new Set;
|
|
1804
|
-
const actions = new Set;
|
|
1805
|
-
const reasonByFile = {};
|
|
1806
|
-
for (const kind of kindSet) {
|
|
1807
|
-
if (kind === "css") {
|
|
1808
|
-
roles.add("css");
|
|
1809
|
-
actions.add("rebuild-css");
|
|
1810
|
-
}
|
|
1811
|
-
if (kind === "config") {
|
|
1812
|
-
roles.add("config");
|
|
1813
|
-
actions.add("restart-dev-host");
|
|
1814
|
-
}
|
|
1815
|
-
}
|
|
1816
|
-
for (const file of fileList) {
|
|
1817
|
-
const reasons = new Set;
|
|
1818
|
-
const fileRoles = this.#rolesForFile(file, { isGenerated: generatedSet.has(path8.resolve(file)), reasons });
|
|
1819
|
-
for (const role of fileRoles)
|
|
1820
|
-
roles.add(role);
|
|
1821
|
-
if (reasons.has("runtime-metadata"))
|
|
1822
|
-
actions.add("restart-builder");
|
|
1823
|
-
if (reasons.size > 0)
|
|
1824
|
-
reasonByFile[path8.resolve(file)] = [...reasons].sort();
|
|
1825
|
-
}
|
|
1826
|
-
if (roles.has("barrel"))
|
|
1827
|
-
actions.add("sync-generated");
|
|
1828
|
-
if (roles.has("server") || roles.has("shared"))
|
|
1829
|
-
actions.add("restart-backend");
|
|
1830
|
-
if (roles.has("client") || roles.has("shared"))
|
|
1831
|
-
actions.add("rebuild-client");
|
|
1832
|
-
if (roles.has("css"))
|
|
1833
|
-
actions.add("rebuild-css");
|
|
1834
|
-
return {
|
|
1835
|
-
generation,
|
|
1836
|
-
files: fileList,
|
|
1837
|
-
generatedFiles: uniqueResolved(generatedFiles),
|
|
1838
|
-
roles: [...roles].sort(),
|
|
1839
|
-
actions: [...actions].sort(),
|
|
1840
|
-
reasonByFile
|
|
1841
|
-
};
|
|
1842
|
-
}
|
|
1843
|
-
#rolesForFile(file, { isGenerated, reasons }) {
|
|
1844
|
-
const roles = new Set;
|
|
1845
|
-
const abs = path8.resolve(file);
|
|
1846
|
-
const base = path8.basename(abs);
|
|
1847
|
-
const ext = path8.extname(abs).toLowerCase();
|
|
1848
|
-
const isSource = SOURCE_EXTS.has(ext);
|
|
1849
|
-
const rel = path8.relative(this.#workspaceRoot, abs);
|
|
1850
|
-
const parts = rel.split(path8.sep).filter(Boolean);
|
|
1851
|
-
if (CONFIG_BASENAMES.has(base)) {
|
|
1852
|
-
roles.add("config");
|
|
1853
|
-
reasons.add("config-file");
|
|
1854
|
-
}
|
|
1855
|
-
if (ext === ".css") {
|
|
1856
|
-
roles.add("css");
|
|
1857
|
-
reasons.add("css-file");
|
|
1858
|
-
}
|
|
1859
|
-
if (isGenerated || isSource && this.#isBarrelFacetChild(parts)) {
|
|
1860
|
-
roles.add("barrel");
|
|
1861
|
-
reasons.add(isGenerated ? "generated-index" : "barrel-facet-child");
|
|
1862
|
-
}
|
|
1863
|
-
if (isSource && this.#isServerBiased(base, parts)) {
|
|
1864
|
-
roles.add("server");
|
|
1865
|
-
reasons.add("server-path");
|
|
1866
|
-
}
|
|
1867
|
-
if (isSource && this.#isClientBiased(base, parts)) {
|
|
1868
|
-
roles.add("client");
|
|
1869
|
-
reasons.add("client-path");
|
|
1870
|
-
}
|
|
1871
|
-
if (isSource && this.#isSharedBiased(base, parts)) {
|
|
1872
|
-
roles.add("shared");
|
|
1873
|
-
reasons.add("shared-path");
|
|
1874
|
-
}
|
|
1875
|
-
if (isSource && this.#isRuntimeMetadataFile(parts, base)) {
|
|
1876
|
-
reasons.add("runtime-metadata");
|
|
1877
|
-
}
|
|
1878
|
-
if (roles.has("server") && roles.has("client")) {
|
|
1879
|
-
roles.delete("server");
|
|
1880
|
-
roles.delete("client");
|
|
1881
|
-
roles.add("shared");
|
|
1882
|
-
reasons.add("server-client-overlap");
|
|
1883
|
-
}
|
|
1884
|
-
if (roles.size === 0 && SOURCE_EXTS.has(ext) && this.#isWorkspaceSource(rel)) {
|
|
1885
|
-
roles.add("shared");
|
|
1886
|
-
reasons.add("workspace-source-fallback");
|
|
1887
|
-
}
|
|
1888
|
-
return roles;
|
|
1889
|
-
}
|
|
1890
|
-
#isServerBiased(base, parts) {
|
|
1891
|
-
return parts.includes("srvkit") || SERVER_SUFFIXES.some((suffix) => base.endsWith(suffix)) || base === "main.ts" || base === "server.ts";
|
|
1892
|
-
}
|
|
1893
|
-
#isClientBiased(base, parts) {
|
|
1894
|
-
return parts.includes("ui") || parts.includes("webkit") || parts.includes("page") || CLIENT_SUFFIXES.some((suffix) => base.endsWith(suffix));
|
|
1895
|
-
}
|
|
1896
|
-
#isSharedBiased(base, parts) {
|
|
1897
|
-
return parts.includes("common") || SHARED_SUFFIXES.some((suffix) => base.endsWith(suffix)) || RUNTIME_METADATA_BASENAMES.has(base);
|
|
1898
|
-
}
|
|
1899
|
-
#isRuntimeMetadataFile(parts, base) {
|
|
1900
|
-
const parent = parts.at(-2);
|
|
1901
|
-
if (parent === "lib" && RUNTIME_METADATA_BASENAMES.has(base))
|
|
1902
|
-
return true;
|
|
1903
|
-
const libIndex = parts.lastIndexOf("lib");
|
|
1904
|
-
if (libIndex < 0 || parts.length <= libIndex + 1)
|
|
1905
|
-
return false;
|
|
1906
|
-
return base.endsWith(".dictionary.ts") || base.endsWith(".signal.ts");
|
|
1907
|
-
}
|
|
1908
|
-
#isBarrelFacetChild(parts) {
|
|
1909
|
-
if (parts.length < 4)
|
|
1910
|
-
return false;
|
|
1911
|
-
const [scope, , facet, child] = parts;
|
|
1912
|
-
if (scope !== "apps" && scope !== "libs")
|
|
1913
|
-
return false;
|
|
1914
|
-
if (!facet || !BARREL_FACETS.has(facet))
|
|
1915
|
-
return false;
|
|
1916
|
-
if (!child || child.startsWith(".") || child === "index.ts" || child === "index.tsx")
|
|
1917
|
-
return false;
|
|
1918
|
-
return true;
|
|
1919
|
-
}
|
|
1920
|
-
#isWorkspaceSource(rel) {
|
|
1921
|
-
if (!rel || rel.startsWith("..") || path8.isAbsolute(rel))
|
|
1922
|
-
return false;
|
|
1923
|
-
const [scope] = rel.split(path8.sep);
|
|
1924
|
-
return scope === "apps" || scope === "libs" || scope === "pkgs";
|
|
1925
|
-
}
|
|
1926
|
-
}
|
|
1927
|
-
var uniqueResolved = (files) => [...new Set(files.map((file) => path8.resolve(file)))].sort();
|
|
1928
|
-
|
|
1929
|
-
// pkgs/@akanjs/devkit/frontendBuild/devGeneratedIndexSync.ts
|
|
1930
|
-
import { mkdir as mkdir2, readdir as readdir2, readFile as readFile2, rm, stat as stat2 } from "fs/promises";
|
|
1931
|
-
import path9 from "path";
|
|
1932
|
-
import { capitalize } from "akanjs/common";
|
|
1933
|
-
var BARREL_FACETS2 = new Set(["common", "srvkit", "ui", "webkit", "plugin"]);
|
|
1934
|
-
var FACET_SOURCE_FILE_RE = /\.(ts|tsx)$/;
|
|
1935
|
-
var FACET_EXCLUDED_FILE_RE = /(^index\.tsx?$|\.d\.ts$|\.(test|spec)\.(ts|tsx)$|\.css$|\.scss$|\.sass$)/;
|
|
1936
|
-
var FACET_PASCAL_CASE_RE = /^[A-Z][A-Za-z0-9]*$/;
|
|
1937
|
-
var FACET_CAMEL_CASE_RE = /^[a-z][A-Za-z0-9]*$/;
|
|
1938
|
-
var MODULE_UI_TYPES = ["Template", "Unit", "Util", "View", "Zone"];
|
|
1939
|
-
var SERVICE_UI_TYPES = ["Util", "Zone"];
|
|
1940
|
-
var SCALAR_UI_TYPES = ["Template", "Unit"];
|
|
1941
|
-
|
|
1942
|
-
class DevGeneratedIndexSync {
|
|
1943
|
-
#workspaceRoot;
|
|
1944
|
-
constructor({ workspaceRoot }) {
|
|
1945
|
-
this.#workspaceRoot = path9.resolve(workspaceRoot);
|
|
1946
|
-
}
|
|
1947
|
-
async syncForBatch(files) {
|
|
1948
|
-
const indexPaths = new Set;
|
|
1949
|
-
const errors = [];
|
|
1950
|
-
for (const file of files) {
|
|
1951
|
-
const facetIndex = this.#facetIndexFor(file);
|
|
1952
|
-
if (facetIndex)
|
|
1953
|
-
indexPaths.add(facetIndex);
|
|
1954
|
-
const moduleIndex = await this.#moduleIndexForDirectoryEvent(file).catch((err) => {
|
|
1955
|
-
errors.push(`[generated-index] module detection failed for ${file}: ${formatError2(err)}`);
|
|
1956
|
-
return null;
|
|
1957
|
-
});
|
|
1958
|
-
if (moduleIndex)
|
|
1959
|
-
indexPaths.add(moduleIndex);
|
|
1960
|
-
}
|
|
1961
|
-
const changedFiles = [];
|
|
1962
|
-
for (const indexPath of [...indexPaths].sort()) {
|
|
1963
|
-
try {
|
|
1964
|
-
const changed = await this.#syncIndex(indexPath);
|
|
1965
|
-
if (changed)
|
|
1966
|
-
changedFiles.push(indexPath);
|
|
1967
|
-
} catch (err) {
|
|
1968
|
-
errors.push(`[generated-index] sync failed for ${indexPath}: ${formatError2(err)}`);
|
|
1969
|
-
}
|
|
1970
|
-
}
|
|
1971
|
-
return { changedFiles, errors };
|
|
1972
|
-
}
|
|
1973
|
-
#facetIndexFor(file) {
|
|
1974
|
-
const abs = path9.resolve(file);
|
|
1975
|
-
const rel = path9.relative(this.#workspaceRoot, abs);
|
|
1976
|
-
if (rel.startsWith("..") || path9.isAbsolute(rel))
|
|
1977
|
-
return null;
|
|
1978
|
-
const parts = rel.split(path9.sep).filter(Boolean);
|
|
1979
|
-
if (parts.length < 4)
|
|
1980
|
-
return null;
|
|
1981
|
-
const [scope, project, facet, child] = parts;
|
|
1982
|
-
if (scope !== "apps" && scope !== "libs" || !project || !facet || !BARREL_FACETS2.has(facet))
|
|
1983
|
-
return null;
|
|
1984
|
-
if (!child || child.startsWith(".") || child === "index.ts" || child === "index.tsx")
|
|
1985
|
-
return null;
|
|
1986
|
-
return path9.join(this.#workspaceRoot, scope, project, facet, "index.ts");
|
|
1987
|
-
}
|
|
1988
|
-
async#moduleIndexForDirectoryEvent(file) {
|
|
1989
|
-
const abs = path9.resolve(file);
|
|
1990
|
-
const rel = path9.relative(this.#workspaceRoot, abs);
|
|
1991
|
-
if (rel.startsWith("..") || path9.isAbsolute(rel))
|
|
1992
|
-
return null;
|
|
1993
|
-
const parts = rel.split(path9.sep).filter(Boolean);
|
|
1994
|
-
if (parts.length !== 4 && parts.length !== 5)
|
|
1995
|
-
return null;
|
|
1996
|
-
const [scope, project, libSegment, moduleSegment, scalarSegment] = parts;
|
|
1997
|
-
if (scope !== "apps" && scope !== "libs" || !project || libSegment !== "lib")
|
|
1998
|
-
return null;
|
|
1999
|
-
const isExistingDirectory = await stat2(abs).then((s) => s.isDirectory()).catch(() => false);
|
|
2000
|
-
const looksLikeDeletedDirectory = !path9.extname(abs);
|
|
2001
|
-
if (!isExistingDirectory && !looksLikeDeletedDirectory)
|
|
2002
|
-
return null;
|
|
2003
|
-
if (moduleSegment === "__scalar" && scalarSegment) {
|
|
2004
|
-
return path9.join(this.#workspaceRoot, scope, project, "lib", "__scalar", scalarSegment, "index.ts");
|
|
2005
|
-
}
|
|
2006
|
-
if (!moduleSegment || moduleSegment === "__scalar")
|
|
2007
|
-
return null;
|
|
2008
|
-
return path9.join(this.#workspaceRoot, scope, project, "lib", moduleSegment, "index.ts");
|
|
2009
|
-
}
|
|
2010
|
-
async#syncIndex(indexPath) {
|
|
2011
|
-
const dir = path9.dirname(indexPath);
|
|
2012
|
-
const content = await this.#contentForIndex(indexPath);
|
|
2013
|
-
if (content === null) {
|
|
2014
|
-
if (!await exists(indexPath))
|
|
2015
|
-
return false;
|
|
2016
|
-
await rm(indexPath, { force: true });
|
|
2017
|
-
return true;
|
|
2018
|
-
}
|
|
2019
|
-
const current = await readFile2(indexPath, "utf8").catch(() => null);
|
|
2020
|
-
if (current === content)
|
|
2021
|
-
return false;
|
|
2022
|
-
await mkdir2(dir, { recursive: true });
|
|
2023
|
-
await FileSys2.writeTextAtomic(indexPath, content);
|
|
2024
|
-
return true;
|
|
2025
|
-
}
|
|
2026
|
-
async#contentForIndex(indexPath) {
|
|
2027
|
-
const parts = path9.relative(this.#workspaceRoot, indexPath).split(path9.sep).filter(Boolean);
|
|
2028
|
-
const facet = parts.at(-2);
|
|
2029
|
-
if (facet && BARREL_FACETS2.has(facet))
|
|
2030
|
-
return this.#facetContent(path9.dirname(indexPath));
|
|
2031
|
-
return this.#moduleContent(path9.dirname(indexPath));
|
|
2032
|
-
}
|
|
2033
|
-
async#facetContent(dir) {
|
|
2034
|
-
const nameCasePattern = path9.basename(dir) === "ui" ? FACET_PASCAL_CASE_RE : FACET_CAMEL_CASE_RE;
|
|
2035
|
-
const entries = await readdir2(dir, { withFileTypes: true }).catch(() => []);
|
|
2036
|
-
const exportNames = entries.flatMap((entry) => {
|
|
2037
|
-
const name = entry.name;
|
|
2038
|
-
if (name.startsWith("."))
|
|
2039
|
-
return [];
|
|
2040
|
-
if (entry.isDirectory())
|
|
2041
|
-
return nameCasePattern.test(name) ? [name] : [];
|
|
2042
|
-
if (!entry.isFile())
|
|
2043
|
-
return [];
|
|
2044
|
-
if (!FACET_SOURCE_FILE_RE.test(name) || FACET_EXCLUDED_FILE_RE.test(name))
|
|
2045
|
-
return [];
|
|
2046
|
-
const exportName = name.replace(FACET_SOURCE_FILE_RE, "");
|
|
2047
|
-
return nameCasePattern.test(exportName) ? [exportName] : [];
|
|
2048
|
-
}).sort();
|
|
2049
|
-
if (exportNames.length === 0)
|
|
2050
|
-
return null;
|
|
2051
|
-
return `${exportNames.map((name) => `export * from "./${name}";`).join(`
|
|
2052
|
-
`)}
|
|
2053
|
-
`;
|
|
2054
|
-
}
|
|
2055
|
-
async#moduleContent(dir) {
|
|
2056
|
-
const rel = path9.relative(this.#workspaceRoot, dir);
|
|
2057
|
-
const parts = rel.split(path9.sep).filter(Boolean);
|
|
2058
|
-
const moduleSegment = parts.at(-1);
|
|
2059
|
-
if (!moduleSegment)
|
|
2060
|
-
return null;
|
|
2061
|
-
const isScalar = parts.at(-2) === "__scalar";
|
|
2062
|
-
const rawModel = moduleSegment.startsWith("_") ? moduleSegment.slice(1) : moduleSegment;
|
|
2063
|
-
if (!rawModel)
|
|
2064
|
-
return null;
|
|
2065
|
-
const modelName = capitalize(rawModel);
|
|
2066
|
-
const allowedTypes = isScalar ? SCALAR_UI_TYPES : moduleSegment.startsWith("_") ? SERVICE_UI_TYPES : MODULE_UI_TYPES;
|
|
2067
|
-
const fileTypes = [];
|
|
2068
|
-
for (const type of allowedTypes) {
|
|
2069
|
-
if (await exists(path9.join(dir, `${modelName}.${type}.tsx`)))
|
|
2070
|
-
fileTypes.push(type);
|
|
2071
|
-
}
|
|
2072
|
-
if (fileTypes.length === 0)
|
|
2073
|
-
return null;
|
|
2074
|
-
return `
|
|
2075
|
-
${fileTypes.map((type) => `import * as ${type} from "./${modelName}.${type}";`).join(`
|
|
2076
|
-
`)}
|
|
2077
|
-
|
|
2078
|
-
export const ${modelName} = { ${fileTypes.join(", ")} };`;
|
|
2079
|
-
}
|
|
2080
|
-
}
|
|
2081
|
-
var exists = async (file) => stat2(file).then(() => true).catch(() => false);
|
|
2082
|
-
var formatError2 = (err) => err instanceof Error ? err.message : String(err);
|
|
2083
|
-
|
|
2084
|
-
export { loaderFor, BarrelAnalyzer, createBarrelImportsPlugin, createTsconfigPackageResolver, rewriteBarrelImports, GraphClientEntryDiscovery, transformUseClient, bundleDefine, ClientEntriesBundler, VENDOR_SPECIFIERS, RouteClientBuilder, AutoImportSync, DevChangePlanner, DevGeneratedIndexSync };
|