@barefootjs/cli 0.2.0 → 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/docs/core/adapters/hono-adapter.md +21 -0
- package/dist/index.js +2486 -1375
- package/dist/preview-globals.css +173 -0
- package/dist/preview-uno.config.ts +134 -0
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -22,9 +22,9 @@ async function loadBuildConfig(configPath) {
|
|
|
22
22
|
let importTarget = configPath;
|
|
23
23
|
let cleanupPath = null;
|
|
24
24
|
if (!isBun) {
|
|
25
|
-
const { build:
|
|
25
|
+
const { build: build3 } = await import("esbuild");
|
|
26
26
|
importTarget = resolve(dirname(configPath), `.barefoot.config.${process.pid}.mjs`);
|
|
27
|
-
await
|
|
27
|
+
await build3({
|
|
28
28
|
entryPoints: [configPath],
|
|
29
29
|
outfile: importTarget,
|
|
30
30
|
bundle: true,
|
|
@@ -36,8 +36,8 @@ async function loadBuildConfig(configPath) {
|
|
|
36
36
|
// resolution still happens at runtime from the user's project.
|
|
37
37
|
plugins: [{
|
|
38
38
|
name: "externalize-non-barefoot",
|
|
39
|
-
setup(
|
|
40
|
-
|
|
39
|
+
setup(build4) {
|
|
40
|
+
build4.onResolve({ filter: /^[^./]/ }, (args2) => {
|
|
41
41
|
if (args2.path.startsWith("@barefootjs/")) return null;
|
|
42
42
|
return { path: args2.path, external: true };
|
|
43
43
|
});
|
|
@@ -1885,6 +1885,9 @@ function irToComponentTemplateWithOpts(node, opts) {
|
|
|
1885
1885
|
}
|
|
1886
1886
|
return `\${${transformExpr(node.expr, node.templateExpr)}}`;
|
|
1887
1887
|
case "conditional": {
|
|
1888
|
+
if (node.clientOnly && node.slotId) {
|
|
1889
|
+
return `<!--bf-cond-start:${node.slotId}--><!--bf-cond-end:${node.slotId}-->`;
|
|
1890
|
+
}
|
|
1888
1891
|
const trueBranch = recurse(node.whenTrue);
|
|
1889
1892
|
const falseBranch = recurse(node.whenFalse);
|
|
1890
1893
|
const trueHtml = node.slotId ? addCondAttrToTemplate(trueBranch, node.slotId) : trueBranch;
|
|
@@ -2103,6 +2106,9 @@ function generateCsrTemplateWithOpts(node, opts) {
|
|
|
2103
2106
|
return `\${${expr}}`;
|
|
2104
2107
|
}
|
|
2105
2108
|
case "conditional": {
|
|
2109
|
+
if (node.clientOnly && node.slotId) {
|
|
2110
|
+
return `<!--bf-cond-start:${node.slotId}--><!--bf-cond-end:${node.slotId}-->`;
|
|
2111
|
+
}
|
|
2106
2112
|
const trueBranch = recurse(node.whenTrue);
|
|
2107
2113
|
const falseBranch = recurse(node.whenFalse);
|
|
2108
2114
|
const trueHtml = node.slotId ? addCondAttrToTemplate(trueBranch, node.slotId) : trueBranch;
|
|
@@ -7147,7 +7153,7 @@ function transformExpressionInner(expr, ctx2, node, isClientOnly) {
|
|
|
7147
7153
|
}
|
|
7148
7154
|
const ir = transformJsxExpression(expr, ctx2, isClientOnly);
|
|
7149
7155
|
if (ir !== null) {
|
|
7150
|
-
if (isClientOnly && ir.type === "conditional") {
|
|
7156
|
+
if ((isClientOnly || shouldAutoDeferReactiveBrand(expr, ctx2)) && ir.type === "conditional") {
|
|
7151
7157
|
ir.clientOnly = true;
|
|
7152
7158
|
if (!ir.slotId) {
|
|
7153
7159
|
ir.slotId = generateSlotId(ctx2);
|
|
@@ -8221,11 +8227,11 @@ function transformMapCall(node, ctx2, isClientOnly = false, method = "map") {
|
|
|
8221
8227
|
if (stmt === returnStmt) break;
|
|
8222
8228
|
const js = ctx2.getJS(stmt);
|
|
8223
8229
|
const tjs = ctx2.getTemplateJS(stmt);
|
|
8224
|
-
const
|
|
8230
|
+
const ts20 = stmt.getText(ctx2.sourceFile);
|
|
8225
8231
|
preambleStmts.push(js.endsWith(";") ? js : js + ";");
|
|
8226
8232
|
templatePreambleStmts.push(tjs.endsWith(";") ? tjs : tjs + ";");
|
|
8227
|
-
typedPreambleStmts.push(
|
|
8228
|
-
if (js !==
|
|
8233
|
+
typedPreambleStmts.push(ts20.endsWith(";") ? ts20 : ts20 + ";");
|
|
8234
|
+
if (js !== ts20) hasTypeDiff = true;
|
|
8229
8235
|
if (js !== tjs) hasTemplateDiff = true;
|
|
8230
8236
|
}
|
|
8231
8237
|
if (preambleStmts.length > 0) {
|
|
@@ -8524,7 +8530,7 @@ function processAttributes(attributes, ctx2) {
|
|
|
8524
8530
|
value = { ...value, templateExpr: rewritten };
|
|
8525
8531
|
}
|
|
8526
8532
|
}
|
|
8527
|
-
if (hasLeadingClientDirective(attr.initializer.expression, ctx2.sourceFile)) {
|
|
8533
|
+
if (hasLeadingClientDirective(attr.initializer.expression, ctx2.sourceFile) || shouldAutoDeferReactiveBrand(attr.initializer.expression, ctx2)) {
|
|
8528
8534
|
clientOnly = true;
|
|
8529
8535
|
}
|
|
8530
8536
|
}
|
|
@@ -8927,6 +8933,13 @@ function isReactiveExpression(expr, ctx2, astNode) {
|
|
|
8927
8933
|
}
|
|
8928
8934
|
return false;
|
|
8929
8935
|
}
|
|
8936
|
+
function shouldAutoDeferReactiveBrand(expr, ctx2) {
|
|
8937
|
+
const checker = ctx2.analyzer.checker;
|
|
8938
|
+
if (!checker) return false;
|
|
8939
|
+
if (!containsReactiveExpression(expr, checker)) return false;
|
|
8940
|
+
if (isSignalOrMemoReference(ctx2.getJS(expr), ctx2)) return false;
|
|
8941
|
+
return true;
|
|
8942
|
+
}
|
|
8930
8943
|
function isSignalOrMemoReference(expr, ctx2, visited) {
|
|
8931
8944
|
for (const { pattern } of ctx2.patterns.signals) {
|
|
8932
8945
|
if (pattern.test(expr)) return true;
|
|
@@ -9026,10 +9039,10 @@ function hasDynamicContent(children) {
|
|
|
9026
9039
|
function inferExpressionType(_node, _ctx) {
|
|
9027
9040
|
return null;
|
|
9028
9041
|
}
|
|
9029
|
-
function replaceBranchLocalRefs(text, branchNames,
|
|
9042
|
+
function replaceBranchLocalRefs(text, branchNames, resolve11) {
|
|
9030
9043
|
if (branchNames.length === 0) return text;
|
|
9031
9044
|
const pattern = new RegExp(`(?<![\\w$])(${branchNames.join("|")})(?![\\w$])`, "g");
|
|
9032
|
-
return replaceInExprContexts(text, pattern, (_match, name) =>
|
|
9045
|
+
return replaceInExprContexts(text, pattern, (_match, name) => resolve11(name));
|
|
9033
9046
|
}
|
|
9034
9047
|
function buildIfStatementChain(analyzer, ctx2) {
|
|
9035
9048
|
const conditionalReturns = analyzer.conditionalReturns;
|
|
@@ -17476,13 +17489,51 @@ function makeIdRefRegex(name) {
|
|
|
17476
17489
|
}
|
|
17477
17490
|
function buildLocalFunctionSetterMap(meta, setterToSignal) {
|
|
17478
17491
|
const setterPatterns = [...setterToSignal.keys()].map((s) => ({ name: s, re: makeIdCallRegex(s) }));
|
|
17479
|
-
const
|
|
17480
|
-
for (const fn of meta.localFunctions)
|
|
17492
|
+
const bodies = /* @__PURE__ */ new Map();
|
|
17493
|
+
for (const fn of meta.localFunctions) bodies.set(fn.name, fn.body);
|
|
17494
|
+
for (const c of meta.localConstants) {
|
|
17495
|
+
if (c.containsArrow && c.value) bodies.set(c.name, c.value);
|
|
17496
|
+
}
|
|
17497
|
+
const fnNamePatterns = [...bodies.keys()].map((n) => ({ name: n, re: makeIdCallRegex(n) }));
|
|
17498
|
+
const directSetters = /* @__PURE__ */ new Map();
|
|
17499
|
+
const directCalls = /* @__PURE__ */ new Map();
|
|
17500
|
+
for (const [name, body] of bodies) {
|
|
17481
17501
|
const setters = [];
|
|
17482
|
-
for (const { name, re } of setterPatterns) {
|
|
17483
|
-
if (re.test(
|
|
17502
|
+
for (const { name: setter, re } of setterPatterns) {
|
|
17503
|
+
if (re.test(body)) setters.push(setter);
|
|
17504
|
+
}
|
|
17505
|
+
directSetters.set(name, setters);
|
|
17506
|
+
const calls = [];
|
|
17507
|
+
for (const { name: callee, re } of fnNamePatterns) {
|
|
17508
|
+
if (callee !== name && re.test(body)) calls.push(callee);
|
|
17509
|
+
}
|
|
17510
|
+
directCalls.set(name, calls);
|
|
17511
|
+
}
|
|
17512
|
+
const resolve11 = (name, stack) => {
|
|
17513
|
+
const out = [];
|
|
17514
|
+
const seen = /* @__PURE__ */ new Set();
|
|
17515
|
+
for (const setter of directSetters.get(name) ?? []) {
|
|
17516
|
+
if (!seen.has(setter)) {
|
|
17517
|
+
out.push({ setter, chain: [] });
|
|
17518
|
+
seen.add(setter);
|
|
17519
|
+
}
|
|
17520
|
+
}
|
|
17521
|
+
for (const callee of directCalls.get(name) ?? []) {
|
|
17522
|
+
if (stack.has(callee)) continue;
|
|
17523
|
+
const sub2 = resolve11(callee, /* @__PURE__ */ new Set([...stack, callee]));
|
|
17524
|
+
for (const r of sub2) {
|
|
17525
|
+
if (!seen.has(r.setter)) {
|
|
17526
|
+
out.push({ setter: r.setter, chain: [callee, ...r.chain] });
|
|
17527
|
+
seen.add(r.setter);
|
|
17528
|
+
}
|
|
17529
|
+
}
|
|
17484
17530
|
}
|
|
17485
|
-
|
|
17531
|
+
return out;
|
|
17532
|
+
};
|
|
17533
|
+
const result = /* @__PURE__ */ new Map();
|
|
17534
|
+
for (const name of bodies.keys()) {
|
|
17535
|
+
const resolved = resolve11(name, /* @__PURE__ */ new Set([name]));
|
|
17536
|
+
if (resolved.length > 0) result.set(name, resolved);
|
|
17486
17537
|
}
|
|
17487
17538
|
return result;
|
|
17488
17539
|
}
|
|
@@ -17574,12 +17625,16 @@ function resolveSetters(handler, setterToSignal, fnSetters) {
|
|
|
17574
17625
|
}
|
|
17575
17626
|
}
|
|
17576
17627
|
}
|
|
17577
|
-
for (const [fnName,
|
|
17628
|
+
for (const [fnName, resolutions] of fnSetters) {
|
|
17578
17629
|
if (trimmed === fnName || makeIdCallRegex(fnName).test(handler)) {
|
|
17579
|
-
for (const
|
|
17580
|
-
if (!seen.has(setter)) {
|
|
17581
|
-
refs.push({
|
|
17582
|
-
|
|
17630
|
+
for (const r of resolutions) {
|
|
17631
|
+
if (!seen.has(r.setter)) {
|
|
17632
|
+
refs.push({
|
|
17633
|
+
setter: r.setter,
|
|
17634
|
+
signal: setterToSignal.get(r.setter) ?? null,
|
|
17635
|
+
via: [fnName, ...r.chain]
|
|
17636
|
+
});
|
|
17637
|
+
seen.add(r.setter);
|
|
17583
17638
|
}
|
|
17584
17639
|
}
|
|
17585
17640
|
}
|
|
@@ -17613,7 +17668,7 @@ function formatEventSummary(summary, graph) {
|
|
|
17613
17668
|
lines.push("");
|
|
17614
17669
|
lines.push(` ${event.elementContext}`);
|
|
17615
17670
|
const setterParts = event.setterCalls.map((s) => {
|
|
17616
|
-
const chain = s.via ? `${s.via} -> ${s.setter}` : s.setter;
|
|
17671
|
+
const chain = s.via && s.via.length > 0 ? `${s.via.join(" -> ")} -> ${s.setter}` : s.setter;
|
|
17617
17672
|
return chain;
|
|
17618
17673
|
});
|
|
17619
17674
|
const setterStr = setterParts.length > 0 ? setterParts.join(", ") : event.handler;
|
|
@@ -17883,6 +17938,95 @@ function buildUpdateEntry(consumer, graph, visited) {
|
|
|
17883
17938
|
}
|
|
17884
17939
|
return { name: consumer, kind: "effect", label: consumer, children: [] };
|
|
17885
17940
|
}
|
|
17941
|
+
function buildWhyUpdate(source, filePath, bindingLabel, componentName) {
|
|
17942
|
+
const { graph, ir } = buildComponentAnalysis(source, filePath, componentName);
|
|
17943
|
+
const matches = graph.domBindings.filter(
|
|
17944
|
+
(d) => d.label === bindingLabel || d.slotId === bindingLabel
|
|
17945
|
+
);
|
|
17946
|
+
if (matches.length === 0) return null;
|
|
17947
|
+
if (matches.length > 1) {
|
|
17948
|
+
return {
|
|
17949
|
+
binding: bindingLabel,
|
|
17950
|
+
expression: null,
|
|
17951
|
+
deps: [],
|
|
17952
|
+
ambiguous: matches.map((d) => ({ label: d.label, slotId: d.slotId }))
|
|
17953
|
+
};
|
|
17954
|
+
}
|
|
17955
|
+
const binding = matches[0];
|
|
17956
|
+
const setterToSignal = /* @__PURE__ */ new Map();
|
|
17957
|
+
for (const s of ir.metadata.signals) {
|
|
17958
|
+
if (s.setter) setterToSignal.set(s.setter, s.getter);
|
|
17959
|
+
}
|
|
17960
|
+
const fnSetters = buildLocalFunctionSetterMap(ir.metadata, setterToSignal);
|
|
17961
|
+
const events = collectEventBindings(ir.root, setterToSignal, fnSetters);
|
|
17962
|
+
const deps = [];
|
|
17963
|
+
const visited = /* @__PURE__ */ new Set();
|
|
17964
|
+
function traceDep(name) {
|
|
17965
|
+
if (visited.has(name)) return;
|
|
17966
|
+
visited.add(name);
|
|
17967
|
+
const signal = graph.signals.find((s) => s.name === name);
|
|
17968
|
+
if (signal) {
|
|
17969
|
+
const changedBy = [];
|
|
17970
|
+
for (const ev of events) {
|
|
17971
|
+
for (const sc of ev.setterCalls) {
|
|
17972
|
+
if (sc.signal === name) {
|
|
17973
|
+
changedBy.push({
|
|
17974
|
+
handler: ev.eventName,
|
|
17975
|
+
setter: sc.setter,
|
|
17976
|
+
elementContext: ev.elementContext,
|
|
17977
|
+
via: sc.via
|
|
17978
|
+
});
|
|
17979
|
+
}
|
|
17980
|
+
}
|
|
17981
|
+
}
|
|
17982
|
+
deps.push({ name, kind: "signal", dependsOn: [], changedBy });
|
|
17983
|
+
return;
|
|
17984
|
+
}
|
|
17985
|
+
const memo = graph.memos.find((m) => m.name === name);
|
|
17986
|
+
if (memo) {
|
|
17987
|
+
deps.push({ name, kind: "memo", dependsOn: memo.deps, changedBy: [] });
|
|
17988
|
+
for (const dep of memo.deps) traceDep(dep);
|
|
17989
|
+
}
|
|
17990
|
+
}
|
|
17991
|
+
for (const dep of binding.deps) traceDep(dep);
|
|
17992
|
+
const stableId = binding.type === "attribute" ? binding.label : binding.slotId;
|
|
17993
|
+
return {
|
|
17994
|
+
binding: stableId,
|
|
17995
|
+
expression: binding.expression ?? null,
|
|
17996
|
+
deps,
|
|
17997
|
+
...binding.classification === "fallback" && { classification: binding.classification },
|
|
17998
|
+
...binding.wrapReason && { wrapReason: binding.wrapReason }
|
|
17999
|
+
};
|
|
18000
|
+
}
|
|
18001
|
+
function formatWhyUpdate(result) {
|
|
18002
|
+
const lines = [];
|
|
18003
|
+
lines.push(`${result.binding} updates because:`);
|
|
18004
|
+
if (result.expression) {
|
|
18005
|
+
lines.push(` ${result.expression}`);
|
|
18006
|
+
}
|
|
18007
|
+
if (result.classification === "fallback") {
|
|
18008
|
+
lines.push("");
|
|
18009
|
+
lines.push(`note: this is a fallback-wrapped binding (${result.wrapReason ?? "unknown"})`);
|
|
18010
|
+
lines.push(" the compiler could not statically prove reactivity \u2014 deps are determined at runtime");
|
|
18011
|
+
}
|
|
18012
|
+
for (const dep of result.deps) {
|
|
18013
|
+
lines.push("");
|
|
18014
|
+
if (dep.kind === "memo") {
|
|
18015
|
+
lines.push(`${dep.name} depends on:`);
|
|
18016
|
+
for (const d of dep.dependsOn) lines.push(` ${d}`);
|
|
18017
|
+
} else {
|
|
18018
|
+
lines.push(`${dep.name} changes from:`);
|
|
18019
|
+
if (dep.changedBy.length === 0) {
|
|
18020
|
+
lines.push(" (no event handlers found)");
|
|
18021
|
+
}
|
|
18022
|
+
for (const src of dep.changedBy) {
|
|
18023
|
+
const chain = src.via && src.via.length > 0 ? `${src.elementContext} ${src.handler} -> ${src.via.join(" -> ")} -> ${src.setter}` : `${src.elementContext} ${src.handler} -> ${src.setter}`;
|
|
18024
|
+
lines.push(` ${chain}`);
|
|
18025
|
+
}
|
|
18026
|
+
}
|
|
18027
|
+
}
|
|
18028
|
+
return lines.join("\n");
|
|
18029
|
+
}
|
|
17886
18030
|
function formatComponentGraph(graph) {
|
|
17887
18031
|
const lines = [];
|
|
17888
18032
|
lines.push(`${graph.componentName} (${graph.sourceFile})`);
|
|
@@ -18054,6 +18198,145 @@ function formatSignalTrace(traces) {
|
|
|
18054
18198
|
}
|
|
18055
18199
|
}).join("\n");
|
|
18056
18200
|
}
|
|
18201
|
+
function describeFallback(binding) {
|
|
18202
|
+
const isEventHandler = binding.type === "event" || binding.type === "attribute" && /^on[A-Z]/.test(binding.label.split(".").pop() ?? "");
|
|
18203
|
+
const reason = describeFallbackReason(binding.wrapReason, binding.type, isEventHandler);
|
|
18204
|
+
const runtimeDeps = binding.deps.length > 0 ? binding.deps.join(", ") : isEventHandler ? "likely none (event handler captures values, does not track reactively)" : "unknown \u2014 subscribes to whatever signals it reads at runtime";
|
|
18205
|
+
const suggestion = isEventHandler ? "event handlers intentionally capture scope values; this fallback is typically safe to ignore" : binding.wrapReason === "fallback-function-calls" ? "inline the reactive source or wrap the result in createMemo so the compiler can prove the dependency" : binding.wrapReason === "fallback-getter-calls" ? "the call looks like a signal getter but is not a known signal; verify the function is pure or extract as createMemo" : "rewrite to use a known signal/memo reference so the compiler can statically prove reactivity";
|
|
18206
|
+
return {
|
|
18207
|
+
label: binding.label,
|
|
18208
|
+
expression: binding.expression ?? "(expression not captured)",
|
|
18209
|
+
reason,
|
|
18210
|
+
runtimeDeps,
|
|
18211
|
+
suggestion,
|
|
18212
|
+
loc: binding.loc ? { file: binding.loc.file, line: binding.loc.start.line } : void 0,
|
|
18213
|
+
isEventHandler
|
|
18214
|
+
};
|
|
18215
|
+
}
|
|
18216
|
+
function describeFallbackReason(wrapReason, bindingType, isEventHandler) {
|
|
18217
|
+
const context = bindingType === "attribute" ? "an attribute expression" : bindingType === "text" ? "a text interpolation" : bindingType === "conditional" ? "a conditional expression" : bindingType === "loop" ? "a loop array expression" : "an expression";
|
|
18218
|
+
switch (wrapReason) {
|
|
18219
|
+
case "fallback-function-calls":
|
|
18220
|
+
return isEventHandler ? `function call in ${context} (event handler prop)` : `opaque function call in ${context} \u2014 the compiler cannot prove it is reactive or pure`;
|
|
18221
|
+
case "fallback-getter-calls":
|
|
18222
|
+
return `call pattern resembles a signal getter in ${context}, but is not a recognized signal`;
|
|
18223
|
+
case "string-reactive":
|
|
18224
|
+
return `string-level match found a signal/memo name in ${context}`;
|
|
18225
|
+
case "props-access":
|
|
18226
|
+
return `props.xxx reference in ${context} \u2014 reactive via prop forwarding`;
|
|
18227
|
+
case "proven-reactive":
|
|
18228
|
+
return `statically proven reactive in ${context}`;
|
|
18229
|
+
default:
|
|
18230
|
+
return `unknown fallback trigger in ${context}`;
|
|
18231
|
+
}
|
|
18232
|
+
}
|
|
18233
|
+
function formatFallbackExplanations(componentName, fallbacks) {
|
|
18234
|
+
const lines = [];
|
|
18235
|
+
if (fallbacks.length === 0) {
|
|
18236
|
+
lines.push(`${componentName} \u2014 no fallback-wrapped expressions.`);
|
|
18237
|
+
return lines.join("\n");
|
|
18238
|
+
}
|
|
18239
|
+
lines.push(`${componentName} \u2014 ${fallbacks.length} fallback-wrapped expression(s)`);
|
|
18240
|
+
for (const f of fallbacks) {
|
|
18241
|
+
const ex = describeFallback(f);
|
|
18242
|
+
lines.push("");
|
|
18243
|
+
if (ex.loc) {
|
|
18244
|
+
const locFile = ex.loc.file.split("/").pop() ?? ex.loc.file;
|
|
18245
|
+
lines.push(` ${locFile}:${ex.loc.line}`);
|
|
18246
|
+
}
|
|
18247
|
+
lines.push(` ${ex.label} fallback:`);
|
|
18248
|
+
lines.push(` expression: ${ex.expression}`);
|
|
18249
|
+
lines.push(` reason: ${ex.reason}`);
|
|
18250
|
+
lines.push(` runtime deps: ${ex.runtimeDeps}`);
|
|
18251
|
+
lines.push(` suggestion: ${ex.suggestion}`);
|
|
18252
|
+
}
|
|
18253
|
+
return lines.join("\n");
|
|
18254
|
+
}
|
|
18255
|
+
function buildComponentSummary(source, filePath, componentName) {
|
|
18256
|
+
const { graph, ir } = buildComponentAnalysis(source, filePath, componentName);
|
|
18257
|
+
const meta = ir.metadata;
|
|
18258
|
+
const clientNeeds = analyzeClientNeeds(ir);
|
|
18259
|
+
const hasReactiveState = meta.signals.length > 0 || meta.memos.length > 0 || meta.effects.length > 0;
|
|
18260
|
+
const needsClient = clientNeeds.needsInit && hasReactiveState;
|
|
18261
|
+
let loopCount = 0;
|
|
18262
|
+
countNodeType(ir.root, "loop", () => {
|
|
18263
|
+
loopCount++;
|
|
18264
|
+
});
|
|
18265
|
+
let conditionalCount = 0;
|
|
18266
|
+
countNodeType(ir.root, "conditional", () => {
|
|
18267
|
+
conditionalCount++;
|
|
18268
|
+
});
|
|
18269
|
+
const eventHandlers = graph.domBindings.filter((d) => d.type === "event").length;
|
|
18270
|
+
const textBindings = graph.domBindings.filter((d) => d.type === "text").length;
|
|
18271
|
+
const attrBindings = graph.domBindings.filter((d) => d.type === "attribute").length;
|
|
18272
|
+
const fallbacks = graph.domBindings.filter((d) => d.classification === "fallback").length;
|
|
18273
|
+
let clientBundle = null;
|
|
18274
|
+
if (needsClient) {
|
|
18275
|
+
const base = filePath.replace(/\.[^.]+$/, "").split("/").pop() ?? meta.componentName;
|
|
18276
|
+
clientBundle = `${base}.client.js`;
|
|
18277
|
+
}
|
|
18278
|
+
return {
|
|
18279
|
+
componentName: graph.componentName,
|
|
18280
|
+
sourceFile: graph.sourceFile,
|
|
18281
|
+
hydrated: needsClient,
|
|
18282
|
+
clientBundle,
|
|
18283
|
+
signals: graph.signals.length,
|
|
18284
|
+
memos: graph.memos.length,
|
|
18285
|
+
effects: graph.effects.length,
|
|
18286
|
+
loops: loopCount,
|
|
18287
|
+
eventHandlers,
|
|
18288
|
+
dynamicTextBindings: textBindings,
|
|
18289
|
+
dynamicAttributes: attrBindings,
|
|
18290
|
+
conditionals: conditionalCount,
|
|
18291
|
+
fallbacks
|
|
18292
|
+
};
|
|
18293
|
+
}
|
|
18294
|
+
function countNodeType(node, targetType, cb) {
|
|
18295
|
+
if (node.type === targetType) cb();
|
|
18296
|
+
switch (node.type) {
|
|
18297
|
+
case "element":
|
|
18298
|
+
case "fragment":
|
|
18299
|
+
case "provider":
|
|
18300
|
+
for (const child of node.children) countNodeType(child, targetType, cb);
|
|
18301
|
+
break;
|
|
18302
|
+
case "component":
|
|
18303
|
+
for (const child of node.children) countNodeType(child, targetType, cb);
|
|
18304
|
+
break;
|
|
18305
|
+
case "conditional":
|
|
18306
|
+
countNodeType(node.whenTrue, targetType, cb);
|
|
18307
|
+
countNodeType(node.whenFalse, targetType, cb);
|
|
18308
|
+
break;
|
|
18309
|
+
case "loop":
|
|
18310
|
+
for (const child of node.children) countNodeType(child, targetType, cb);
|
|
18311
|
+
break;
|
|
18312
|
+
case "if-statement":
|
|
18313
|
+
countNodeType(node.consequent, targetType, cb);
|
|
18314
|
+
if (node.alternate) countNodeType(node.alternate, targetType, cb);
|
|
18315
|
+
break;
|
|
18316
|
+
case "async":
|
|
18317
|
+
countNodeType(node.fallback, targetType, cb);
|
|
18318
|
+
for (const child of node.children) countNodeType(child, targetType, cb);
|
|
18319
|
+
break;
|
|
18320
|
+
}
|
|
18321
|
+
}
|
|
18322
|
+
function formatComponentSummary(summary) {
|
|
18323
|
+
const lines = [];
|
|
18324
|
+
lines.push(summary.componentName);
|
|
18325
|
+
lines.push(` hydrated: ${summary.hydrated ? "yes" : "no"}`);
|
|
18326
|
+
if (summary.clientBundle) {
|
|
18327
|
+
lines.push(` client bundle: ${summary.clientBundle}`);
|
|
18328
|
+
}
|
|
18329
|
+
lines.push(` signals: ${summary.signals}`);
|
|
18330
|
+
lines.push(` memos: ${summary.memos}`);
|
|
18331
|
+
if (summary.effects > 0) lines.push(` effects: ${summary.effects}`);
|
|
18332
|
+
lines.push(` loops: ${summary.loops}`);
|
|
18333
|
+
lines.push(` event handlers: ${summary.eventHandlers}`);
|
|
18334
|
+
lines.push(` dynamic text bindings: ${summary.dynamicTextBindings}`);
|
|
18335
|
+
lines.push(` dynamic attributes: ${summary.dynamicAttributes}`);
|
|
18336
|
+
if (summary.conditionals > 0) lines.push(` conditionals: ${summary.conditionals}`);
|
|
18337
|
+
if (summary.fallbacks > 0) lines.push(` fallbacks: ${summary.fallbacks}`);
|
|
18338
|
+
return lines.join("\n");
|
|
18339
|
+
}
|
|
18057
18340
|
function inferWrapReasonForAttrLike(hasStringReactive, hasPropsRef, flags) {
|
|
18058
18341
|
if (hasPropsRef) return "props-access";
|
|
18059
18342
|
if (hasStringReactive) return "string-reactive";
|
|
@@ -18284,6 +18567,7 @@ var init_debug = __esm({
|
|
|
18284
18567
|
init_analyzer();
|
|
18285
18568
|
init_jsx_to_ir();
|
|
18286
18569
|
init_compiler();
|
|
18570
|
+
init_ir_to_client_js();
|
|
18287
18571
|
init_reactivity();
|
|
18288
18572
|
}
|
|
18289
18573
|
});
|
|
@@ -18304,18 +18588,22 @@ __export(src_exports, {
|
|
|
18304
18588
|
applyCssLayerPrefix: () => applyCssLayerPrefix,
|
|
18305
18589
|
buildComponentAnalysis: () => buildComponentAnalysis,
|
|
18306
18590
|
buildComponentGraph: () => buildComponentGraph,
|
|
18591
|
+
buildComponentSummary: () => buildComponentSummary,
|
|
18307
18592
|
buildEventSummary: () => buildEventSummary,
|
|
18308
18593
|
buildGraphFromIR: () => buildGraphFromIR,
|
|
18594
|
+
buildLocalFunctionSetterMap: () => buildLocalFunctionSetterMap,
|
|
18309
18595
|
buildLoopChainExpr: () => buildLoopChainExpr,
|
|
18310
18596
|
buildLoopSummary: () => buildLoopSummary,
|
|
18311
18597
|
buildMetadata: () => buildMetadata,
|
|
18312
18598
|
buildSourceMapFromIR: () => buildSourceMapFromIR,
|
|
18599
|
+
buildWhyUpdate: () => buildWhyUpdate,
|
|
18313
18600
|
combineParentChildClientJs: () => combineParentChildClientJs,
|
|
18314
18601
|
compileJSX: () => compileJSX,
|
|
18315
18602
|
containsHigherOrder: () => containsHigherOrder,
|
|
18316
18603
|
createError: () => createError,
|
|
18317
18604
|
createProgramForCorpus: () => createProgramForCorpus,
|
|
18318
18605
|
createProgramForFile: () => createProgramForFile,
|
|
18606
|
+
describeFallback: () => describeFallback,
|
|
18319
18607
|
disableCompilerInstrumentation: () => disableCompilerInstrumentation,
|
|
18320
18608
|
emitAttrValue: () => emitAttrValue,
|
|
18321
18609
|
emitIRNode: () => emitIRNode,
|
|
@@ -18326,12 +18614,15 @@ __export(src_exports, {
|
|
|
18326
18614
|
extractSsrDefaults: () => extractSsrDefaults,
|
|
18327
18615
|
findReachableNames: () => findReachableNames,
|
|
18328
18616
|
formatComponentGraph: () => formatComponentGraph,
|
|
18617
|
+
formatComponentSummary: () => formatComponentSummary,
|
|
18329
18618
|
formatError: () => formatError,
|
|
18330
18619
|
formatEventSummary: () => formatEventSummary,
|
|
18620
|
+
formatFallbackExplanations: () => formatFallbackExplanations,
|
|
18331
18621
|
formatLoopSummary: () => formatLoopSummary,
|
|
18332
18622
|
formatParamWithType: () => formatParamWithType,
|
|
18333
18623
|
formatSignalTrace: () => formatSignalTrace,
|
|
18334
18624
|
formatUpdatePath: () => formatUpdatePath,
|
|
18625
|
+
formatWhyUpdate: () => formatWhyUpdate,
|
|
18335
18626
|
generateClientJs: () => generateClientJs,
|
|
18336
18627
|
generateClientJsWithSourceMap: () => generateClientJsWithSourceMap,
|
|
18337
18628
|
generateCodeFrame: () => generateCodeFrame,
|
|
@@ -18345,10 +18636,12 @@ __export(src_exports, {
|
|
|
18345
18636
|
jsxToIR: () => jsxToIR,
|
|
18346
18637
|
listComponentFunctions: () => listComponentFunctions,
|
|
18347
18638
|
listExportedComponents: () => listComponentFunctions,
|
|
18639
|
+
makeIdCallRegex: () => makeIdCallRegex,
|
|
18348
18640
|
needsTypeBasedDetection: () => needsTypeBasedDetection,
|
|
18349
18641
|
parseBlockBody: () => parseBlockBody,
|
|
18350
18642
|
parseExpression: () => parseExpression,
|
|
18351
18643
|
resetCompilerCounters: () => resetCompilerCounters,
|
|
18644
|
+
resolveSetters: () => resolveSetters,
|
|
18352
18645
|
rewriteImportsForTemplate: () => rewriteImportsForTemplate,
|
|
18353
18646
|
stringifyParsedExpr: () => stringifyParsedExpr,
|
|
18354
18647
|
traceUpdatePath: () => traceUpdatePath
|
|
@@ -19174,9 +19467,78 @@ var init_emit_ledger = __esm({
|
|
|
19174
19467
|
}
|
|
19175
19468
|
});
|
|
19176
19469
|
|
|
19470
|
+
// src/lib/assets-ignore.ts
|
|
19471
|
+
import { resolve as resolve5 } from "node:path";
|
|
19472
|
+
async function isCloudflareWorkersProject(projectDir) {
|
|
19473
|
+
for (const name of WRANGLER_CONFIG_NAMES) {
|
|
19474
|
+
if (await fileExists(resolve5(projectDir, name))) return true;
|
|
19475
|
+
}
|
|
19476
|
+
return false;
|
|
19477
|
+
}
|
|
19478
|
+
function collectServerOnlyAssets(input) {
|
|
19479
|
+
const entries = /* @__PURE__ */ new Set();
|
|
19480
|
+
entries.add(`${input.devSentinelSubdir}/`);
|
|
19481
|
+
entries.add(EMIT_LEDGER_FILENAME);
|
|
19482
|
+
entries.add(CACHE_FILENAME);
|
|
19483
|
+
if (input.hasExternals) entries.add("barefoot-externals.json");
|
|
19484
|
+
if (!input.clientOnly) {
|
|
19485
|
+
entries.add(`${input.templatesSubdir}/manifest.json`);
|
|
19486
|
+
for (const row of Object.values(input.manifest)) {
|
|
19487
|
+
if (row.markedTemplate) entries.add(row.markedTemplate);
|
|
19488
|
+
}
|
|
19489
|
+
}
|
|
19490
|
+
return [...entries].sort();
|
|
19491
|
+
}
|
|
19492
|
+
function stripManagedBlock(content) {
|
|
19493
|
+
const lines = content.split("\n");
|
|
19494
|
+
const begin = lines.indexOf(BLOCK_BEGIN);
|
|
19495
|
+
const end = lines.indexOf(BLOCK_END);
|
|
19496
|
+
if (begin !== -1 && end !== -1 && end > begin) {
|
|
19497
|
+
const kept = [...lines.slice(0, begin), ...lines.slice(end + 1)];
|
|
19498
|
+
return kept.join("\n").replace(/\n{3,}/g, "\n\n").trim();
|
|
19499
|
+
}
|
|
19500
|
+
return content.trim();
|
|
19501
|
+
}
|
|
19502
|
+
function buildManagedBlock(entries) {
|
|
19503
|
+
return [
|
|
19504
|
+
BLOCK_BEGIN,
|
|
19505
|
+
"# Server/build-only barefoot outputs \u2014 not browser-served. Regenerated on",
|
|
19506
|
+
"# every `bf build`; add your own entries outside this block.",
|
|
19507
|
+
...entries,
|
|
19508
|
+
BLOCK_END
|
|
19509
|
+
].join("\n");
|
|
19510
|
+
}
|
|
19511
|
+
async function writeAssetsIgnore(outDir, entries) {
|
|
19512
|
+
const path23 = resolve5(outDir, ASSETS_IGNORE_FILENAME);
|
|
19513
|
+
const existing = await fileExists(path23) ? await readText(path23) : "";
|
|
19514
|
+
const userContent = stripManagedBlock(existing);
|
|
19515
|
+
const block = buildManagedBlock(entries);
|
|
19516
|
+
const merged = userContent.length > 0 ? `${userContent}
|
|
19517
|
+
|
|
19518
|
+
${block}
|
|
19519
|
+
` : `${block}
|
|
19520
|
+
`;
|
|
19521
|
+
return writeIfChanged(path23, merged);
|
|
19522
|
+
}
|
|
19523
|
+
var ASSETS_IGNORE_FILENAME, BLOCK_BEGIN, BLOCK_END, WRANGLER_CONFIG_NAMES;
|
|
19524
|
+
var init_assets_ignore = __esm({
|
|
19525
|
+
"src/lib/assets-ignore.ts"() {
|
|
19526
|
+
"use strict";
|
|
19527
|
+
init_runtime();
|
|
19528
|
+
init_fs_utils();
|
|
19529
|
+
init_build_cache();
|
|
19530
|
+
init_emit_ledger();
|
|
19531
|
+
ASSETS_IGNORE_FILENAME = ".assetsignore";
|
|
19532
|
+
BLOCK_BEGIN = "# >>> barefoot managed block (generated by `bf build`) >>>";
|
|
19533
|
+
BLOCK_END = "# <<< barefoot managed block <<<";
|
|
19534
|
+
WRANGLER_CONFIG_NAMES = ["wrangler.toml", "wrangler.json", "wrangler.jsonc"];
|
|
19535
|
+
}
|
|
19536
|
+
});
|
|
19537
|
+
|
|
19177
19538
|
// src/lib/build.ts
|
|
19539
|
+
import ts19 from "typescript";
|
|
19178
19540
|
import { mkdir, readdir, stat, unlink } from "node:fs/promises";
|
|
19179
|
-
import { resolve as
|
|
19541
|
+
import { resolve as resolve6, basename, relative as relative2, dirname as dirname3, isAbsolute as isAbsolute2 } from "node:path";
|
|
19180
19542
|
import { fileURLToPath as fileURLToPath2 } from "node:url";
|
|
19181
19543
|
import { build as esbuildBuild } from "esbuild";
|
|
19182
19544
|
function detectMissingUseClient(content) {
|
|
@@ -19216,7 +19578,7 @@ async function discoverComponentFiles(dir, options) {
|
|
|
19216
19578
|
return results;
|
|
19217
19579
|
}
|
|
19218
19580
|
for (const entry of entries) {
|
|
19219
|
-
const fullPath =
|
|
19581
|
+
const fullPath = resolve6(dir, String(entry.name));
|
|
19220
19582
|
if (entry.isDirectory()) {
|
|
19221
19583
|
if (skipDirs?.has(String(entry.name))) continue;
|
|
19222
19584
|
results.push(...await discoverComponentFiles(fullPath, options));
|
|
@@ -19231,9 +19593,9 @@ function generateHash(content) {
|
|
|
19231
19593
|
}
|
|
19232
19594
|
function resolveBuildConfigFromTs(projectDir, tsConfig, overrides) {
|
|
19233
19595
|
const componentDirs = (tsConfig.components ?? ["components"]).map(
|
|
19234
|
-
(dir) =>
|
|
19596
|
+
(dir) => resolve6(projectDir, dir)
|
|
19235
19597
|
);
|
|
19236
|
-
const outDir =
|
|
19598
|
+
const outDir = resolve6(projectDir, tsConfig.outDir ?? "dist");
|
|
19237
19599
|
return {
|
|
19238
19600
|
projectDir,
|
|
19239
19601
|
adapter: tsConfig.adapter,
|
|
@@ -19248,7 +19610,7 @@ function resolveBuildConfigFromTs(projectDir, tsConfig, overrides) {
|
|
|
19248
19610
|
externals: tsConfig.externals,
|
|
19249
19611
|
externalsBasePath: tsConfig.externalsBasePath,
|
|
19250
19612
|
bundleEntries: tsConfig.bundleEntries?.map((e) => ({
|
|
19251
|
-
entry:
|
|
19613
|
+
entry: resolve6(projectDir, e.entry),
|
|
19252
19614
|
outfile: e.outfile,
|
|
19253
19615
|
externals: e.externals
|
|
19254
19616
|
})),
|
|
@@ -19258,9 +19620,9 @@ function resolveBuildConfigFromTs(projectDir, tsConfig, overrides) {
|
|
|
19258
19620
|
async function findCliPackageJson() {
|
|
19259
19621
|
const here = dirname3(fileURLToPath2(import.meta.url));
|
|
19260
19622
|
const candidates = [
|
|
19261
|
-
|
|
19623
|
+
resolve6(here, "../package.json"),
|
|
19262
19624
|
// bundled dist/index.js
|
|
19263
|
-
|
|
19625
|
+
resolve6(here, "../../package.json")
|
|
19264
19626
|
// source src/lib/build.ts
|
|
19265
19627
|
];
|
|
19266
19628
|
for (const cand of candidates) {
|
|
@@ -19272,7 +19634,7 @@ async function findNearestLockfile(projectDir) {
|
|
|
19272
19634
|
let dir = projectDir;
|
|
19273
19635
|
while (true) {
|
|
19274
19636
|
for (const name of LOCKFILE_NAMES) {
|
|
19275
|
-
const candidate =
|
|
19637
|
+
const candidate = resolve6(dir, name);
|
|
19276
19638
|
if (await fileExists(candidate)) return candidate;
|
|
19277
19639
|
}
|
|
19278
19640
|
const parent = dirname3(dir);
|
|
@@ -19293,9 +19655,9 @@ async function computeGlobalHash(config) {
|
|
|
19293
19655
|
parts.push(await readText(cliPkgPath));
|
|
19294
19656
|
}
|
|
19295
19657
|
const configCandidates = [
|
|
19296
|
-
|
|
19297
|
-
|
|
19298
|
-
|
|
19658
|
+
resolve6(config.projectDir, "barefoot.config.ts"),
|
|
19659
|
+
resolve6(config.projectDir, "barefoot.config.js"),
|
|
19660
|
+
resolve6(config.projectDir, "barefoot.config.mjs")
|
|
19299
19661
|
];
|
|
19300
19662
|
for (const cand of configCandidates) {
|
|
19301
19663
|
if (await fileExists(cand)) {
|
|
@@ -19316,9 +19678,9 @@ async function build(config, options = {}) {
|
|
|
19316
19678
|
const templatesSubdir = layout?.templates ?? "components";
|
|
19317
19679
|
const clientJsSubdir = layout?.clientJs ?? "components";
|
|
19318
19680
|
const runtimeSubdir = layout?.runtime ?? clientJsSubdir;
|
|
19319
|
-
const templatesOutDir =
|
|
19320
|
-
const clientJsOutDir =
|
|
19321
|
-
const runtimeOutDir =
|
|
19681
|
+
const templatesOutDir = resolve6(config.outDir, templatesSubdir);
|
|
19682
|
+
const clientJsOutDir = resolve6(config.outDir, clientJsSubdir);
|
|
19683
|
+
const runtimeOutDir = resolve6(config.outDir, runtimeSubdir);
|
|
19322
19684
|
await Promise.all([
|
|
19323
19685
|
mkdir(templatesOutDir, { recursive: true }),
|
|
19324
19686
|
mkdir(clientJsOutDir, { recursive: true }),
|
|
@@ -19332,14 +19694,14 @@ async function build(config, options = {}) {
|
|
|
19332
19694
|
let anyOutputChanged = false;
|
|
19333
19695
|
const loadedLedger = await loadEmitLedger(config.outDir, config.projectDir);
|
|
19334
19696
|
const previousEmitEntries = loadedLedger?.entries ?? extractLedgerFromCache(onDiskCache);
|
|
19335
|
-
const domPkgDir =
|
|
19697
|
+
const domPkgDir = resolve6(config.projectDir, "node_modules/@barefootjs/client");
|
|
19336
19698
|
const domDistCandidates = [
|
|
19337
|
-
|
|
19338
|
-
|
|
19699
|
+
resolve6(config.projectDir, "../../packages/client/dist/runtime/standalone.js"),
|
|
19700
|
+
resolve6(domPkgDir, "dist/runtime/standalone.js"),
|
|
19339
19701
|
// Legacy fallback for older @barefootjs/client dists that only shipped
|
|
19340
19702
|
// the single runtime entry.
|
|
19341
|
-
|
|
19342
|
-
|
|
19703
|
+
resolve6(config.projectDir, "../../packages/client/dist/runtime/index.js"),
|
|
19704
|
+
resolve6(domPkgDir, "dist/runtime/index.js")
|
|
19343
19705
|
];
|
|
19344
19706
|
let domDistFile = null;
|
|
19345
19707
|
for (const candidate of domDistCandidates) {
|
|
@@ -19351,7 +19713,7 @@ async function build(config, options = {}) {
|
|
|
19351
19713
|
}
|
|
19352
19714
|
}
|
|
19353
19715
|
if (domDistFile) {
|
|
19354
|
-
const runtimeOutPath =
|
|
19716
|
+
const runtimeOutPath = resolve6(runtimeOutDir, "barefoot.js");
|
|
19355
19717
|
let runtimeContent;
|
|
19356
19718
|
if (config.minify) {
|
|
19357
19719
|
runtimeContent = transpile(await readText(domDistFile), { loader: "js", minify: true });
|
|
@@ -19524,9 +19886,9 @@ async function build(config, options = {}) {
|
|
|
19524
19886
|
if (!currentEmitSet.has(output)) orphanedOutputs.add(output);
|
|
19525
19887
|
}
|
|
19526
19888
|
}
|
|
19527
|
-
const outDirAbs =
|
|
19889
|
+
const outDirAbs = resolve6(config.outDir);
|
|
19528
19890
|
for (const output of orphanedOutputs) {
|
|
19529
|
-
const abs =
|
|
19891
|
+
const abs = resolve6(config.outDir, output);
|
|
19530
19892
|
const rel = relative2(outDirAbs, abs);
|
|
19531
19893
|
const escapes = rel === "" || rel === "." || rel.startsWith("..") || isAbsolute2(rel);
|
|
19532
19894
|
if (escapes) {
|
|
@@ -19554,7 +19916,7 @@ async function build(config, options = {}) {
|
|
|
19554
19916
|
if (!entry.clientJs) continue;
|
|
19555
19917
|
let raw = compiledClientJsByKey.get(name);
|
|
19556
19918
|
if (!raw) {
|
|
19557
|
-
const filePath =
|
|
19919
|
+
const filePath = resolve6(config.outDir, entry.clientJs);
|
|
19558
19920
|
try {
|
|
19559
19921
|
raw = await readText(filePath);
|
|
19560
19922
|
} catch {
|
|
@@ -19569,7 +19931,7 @@ async function build(config, options = {}) {
|
|
|
19569
19931
|
for (const [name, content] of combined) {
|
|
19570
19932
|
const entry = manifest[name];
|
|
19571
19933
|
if (!entry?.clientJs) continue;
|
|
19572
|
-
const filePath =
|
|
19934
|
+
const filePath = resolve6(config.outDir, entry.clientJs);
|
|
19573
19935
|
if (await writeIfChanged(filePath, content)) {
|
|
19574
19936
|
anyOutputChanged = true;
|
|
19575
19937
|
console.log(`Combined: ${entry.clientJs}`);
|
|
@@ -19622,10 +19984,10 @@ async function build(config, options = {}) {
|
|
|
19622
19984
|
}
|
|
19623
19985
|
}
|
|
19624
19986
|
{
|
|
19625
|
-
const runtimeAbs =
|
|
19987
|
+
const runtimeAbs = resolve6(config.outDir, runtimeSubdir, "barefoot.js");
|
|
19626
19988
|
for (const [name, entry] of Object.entries(manifest)) {
|
|
19627
19989
|
if (!entry.clientJs || name === "__barefoot__") continue;
|
|
19628
|
-
const filePath =
|
|
19990
|
+
const filePath = resolve6(config.outDir, entry.clientJs);
|
|
19629
19991
|
let rel = relative2(dirname3(filePath), runtimeAbs);
|
|
19630
19992
|
if (!rel.startsWith(".")) rel = "./" + rel;
|
|
19631
19993
|
try {
|
|
@@ -19648,7 +20010,7 @@ async function build(config, options = {}) {
|
|
|
19648
20010
|
if (config.minify) {
|
|
19649
20011
|
for (const [name, entry] of Object.entries(manifest)) {
|
|
19650
20012
|
if (!entry.clientJs || name === "__barefoot__") continue;
|
|
19651
|
-
const filePath =
|
|
20013
|
+
const filePath = resolve6(config.outDir, entry.clientJs);
|
|
19652
20014
|
try {
|
|
19653
20015
|
const content = await readText(filePath);
|
|
19654
20016
|
if (content) {
|
|
@@ -19673,8 +20035,8 @@ async function build(config, options = {}) {
|
|
|
19673
20035
|
});
|
|
19674
20036
|
}
|
|
19675
20037
|
if (!config.clientOnly) {
|
|
19676
|
-
const manifestDir =
|
|
19677
|
-
const manifestPath =
|
|
20038
|
+
const manifestDir = resolve6(config.outDir, templatesSubdir);
|
|
20039
|
+
const manifestPath = resolve6(manifestDir, "manifest.json");
|
|
19678
20040
|
const manifestContent = JSON.stringify(manifest, null, 2);
|
|
19679
20041
|
if (await writeIfChanged(manifestPath, manifestContent)) {
|
|
19680
20042
|
anyOutputChanged = true;
|
|
@@ -19702,6 +20064,18 @@ async function build(config, options = {}) {
|
|
|
19702
20064
|
saveCache(config.outDir, nextCache),
|
|
19703
20065
|
saveEmitLedger(config.outDir, config.projectDir, nextLedger)
|
|
19704
20066
|
]);
|
|
20067
|
+
if (await isCloudflareWorkersProject(config.projectDir)) {
|
|
20068
|
+
const ignored = collectServerOnlyAssets({
|
|
20069
|
+
devSentinelSubdir: DEV_SENTINEL_SUBDIR,
|
|
20070
|
+
templatesSubdir,
|
|
20071
|
+
manifest,
|
|
20072
|
+
hasExternals: !!config.externals && Object.keys(config.externals).length > 0,
|
|
20073
|
+
clientOnly: config.clientOnly
|
|
20074
|
+
});
|
|
20075
|
+
if (await writeAssetsIgnore(config.outDir, ignored)) {
|
|
20076
|
+
console.log(`Generated: ${ASSETS_IGNORE_FILENAME}`);
|
|
20077
|
+
}
|
|
20078
|
+
}
|
|
19705
20079
|
return {
|
|
19706
20080
|
compiledCount,
|
|
19707
20081
|
skippedCount,
|
|
@@ -19712,6 +20086,28 @@ async function build(config, options = {}) {
|
|
|
19712
20086
|
sharedProgram
|
|
19713
20087
|
};
|
|
19714
20088
|
}
|
|
20089
|
+
function extractBareImports(code) {
|
|
20090
|
+
const { importedFiles } = ts19.preProcessFile(code, true, true);
|
|
20091
|
+
const specifiers = /* @__PURE__ */ new Set();
|
|
20092
|
+
for (const { fileName } of importedFiles) {
|
|
20093
|
+
if (!fileName.startsWith(".") && !fileName.startsWith("/") && !fileName.includes("://")) {
|
|
20094
|
+
specifiers.add(fileName);
|
|
20095
|
+
}
|
|
20096
|
+
}
|
|
20097
|
+
return [...specifiers];
|
|
20098
|
+
}
|
|
20099
|
+
function unresolvedBareImports(code, externals) {
|
|
20100
|
+
const keys = /* @__PURE__ */ new Set([...Object.keys(externals), ...BF_CLIENT_DEDUP_KEYS]);
|
|
20101
|
+
const isResolved = (spec) => keys.has(spec) || [...keys].some((k) => k.endsWith("/") && spec.startsWith(k));
|
|
20102
|
+
return extractBareImports(code).filter((spec) => !isResolved(spec));
|
|
20103
|
+
}
|
|
20104
|
+
function rebundleExternalsFor(pkgName, externals) {
|
|
20105
|
+
return [.../* @__PURE__ */ new Set([
|
|
20106
|
+
...Object.keys(externals).filter((k) => k !== pkgName),
|
|
20107
|
+
...BF_CLIENT_DEDUP_KEYS,
|
|
20108
|
+
"@barefootjs/client/*"
|
|
20109
|
+
])];
|
|
20110
|
+
}
|
|
19715
20111
|
function vendorChunkFilename(pkgName) {
|
|
19716
20112
|
const base = pkgName.includes("/") ? pkgName.split("/").pop() : pkgName;
|
|
19717
20113
|
return `${base}.js`;
|
|
@@ -19720,7 +20116,7 @@ function effectiveNamesFor(entryPath, componentDirs) {
|
|
|
19720
20116
|
const bn = basename(entryPath);
|
|
19721
20117
|
if (componentDirs && componentDirs.length > 0) {
|
|
19722
20118
|
for (const dir of componentDirs) {
|
|
19723
|
-
const root =
|
|
20119
|
+
const root = resolve6(dir);
|
|
19724
20120
|
if (entryPath !== root && entryPath.startsWith(root + "/")) {
|
|
19725
20121
|
const rel = entryPath.slice(root.length + 1);
|
|
19726
20122
|
const noExt2 = rel.replace(/\.[^.]+$/, "");
|
|
@@ -19734,14 +20130,14 @@ function effectiveNamesFor(entryPath, componentDirs) {
|
|
|
19734
20130
|
function buildRelativeImportRewriter(sourcePath, outputPath, componentDirs, templatesOutDir) {
|
|
19735
20131
|
const sourceDir = dirname3(sourcePath);
|
|
19736
20132
|
const outputDir = dirname3(outputPath);
|
|
19737
|
-
const resolvedComponentDirs = componentDirs.map((d) =>
|
|
20133
|
+
const resolvedComponentDirs = componentDirs.map((d) => resolve6(d));
|
|
19738
20134
|
return (importPath) => {
|
|
19739
|
-
const srcAbs =
|
|
20135
|
+
const srcAbs = resolve6(sourceDir, importPath);
|
|
19740
20136
|
let targetAbs = srcAbs;
|
|
19741
20137
|
for (const componentDir of resolvedComponentDirs) {
|
|
19742
20138
|
if (srcAbs === componentDir || srcAbs.startsWith(componentDir + "/")) {
|
|
19743
20139
|
const relUnderComponentDir = srcAbs.slice(componentDir.length + 1);
|
|
19744
|
-
targetAbs = relUnderComponentDir ?
|
|
20140
|
+
targetAbs = relUnderComponentDir ? resolve6(templatesOutDir, relUnderComponentDir) : templatesOutDir;
|
|
19745
20141
|
break;
|
|
19746
20142
|
}
|
|
19747
20143
|
}
|
|
@@ -19794,7 +20190,7 @@ function mergeDuplicateNamedImports(content) {
|
|
|
19794
20190
|
return out.join("\n");
|
|
19795
20191
|
}
|
|
19796
20192
|
async function resolvePkgBrowserEntry(pkgDir) {
|
|
19797
|
-
const pkgJsonPath =
|
|
20193
|
+
const pkgJsonPath = resolve6(pkgDir, "package.json");
|
|
19798
20194
|
if (!await fileExists(pkgJsonPath)) return null;
|
|
19799
20195
|
const pkg = JSON.parse(await readText(pkgJsonPath));
|
|
19800
20196
|
const browserCandidates = [
|
|
@@ -19803,7 +20199,7 @@ async function resolvePkgBrowserEntry(pkgDir) {
|
|
|
19803
20199
|
pkg.jsdelivr
|
|
19804
20200
|
].filter((v) => typeof v === "string");
|
|
19805
20201
|
for (const rel of browserCandidates) {
|
|
19806
|
-
const abs =
|
|
20202
|
+
const abs = resolve6(pkgDir, rel);
|
|
19807
20203
|
if (await fileExists(abs)) return { path: abs, isBrowserReady: true };
|
|
19808
20204
|
}
|
|
19809
20205
|
const fallbackCandidates = [
|
|
@@ -19811,7 +20207,7 @@ async function resolvePkgBrowserEntry(pkgDir) {
|
|
|
19811
20207
|
pkg.main
|
|
19812
20208
|
].filter((v) => typeof v === "string");
|
|
19813
20209
|
for (const rel of fallbackCandidates) {
|
|
19814
|
-
const abs =
|
|
20210
|
+
const abs = resolve6(pkgDir, rel);
|
|
19815
20211
|
if (await fileExists(abs)) return { path: abs, isBrowserReady: false };
|
|
19816
20212
|
}
|
|
19817
20213
|
return null;
|
|
@@ -19834,35 +20230,41 @@ async function processExternals(config, runtimeSubdir, runtimeOutDir) {
|
|
|
19834
20230
|
continue;
|
|
19835
20231
|
}
|
|
19836
20232
|
if (isChunk) {
|
|
19837
|
-
const pkgDir =
|
|
20233
|
+
const pkgDir = resolve6(config.projectDir, "node_modules", pkgName);
|
|
19838
20234
|
const entry = await resolvePkgBrowserEntry(pkgDir);
|
|
19839
20235
|
if (!entry) {
|
|
19840
20236
|
console.warn(`Warning: externals \u2014 could not resolve browser entry for "${pkgName}". Skipping.`);
|
|
19841
20237
|
continue;
|
|
19842
20238
|
}
|
|
19843
20239
|
const wantRebundle = typeof spec === "object" && !("url" in spec) && spec.rebundle === true;
|
|
19844
|
-
if (!entry.isBrowserReady && !wantRebundle) {
|
|
19845
|
-
console.warn(
|
|
19846
|
-
`Warning: externals \u2014 "${pkgName}" resolved via import/main entry (no umd/unpkg/jsdelivr found). The copied file may contain external imports that are not browser-ready. Set rebundle: true to re-bundle it into a self-contained ESM file.`
|
|
19847
|
-
);
|
|
19848
|
-
}
|
|
19849
20240
|
const srcFile = entry.path;
|
|
19850
20241
|
const filename = vendorChunkFilename(pkgName);
|
|
19851
|
-
const destPath =
|
|
20242
|
+
const destPath = resolve6(runtimeOutDir, filename);
|
|
19852
20243
|
if (wantRebundle) {
|
|
19853
20244
|
await esbuildBuild({
|
|
19854
20245
|
entryPoints: [srcFile],
|
|
19855
20246
|
outfile: destPath,
|
|
19856
20247
|
format: "esm",
|
|
19857
20248
|
bundle: true,
|
|
19858
|
-
minify: config.minify ?? false
|
|
20249
|
+
minify: config.minify ?? false,
|
|
20250
|
+
external: rebundleExternalsFor(pkgName, config.externals)
|
|
19859
20251
|
});
|
|
19860
20252
|
anyChanged = true;
|
|
19861
20253
|
console.log(`Generated (bundled): ${runtimeSubdir}/${filename}`);
|
|
19862
20254
|
} else {
|
|
19863
|
-
|
|
20255
|
+
const bytes = await readBytes(srcFile);
|
|
20256
|
+
const text = new TextDecoder().decode(bytes);
|
|
20257
|
+
if (!entry.isBrowserReady) {
|
|
20258
|
+
const unresolved = unresolvedBareImports(text, config.externals);
|
|
20259
|
+
if (unresolved.length > 0) {
|
|
20260
|
+
console.warn(
|
|
20261
|
+
`Warning: externals \u2014 "${pkgName}" resolved via import/main entry (no umd/unpkg/jsdelivr found) and imports packages not in the importmap: ${unresolved.join(", ")}. These are not browser-ready. Set rebundle: true to re-bundle it into a self-contained ESM file.`
|
|
20262
|
+
);
|
|
20263
|
+
}
|
|
20264
|
+
}
|
|
20265
|
+
let content = bytes;
|
|
19864
20266
|
if (config.minify) {
|
|
19865
|
-
content = transpile(
|
|
20267
|
+
content = transpile(text, { loader: "js", minify: true });
|
|
19866
20268
|
}
|
|
19867
20269
|
if (await writeIfChanged(destPath, content)) {
|
|
19868
20270
|
anyChanged = true;
|
|
@@ -19887,7 +20289,7 @@ async function processExternals(config, runtimeSubdir, runtimeOutDir) {
|
|
|
19887
20289
|
preloads,
|
|
19888
20290
|
externals: allExternals
|
|
19889
20291
|
};
|
|
19890
|
-
const manifestPath =
|
|
20292
|
+
const manifestPath = resolve6(config.outDir, "barefoot-externals.json");
|
|
19891
20293
|
if (await writeIfChanged(manifestPath, JSON.stringify(manifest, null, 2))) {
|
|
19892
20294
|
anyChanged = true;
|
|
19893
20295
|
console.log("Generated: barefoot-externals.json");
|
|
@@ -19899,8 +20301,8 @@ async function processBundleEntries(config, clientJsOutDir, clientJsSubdir, allE
|
|
|
19899
20301
|
let anyChanged = false;
|
|
19900
20302
|
for (const entry of config.bundleEntries) {
|
|
19901
20303
|
const entryExternals = [...allExternals, ...entry.externals ?? []];
|
|
19902
|
-
const outfilePath =
|
|
19903
|
-
const absEntry =
|
|
20304
|
+
const outfilePath = resolve6(clientJsOutDir, entry.outfile);
|
|
20305
|
+
const absEntry = resolve6(entry.entry);
|
|
19904
20306
|
const cacheKey = `${BUNDLE_KEY_PREFIX}${absEntry}`;
|
|
19905
20307
|
const sourceContent = await readText(absEntry);
|
|
19906
20308
|
const sourceHash = hashContent(sourceContent);
|
|
@@ -19940,7 +20342,7 @@ async function processBundleEntries(config, clientJsOutDir, clientJsSubdir, allE
|
|
|
19940
20342
|
if (metafile) {
|
|
19941
20343
|
for (const inputPath of Object.keys(metafile.inputs)) {
|
|
19942
20344
|
if (externalsSet.has(inputPath)) continue;
|
|
19943
|
-
const abs =
|
|
20345
|
+
const abs = resolve6(absWorkingDir, inputPath);
|
|
19944
20346
|
if (abs === absEntry) continue;
|
|
19945
20347
|
if (abs.includes("/node_modules/")) continue;
|
|
19946
20348
|
if (await fileExists(abs)) {
|
|
@@ -19965,7 +20367,7 @@ async function collectRelativeImportDeps(entryPath, sourceContent) {
|
|
|
19965
20367
|
for (const match of sourceContent.matchAll(RELATIVE_IMPORT_SCAN_RE)) {
|
|
19966
20368
|
const rel = match[1];
|
|
19967
20369
|
if (!rel.startsWith(".")) continue;
|
|
19968
|
-
const base =
|
|
20370
|
+
const base = resolve6(baseDir, rel);
|
|
19969
20371
|
const candidates = [base, ...EXT_CANDIDATES.map((ext) => base + ext)];
|
|
19970
20372
|
for (const cand of candidates) {
|
|
19971
20373
|
if (seen.has(cand)) continue;
|
|
@@ -19999,7 +20401,7 @@ async function compileEntry(args2) {
|
|
|
19999
20401
|
for (const depPath of await collectRelativeImportDeps(entryPath, sourceContent)) {
|
|
20000
20402
|
deps[depPath] = hashContent(await readText(depPath));
|
|
20001
20403
|
}
|
|
20002
|
-
const presumedOutputPath =
|
|
20404
|
+
const presumedOutputPath = resolve6(
|
|
20003
20405
|
templatesOutDir,
|
|
20004
20406
|
baseFileName.replace(/\.tsx?$/, config.adapter.extension)
|
|
20005
20407
|
);
|
|
@@ -20065,7 +20467,7 @@ async function compileEntry(args2) {
|
|
|
20065
20467
|
if (hasClientJs) {
|
|
20066
20468
|
const rel = `${clientJsSubdir}/${clientJsFilename}`;
|
|
20067
20469
|
outputs.push(rel);
|
|
20068
|
-
const target =
|
|
20470
|
+
const target = resolve6(clientJsOutDir, clientJsFilename);
|
|
20069
20471
|
await mkdir(dirname3(target), { recursive: true });
|
|
20070
20472
|
if (await writeIfChanged(target, clientJsContent)) {
|
|
20071
20473
|
wroteAny = true;
|
|
@@ -20082,7 +20484,7 @@ async function compileEntry(args2) {
|
|
|
20082
20484
|
}
|
|
20083
20485
|
const rel = `${templatesSubdir}/${outName}`;
|
|
20084
20486
|
outputs.push(rel);
|
|
20085
|
-
const target =
|
|
20487
|
+
const target = resolve6(templatesOutDir, outName);
|
|
20086
20488
|
await mkdir(dirname3(target), { recursive: true });
|
|
20087
20489
|
if (await writeIfChanged(target, outputContent)) {
|
|
20088
20490
|
wroteAny = true;
|
|
@@ -20127,14 +20529,14 @@ async function compileEntry(args2) {
|
|
|
20127
20529
|
}
|
|
20128
20530
|
async function writeBuildId(outDir, result) {
|
|
20129
20531
|
if (!result.changed) return;
|
|
20130
|
-
const devDir =
|
|
20532
|
+
const devDir = resolve6(outDir, DEV_SENTINEL_SUBDIR);
|
|
20131
20533
|
await mkdir(devDir, { recursive: true });
|
|
20132
|
-
const path23 =
|
|
20534
|
+
const path23 = resolve6(devDir, DEV_SENTINEL_FILENAME);
|
|
20133
20535
|
await writeIfChanged(path23, String(Date.now()));
|
|
20134
20536
|
}
|
|
20135
20537
|
async function watch(config, options = {}) {
|
|
20136
20538
|
const { debounceMs = 100, signal } = options;
|
|
20137
|
-
const { watch:
|
|
20539
|
+
const { watch: fsWatch2 } = await import("node:fs/promises");
|
|
20138
20540
|
const initial = await build(config);
|
|
20139
20541
|
let cachedProgram = initial.sharedProgram;
|
|
20140
20542
|
console.log("");
|
|
@@ -20212,7 +20614,7 @@ async function watch(config, options = {}) {
|
|
|
20212
20614
|
};
|
|
20213
20615
|
const watchRoot = async (root, recursive) => {
|
|
20214
20616
|
try {
|
|
20215
|
-
const iter =
|
|
20617
|
+
const iter = fsWatch2(root, { recursive, signal });
|
|
20216
20618
|
for await (const event of iter) {
|
|
20217
20619
|
if (!isRelevant(root, event.filename)) continue;
|
|
20218
20620
|
schedule();
|
|
@@ -20272,6 +20674,7 @@ var init_build = __esm({
|
|
|
20272
20674
|
init_build_cache();
|
|
20273
20675
|
init_emit_ledger();
|
|
20274
20676
|
init_fs_utils();
|
|
20677
|
+
init_assets_ignore();
|
|
20275
20678
|
init_runtime();
|
|
20276
20679
|
init_resolve_imports();
|
|
20277
20680
|
BF001_TRIPWIRE_IMPORTS = /* @__PURE__ */ new Set([
|
|
@@ -23588,7 +23991,7 @@ async function select(args2) {
|
|
|
23588
23991
|
output.write(`\x1B[33m?\x1B[0m \x1B[1m${args2.message}\x1B[0m
|
|
23589
23992
|
`);
|
|
23590
23993
|
render(true);
|
|
23591
|
-
return new Promise((
|
|
23994
|
+
return new Promise((resolve11, reject) => {
|
|
23592
23995
|
readline.emitKeypressEvents(input);
|
|
23593
23996
|
input.setRawMode?.(true);
|
|
23594
23997
|
input.resume();
|
|
@@ -23633,7 +24036,7 @@ async function select(args2) {
|
|
|
23633
24036
|
output.write(`\x1B[${totalLines}A`);
|
|
23634
24037
|
output.write(`\u2714 ${args2.message} \x1B[1;32m${shortLabel}\x1B[0m
|
|
23635
24038
|
`);
|
|
23636
|
-
|
|
24039
|
+
resolve11(args2.options[cursor].value);
|
|
23637
24040
|
return;
|
|
23638
24041
|
}
|
|
23639
24042
|
};
|
|
@@ -24532,325 +24935,268 @@ var init_scaffold_layout = __esm({
|
|
|
24532
24935
|
}
|
|
24533
24936
|
});
|
|
24534
24937
|
|
|
24535
|
-
// src/
|
|
24536
|
-
|
|
24537
|
-
|
|
24538
|
-
|
|
24539
|
-
}
|
|
24540
|
-
import {
|
|
24541
|
-
|
|
24542
|
-
|
|
24543
|
-
const {
|
|
24544
|
-
const
|
|
24545
|
-
|
|
24546
|
-
|
|
24547
|
-
|
|
24548
|
-
|
|
24549
|
-
|
|
24550
|
-
|
|
24551
|
-
|
|
24552
|
-
|
|
24553
|
-
|
|
24554
|
-
|
|
24555
|
-
|
|
24556
|
-
|
|
24557
|
-
|
|
24558
|
-
|
|
24559
|
-
|
|
24560
|
-
|
|
24561
|
-
|
|
24562
|
-
|
|
24563
|
-
|
|
24564
|
-
|
|
24938
|
+
// src/lib/preview/compile.ts
|
|
24939
|
+
import { mkdir as mkdir2, readFile as readFile2, writeFile as writeFile2 } from "node:fs/promises";
|
|
24940
|
+
import { existsSync as existsSync12 } from "node:fs";
|
|
24941
|
+
import { execFileSync } from "node:child_process";
|
|
24942
|
+
import { resolve as resolve7, relative as relative3 } from "node:path";
|
|
24943
|
+
import { build as build2 } from "esbuild";
|
|
24944
|
+
async function compile(options) {
|
|
24945
|
+
const { assets, previewsPath, previewNames, componentName, liveReload } = options;
|
|
24946
|
+
const { rootDir, srcComponentsDir, tokensCss, globalsCss, runtimeStandalone, uno } = assets;
|
|
24947
|
+
const DIST_DIR = resolve7(rootDir, ".preview-dist");
|
|
24948
|
+
const MODULES_DIR = resolve7(DIST_DIR, "_modules");
|
|
24949
|
+
await mkdir2(MODULES_DIR, { recursive: true });
|
|
24950
|
+
console.log("Generating CSS...");
|
|
24951
|
+
await writeFile2(resolve7(DIST_DIR, "globals.css"), tokensCss + "\n" + globalsCss);
|
|
24952
|
+
console.log("Generated: .preview-dist/globals.css");
|
|
24953
|
+
console.log("Generating UnoCSS...");
|
|
24954
|
+
let unoConfigPath = uno.configPath;
|
|
24955
|
+
if (uno.configIsBundled) {
|
|
24956
|
+
unoConfigPath = resolve7(DIST_DIR, "uno.config.ts");
|
|
24957
|
+
await writeFile2(unoConfigPath, await readFile2(uno.configPath, "utf-8"));
|
|
24958
|
+
}
|
|
24959
|
+
execFileSync(uno.bin, [
|
|
24960
|
+
...uno.globs,
|
|
24961
|
+
"--config",
|
|
24962
|
+
unoConfigPath,
|
|
24963
|
+
"-o",
|
|
24964
|
+
resolve7(DIST_DIR, "uno.css")
|
|
24965
|
+
], { cwd: uno.cwd, stdio: "inherit" });
|
|
24966
|
+
console.log("Generated: .preview-dist/uno.css");
|
|
24967
|
+
const previewSource = await readFile2(previewsPath, "utf-8");
|
|
24968
|
+
const previewSiblings = [
|
|
24969
|
+
...previewSource.matchAll(/from\s*['"]\.\.\/([a-z][a-z0-9-]*)(?:\/[^'"]*)?['"]/g)
|
|
24970
|
+
].map((m) => m[1]);
|
|
24971
|
+
const componentFiles = resolveDependenciesFromSource(
|
|
24972
|
+
[componentName, ...previewSiblings],
|
|
24973
|
+
srcComponentsDir
|
|
24974
|
+
).map((name) => resolve7(srcComponentsDir, name, "index.tsx")).filter(existsSync12);
|
|
24975
|
+
console.log(`Compiling ${componentFiles.length + 1} files...`);
|
|
24976
|
+
const allFiles = [...componentFiles, previewsPath];
|
|
24977
|
+
const adapter = new PreviewCsrAdapter();
|
|
24978
|
+
const previewKey = relative3(rootDir, previewsPath).replace(/\.tsx$/, "");
|
|
24979
|
+
const clientJsByKey = /* @__PURE__ */ new Map();
|
|
24980
|
+
let previewProducedClientJs = false;
|
|
24981
|
+
for (const filePath of allFiles) {
|
|
24982
|
+
const source = await readFile2(filePath, "utf-8");
|
|
24983
|
+
const isPreview = filePath === previewsPath;
|
|
24984
|
+
const result = compileJSX(source, filePath, { adapter });
|
|
24985
|
+
const errors = result.errors.filter((e) => e.severity === "error");
|
|
24986
|
+
const warnings = result.errors.filter((e) => e.severity === "warning");
|
|
24987
|
+
for (const w of warnings) console.warn(formatError(w, source, { projectDir: rootDir }));
|
|
24988
|
+
if (errors.length > 0) {
|
|
24989
|
+
for (const e of errors) console.error(formatError(e, source, { projectDir: rootDir }));
|
|
24990
|
+
if (isPreview) {
|
|
24991
|
+
throw new Error(`Preview compilation failed for ${previewKey} (see errors above).`);
|
|
24992
|
+
}
|
|
24993
|
+
continue;
|
|
24565
24994
|
}
|
|
24566
|
-
|
|
24567
|
-
|
|
24568
|
-
|
|
24569
|
-
|
|
24570
|
-
|
|
24571
|
-
}
|
|
24572
|
-
let runPreview = null;
|
|
24573
|
-
try {
|
|
24574
|
-
const specifier = ["..", "..", "..", "preview", "src", "index"].join("/");
|
|
24575
|
-
const mod = await import(specifier);
|
|
24576
|
-
runPreview = mod.runPreview;
|
|
24577
|
-
} catch {
|
|
24995
|
+
const clientJs = result.files.find((f) => f.type === "clientJs")?.content;
|
|
24996
|
+
if (!clientJs) continue;
|
|
24997
|
+
const key = relative3(rootDir, filePath).replace(/\.tsx$/, "");
|
|
24998
|
+
clientJsByKey.set(key, clientJs);
|
|
24999
|
+
if (isPreview) previewProducedClientJs = true;
|
|
24578
25000
|
}
|
|
24579
|
-
if (!
|
|
24580
|
-
|
|
24581
|
-
|
|
24582
|
-
|
|
24583
|
-
process.exit(1);
|
|
25001
|
+
if (!previewProducedClientJs) {
|
|
25002
|
+
throw new Error(
|
|
25003
|
+
`Preview ${previewKey} produced no client JS. Each preview function must return a single root element (wrap multiple roots in <>...</>).`
|
|
25004
|
+
);
|
|
24584
25005
|
}
|
|
24585
|
-
|
|
25006
|
+
const combined = combineParentChildClientJs(clientJsByKey);
|
|
25007
|
+
const allModules = new Map([...clientJsByKey, ...combined]);
|
|
25008
|
+
for (const [key, content] of allModules) {
|
|
25009
|
+
const safeName = key.replace(/[/\\]/g, "__") + ".js";
|
|
25010
|
+
await writeFile2(resolve7(MODULES_DIR, safeName), content);
|
|
25011
|
+
}
|
|
25012
|
+
const previewModuleFile = previewKey.replace(/[/\\]/g, "__") + ".js";
|
|
25013
|
+
const entrySource = generateEntryScript(previewModuleFile, previewNames);
|
|
25014
|
+
const entryPath = resolve7(DIST_DIR, "_entry.js");
|
|
25015
|
+
await writeFile2(entryPath, entrySource);
|
|
25016
|
+
console.log("Bundling for browser...");
|
|
25017
|
+
await build2({
|
|
25018
|
+
entryPoints: [entryPath],
|
|
25019
|
+
outfile: resolve7(DIST_DIR, "_bundle.js"),
|
|
25020
|
+
bundle: true,
|
|
25021
|
+
format: "esm",
|
|
25022
|
+
platform: "browser",
|
|
25023
|
+
minify: false,
|
|
25024
|
+
sourcemap: "inline",
|
|
25025
|
+
absWorkingDir: rootDir,
|
|
25026
|
+
alias: { "@barefootjs/client/runtime": runtimeStandalone },
|
|
25027
|
+
define: { "process.env.NODE_ENV": '"development"' }
|
|
25028
|
+
});
|
|
25029
|
+
console.log("Generated: .preview-dist/_bundle.js");
|
|
25030
|
+
await writeFile2(resolve7(DIST_DIR, "index.html"), generateHTML(componentName, liveReload));
|
|
25031
|
+
console.log("Generated: .preview-dist/index.html");
|
|
25032
|
+
return { distDir: DIST_DIR };
|
|
24586
25033
|
}
|
|
24587
|
-
|
|
24588
|
-
|
|
24589
|
-
|
|
24590
|
-
|
|
24591
|
-
|
|
24592
|
-
}
|
|
25034
|
+
function generateEntryScript(previewModuleFile, previewNames) {
|
|
25035
|
+
const namesJson = JSON.stringify(previewNames);
|
|
25036
|
+
return `import { render } from '@barefootjs/client/runtime'
|
|
25037
|
+
import './_modules/${previewModuleFile}'
|
|
25038
|
+
|
|
25039
|
+
const previews = ${namesJson}
|
|
25040
|
+
const app = document.getElementById('preview-root')
|
|
25041
|
+
|
|
25042
|
+
for (const name of previews) {
|
|
25043
|
+
const section = document.createElement('div')
|
|
25044
|
+
section.className = 'preview-section'
|
|
25045
|
+
section.dataset.preview = name
|
|
25046
|
+
|
|
25047
|
+
const title = document.createElement('div')
|
|
25048
|
+
title.className = 'preview-title'
|
|
25049
|
+
title.textContent = name.replace(/([a-z])([A-Z])/g, '$1 $2')
|
|
25050
|
+
section.appendChild(title)
|
|
25051
|
+
|
|
25052
|
+
const content = document.createElement('div')
|
|
25053
|
+
section.appendChild(content)
|
|
25054
|
+
app.appendChild(section)
|
|
24593
25055
|
|
|
24594
|
-
// src/commands/tokens-apply.ts
|
|
24595
|
-
var tokens_apply_exports = {};
|
|
24596
|
-
__export(tokens_apply_exports, {
|
|
24597
|
-
applyCssOverrides: () => applyCssOverrides,
|
|
24598
|
-
applyTokenOverrides: () => applyTokenOverrides,
|
|
24599
|
-
parseStudioUrl: () => parseStudioUrl,
|
|
24600
|
-
resolveTokensCss: () => resolveTokensCss,
|
|
24601
|
-
run: () => run9
|
|
24602
|
-
});
|
|
24603
|
-
import { existsSync as existsSync13, readFileSync as readFileSync6, writeFileSync as writeFileSync4 } from "fs";
|
|
24604
|
-
import path17 from "path";
|
|
24605
|
-
async function run9(args2, ctx2) {
|
|
24606
|
-
const url = args2[0];
|
|
24607
|
-
if (!url) {
|
|
24608
|
-
console.error("Error: tokens apply requires a Studio URL.");
|
|
24609
|
-
console.error("Usage: bf tokens apply <url>");
|
|
24610
|
-
process.exit(1);
|
|
24611
|
-
}
|
|
24612
|
-
const projectDir = ctx2.projectDir ?? process.cwd();
|
|
24613
|
-
const tokensRelDir = ctx2.config?.paths.tokens ?? "tokens";
|
|
24614
|
-
const studioConfig = parseStudioUrl(url);
|
|
24615
|
-
if (!studioConfig) {
|
|
24616
|
-
console.error("Error: could not decode Studio config from the URL (no `?c=` param or malformed payload).");
|
|
24617
|
-
process.exit(1);
|
|
24618
|
-
}
|
|
24619
|
-
const cssPath = resolveTokensCss(projectDir, tokensRelDir);
|
|
24620
|
-
if (!cssPath) {
|
|
24621
|
-
console.error("Error: tokens.css not found. Checked:");
|
|
24622
|
-
for (const p of candidateCssPaths(projectDir, tokensRelDir)) {
|
|
24623
|
-
console.error(` - ${path17.relative(projectDir, p)}`);
|
|
24624
|
-
}
|
|
24625
|
-
console.error(" Run `npm create barefootjs@latest` to scaffold a project first.");
|
|
24626
|
-
process.exit(1);
|
|
24627
|
-
}
|
|
24628
|
-
applyCssOverrides(cssPath, studioConfig);
|
|
24629
|
-
console.log(` Patched ${path17.relative(projectDir, cssPath)}`);
|
|
24630
|
-
const tokensJsonPath = path17.join(projectDir, tokensRelDir, "tokens.json");
|
|
24631
|
-
if (existsSync13(tokensJsonPath)) {
|
|
24632
|
-
applyTokenOverrides(tokensJsonPath, studioConfig);
|
|
24633
|
-
console.log(` Patched ${path17.relative(projectDir, tokensJsonPath)}`);
|
|
24634
|
-
}
|
|
24635
|
-
}
|
|
24636
|
-
function parseStudioUrl(url) {
|
|
24637
25056
|
try {
|
|
24638
|
-
|
|
24639
|
-
|
|
24640
|
-
|
|
24641
|
-
|
|
24642
|
-
return JSON.parse(json);
|
|
24643
|
-
} catch {
|
|
24644
|
-
return void 0;
|
|
25057
|
+
render(content, name, {})
|
|
25058
|
+
} catch (err) {
|
|
25059
|
+
content.textContent = 'Render error: ' + (err && err.message || err)
|
|
25060
|
+
console.error('[preview]', name, err)
|
|
24645
25061
|
}
|
|
24646
25062
|
}
|
|
24647
|
-
|
|
24648
|
-
return [
|
|
24649
|
-
path17.join(projectDir, tokensRelDir, "tokens.css"),
|
|
24650
|
-
path17.join(projectDir, "public", "tokens.css"),
|
|
24651
|
-
path17.join(projectDir, "static", "tokens.css")
|
|
24652
|
-
];
|
|
24653
|
-
}
|
|
24654
|
-
function resolveTokensCss(projectDir, tokensRelDir) {
|
|
24655
|
-
for (const p of candidateCssPaths(projectDir, tokensRelDir)) {
|
|
24656
|
-
if (existsSync13(p)) return p;
|
|
24657
|
-
}
|
|
24658
|
-
return void 0;
|
|
25063
|
+
`;
|
|
24659
25064
|
}
|
|
24660
|
-
function
|
|
24661
|
-
const
|
|
24662
|
-
|
|
24663
|
-
|
|
24664
|
-
|
|
24665
|
-
|
|
24666
|
-
|
|
24667
|
-
}
|
|
24668
|
-
|
|
24669
|
-
|
|
24670
|
-
|
|
24671
|
-
|
|
24672
|
-
|
|
24673
|
-
|
|
24674
|
-
}
|
|
24675
|
-
|
|
24676
|
-
|
|
24677
|
-
|
|
24678
|
-
|
|
24679
|
-
|
|
24680
|
-
}
|
|
24681
|
-
|
|
24682
|
-
|
|
24683
|
-
|
|
24684
|
-
|
|
24685
|
-
|
|
24686
|
-
|
|
24687
|
-
|
|
24688
|
-
|
|
24689
|
-
|
|
25065
|
+
function generateHTML(componentName, liveReload = false) {
|
|
25066
|
+
const displayName = componentName.charAt(0).toUpperCase() + componentName.slice(1);
|
|
25067
|
+
return `<!DOCTYPE html>
|
|
25068
|
+
<html lang="en">
|
|
25069
|
+
<head>
|
|
25070
|
+
<meta charset="UTF-8" />
|
|
25071
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
25072
|
+
<title>${displayName} \u2014 Preview</title>
|
|
25073
|
+
<link rel="stylesheet" href="/globals.css" />
|
|
25074
|
+
<link rel="stylesheet" href="/uno.css" />
|
|
25075
|
+
<style>
|
|
25076
|
+
body {
|
|
25077
|
+
padding: 2rem;
|
|
25078
|
+
font-family: system-ui, -apple-system, sans-serif;
|
|
25079
|
+
}
|
|
25080
|
+
.preview-section {
|
|
25081
|
+
margin-bottom: 2rem;
|
|
25082
|
+
padding: 1.5rem;
|
|
25083
|
+
border: 1px solid var(--border);
|
|
25084
|
+
border-radius: var(--radius);
|
|
25085
|
+
}
|
|
25086
|
+
.preview-title {
|
|
25087
|
+
font-size: 0.875rem;
|
|
25088
|
+
font-weight: 500;
|
|
25089
|
+
color: var(--muted-foreground);
|
|
25090
|
+
margin-bottom: 1rem;
|
|
25091
|
+
text-transform: uppercase;
|
|
25092
|
+
letter-spacing: 0.05em;
|
|
25093
|
+
}
|
|
25094
|
+
h1 {
|
|
25095
|
+
font-size: 1.5rem;
|
|
25096
|
+
font-weight: 600;
|
|
25097
|
+
margin-bottom: 1.5rem;
|
|
25098
|
+
}
|
|
25099
|
+
#bf-theme-toggle {
|
|
25100
|
+
position: fixed;
|
|
25101
|
+
bottom: 1rem;
|
|
25102
|
+
right: 1rem;
|
|
25103
|
+
z-index: 9999;
|
|
25104
|
+
width: 2.5rem;
|
|
25105
|
+
height: 2.5rem;
|
|
25106
|
+
border-radius: var(--radius);
|
|
25107
|
+
border: 1px solid var(--border);
|
|
25108
|
+
background: var(--card);
|
|
25109
|
+
color: var(--foreground);
|
|
25110
|
+
display: flex;
|
|
25111
|
+
align-items: center;
|
|
25112
|
+
justify-content: center;
|
|
25113
|
+
cursor: pointer;
|
|
25114
|
+
box-shadow: 0 1px 3px rgba(0,0,0,.1);
|
|
25115
|
+
}
|
|
25116
|
+
#bf-theme-toggle:hover { background: var(--accent); }
|
|
25117
|
+
#bf-theme-toggle .sun { display: none; }
|
|
25118
|
+
#bf-theme-toggle .moon { display: block; }
|
|
25119
|
+
.dark #bf-theme-toggle .sun { display: block; }
|
|
25120
|
+
.dark #bf-theme-toggle .moon { display: none; }
|
|
25121
|
+
</style>
|
|
25122
|
+
</head>
|
|
25123
|
+
<body>
|
|
25124
|
+
<h1>${displayName}</h1>
|
|
25125
|
+
<div id="preview-root"></div>
|
|
25126
|
+
<button id="bf-theme-toggle" type="button" aria-label="Toggle dark mode"
|
|
25127
|
+
onclick="var r=document.documentElement;r.classList.add('theme-transition');r.classList.toggle('dark');setTimeout(function(){r.classList.remove('theme-transition')},300)">
|
|
25128
|
+
<svg class="sun" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
|
25129
|
+
<circle cx="12" cy="12" r="4"></circle>
|
|
25130
|
+
<path d="M12 2v2M12 20v2M4.93 4.93l1.41 1.41M17.66 17.66l1.41 1.41M2 12h2M20 12h2M6.34 17.66l-1.41 1.41M19.07 4.93l-1.41 1.41"></path>
|
|
25131
|
+
</svg>
|
|
25132
|
+
<svg class="moon" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
|
25133
|
+
<path d="M21 12.79A9 9 0 1 1 11.21 3 7 7 0 0 0 21 12.79z"></path>
|
|
25134
|
+
</svg>
|
|
25135
|
+
</button>
|
|
25136
|
+
<script type="module" src="/_bundle.js"></script>
|
|
25137
|
+
${liveReload ? LIVE_RELOAD_SCRIPT : ""}
|
|
25138
|
+
</body>
|
|
25139
|
+
</html>`;
|
|
24690
25140
|
}
|
|
24691
|
-
|
|
24692
|
-
|
|
24693
|
-
|
|
24694
|
-
|
|
24695
|
-
|
|
24696
|
-
|
|
24697
|
-
|
|
24698
|
-
|
|
24699
|
-
|
|
24700
|
-
|
|
24701
|
-
|
|
24702
|
-
|
|
24703
|
-
|
|
24704
|
-
|
|
24705
|
-
|
|
25141
|
+
var EMPTY_OUTPUT, PreviewCsrAdapter, LIVE_RELOAD_SCRIPT;
|
|
25142
|
+
var init_compile = __esm({
|
|
25143
|
+
"src/lib/preview/compile.ts"() {
|
|
25144
|
+
"use strict";
|
|
25145
|
+
init_src2();
|
|
25146
|
+
init_dependency_resolver();
|
|
25147
|
+
EMPTY_OUTPUT = Object.freeze({
|
|
25148
|
+
template: "",
|
|
25149
|
+
sections: Object.freeze({ imports: "", types: "", component: "", defaultExport: "" }),
|
|
25150
|
+
extension: ".tsx"
|
|
25151
|
+
});
|
|
25152
|
+
PreviewCsrAdapter = class extends BaseAdapter {
|
|
25153
|
+
name = "csr";
|
|
25154
|
+
extension = ".tsx";
|
|
25155
|
+
acceptsTemplateCall = () => true;
|
|
25156
|
+
generate() {
|
|
25157
|
+
return EMPTY_OUTPUT;
|
|
25158
|
+
}
|
|
25159
|
+
renderNode() {
|
|
25160
|
+
return "";
|
|
24706
25161
|
}
|
|
24707
|
-
|
|
24708
|
-
|
|
24709
|
-
if (blockEnd === -1) return css;
|
|
24710
|
-
let block = css.slice(blockStart, blockEnd);
|
|
24711
|
-
const toAppend = [];
|
|
24712
|
-
for (const [name, value] of Object.entries(overrides)) {
|
|
24713
|
-
const re = new RegExp(`(${escapeRegex2(name)}\\s*:\\s*)[^;]+(;)`);
|
|
24714
|
-
if (re.test(block)) {
|
|
24715
|
-
block = block.replace(re, `$1${value}$2`);
|
|
24716
|
-
} else {
|
|
24717
|
-
toAppend.push([name, value]);
|
|
24718
|
-
}
|
|
24719
|
-
}
|
|
24720
|
-
if (toAppend.length > 0) {
|
|
24721
|
-
const lines = toAppend.map(([n, v]) => ` ${n}: ${v};`).join("\n");
|
|
24722
|
-
const trailing = block.match(/\s*$/)?.[0] ?? "";
|
|
24723
|
-
block = block.slice(0, block.length - trailing.length) + `
|
|
24724
|
-
|
|
24725
|
-
/* \u2500\u2500 Studio overrides \u2500\u2500 */
|
|
24726
|
-
${lines}
|
|
24727
|
-
`;
|
|
24728
|
-
}
|
|
24729
|
-
return css.slice(0, blockStart) + block + css.slice(blockEnd);
|
|
24730
|
-
}
|
|
24731
|
-
function escapeRegex2(s) {
|
|
24732
|
-
return s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
24733
|
-
}
|
|
24734
|
-
function applyTokenOverrides(tokensJsonPath, config) {
|
|
24735
|
-
const raw = readFileSync6(tokensJsonPath, "utf-8");
|
|
24736
|
-
const tokensData = JSON.parse(raw);
|
|
24737
|
-
if (config.tokens) {
|
|
24738
|
-
for (const [name, values] of Object.entries(config.tokens)) {
|
|
24739
|
-
applyColorOverride(tokensData, name, values);
|
|
24740
|
-
}
|
|
24741
|
-
}
|
|
24742
|
-
if (config.spacing) {
|
|
24743
|
-
applySimpleOverride(tokensData, "--spacing", config.spacing);
|
|
24744
|
-
}
|
|
24745
|
-
if (config.radius) {
|
|
24746
|
-
applySimpleOverride(tokensData, "--radius", config.radius);
|
|
24747
|
-
}
|
|
24748
|
-
if (config.font) {
|
|
24749
|
-
const fontValue = FONT_MAP[config.font] || config.font;
|
|
24750
|
-
applySimpleOverride(tokensData, "--font-sans", fontValue);
|
|
24751
|
-
}
|
|
24752
|
-
if (config.style) {
|
|
24753
|
-
applyShadowPreset(tokensData, config.style);
|
|
24754
|
-
}
|
|
24755
|
-
writeFileSync4(tokensJsonPath, JSON.stringify(tokensData, null, 2) + "\n");
|
|
24756
|
-
}
|
|
24757
|
-
function applyColorOverride(tokensData, name, values) {
|
|
24758
|
-
const varName = `--${name}`;
|
|
24759
|
-
if (Array.isArray(tokensData.colors)) {
|
|
24760
|
-
for (const token of tokensData.colors) {
|
|
24761
|
-
if (token.name === varName || token.name === name) {
|
|
24762
|
-
if (values.light) token.value = values.light;
|
|
24763
|
-
if (values.dark) token.dark = values.dark;
|
|
24764
|
-
return;
|
|
25162
|
+
renderElement() {
|
|
25163
|
+
return "";
|
|
24765
25164
|
}
|
|
24766
|
-
|
|
24767
|
-
|
|
24768
|
-
if (Array.isArray(tokensData.tokens)) {
|
|
24769
|
-
for (const token of tokensData.tokens) {
|
|
24770
|
-
if (token.name === varName || token.name === name) {
|
|
24771
|
-
if (values.light) token.value = values.light;
|
|
24772
|
-
if (values.dark) token.dark = values.dark;
|
|
24773
|
-
return;
|
|
25165
|
+
renderExpression() {
|
|
25166
|
+
return "";
|
|
24774
25167
|
}
|
|
24775
|
-
|
|
24776
|
-
|
|
24777
|
-
}
|
|
24778
|
-
function applySimpleOverride(tokensData, name, value) {
|
|
24779
|
-
const bareName = name.startsWith("--") ? name.slice(2) : name;
|
|
24780
|
-
const sections = [
|
|
24781
|
-
tokensData.colors,
|
|
24782
|
-
tokensData.spacing,
|
|
24783
|
-
tokensData.borderRadius,
|
|
24784
|
-
tokensData.shadows,
|
|
24785
|
-
tokensData.layout
|
|
24786
|
-
];
|
|
24787
|
-
if (tokensData.typography) {
|
|
24788
|
-
for (const arr of Object.values(tokensData.typography)) {
|
|
24789
|
-
if (Array.isArray(arr)) sections.push(arr);
|
|
24790
|
-
}
|
|
24791
|
-
}
|
|
24792
|
-
for (const arr of sections) {
|
|
24793
|
-
if (!Array.isArray(arr)) continue;
|
|
24794
|
-
for (const token of arr) {
|
|
24795
|
-
if (token.name === bareName || token.name === name) {
|
|
24796
|
-
token.value = value;
|
|
24797
|
-
return;
|
|
25168
|
+
renderConditional() {
|
|
25169
|
+
return "";
|
|
24798
25170
|
}
|
|
24799
|
-
|
|
24800
|
-
|
|
24801
|
-
}
|
|
24802
|
-
|
|
24803
|
-
|
|
24804
|
-
|
|
24805
|
-
|
|
24806
|
-
|
|
24807
|
-
|
|
24808
|
-
|
|
24809
|
-
|
|
24810
|
-
|
|
24811
|
-
|
|
24812
|
-
|
|
24813
|
-
FONT_MAP = {
|
|
24814
|
-
system: '-apple-system, BlinkMacSystemFont, "Segoe UI", "Noto Sans", Helvetica, Arial, sans-serif',
|
|
24815
|
-
inter: '"Inter", sans-serif',
|
|
24816
|
-
"noto-sans": '"Noto Sans", sans-serif',
|
|
24817
|
-
"nunito-sans": '"Nunito Sans", sans-serif',
|
|
24818
|
-
figtree: '"Figtree", sans-serif'
|
|
24819
|
-
};
|
|
24820
|
-
SHADOW_PRESETS = {
|
|
24821
|
-
Sharp: {
|
|
24822
|
-
"shadow-sm": "0 1px 2px 0 rgb(0 0 0 / 0.04)",
|
|
24823
|
-
"shadow": "0 1px 2px 0 rgb(0 0 0 / 0.06)",
|
|
24824
|
-
"shadow-md": "0 2px 4px -1px rgb(0 0 0 / 0.08)",
|
|
24825
|
-
"shadow-lg": "0 4px 8px -2px rgb(0 0 0 / 0.1)"
|
|
24826
|
-
},
|
|
24827
|
-
Soft: {
|
|
24828
|
-
"shadow-sm": "0 1px 3px 0 rgb(0 0 0 / 0.06)",
|
|
24829
|
-
"shadow": "0 2px 6px 0 rgb(0 0 0 / 0.08), 0 1px 3px -1px rgb(0 0 0 / 0.06)",
|
|
24830
|
-
"shadow-md": "0 6px 12px -2px rgb(0 0 0 / 0.08), 0 3px 6px -3px rgb(0 0 0 / 0.06)",
|
|
24831
|
-
"shadow-lg": "0 12px 24px -4px rgb(0 0 0 / 0.08), 0 6px 10px -5px rgb(0 0 0 / 0.06)"
|
|
24832
|
-
},
|
|
24833
|
-
Compact: {
|
|
24834
|
-
"shadow-sm": "none",
|
|
24835
|
-
"shadow": "none",
|
|
24836
|
-
"shadow-md": "none",
|
|
24837
|
-
"shadow-lg": "0 1px 2px 0 rgb(0 0 0 / 0.05)"
|
|
25171
|
+
renderLoop() {
|
|
25172
|
+
return "";
|
|
25173
|
+
}
|
|
25174
|
+
renderComponent() {
|
|
25175
|
+
return "";
|
|
25176
|
+
}
|
|
25177
|
+
renderScopeMarker() {
|
|
25178
|
+
return "";
|
|
25179
|
+
}
|
|
25180
|
+
renderSlotMarker() {
|
|
25181
|
+
return "";
|
|
25182
|
+
}
|
|
25183
|
+
renderCondMarker() {
|
|
25184
|
+
return "";
|
|
24838
25185
|
}
|
|
24839
25186
|
};
|
|
25187
|
+
LIVE_RELOAD_SCRIPT = `<script>
|
|
25188
|
+
(function(){var seen;function poll(){fetch('/__preview_reload').then(function(r){return r.text()}).then(function(v){if(seen!==undefined&&v!==seen){location.reload();return}seen=v}).catch(function(){}).then(function(){setTimeout(poll,1000)})}poll()})()
|
|
25189
|
+
</script>`;
|
|
24840
25190
|
}
|
|
24841
25191
|
});
|
|
24842
25192
|
|
|
24843
|
-
// src/
|
|
24844
|
-
|
|
24845
|
-
|
|
24846
|
-
|
|
24847
|
-
});
|
|
24848
|
-
import { readFile as readFile2 } from "node:fs/promises";
|
|
24849
|
-
import { existsSync as existsSync14 } from "node:fs";
|
|
24850
|
-
import { resolve as resolve6, dirname as dirname4 } from "node:path";
|
|
25193
|
+
// src/lib/tokens.ts
|
|
25194
|
+
import { readFile as readFile3 } from "node:fs/promises";
|
|
25195
|
+
import { existsSync as existsSync13 } from "node:fs";
|
|
25196
|
+
import { resolve as resolve8, dirname as dirname4 } from "node:path";
|
|
24851
25197
|
import { fileURLToPath as fileURLToPath4 } from "node:url";
|
|
24852
25198
|
async function loadTokens(jsonPath) {
|
|
24853
|
-
const content = await
|
|
25199
|
+
const content = await readFile3(jsonPath, "utf-8");
|
|
24854
25200
|
return JSON.parse(content);
|
|
24855
25201
|
}
|
|
24856
25202
|
function mergeTokenSets(...sets) {
|
|
@@ -24874,26 +25220,64 @@ function mergeTokenSets(...sets) {
|
|
|
24874
25220
|
function mergeTokenArray(base, ext) {
|
|
24875
25221
|
for (const token of ext) {
|
|
24876
25222
|
const idx = base.findIndex((t) => t.name === token.name);
|
|
24877
|
-
if (idx >= 0)
|
|
24878
|
-
|
|
24879
|
-
|
|
24880
|
-
|
|
24881
|
-
|
|
24882
|
-
|
|
25223
|
+
if (idx >= 0) base[idx] = token;
|
|
25224
|
+
else base.push(token);
|
|
25225
|
+
}
|
|
25226
|
+
}
|
|
25227
|
+
function generateCSS(tokenSet) {
|
|
25228
|
+
const rootLines = [];
|
|
25229
|
+
const darkLines = [];
|
|
25230
|
+
function addSection(label, tokens) {
|
|
25231
|
+
if (tokens.length === 0) return;
|
|
25232
|
+
rootLines.push(` /* \u2500\u2500 ${label} ${"\u2500".repeat(Math.max(1, 50 - label.length))} */`);
|
|
25233
|
+
for (const t of tokens) rootLines.push(` --${t.name}: ${t.value};`);
|
|
25234
|
+
rootLines.push("");
|
|
25235
|
+
}
|
|
25236
|
+
function addColorSection(tokens) {
|
|
25237
|
+
if (tokens.length === 0) return;
|
|
25238
|
+
rootLines.push(` /* \u2500\u2500 Colors (OKLCH, neutral theme) ${"\u2500".repeat(15)} */`);
|
|
25239
|
+
for (const t of tokens) rootLines.push(` --${t.name}: ${t.value};`);
|
|
25240
|
+
rootLines.push("");
|
|
25241
|
+
for (const t of tokens.filter((t2) => t2.dark)) {
|
|
25242
|
+
darkLines.push(` --${t.name}: ${t.dark};`);
|
|
25243
|
+
}
|
|
25244
|
+
}
|
|
25245
|
+
addSection("Typography", [...tokenSet.typography.fontFamily, ...tokenSet.typography.letterSpacing]);
|
|
25246
|
+
addSection("Spacing scale", tokenSet.spacing);
|
|
25247
|
+
addSection("Border radius", tokenSet.borderRadius);
|
|
25248
|
+
addSection("Transitions", [...tokenSet.transitions.duration, ...tokenSet.transitions.easing]);
|
|
25249
|
+
addSection("Layout", tokenSet.layout);
|
|
25250
|
+
addColorSection(tokenSet.colors);
|
|
25251
|
+
addSection("Shadows", tokenSet.shadows);
|
|
25252
|
+
const header = `/**
|
|
25253
|
+
* AUTO-GENERATED \u2014 Do not edit manually.
|
|
25254
|
+
* Generated from tokens.json.
|
|
25255
|
+
*
|
|
25256
|
+
* BarefootJS Design Tokens
|
|
25257
|
+
*/`;
|
|
25258
|
+
let css = `${header}
|
|
25259
|
+
|
|
25260
|
+
:root {
|
|
25261
|
+
${rootLines.join("\n")}}
|
|
25262
|
+
`;
|
|
25263
|
+
if (darkLines.length > 0) css += `
|
|
25264
|
+
.dark {
|
|
25265
|
+
${darkLines.join("\n")}
|
|
25266
|
+
}
|
|
25267
|
+
`;
|
|
25268
|
+
return css;
|
|
24883
25269
|
}
|
|
24884
25270
|
function findBaseTokensJson(ctx2) {
|
|
24885
|
-
const monorepoTokens =
|
|
24886
|
-
if (
|
|
24887
|
-
const bundledTokens =
|
|
24888
|
-
if (
|
|
25271
|
+
const monorepoTokens = resolve8(ctx2.root, "site/shared/tokens/tokens.json");
|
|
25272
|
+
if (existsSync13(monorepoTokens)) return monorepoTokens;
|
|
25273
|
+
const bundledTokens = resolve8(dirname4(thisFile2), "tokens.json");
|
|
25274
|
+
if (existsSync13(bundledTokens)) return bundledTokens;
|
|
24889
25275
|
return null;
|
|
24890
25276
|
}
|
|
24891
25277
|
async function loadTokenSet(ctx2) {
|
|
24892
25278
|
if (ctx2.projectDir && ctx2.config?.paths.tokens) {
|
|
24893
|
-
const userTokens =
|
|
24894
|
-
if (await fileExists(userTokens))
|
|
24895
|
-
return loadTokens(userTokens);
|
|
24896
|
-
}
|
|
25279
|
+
const userTokens = resolve8(ctx2.projectDir, ctx2.config.paths.tokens, "tokens.json");
|
|
25280
|
+
if (await fileExists(userTokens)) return loadTokens(userTokens);
|
|
24897
25281
|
}
|
|
24898
25282
|
const basePath = findBaseTokensJson(ctx2);
|
|
24899
25283
|
if (!basePath) {
|
|
@@ -24902,1045 +25286,1679 @@ async function loadTokenSet(ctx2) {
|
|
|
24902
25286
|
);
|
|
24903
25287
|
}
|
|
24904
25288
|
const base = await loadTokens(basePath);
|
|
24905
|
-
const uiJsonPath =
|
|
25289
|
+
const uiJsonPath = resolve8(ctx2.root, "site/ui/tokens.json");
|
|
24906
25290
|
if (await fileExists(uiJsonPath)) {
|
|
24907
|
-
|
|
24908
|
-
return mergeTokenSets(base, ext);
|
|
25291
|
+
return mergeTokenSets(base, await loadTokens(uiJsonPath));
|
|
24909
25292
|
}
|
|
24910
25293
|
return base;
|
|
24911
25294
|
}
|
|
24912
|
-
function
|
|
24913
|
-
|
|
24914
|
-
function add(cat, tokens) {
|
|
24915
|
-
if (category && category !== cat) return;
|
|
24916
|
-
result.push(...tokens);
|
|
24917
|
-
}
|
|
24918
|
-
add("typography", [...tokenSet.typography.fontFamily, ...tokenSet.typography.letterSpacing]);
|
|
24919
|
-
add("spacing", tokenSet.spacing);
|
|
24920
|
-
add("borderRadius", tokenSet.borderRadius);
|
|
24921
|
-
add("transitions", [...tokenSet.transitions.duration, ...tokenSet.transitions.easing]);
|
|
24922
|
-
add("layout", tokenSet.layout);
|
|
24923
|
-
add("colors", tokenSet.colors);
|
|
24924
|
-
add("shadows", tokenSet.shadows);
|
|
24925
|
-
return result;
|
|
24926
|
-
}
|
|
24927
|
-
function printTokens(tokens, jsonFlag2) {
|
|
24928
|
-
if (jsonFlag2) {
|
|
24929
|
-
console.log(JSON.stringify(tokens, null, 2));
|
|
24930
|
-
return;
|
|
24931
|
-
}
|
|
24932
|
-
if (tokens.length === 0) {
|
|
24933
|
-
console.log("No tokens found.");
|
|
24934
|
-
return;
|
|
24935
|
-
}
|
|
24936
|
-
const nameWidth = Math.max(25, ...tokens.map((t) => t.name.length + 4));
|
|
24937
|
-
const header = `${"NAME".padEnd(nameWidth)}VALUE`;
|
|
24938
|
-
console.log(header);
|
|
24939
|
-
console.log("-".repeat(header.length + 20));
|
|
24940
|
-
for (const t of tokens) {
|
|
24941
|
-
const name = `--${t.name}`;
|
|
24942
|
-
const dark = t.dark;
|
|
24943
|
-
const darkSuffix = dark ? ` (dark: ${dark})` : "";
|
|
24944
|
-
console.log(`${name.padEnd(nameWidth)}${t.value}${darkSuffix}`);
|
|
24945
|
-
}
|
|
24946
|
-
console.log(`
|
|
24947
|
-
${tokens.length} token(s)`);
|
|
24948
|
-
}
|
|
24949
|
-
async function run10(args2, ctx2) {
|
|
24950
|
-
let category;
|
|
24951
|
-
const catIdx = args2.indexOf("--category");
|
|
24952
|
-
if (catIdx >= 0 && args2[catIdx + 1]) {
|
|
24953
|
-
const val = args2[catIdx + 1];
|
|
24954
|
-
if (!CATEGORY_NAMES.includes(val)) {
|
|
24955
|
-
console.error(`Unknown category: ${val}`);
|
|
24956
|
-
console.error(`Available: ${CATEGORY_NAMES.join(", ")}`);
|
|
24957
|
-
process.exit(1);
|
|
24958
|
-
}
|
|
24959
|
-
category = val;
|
|
24960
|
-
}
|
|
24961
|
-
const tokenSet = await loadTokenSet(ctx2);
|
|
24962
|
-
const tokens = flattenTokens(tokenSet, category);
|
|
24963
|
-
printTokens(tokens, ctx2.jsonFlag);
|
|
25295
|
+
async function loadTokensCss(ctx2) {
|
|
25296
|
+
return generateCSS(await loadTokenSet(ctx2));
|
|
24964
25297
|
}
|
|
24965
|
-
var thisFile2
|
|
25298
|
+
var thisFile2;
|
|
24966
25299
|
var init_tokens = __esm({
|
|
24967
|
-
"src/
|
|
25300
|
+
"src/lib/tokens.ts"() {
|
|
24968
25301
|
"use strict";
|
|
24969
25302
|
init_runtime();
|
|
24970
25303
|
thisFile2 = fileURLToPath4(import.meta.url);
|
|
24971
|
-
CATEGORY_NAMES = [
|
|
24972
|
-
"typography",
|
|
24973
|
-
"spacing",
|
|
24974
|
-
"borderRadius",
|
|
24975
|
-
"transitions",
|
|
24976
|
-
"layout",
|
|
24977
|
-
"colors",
|
|
24978
|
-
"shadows"
|
|
24979
|
-
];
|
|
24980
25304
|
}
|
|
24981
25305
|
});
|
|
24982
25306
|
|
|
24983
|
-
// src/lib/
|
|
24984
|
-
|
|
24985
|
-
|
|
24986
|
-
|
|
24987
|
-
|
|
24988
|
-
|
|
24989
|
-
|
|
25307
|
+
// src/lib/preview/errors.ts
|
|
25308
|
+
var PreviewError;
|
|
25309
|
+
var init_errors2 = __esm({
|
|
25310
|
+
"src/lib/preview/errors.ts"() {
|
|
25311
|
+
"use strict";
|
|
25312
|
+
PreviewError = class extends Error {
|
|
25313
|
+
};
|
|
25314
|
+
}
|
|
25315
|
+
});
|
|
25316
|
+
|
|
25317
|
+
// src/lib/preview/assets.ts
|
|
25318
|
+
import { existsSync as existsSync14 } from "node:fs";
|
|
25319
|
+
import { readFile as readFile4 } from "node:fs/promises";
|
|
25320
|
+
import { resolve as resolve9, dirname as dirname5 } from "node:path";
|
|
25321
|
+
import { fileURLToPath as fileURLToPath5 } from "node:url";
|
|
25322
|
+
function firstExisting(...candidates) {
|
|
25323
|
+
return candidates.find((c) => !!c && existsSync14(c));
|
|
25324
|
+
}
|
|
25325
|
+
function unoBinCandidates(dir) {
|
|
25326
|
+
return ["unocss", "unocss.cmd", "unocss.CMD"].map((n) => resolve9(dir, "node_modules/.bin", n));
|
|
25327
|
+
}
|
|
25328
|
+
async function resolvePreviewAssets(ctx2) {
|
|
25329
|
+
const monorepo = ctx2.config === null;
|
|
25330
|
+
const projectDir = ctx2.projectDir;
|
|
25331
|
+
const rootDir = projectDir ?? ctx2.root;
|
|
25332
|
+
const srcComponentsDir = monorepo ? resolve9(ctx2.root, "ui/components/ui") : resolve9(projectDir, ctx2.config.paths.components);
|
|
25333
|
+
const tokensCss = await loadTokensCss(ctx2);
|
|
25334
|
+
const globalsPath = firstExisting(
|
|
25335
|
+
projectDir && resolve9(projectDir, "styles/globals.css"),
|
|
25336
|
+
projectDir && resolve9(projectDir, "globals.css"),
|
|
25337
|
+
projectDir && resolve9(projectDir, "app/globals.css"),
|
|
25338
|
+
monorepo ? resolve9(ctx2.root, "site/ui/styles/globals.css") : void 0,
|
|
25339
|
+
resolve9(assetDir, "preview-globals.css")
|
|
25340
|
+
);
|
|
25341
|
+
const globalsCss = globalsPath ? await readFile4(globalsPath, "utf-8") : "";
|
|
25342
|
+
const bundledUnoConfig = resolve9(assetDir, "preview-uno.config.ts");
|
|
25343
|
+
const configPath = firstExisting(
|
|
25344
|
+
projectDir && resolve9(projectDir, "uno.config.ts"),
|
|
25345
|
+
projectDir && resolve9(projectDir, "uno.config.js"),
|
|
25346
|
+
monorepo ? resolve9(ctx2.root, "site/ui/uno.config.ts") : void 0,
|
|
25347
|
+
bundledUnoConfig
|
|
25348
|
+
);
|
|
25349
|
+
if (!configPath) {
|
|
25350
|
+
throw new PreviewError(
|
|
25351
|
+
"No UnoCSS config found and the bundled default is missing \u2014 reinstall @barefootjs/cli."
|
|
25352
|
+
);
|
|
25353
|
+
}
|
|
25354
|
+
const unoCwd = monorepo ? resolve9(ctx2.root, "site/ui") : rootDir;
|
|
25355
|
+
const globs = monorepo ? ["../../ui/components/**/*.tsx", "./**/*.tsx", "./dist/**/*.tsx"] : [resolve9(srcComponentsDir, "**/*.tsx")];
|
|
25356
|
+
const unoBin = firstExisting(
|
|
25357
|
+
...unoBinCandidates(rootDir),
|
|
25358
|
+
...monorepo ? unoBinCandidates(resolve9(ctx2.root, "site/ui")) : [],
|
|
25359
|
+
...monorepo ? unoBinCandidates(ctx2.root) : []
|
|
25360
|
+
);
|
|
25361
|
+
if (!unoBin) {
|
|
25362
|
+
throw new PreviewError(
|
|
25363
|
+
"UnoCSS CLI not found. Install it in your project to generate preview styles:\n npm install -D unocss@^66 @unocss/cli@^66\n(v66 matches the bundled config's presetWind4; older majors won't generate the same utilities.)"
|
|
25364
|
+
);
|
|
25365
|
+
}
|
|
25366
|
+
const runtimeStandalone = firstExisting(
|
|
25367
|
+
monorepo ? resolve9(ctx2.root, "packages/client/dist/runtime/standalone.js") : void 0,
|
|
25368
|
+
resolve9(rootDir, "node_modules/@barefootjs/client/dist/runtime/standalone.js"),
|
|
25369
|
+
resolve9(rootDir, "node_modules/@barefootjs/client/dist/runtime/index.js")
|
|
25370
|
+
);
|
|
25371
|
+
if (!runtimeStandalone) {
|
|
25372
|
+
throw new PreviewError(
|
|
25373
|
+
"The @barefootjs/client runtime was not found. Install @barefootjs/client in your project (its dist must include runtime/standalone.js)."
|
|
25374
|
+
);
|
|
25375
|
+
}
|
|
25376
|
+
return {
|
|
25377
|
+
rootDir,
|
|
25378
|
+
srcComponentsDir,
|
|
25379
|
+
tokensCss,
|
|
25380
|
+
globalsCss,
|
|
25381
|
+
runtimeStandalone,
|
|
25382
|
+
uno: { bin: unoBin, cwd: unoCwd, configPath, configIsBundled: configPath === bundledUnoConfig, globs }
|
|
25383
|
+
};
|
|
24990
25384
|
}
|
|
25385
|
+
var assetDir;
|
|
25386
|
+
var init_assets = __esm({
|
|
25387
|
+
"src/lib/preview/assets.ts"() {
|
|
25388
|
+
"use strict";
|
|
25389
|
+
init_tokens();
|
|
25390
|
+
init_errors2();
|
|
25391
|
+
assetDir = dirname5(fileURLToPath5(import.meta.url));
|
|
25392
|
+
}
|
|
25393
|
+
});
|
|
25394
|
+
|
|
25395
|
+
// src/lib/preview-generate.ts
|
|
24991
25396
|
function toPascalCase(kebab) {
|
|
24992
25397
|
return kebab.split("-").map((w) => w[0].toUpperCase() + w.slice(1)).join("");
|
|
24993
25398
|
}
|
|
24994
|
-
function
|
|
24995
|
-
|
|
24996
|
-
return pascal[0].toLowerCase() + pascal.slice(1);
|
|
25399
|
+
function toKebabCase(pascal) {
|
|
25400
|
+
return pascal.replace(/([a-z])([A-Z])/g, "$1-$2").replace(/([A-Z])([A-Z][a-z])/g, "$1-$2").toLowerCase();
|
|
24997
25401
|
}
|
|
24998
|
-
function
|
|
24999
|
-
|
|
25000
|
-
const found = metas.filter((m) => m.meta !== null);
|
|
25001
|
-
const notFound = metas.filter((m) => m.meta === null).map((m) => m.name);
|
|
25002
|
-
const needsClient = found.some((m) => m.meta.stateful);
|
|
25003
|
-
const imports = buildImports(found);
|
|
25004
|
-
const componentCode = generateComponentCode(componentName, imports, needsClient, notFound);
|
|
25005
|
-
const testCode = generateTestCode(componentName, needsClient, options.testImportSource ?? "bun:test");
|
|
25006
|
-
const basePath = `${componentsBasePath}/${componentName}`;
|
|
25007
|
-
return {
|
|
25008
|
-
componentCode,
|
|
25009
|
-
testCode,
|
|
25010
|
-
componentPath: `${basePath}/index.tsx`,
|
|
25011
|
-
testPath: `${basePath}/index.test.tsx`
|
|
25012
|
-
};
|
|
25402
|
+
function capitalize2(s) {
|
|
25403
|
+
return s.charAt(0).toUpperCase() + s.slice(1);
|
|
25013
25404
|
}
|
|
25014
|
-
function
|
|
25015
|
-
const
|
|
25016
|
-
|
|
25017
|
-
|
|
25018
|
-
|
|
25019
|
-
|
|
25020
|
-
|
|
25021
|
-
|
|
25405
|
+
function inferVariantPropName(typeName, props) {
|
|
25406
|
+
const match = props.find((p) => p.type === typeName);
|
|
25407
|
+
return match ? match.name : null;
|
|
25408
|
+
}
|
|
25409
|
+
function findExternalTags(code, knownNames) {
|
|
25410
|
+
const external = [];
|
|
25411
|
+
for (const m of code.matchAll(/<([A-Z][a-zA-Z0-9]*)/g)) {
|
|
25412
|
+
if (!knownNames.has(m[1]) && !external.includes(m[1])) {
|
|
25413
|
+
external.push(m[1]);
|
|
25022
25414
|
}
|
|
25023
|
-
imports.push({ from: `../${name}`, names });
|
|
25024
25415
|
}
|
|
25025
|
-
return
|
|
25416
|
+
return external;
|
|
25026
25417
|
}
|
|
25027
|
-
function
|
|
25028
|
-
|
|
25029
|
-
|
|
25030
|
-
if (needsClient) {
|
|
25031
|
-
lines.push(`"use client"`);
|
|
25032
|
-
lines.push(``);
|
|
25033
|
-
lines.push(`import { createSignal } from '@barefootjs/client'`);
|
|
25418
|
+
function resolveExternalTagImport(tag, parentModuleName, parentPascalName) {
|
|
25419
|
+
if (tag.endsWith("Icon")) {
|
|
25420
|
+
return { from: "../icon", name: tag };
|
|
25034
25421
|
}
|
|
25035
|
-
|
|
25036
|
-
|
|
25037
|
-
lines.push(`import { ${imp.names.join(", ")} } from '${imp.from}'`);
|
|
25422
|
+
if (tag.startsWith(parentPascalName)) {
|
|
25423
|
+
return { from: `../${parentModuleName}`, name: tag };
|
|
25038
25424
|
}
|
|
25039
|
-
|
|
25040
|
-
|
|
25041
|
-
|
|
25042
|
-
|
|
25043
|
-
|
|
25044
|
-
|
|
25425
|
+
return { from: `../${toKebabCase(tag)}`, name: tag };
|
|
25426
|
+
}
|
|
25427
|
+
function emitJsxReturn(lines, jsx, indent = " ") {
|
|
25428
|
+
const jsxLines = jsx.split("\n");
|
|
25429
|
+
const tagLines = jsxLines.filter((l) => /^\s*<[A-Za-z]/.test(l));
|
|
25430
|
+
if (tagLines.length === 0) {
|
|
25431
|
+
lines.push(`${indent}return ${jsx}`);
|
|
25432
|
+
return;
|
|
25045
25433
|
}
|
|
25046
|
-
|
|
25047
|
-
|
|
25048
|
-
|
|
25049
|
-
|
|
25050
|
-
|
|
25051
|
-
|
|
25052
|
-
|
|
25053
|
-
|
|
25054
|
-
|
|
25055
|
-
|
|
25056
|
-
lines.push(` // TODO: Add signals`);
|
|
25057
|
-
lines.push(` // const [value, setValue] = createSignal(...)`);
|
|
25058
|
-
lines.push(``);
|
|
25059
|
-
lines.push(` return (`);
|
|
25060
|
-
lines.push(` <div data-slot="${componentName}">`);
|
|
25061
|
-
lines.push(` {/* TODO: Compose components */}`);
|
|
25062
|
-
lines.push(` </div>`);
|
|
25063
|
-
lines.push(` )`);
|
|
25064
|
-
lines.push(`}`);
|
|
25434
|
+
const minIndent = Math.min(...tagLines.map((l) => l.match(/^(\s*)/)?.[1].length ?? 0));
|
|
25435
|
+
const rootElements = tagLines.filter((l) => (l.match(/^(\s*)/)?.[1].length ?? 0) === minIndent);
|
|
25436
|
+
const needsFragment = rootElements.length > 1 && !jsx.trim().startsWith("<>");
|
|
25437
|
+
if (jsxLines.length === 1 && !needsFragment) {
|
|
25438
|
+
const singleLineRoots = (jsx.match(/<(?![/])[A-Za-z]/g) ?? []).length;
|
|
25439
|
+
if (singleLineRoots > 1) {
|
|
25440
|
+
lines.push(`${indent}return (<>${jsx}</>)`);
|
|
25441
|
+
} else {
|
|
25442
|
+
lines.push(`${indent}return ${jsx}`);
|
|
25443
|
+
}
|
|
25065
25444
|
} else {
|
|
25066
|
-
lines.push(
|
|
25067
|
-
lines.push(
|
|
25068
|
-
|
|
25069
|
-
|
|
25070
|
-
|
|
25071
|
-
lines.push(
|
|
25072
|
-
lines.push(
|
|
25073
|
-
lines.push(` {/* TODO: Compose components */}`);
|
|
25074
|
-
lines.push(` {children}`);
|
|
25075
|
-
lines.push(` </div>`);
|
|
25076
|
-
lines.push(` )`);
|
|
25077
|
-
lines.push(`}`);
|
|
25445
|
+
lines.push(`${indent}return (`);
|
|
25446
|
+
if (needsFragment) lines.push(`${indent} <>`);
|
|
25447
|
+
for (const l of jsxLines) {
|
|
25448
|
+
lines.push(`${indent} ${needsFragment ? " " : ""}${l}`);
|
|
25449
|
+
}
|
|
25450
|
+
if (needsFragment) lines.push(`${indent} </>`);
|
|
25451
|
+
lines.push(`${indent})`);
|
|
25078
25452
|
}
|
|
25079
|
-
lines.push(``);
|
|
25080
|
-
lines.push(`export { ${pascalName} }`);
|
|
25081
|
-
lines.push(`export type { ${pascalName}Props }`);
|
|
25082
|
-
lines.push(``);
|
|
25083
|
-
return lines.join("\n");
|
|
25084
25453
|
}
|
|
25085
|
-
function
|
|
25086
|
-
const
|
|
25087
|
-
const
|
|
25454
|
+
function splitExampleCode(code) {
|
|
25455
|
+
const lines = code.split("\n");
|
|
25456
|
+
const firstJsxLine = lines.findIndex((l) => l.trim().startsWith("<"));
|
|
25457
|
+
if (firstJsxLine <= 0) {
|
|
25458
|
+
return { statements: [], jsx: code };
|
|
25459
|
+
}
|
|
25460
|
+
return {
|
|
25461
|
+
statements: lines.slice(0, firstJsxLine).filter((l) => l.trim() !== ""),
|
|
25462
|
+
jsx: lines.slice(firstJsxLine).join("\n")
|
|
25463
|
+
};
|
|
25464
|
+
}
|
|
25465
|
+
function isSimpleJsx(code) {
|
|
25466
|
+
const trimmed = code.trim();
|
|
25467
|
+
return trimmed.startsWith("<") && !trimmed.includes("createSignal");
|
|
25468
|
+
}
|
|
25469
|
+
function generatePreview(meta, componentsBasePath = "ui/components/ui") {
|
|
25470
|
+
const pascalName = toPascalCase(meta.name);
|
|
25471
|
+
const hasVariants = meta.variants != null && Object.keys(meta.variants).length > 0;
|
|
25472
|
+
const hasSubComponents = meta.subComponents != null && meta.subComponents.length > 0;
|
|
25473
|
+
let needsClient = meta.stateful || meta.tags.includes("stateful");
|
|
25474
|
+
const exampleCode = hasSubComponents && meta.examples.length > 0 ? meta.examples[0].code : "";
|
|
25475
|
+
if (exampleCode.includes("createSignal")) {
|
|
25476
|
+
needsClient = true;
|
|
25477
|
+
}
|
|
25478
|
+
const needsCreateSignalImport = exampleCode.includes("createSignal");
|
|
25088
25479
|
const lines = [];
|
|
25089
|
-
|
|
25090
|
-
lines.push(
|
|
25091
|
-
lines.push(`import { resolve } from 'path'`);
|
|
25092
|
-
lines.push(`import { renderToTest } from '@barefootjs/test'`);
|
|
25093
|
-
lines.push(``);
|
|
25094
|
-
lines.push(`const ${varName} = readFileSync(resolve(__dirname, 'index.tsx'), 'utf-8')`);
|
|
25095
|
-
lines.push(``);
|
|
25096
|
-
lines.push(`describe('${pascalName}', () => {`);
|
|
25097
|
-
lines.push(` const result = renderToTest(${varName}, '${componentName}.tsx')`);
|
|
25098
|
-
lines.push(``);
|
|
25099
|
-
lines.push(` test('has no compiler errors', () => {`);
|
|
25100
|
-
lines.push(` expect(result.errors).toEqual([])`);
|
|
25101
|
-
lines.push(` })`);
|
|
25102
|
-
lines.push(``);
|
|
25103
|
-
lines.push(` test('componentName is ${pascalName}', () => {`);
|
|
25104
|
-
lines.push(` expect(result.componentName).toBe('${pascalName}')`);
|
|
25105
|
-
lines.push(` })`);
|
|
25106
|
-
lines.push(``);
|
|
25480
|
+
const previewNames = [];
|
|
25481
|
+
lines.push("// Auto-generated preview. Customize by editing this file.");
|
|
25107
25482
|
if (needsClient) {
|
|
25108
|
-
lines.push(
|
|
25109
|
-
lines.push(` expect(result.isClient).toBe(true)`);
|
|
25110
|
-
lines.push(` })`);
|
|
25483
|
+
lines.push('"use client"');
|
|
25111
25484
|
}
|
|
25112
|
-
lines.push(
|
|
25113
|
-
|
|
25114
|
-
|
|
25115
|
-
lines.push(` })`);
|
|
25116
|
-
lines.push(``);
|
|
25117
|
-
lines.push(` test('has data-slot=${componentName}', () => {`);
|
|
25118
|
-
lines.push(` expect(result.root.props['data-slot']).toBe('${componentName}')`);
|
|
25119
|
-
lines.push(` })`);
|
|
25120
|
-
lines.push(``);
|
|
25121
|
-
lines.push(` test('toStructure() shows expected tree', () => {`);
|
|
25122
|
-
lines.push(` const structure = result.toStructure()`);
|
|
25123
|
-
lines.push(` expect(structure.length).toBeGreaterThan(0)`);
|
|
25124
|
-
lines.push(` })`);
|
|
25125
|
-
lines.push(`})`);
|
|
25126
|
-
lines.push(``);
|
|
25127
|
-
return lines.join("\n");
|
|
25128
|
-
}
|
|
25129
|
-
var init_scaffold = __esm({
|
|
25130
|
-
"src/lib/scaffold.ts"() {
|
|
25131
|
-
"use strict";
|
|
25485
|
+
lines.push("");
|
|
25486
|
+
if (needsCreateSignalImport) {
|
|
25487
|
+
lines.push("import { createSignal } from '@barefootjs/client'");
|
|
25132
25488
|
}
|
|
25133
|
-
|
|
25134
|
-
|
|
25135
|
-
|
|
25136
|
-
|
|
25137
|
-
|
|
25138
|
-
run: () => run11
|
|
25139
|
-
});
|
|
25140
|
-
import { writeFileSync as writeFileSync5, mkdirSync as mkdirSync4, existsSync as existsSync16 } from "fs";
|
|
25141
|
-
import path19 from "path";
|
|
25142
|
-
function run11(args2, ctx2) {
|
|
25143
|
-
if (args2.length < 1) {
|
|
25144
|
-
console.error("Usage: bf gen component <component-name> [use-component1] [use-component2] ...");
|
|
25145
|
-
console.error("Example: bf gen component settings-form input switch button");
|
|
25146
|
-
process.exit(1);
|
|
25489
|
+
const subNames = [];
|
|
25490
|
+
if (hasSubComponents) {
|
|
25491
|
+
for (const sub2 of meta.subComponents) {
|
|
25492
|
+
subNames.push(sub2.name);
|
|
25493
|
+
}
|
|
25147
25494
|
}
|
|
25148
|
-
const
|
|
25149
|
-
const
|
|
25150
|
-
|
|
25151
|
-
|
|
25152
|
-
|
|
25153
|
-
|
|
25154
|
-
|
|
25155
|
-
|
|
25156
|
-
|
|
25157
|
-
|
|
25158
|
-
|
|
25495
|
+
const importsBySource = /* @__PURE__ */ new Map();
|
|
25496
|
+
const moduleNames = [pascalName, ...subNames];
|
|
25497
|
+
if (hasSubComponents && exampleCode) {
|
|
25498
|
+
const allTags = [...exampleCode.matchAll(/<([A-Z][a-zA-Z0-9]*)/g)].map((m) => m[1]);
|
|
25499
|
+
const usedFromModule = [...new Set(allTags.filter((t) => moduleNames.includes(t)))];
|
|
25500
|
+
if (usedFromModule.length > 0) {
|
|
25501
|
+
importsBySource.set(`../${meta.name}`, usedFromModule);
|
|
25502
|
+
}
|
|
25503
|
+
const knownNames = new Set(moduleNames);
|
|
25504
|
+
for (const tag of findExternalTags(exampleCode, knownNames)) {
|
|
25505
|
+
const resolved = resolveExternalTagImport(tag, meta.name, pascalName);
|
|
25506
|
+
const list = importsBySource.get(resolved.from) ?? [];
|
|
25507
|
+
if (!list.includes(resolved.name)) list.push(resolved.name);
|
|
25508
|
+
importsBySource.set(resolved.from, list);
|
|
25509
|
+
}
|
|
25510
|
+
} else {
|
|
25511
|
+
importsBySource.set(`../${meta.name}`, moduleNames);
|
|
25159
25512
|
}
|
|
25160
|
-
const
|
|
25161
|
-
|
|
25162
|
-
if (!existsSync16(testDir)) {
|
|
25163
|
-
mkdirSync4(testDir, { recursive: true });
|
|
25513
|
+
for (const [from, names] of importsBySource) {
|
|
25514
|
+
lines.push(`import { ${names.join(", ")} } from '${from}'`);
|
|
25164
25515
|
}
|
|
25165
|
-
|
|
25166
|
-
|
|
25167
|
-
|
|
25168
|
-
|
|
25169
|
-
|
|
25170
|
-
|
|
25171
|
-
|
|
25172
|
-
|
|
25173
|
-
|
|
25174
|
-
|
|
25175
|
-
|
|
25176
|
-
}
|
|
25177
|
-
var init_gen_component = __esm({
|
|
25178
|
-
"src/commands/gen-component.ts"() {
|
|
25179
|
-
"use strict";
|
|
25180
|
-
init_scaffold();
|
|
25181
|
-
init_scaffold_layout();
|
|
25182
|
-
init_pm();
|
|
25516
|
+
lines.push("");
|
|
25517
|
+
if (hasSubComponents) {
|
|
25518
|
+
generateMultiComponent(lines, previewNames, meta, pascalName, subNames);
|
|
25519
|
+
} else if (needsClient && hasVariants) {
|
|
25520
|
+
generateStatefulWithVariants(lines, previewNames, meta, pascalName);
|
|
25521
|
+
} else if (needsClient) {
|
|
25522
|
+
generateStateful(lines, previewNames, meta, pascalName);
|
|
25523
|
+
} else if (hasVariants) {
|
|
25524
|
+
generateStatelessWithVariants(lines, previewNames, meta, pascalName);
|
|
25525
|
+
} else {
|
|
25526
|
+
generateStatelessSimple(lines, previewNames, meta, pascalName);
|
|
25183
25527
|
}
|
|
25184
|
-
|
|
25185
|
-
|
|
25186
|
-
// src/lib/parse-component.ts
|
|
25187
|
-
function parseComponent(source) {
|
|
25528
|
+
lines.push("");
|
|
25188
25529
|
return {
|
|
25189
|
-
|
|
25190
|
-
|
|
25191
|
-
|
|
25192
|
-
props: extractMainProps2(source),
|
|
25193
|
-
subComponents: extractSubComponents2(source),
|
|
25194
|
-
variants: extractVariants2(source),
|
|
25195
|
-
accessibility: extractAccessibility2(source),
|
|
25196
|
-
dependencies: extractDependencies3(source),
|
|
25197
|
-
exportedNames: extractExportedNames(source)
|
|
25530
|
+
code: lines.join("\n"),
|
|
25531
|
+
previewNames,
|
|
25532
|
+
filePath: `${componentsBasePath}/${meta.name}/index.preview.tsx`
|
|
25198
25533
|
};
|
|
25199
25534
|
}
|
|
25200
|
-
function
|
|
25201
|
-
|
|
25202
|
-
|
|
25535
|
+
function generateStatelessWithVariants(lines, previewNames, meta, pascalName) {
|
|
25536
|
+
previewNames.push("Default");
|
|
25537
|
+
lines.push("export function Default() {");
|
|
25538
|
+
lines.push(` return <${pascalName}>${meta.title}</${pascalName}>`);
|
|
25539
|
+
lines.push("}");
|
|
25540
|
+
lines.push("");
|
|
25541
|
+
for (const [typeName, values] of Object.entries(meta.variants)) {
|
|
25542
|
+
const propName = inferVariantPropName(typeName, meta.props);
|
|
25543
|
+
if (!propName) continue;
|
|
25544
|
+
const funcName = capitalize2(propName) + "s";
|
|
25545
|
+
previewNames.push(funcName);
|
|
25546
|
+
lines.push(`export function ${funcName}() {`);
|
|
25547
|
+
lines.push(" return (");
|
|
25548
|
+
lines.push(' <div className="flex flex-wrap items-center gap-4">');
|
|
25549
|
+
for (const value of values) {
|
|
25550
|
+
lines.push(` <${pascalName} ${propName}="${value}">${capitalize2(value)}</${pascalName}>`);
|
|
25551
|
+
}
|
|
25552
|
+
lines.push(" </div>");
|
|
25553
|
+
lines.push(" )");
|
|
25554
|
+
lines.push("}");
|
|
25555
|
+
lines.push("");
|
|
25556
|
+
}
|
|
25203
25557
|
}
|
|
25204
|
-
function
|
|
25205
|
-
|
|
25206
|
-
|
|
25207
|
-
const
|
|
25208
|
-
|
|
25209
|
-
|
|
25210
|
-
|
|
25211
|
-
const
|
|
25212
|
-
if (
|
|
25213
|
-
|
|
25558
|
+
function generateStatelessSimple(lines, previewNames, meta, pascalName) {
|
|
25559
|
+
previewNames.push("Default");
|
|
25560
|
+
lines.push("export function Default() {");
|
|
25561
|
+
const simpleExample = meta.examples.find((e) => isSimpleJsx(e.code));
|
|
25562
|
+
if (simpleExample) {
|
|
25563
|
+
emitJsxReturn(lines, simpleExample.code);
|
|
25564
|
+
} else {
|
|
25565
|
+
const hasChildren = meta.props.some((p) => p.name === "children");
|
|
25566
|
+
if (hasChildren) {
|
|
25567
|
+
lines.push(` return <${pascalName}>${meta.title}</${pascalName}>`);
|
|
25568
|
+
} else {
|
|
25569
|
+
lines.push(` return <${pascalName} />`);
|
|
25570
|
+
}
|
|
25214
25571
|
}
|
|
25215
|
-
|
|
25572
|
+
lines.push("}");
|
|
25573
|
+
lines.push("");
|
|
25216
25574
|
}
|
|
25217
|
-
function
|
|
25218
|
-
|
|
25219
|
-
|
|
25220
|
-
|
|
25221
|
-
|
|
25222
|
-
|
|
25223
|
-
|
|
25224
|
-
|
|
25225
|
-
|
|
25226
|
-
|
|
25227
|
-
|
|
25575
|
+
function generateStateful(lines, previewNames, meta, pascalName) {
|
|
25576
|
+
previewNames.push("Default");
|
|
25577
|
+
lines.push("export function Default() {");
|
|
25578
|
+
lines.push(" return (");
|
|
25579
|
+
lines.push(' <div className="flex gap-4">');
|
|
25580
|
+
lines.push(` <${pascalName} />`);
|
|
25581
|
+
const defaultStateProp = meta.props.find(
|
|
25582
|
+
(p) => p.name === "defaultChecked" || p.name === "defaultPressed" || p.name === "defaultValue" || p.name === "defaultOpen"
|
|
25583
|
+
);
|
|
25584
|
+
if (defaultStateProp) {
|
|
25585
|
+
lines.push(` <${pascalName} ${defaultStateProp.name} />`);
|
|
25228
25586
|
}
|
|
25229
|
-
|
|
25587
|
+
if (meta.props.some((p) => p.name === "disabled")) {
|
|
25588
|
+
lines.push(` <${pascalName} disabled />`);
|
|
25589
|
+
}
|
|
25590
|
+
lines.push(" </div>");
|
|
25591
|
+
lines.push(" )");
|
|
25592
|
+
lines.push("}");
|
|
25593
|
+
lines.push("");
|
|
25230
25594
|
}
|
|
25231
|
-
function
|
|
25232
|
-
|
|
25233
|
-
|
|
25234
|
-
|
|
25595
|
+
function generateStatefulWithVariants(lines, previewNames, meta, pascalName) {
|
|
25596
|
+
generateStateful(lines, previewNames, meta, pascalName);
|
|
25597
|
+
const hasChildren = meta.props.some((p) => p.name === "children");
|
|
25598
|
+
for (const [typeName, values] of Object.entries(meta.variants)) {
|
|
25599
|
+
const propName = inferVariantPropName(typeName, meta.props);
|
|
25600
|
+
if (!propName) continue;
|
|
25601
|
+
const funcName = capitalize2(propName) + "s";
|
|
25602
|
+
previewNames.push(funcName);
|
|
25603
|
+
lines.push(`export function ${funcName}() {`);
|
|
25604
|
+
lines.push(" return (");
|
|
25605
|
+
lines.push(' <div className="flex flex-wrap items-center gap-4">');
|
|
25606
|
+
for (const value of values) {
|
|
25607
|
+
if (hasChildren) {
|
|
25608
|
+
lines.push(` <${pascalName} ${propName}="${value}">${capitalize2(value)}</${pascalName}>`);
|
|
25609
|
+
} else {
|
|
25610
|
+
lines.push(` <${pascalName} ${propName}="${value}" />`);
|
|
25611
|
+
}
|
|
25612
|
+
}
|
|
25613
|
+
lines.push(" </div>");
|
|
25614
|
+
lines.push(" )");
|
|
25615
|
+
lines.push("}");
|
|
25616
|
+
lines.push("");
|
|
25617
|
+
}
|
|
25235
25618
|
}
|
|
25236
|
-
function
|
|
25237
|
-
|
|
25238
|
-
if (
|
|
25239
|
-
|
|
25240
|
-
|
|
25241
|
-
|
|
25242
|
-
|
|
25243
|
-
|
|
25244
|
-
|
|
25245
|
-
|
|
25246
|
-
|
|
25247
|
-
|
|
25619
|
+
function generateMultiComponent(lines, previewNames, meta, pascalName, _subNames) {
|
|
25620
|
+
previewNames.push("Default");
|
|
25621
|
+
if (meta.examples.length > 0) {
|
|
25622
|
+
const example = meta.examples[0];
|
|
25623
|
+
const { statements, jsx } = splitExampleCode(example.code);
|
|
25624
|
+
lines.push("export function Default() {");
|
|
25625
|
+
for (const stmt of statements) {
|
|
25626
|
+
lines.push(` ${stmt}`);
|
|
25627
|
+
}
|
|
25628
|
+
const definedNames = new Set(
|
|
25629
|
+
statements.flatMap((s) => [...s.matchAll(/\b(?:const|let|var)\s+(?:\[([^\]]+)\]|(\w+))/g)]).flatMap((m) => m[1] ? m[1].split(",").map((v) => v.trim()) : [m[2]])
|
|
25630
|
+
);
|
|
25631
|
+
const handlerRefs = [...new Set([...jsx.matchAll(/\b(handle[A-Z]\w*)\b/g)].map((m) => m[1]))];
|
|
25632
|
+
for (const h of handlerRefs) {
|
|
25633
|
+
if (!definedNames.has(h)) {
|
|
25634
|
+
lines.push(` const ${h} = () => {}`);
|
|
25635
|
+
}
|
|
25636
|
+
}
|
|
25637
|
+
if (statements.length > 0 || handlerRefs.length > 0) {
|
|
25638
|
+
lines.push("");
|
|
25639
|
+
}
|
|
25640
|
+
emitJsxReturn(lines, jsx);
|
|
25641
|
+
lines.push("}");
|
|
25642
|
+
} else {
|
|
25643
|
+
lines.push("export function Default() {");
|
|
25644
|
+
lines.push(" return (");
|
|
25645
|
+
lines.push(` <${pascalName}>`);
|
|
25646
|
+
for (const sub2 of meta.subComponents) {
|
|
25647
|
+
const hasChildrenProp = sub2.props.some((p) => p.name === "children");
|
|
25648
|
+
const label = sub2.name.replace(pascalName, "") || sub2.name;
|
|
25649
|
+
if (hasChildrenProp) {
|
|
25650
|
+
lines.push(` <${sub2.name}>${label}</${sub2.name}>`);
|
|
25651
|
+
} else {
|
|
25652
|
+
lines.push(` <${sub2.name} />`);
|
|
25248
25653
|
}
|
|
25249
25654
|
}
|
|
25655
|
+
lines.push(` </${pascalName}>`);
|
|
25656
|
+
lines.push(" )");
|
|
25657
|
+
lines.push("}");
|
|
25250
25658
|
}
|
|
25251
|
-
|
|
25252
|
-
return parsePropsBody(body);
|
|
25659
|
+
lines.push("");
|
|
25253
25660
|
}
|
|
25254
|
-
|
|
25255
|
-
|
|
25256
|
-
|
|
25257
|
-
let m;
|
|
25258
|
-
while ((m = propRegex.exec(body)) !== null) {
|
|
25259
|
-
const jsdoc = m[1] || "";
|
|
25260
|
-
const name = m[2];
|
|
25261
|
-
const optional = m[3] === "?";
|
|
25262
|
-
const rawType = m[4].trim();
|
|
25263
|
-
if (name.startsWith("__")) continue;
|
|
25264
|
-
const type = rawType.replace(/;?\s*$/, "").trim();
|
|
25265
|
-
const description = extractPropDescription(jsdoc);
|
|
25266
|
-
const defaultMatch = jsdoc.match(/@default\s+(.+?)(?:\s*$|\s*\*)/m);
|
|
25267
|
-
const defaultValue = defaultMatch ? defaultMatch[1].trim().replace(/^['"]|['"]$/g, "") : void 0;
|
|
25268
|
-
props.push({
|
|
25269
|
-
name,
|
|
25270
|
-
type,
|
|
25271
|
-
required: !optional && defaultValue === void 0,
|
|
25272
|
-
default: defaultValue,
|
|
25273
|
-
description
|
|
25274
|
-
});
|
|
25661
|
+
var init_preview_generate = __esm({
|
|
25662
|
+
"src/lib/preview-generate.ts"() {
|
|
25663
|
+
"use strict";
|
|
25275
25664
|
}
|
|
25276
|
-
|
|
25665
|
+
});
|
|
25666
|
+
|
|
25667
|
+
// src/lib/preview/run.ts
|
|
25668
|
+
import { resolve as resolve10, relative as relative4 } from "node:path";
|
|
25669
|
+
import { readFileSync as readFileSync6, writeFileSync as writeFileSync4, existsSync as existsSync15 } from "node:fs";
|
|
25670
|
+
async function runPreview(componentName, ctx2, opts = {}) {
|
|
25671
|
+
const assets = await resolvePreviewAssets(ctx2);
|
|
25672
|
+
const previewsPath = resolve10(assets.srcComponentsDir, componentName, "index.preview.tsx");
|
|
25673
|
+
if (!existsSync15(previewsPath)) {
|
|
25674
|
+
try {
|
|
25675
|
+
const meta = loadComponent(ctx2.metaDir, componentName);
|
|
25676
|
+
const result = generatePreview(meta);
|
|
25677
|
+
writeFileSync4(previewsPath, result.code);
|
|
25678
|
+
console.log(`Auto-generated preview: ${relative4(assets.rootDir, previewsPath)}`);
|
|
25679
|
+
} catch {
|
|
25680
|
+
throw new PreviewError(
|
|
25681
|
+
`Preview file not found and auto-generation failed for "${componentName}".
|
|
25682
|
+
Run: bf gen preview ${componentName}`
|
|
25683
|
+
);
|
|
25684
|
+
}
|
|
25685
|
+
}
|
|
25686
|
+
const source = readFileSync6(previewsPath, "utf-8");
|
|
25687
|
+
const previewNames = [
|
|
25688
|
+
...source.matchAll(/export\s+(?:async\s+)?function\s+(\w+)/g),
|
|
25689
|
+
...source.matchAll(/export\s+const\s+(\w+)\s*=/g)
|
|
25690
|
+
].map((m) => m[1]);
|
|
25691
|
+
if (previewNames.length === 0) {
|
|
25692
|
+
throw new PreviewError("No exported preview functions found in the preview file.");
|
|
25693
|
+
}
|
|
25694
|
+
console.log(`Found ${previewNames.length} previews: ${previewNames.join(", ")}`);
|
|
25695
|
+
return compile({ assets, previewsPath, previewNames, componentName, liveReload: opts.liveReload });
|
|
25277
25696
|
}
|
|
25278
|
-
|
|
25279
|
-
|
|
25280
|
-
|
|
25281
|
-
|
|
25282
|
-
|
|
25283
|
-
|
|
25284
|
-
|
|
25285
|
-
|
|
25697
|
+
var init_run = __esm({
|
|
25698
|
+
"src/lib/preview/run.ts"() {
|
|
25699
|
+
"use strict";
|
|
25700
|
+
init_compile();
|
|
25701
|
+
init_assets();
|
|
25702
|
+
init_errors2();
|
|
25703
|
+
init_meta_loader();
|
|
25704
|
+
init_preview_generate();
|
|
25705
|
+
init_errors2();
|
|
25286
25706
|
}
|
|
25287
|
-
|
|
25707
|
+
});
|
|
25708
|
+
|
|
25709
|
+
// src/lib/preview/serve.ts
|
|
25710
|
+
import { createServer } from "node:http";
|
|
25711
|
+
import { createReadStream, existsSync as existsSync16, statSync as statSync2 } from "node:fs";
|
|
25712
|
+
import { join, extname, normalize } from "node:path";
|
|
25713
|
+
function startPreviewServer(distDir, port) {
|
|
25714
|
+
let reloadToken = String(Date.now());
|
|
25715
|
+
const server = createServer((req, res) => {
|
|
25716
|
+
const url = (req.url ?? "/").split("?")[0];
|
|
25717
|
+
if (url === "/__preview_reload") {
|
|
25718
|
+
res.setHeader("Content-Type", "text/plain; charset=utf-8");
|
|
25719
|
+
res.setHeader("Cache-Control", "no-store");
|
|
25720
|
+
res.end(reloadToken);
|
|
25721
|
+
return;
|
|
25722
|
+
}
|
|
25723
|
+
const rel = decodeURIComponent(url === "/" ? "/index.html" : url);
|
|
25724
|
+
const filePath = normalize(join(distDir, rel));
|
|
25725
|
+
if (!filePath.startsWith(distDir) || !existsSync16(filePath) || statSync2(filePath).isDirectory()) {
|
|
25726
|
+
res.statusCode = 404;
|
|
25727
|
+
res.end("Not found");
|
|
25728
|
+
return;
|
|
25729
|
+
}
|
|
25730
|
+
res.setHeader("Content-Type", MIME[extname(filePath)] ?? "application/octet-stream");
|
|
25731
|
+
res.setHeader("Cache-Control", "no-store");
|
|
25732
|
+
createReadStream(filePath).pipe(res);
|
|
25733
|
+
});
|
|
25734
|
+
server.listen(port);
|
|
25735
|
+
return {
|
|
25736
|
+
url: `http://localhost:${port}`,
|
|
25737
|
+
bumpReload() {
|
|
25738
|
+
reloadToken = String(Date.now());
|
|
25739
|
+
},
|
|
25740
|
+
close() {
|
|
25741
|
+
server.close();
|
|
25742
|
+
}
|
|
25743
|
+
};
|
|
25744
|
+
}
|
|
25745
|
+
var MIME;
|
|
25746
|
+
var init_serve = __esm({
|
|
25747
|
+
"src/lib/preview/serve.ts"() {
|
|
25748
|
+
"use strict";
|
|
25749
|
+
MIME = {
|
|
25750
|
+
".html": "text/html; charset=utf-8",
|
|
25751
|
+
".js": "text/javascript; charset=utf-8",
|
|
25752
|
+
".css": "text/css; charset=utf-8",
|
|
25753
|
+
".json": "application/json; charset=utf-8",
|
|
25754
|
+
".svg": "image/svg+xml",
|
|
25755
|
+
".map": "application/json; charset=utf-8"
|
|
25756
|
+
};
|
|
25757
|
+
}
|
|
25758
|
+
});
|
|
25759
|
+
|
|
25760
|
+
// src/commands/preview.ts
|
|
25761
|
+
var preview_exports = {};
|
|
25762
|
+
__export(preview_exports, {
|
|
25763
|
+
run: () => run8
|
|
25764
|
+
});
|
|
25765
|
+
import { existsSync as existsSync17, readdirSync as readdirSync5, watch as fsWatch } from "fs";
|
|
25766
|
+
import path16 from "path";
|
|
25767
|
+
function parseArgs(args2) {
|
|
25768
|
+
const out = { serve: false, watch: false, help: false, port: DEFAULT_PORT };
|
|
25769
|
+
for (let i = 0; i < args2.length; i++) {
|
|
25770
|
+
const a = args2[i];
|
|
25771
|
+
if (a === "-h" || a === "--help") out.help = true;
|
|
25772
|
+
else if (a === "--serve") out.serve = true;
|
|
25773
|
+
else if (a === "--watch") out.watch = true;
|
|
25774
|
+
else if (a === "--port") out.port = parseInt(args2[++i] ?? "", 10);
|
|
25775
|
+
else if (a.startsWith("--port=")) out.port = parseInt(a.slice("--port=".length), 10);
|
|
25776
|
+
else if (!a.startsWith("-") && out.component === void 0) out.component = a;
|
|
25777
|
+
}
|
|
25778
|
+
if (out.watch) out.serve = true;
|
|
25779
|
+
return out;
|
|
25780
|
+
}
|
|
25781
|
+
function listPreviewableComponents(ctx2) {
|
|
25782
|
+
const { writeRoot, componentsBasePath } = resolveScaffoldLayout(ctx2);
|
|
25783
|
+
const componentsDir = path16.join(writeRoot, componentsBasePath);
|
|
25784
|
+
if (!existsSync17(componentsDir)) return [];
|
|
25785
|
+
const names = [];
|
|
25786
|
+
for (const name of readdirSync5(componentsDir)) {
|
|
25787
|
+
const previewFile = path16.join(componentsDir, name, "index.preview.tsx");
|
|
25788
|
+
if (existsSync17(previewFile)) names.push(name);
|
|
25789
|
+
}
|
|
25790
|
+
return names.sort();
|
|
25791
|
+
}
|
|
25792
|
+
async function run8(args2, ctx2) {
|
|
25793
|
+
const opts = parseArgs(args2);
|
|
25794
|
+
if (opts.help) {
|
|
25795
|
+
console.log(HELP);
|
|
25796
|
+
return;
|
|
25797
|
+
}
|
|
25798
|
+
if (!opts.component) {
|
|
25799
|
+
const available = listPreviewableComponents(ctx2);
|
|
25800
|
+
if (ctx2.jsonFlag) {
|
|
25801
|
+
console.log(JSON.stringify({ previewable: available }, null, 2));
|
|
25802
|
+
return;
|
|
25803
|
+
}
|
|
25804
|
+
if (available.length === 0) {
|
|
25805
|
+
console.error("No previewable components found.");
|
|
25806
|
+
console.error("Generate one with: bf gen preview <component>");
|
|
25807
|
+
process.exit(1);
|
|
25808
|
+
}
|
|
25809
|
+
console.log(`${available.length} previewable component(s):`);
|
|
25810
|
+
for (const name of available) console.log(` ${name}`);
|
|
25811
|
+
console.log();
|
|
25812
|
+
console.log("Open one with: bf preview <component>");
|
|
25813
|
+
return;
|
|
25814
|
+
}
|
|
25815
|
+
if (!Number.isInteger(opts.port) || opts.port < 1 || opts.port > 65535) {
|
|
25816
|
+
console.error(`Invalid --port: must be an integer between 1 and 65535.`);
|
|
25817
|
+
process.exit(1);
|
|
25818
|
+
}
|
|
25819
|
+
const component = opts.component;
|
|
25820
|
+
const runOpts = { liveReload: opts.watch };
|
|
25821
|
+
let result;
|
|
25822
|
+
try {
|
|
25823
|
+
result = await runPreview(component, ctx2, runOpts);
|
|
25824
|
+
} catch (err) {
|
|
25825
|
+
if (err instanceof PreviewError) {
|
|
25826
|
+
console.error(`Error: ${err.message}`);
|
|
25827
|
+
process.exit(1);
|
|
25828
|
+
}
|
|
25829
|
+
throw err;
|
|
25830
|
+
}
|
|
25831
|
+
const relDir = path16.relative(process.cwd(), result.distDir);
|
|
25832
|
+
console.log(`
|
|
25833
|
+
\u2713 Preview built \u2192 ${relDir}/`);
|
|
25834
|
+
if (!opts.serve) {
|
|
25835
|
+
console.log(`
|
|
25836
|
+
npx serve ${relDir}`);
|
|
25837
|
+
return;
|
|
25838
|
+
}
|
|
25839
|
+
const server = startPreviewServer(result.distDir, opts.port);
|
|
25840
|
+
console.log(`
|
|
25841
|
+
Serving ${server.url}`);
|
|
25842
|
+
if (!opts.watch) {
|
|
25843
|
+
console.log(" Press Ctrl+C to stop.");
|
|
25844
|
+
await new Promise((resolve11) => process.on("SIGINT", resolve11));
|
|
25845
|
+
server.close();
|
|
25846
|
+
return;
|
|
25847
|
+
}
|
|
25848
|
+
console.log(" Watching for changes \u2014 edit a component and save. Press Ctrl+C to stop.");
|
|
25849
|
+
let rebuilding = false;
|
|
25850
|
+
let pending = false;
|
|
25851
|
+
let timer;
|
|
25852
|
+
const rebuild = async () => {
|
|
25853
|
+
if (rebuilding) {
|
|
25854
|
+
pending = true;
|
|
25855
|
+
return;
|
|
25856
|
+
}
|
|
25857
|
+
rebuilding = true;
|
|
25858
|
+
console.log("\nChange detected \u2014 rebuilding...");
|
|
25859
|
+
try {
|
|
25860
|
+
await runPreview(component, ctx2, runOpts);
|
|
25861
|
+
server.bumpReload();
|
|
25862
|
+
console.log("\u2713 Rebuilt");
|
|
25863
|
+
} catch (err) {
|
|
25864
|
+
const msg = err instanceof PreviewError ? err.message : err.message;
|
|
25865
|
+
console.error(`\u2717 Rebuild failed: ${msg}`);
|
|
25866
|
+
} finally {
|
|
25867
|
+
rebuilding = false;
|
|
25868
|
+
if (pending) {
|
|
25869
|
+
pending = false;
|
|
25870
|
+
void rebuild();
|
|
25871
|
+
}
|
|
25872
|
+
}
|
|
25873
|
+
};
|
|
25874
|
+
const schedule = () => {
|
|
25875
|
+
clearTimeout(timer);
|
|
25876
|
+
timer = setTimeout(() => void rebuild(), 150);
|
|
25877
|
+
};
|
|
25878
|
+
const { writeRoot, componentsBasePath } = resolveScaffoldLayout(ctx2);
|
|
25879
|
+
const watchTargets = [
|
|
25880
|
+
path16.join(writeRoot, componentsBasePath),
|
|
25881
|
+
// Monorepo token/CSS sources
|
|
25882
|
+
path16.join(ctx2.root, "site/ui/styles"),
|
|
25883
|
+
path16.join(ctx2.root, "site/ui/tokens.json"),
|
|
25884
|
+
path16.join(ctx2.root, "site/shared/tokens"),
|
|
25885
|
+
// Project token/CSS sources
|
|
25886
|
+
ctx2.projectDir && path16.join(ctx2.projectDir, "styles"),
|
|
25887
|
+
ctx2.projectDir && path16.join(ctx2.projectDir, "globals.css"),
|
|
25888
|
+
ctx2.projectDir && path16.join(ctx2.projectDir, "uno.config.ts"),
|
|
25889
|
+
ctx2.projectDir && ctx2.config?.paths.tokens && path16.join(ctx2.projectDir, ctx2.config.paths.tokens)
|
|
25890
|
+
].filter((t) => !!t && existsSync17(t));
|
|
25891
|
+
const watchers = watchTargets.map(
|
|
25892
|
+
(target) => fsWatch(target, { recursive: true }, schedule)
|
|
25893
|
+
);
|
|
25894
|
+
await new Promise((resolve11) => process.on("SIGINT", resolve11));
|
|
25895
|
+
for (const w of watchers) w.close();
|
|
25896
|
+
server.close();
|
|
25897
|
+
}
|
|
25898
|
+
var DEFAULT_PORT, HELP;
|
|
25899
|
+
var init_preview = __esm({
|
|
25900
|
+
"src/commands/preview.ts"() {
|
|
25901
|
+
"use strict";
|
|
25902
|
+
init_scaffold_layout();
|
|
25903
|
+
init_run();
|
|
25904
|
+
init_serve();
|
|
25905
|
+
DEFAULT_PORT = 4321;
|
|
25906
|
+
HELP = `Usage: bf preview [component] [options]
|
|
25907
|
+
|
|
25908
|
+
bf preview List previewable components
|
|
25909
|
+
bf preview <component> Build a static preview into .preview-dist/
|
|
25910
|
+
|
|
25911
|
+
Options:
|
|
25912
|
+
--serve Serve the build on a local server and print its URL
|
|
25913
|
+
--watch Rebuild on source changes and live-reload (implies --serve)
|
|
25914
|
+
--port <number> Server port for --serve/--watch (default ${DEFAULT_PORT})
|
|
25915
|
+
-h, --help Show this help`;
|
|
25916
|
+
}
|
|
25917
|
+
});
|
|
25918
|
+
|
|
25919
|
+
// src/commands/tokens-apply.ts
|
|
25920
|
+
var tokens_apply_exports = {};
|
|
25921
|
+
__export(tokens_apply_exports, {
|
|
25922
|
+
applyCssOverrides: () => applyCssOverrides,
|
|
25923
|
+
applyTokenOverrides: () => applyTokenOverrides,
|
|
25924
|
+
parseStudioUrl: () => parseStudioUrl,
|
|
25925
|
+
resolveTokensCss: () => resolveTokensCss,
|
|
25926
|
+
run: () => run9
|
|
25927
|
+
});
|
|
25928
|
+
import { existsSync as existsSync18, readFileSync as readFileSync7, writeFileSync as writeFileSync5 } from "fs";
|
|
25929
|
+
import path17 from "path";
|
|
25930
|
+
async function run9(args2, ctx2) {
|
|
25931
|
+
const url = args2[0];
|
|
25932
|
+
if (!url) {
|
|
25933
|
+
console.error("Error: tokens apply requires a Studio URL.");
|
|
25934
|
+
console.error("Usage: bf tokens apply <url>");
|
|
25935
|
+
process.exit(1);
|
|
25936
|
+
}
|
|
25937
|
+
const projectDir = ctx2.projectDir ?? process.cwd();
|
|
25938
|
+
const tokensRelDir = ctx2.config?.paths.tokens ?? "tokens";
|
|
25939
|
+
const studioConfig = parseStudioUrl(url);
|
|
25940
|
+
if (!studioConfig) {
|
|
25941
|
+
console.error("Error: could not decode Studio config from the URL (no `?c=` param or malformed payload).");
|
|
25942
|
+
process.exit(1);
|
|
25943
|
+
}
|
|
25944
|
+
const cssPath = resolveTokensCss(projectDir, tokensRelDir);
|
|
25945
|
+
if (!cssPath) {
|
|
25946
|
+
console.error("Error: tokens.css not found. Checked:");
|
|
25947
|
+
for (const p of candidateCssPaths(projectDir, tokensRelDir)) {
|
|
25948
|
+
console.error(` - ${path17.relative(projectDir, p)}`);
|
|
25949
|
+
}
|
|
25950
|
+
console.error(" Run `npm create barefootjs@latest` to scaffold a project first.");
|
|
25951
|
+
process.exit(1);
|
|
25952
|
+
}
|
|
25953
|
+
applyCssOverrides(cssPath, studioConfig);
|
|
25954
|
+
console.log(` Patched ${path17.relative(projectDir, cssPath)}`);
|
|
25955
|
+
const tokensJsonPath = path17.join(projectDir, tokensRelDir, "tokens.json");
|
|
25956
|
+
if (existsSync18(tokensJsonPath)) {
|
|
25957
|
+
applyTokenOverrides(tokensJsonPath, studioConfig);
|
|
25958
|
+
console.log(` Patched ${path17.relative(projectDir, tokensJsonPath)}`);
|
|
25959
|
+
}
|
|
25960
|
+
}
|
|
25961
|
+
function parseStudioUrl(url) {
|
|
25962
|
+
try {
|
|
25963
|
+
const parsed = new URL(url);
|
|
25964
|
+
const encoded = parsed.searchParams.get("c");
|
|
25965
|
+
if (!encoded) return void 0;
|
|
25966
|
+
const json = atob(decodeURIComponent(encoded));
|
|
25967
|
+
return JSON.parse(json);
|
|
25968
|
+
} catch {
|
|
25969
|
+
return void 0;
|
|
25970
|
+
}
|
|
25971
|
+
}
|
|
25972
|
+
function candidateCssPaths(projectDir, tokensRelDir) {
|
|
25973
|
+
return [
|
|
25974
|
+
path17.join(projectDir, tokensRelDir, "tokens.css"),
|
|
25975
|
+
path17.join(projectDir, "public", "tokens.css"),
|
|
25976
|
+
path17.join(projectDir, "static", "tokens.css")
|
|
25977
|
+
];
|
|
25978
|
+
}
|
|
25979
|
+
function resolveTokensCss(projectDir, tokensRelDir) {
|
|
25980
|
+
for (const p of candidateCssPaths(projectDir, tokensRelDir)) {
|
|
25981
|
+
if (existsSync18(p)) return p;
|
|
25982
|
+
}
|
|
25983
|
+
return void 0;
|
|
25984
|
+
}
|
|
25985
|
+
function buildBlockOverrides(config) {
|
|
25986
|
+
const root = {};
|
|
25987
|
+
const dark = {};
|
|
25988
|
+
if (config.spacing) root["--spacing"] = config.spacing;
|
|
25989
|
+
if (config.radius) root["--radius"] = config.radius;
|
|
25990
|
+
if (config.font) {
|
|
25991
|
+
root["--font-sans"] = FONT_MAP[config.font] ?? config.font;
|
|
25992
|
+
}
|
|
25993
|
+
if (config.style) {
|
|
25994
|
+
const preset = SHADOW_PRESETS[config.style];
|
|
25995
|
+
if (preset) {
|
|
25996
|
+
for (const [name, value] of Object.entries(preset)) {
|
|
25997
|
+
root[`--${name}`] = value;
|
|
25998
|
+
}
|
|
25999
|
+
}
|
|
26000
|
+
}
|
|
26001
|
+
if (config.tokens) {
|
|
26002
|
+
for (const [name, values] of Object.entries(config.tokens)) {
|
|
26003
|
+
if (values.light !== void 0) root[`--${name}`] = values.light;
|
|
26004
|
+
if (values.dark !== void 0) dark[`--${name}`] = values.dark;
|
|
26005
|
+
}
|
|
26006
|
+
}
|
|
26007
|
+
return { root, dark };
|
|
26008
|
+
}
|
|
26009
|
+
function applyCssOverrides(cssPath, config) {
|
|
26010
|
+
const overrides = buildBlockOverrides(config);
|
|
26011
|
+
let css = readFileSync7(cssPath, "utf-8");
|
|
26012
|
+
css = patchBlock(css, /:root\s*\{/, overrides.root);
|
|
26013
|
+
css = patchBlock(css, /\.dark\s*\{/, overrides.dark);
|
|
26014
|
+
writeFileSync5(cssPath, css);
|
|
26015
|
+
}
|
|
26016
|
+
function patchBlock(css, openRe, overrides) {
|
|
26017
|
+
if (Object.keys(overrides).length === 0) return css;
|
|
26018
|
+
const openMatch = openRe.exec(css);
|
|
26019
|
+
if (!openMatch) return css;
|
|
26020
|
+
const blockStart = openMatch.index + openMatch[0].length;
|
|
26021
|
+
let depth = 1;
|
|
26022
|
+
let blockEnd = -1;
|
|
26023
|
+
for (let i = blockStart; i < css.length; i++) {
|
|
26024
|
+
const ch = css[i];
|
|
26025
|
+
if (ch === "{") depth++;
|
|
26026
|
+
else if (ch === "}") {
|
|
26027
|
+
depth--;
|
|
26028
|
+
if (depth === 0) {
|
|
26029
|
+
blockEnd = i;
|
|
26030
|
+
break;
|
|
26031
|
+
}
|
|
26032
|
+
}
|
|
26033
|
+
}
|
|
26034
|
+
if (blockEnd === -1) return css;
|
|
26035
|
+
let block = css.slice(blockStart, blockEnd);
|
|
26036
|
+
const toAppend = [];
|
|
26037
|
+
for (const [name, value] of Object.entries(overrides)) {
|
|
26038
|
+
const re = new RegExp(`(${escapeRegex2(name)}\\s*:\\s*)[^;]+(;)`);
|
|
26039
|
+
if (re.test(block)) {
|
|
26040
|
+
block = block.replace(re, `$1${value}$2`);
|
|
26041
|
+
} else {
|
|
26042
|
+
toAppend.push([name, value]);
|
|
26043
|
+
}
|
|
26044
|
+
}
|
|
26045
|
+
if (toAppend.length > 0) {
|
|
26046
|
+
const lines = toAppend.map(([n, v]) => ` ${n}: ${v};`).join("\n");
|
|
26047
|
+
const trailing = block.match(/\s*$/)?.[0] ?? "";
|
|
26048
|
+
block = block.slice(0, block.length - trailing.length) + `
|
|
26049
|
+
|
|
26050
|
+
/* \u2500\u2500 Studio overrides \u2500\u2500 */
|
|
26051
|
+
${lines}
|
|
26052
|
+
`;
|
|
26053
|
+
}
|
|
26054
|
+
return css.slice(0, blockStart) + block + css.slice(blockEnd);
|
|
26055
|
+
}
|
|
26056
|
+
function escapeRegex2(s) {
|
|
26057
|
+
return s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
26058
|
+
}
|
|
26059
|
+
function applyTokenOverrides(tokensJsonPath, config) {
|
|
26060
|
+
const raw = readFileSync7(tokensJsonPath, "utf-8");
|
|
26061
|
+
const tokensData = JSON.parse(raw);
|
|
26062
|
+
if (config.tokens) {
|
|
26063
|
+
for (const [name, values] of Object.entries(config.tokens)) {
|
|
26064
|
+
applyColorOverride(tokensData, name, values);
|
|
26065
|
+
}
|
|
26066
|
+
}
|
|
26067
|
+
if (config.spacing) {
|
|
26068
|
+
applySimpleOverride(tokensData, "--spacing", config.spacing);
|
|
26069
|
+
}
|
|
26070
|
+
if (config.radius) {
|
|
26071
|
+
applySimpleOverride(tokensData, "--radius", config.radius);
|
|
26072
|
+
}
|
|
26073
|
+
if (config.font) {
|
|
26074
|
+
const fontValue = FONT_MAP[config.font] || config.font;
|
|
26075
|
+
applySimpleOverride(tokensData, "--font-sans", fontValue);
|
|
26076
|
+
}
|
|
26077
|
+
if (config.style) {
|
|
26078
|
+
applyShadowPreset(tokensData, config.style);
|
|
26079
|
+
}
|
|
26080
|
+
writeFileSync5(tokensJsonPath, JSON.stringify(tokensData, null, 2) + "\n");
|
|
26081
|
+
}
|
|
26082
|
+
function applyColorOverride(tokensData, name, values) {
|
|
26083
|
+
const varName = `--${name}`;
|
|
26084
|
+
if (Array.isArray(tokensData.colors)) {
|
|
26085
|
+
for (const token of tokensData.colors) {
|
|
26086
|
+
if (token.name === varName || token.name === name) {
|
|
26087
|
+
if (values.light) token.value = values.light;
|
|
26088
|
+
if (values.dark) token.dark = values.dark;
|
|
26089
|
+
return;
|
|
26090
|
+
}
|
|
26091
|
+
}
|
|
26092
|
+
}
|
|
26093
|
+
if (Array.isArray(tokensData.tokens)) {
|
|
26094
|
+
for (const token of tokensData.tokens) {
|
|
26095
|
+
if (token.name === varName || token.name === name) {
|
|
26096
|
+
if (values.light) token.value = values.light;
|
|
26097
|
+
if (values.dark) token.dark = values.dark;
|
|
26098
|
+
return;
|
|
26099
|
+
}
|
|
26100
|
+
}
|
|
26101
|
+
}
|
|
26102
|
+
}
|
|
26103
|
+
function applySimpleOverride(tokensData, name, value) {
|
|
26104
|
+
const bareName = name.startsWith("--") ? name.slice(2) : name;
|
|
26105
|
+
const sections = [
|
|
26106
|
+
tokensData.colors,
|
|
26107
|
+
tokensData.spacing,
|
|
26108
|
+
tokensData.borderRadius,
|
|
26109
|
+
tokensData.shadows,
|
|
26110
|
+
tokensData.layout
|
|
26111
|
+
];
|
|
26112
|
+
if (tokensData.typography) {
|
|
26113
|
+
for (const arr of Object.values(tokensData.typography)) {
|
|
26114
|
+
if (Array.isArray(arr)) sections.push(arr);
|
|
26115
|
+
}
|
|
26116
|
+
}
|
|
26117
|
+
for (const arr of sections) {
|
|
26118
|
+
if (!Array.isArray(arr)) continue;
|
|
26119
|
+
for (const token of arr) {
|
|
26120
|
+
if (token.name === bareName || token.name === name) {
|
|
26121
|
+
token.value = value;
|
|
26122
|
+
return;
|
|
26123
|
+
}
|
|
26124
|
+
}
|
|
26125
|
+
}
|
|
26126
|
+
}
|
|
26127
|
+
function applyShadowPreset(tokensData, styleName) {
|
|
26128
|
+
const shadows = SHADOW_PRESETS[styleName];
|
|
26129
|
+
if (!shadows) return;
|
|
26130
|
+
for (const [name, value] of Object.entries(shadows)) {
|
|
26131
|
+
applySimpleOverride(tokensData, name, value);
|
|
26132
|
+
}
|
|
26133
|
+
}
|
|
26134
|
+
var FONT_MAP, SHADOW_PRESETS;
|
|
26135
|
+
var init_tokens_apply = __esm({
|
|
26136
|
+
"src/commands/tokens-apply.ts"() {
|
|
26137
|
+
"use strict";
|
|
26138
|
+
FONT_MAP = {
|
|
26139
|
+
system: '-apple-system, BlinkMacSystemFont, "Segoe UI", "Noto Sans", Helvetica, Arial, sans-serif',
|
|
26140
|
+
inter: '"Inter", sans-serif',
|
|
26141
|
+
"noto-sans": '"Noto Sans", sans-serif',
|
|
26142
|
+
"nunito-sans": '"Nunito Sans", sans-serif',
|
|
26143
|
+
figtree: '"Figtree", sans-serif'
|
|
26144
|
+
};
|
|
26145
|
+
SHADOW_PRESETS = {
|
|
26146
|
+
Sharp: {
|
|
26147
|
+
"shadow-sm": "0 1px 2px 0 rgb(0 0 0 / 0.04)",
|
|
26148
|
+
"shadow": "0 1px 2px 0 rgb(0 0 0 / 0.06)",
|
|
26149
|
+
"shadow-md": "0 2px 4px -1px rgb(0 0 0 / 0.08)",
|
|
26150
|
+
"shadow-lg": "0 4px 8px -2px rgb(0 0 0 / 0.1)"
|
|
26151
|
+
},
|
|
26152
|
+
Soft: {
|
|
26153
|
+
"shadow-sm": "0 1px 3px 0 rgb(0 0 0 / 0.06)",
|
|
26154
|
+
"shadow": "0 2px 6px 0 rgb(0 0 0 / 0.08), 0 1px 3px -1px rgb(0 0 0 / 0.06)",
|
|
26155
|
+
"shadow-md": "0 6px 12px -2px rgb(0 0 0 / 0.08), 0 3px 6px -3px rgb(0 0 0 / 0.06)",
|
|
26156
|
+
"shadow-lg": "0 12px 24px -4px rgb(0 0 0 / 0.08), 0 6px 10px -5px rgb(0 0 0 / 0.06)"
|
|
26157
|
+
},
|
|
26158
|
+
Compact: {
|
|
26159
|
+
"shadow-sm": "none",
|
|
26160
|
+
"shadow": "none",
|
|
26161
|
+
"shadow-md": "none",
|
|
26162
|
+
"shadow-lg": "0 1px 2px 0 rgb(0 0 0 / 0.05)"
|
|
26163
|
+
}
|
|
26164
|
+
};
|
|
26165
|
+
}
|
|
26166
|
+
});
|
|
26167
|
+
|
|
26168
|
+
// src/commands/tokens.ts
|
|
26169
|
+
var tokens_exports = {};
|
|
26170
|
+
__export(tokens_exports, {
|
|
26171
|
+
run: () => run10
|
|
26172
|
+
});
|
|
26173
|
+
function flattenTokens(tokenSet, category) {
|
|
26174
|
+
const result = [];
|
|
26175
|
+
function add(cat, tokens) {
|
|
26176
|
+
if (category && category !== cat) return;
|
|
26177
|
+
result.push(...tokens);
|
|
26178
|
+
}
|
|
26179
|
+
add("typography", [...tokenSet.typography.fontFamily, ...tokenSet.typography.letterSpacing]);
|
|
26180
|
+
add("spacing", tokenSet.spacing);
|
|
26181
|
+
add("borderRadius", tokenSet.borderRadius);
|
|
26182
|
+
add("transitions", [...tokenSet.transitions.duration, ...tokenSet.transitions.easing]);
|
|
26183
|
+
add("layout", tokenSet.layout);
|
|
26184
|
+
add("colors", tokenSet.colors);
|
|
26185
|
+
add("shadows", tokenSet.shadows);
|
|
26186
|
+
return result;
|
|
25288
26187
|
}
|
|
25289
|
-
function
|
|
25290
|
-
|
|
25291
|
-
|
|
25292
|
-
|
|
25293
|
-
const interfaceRegex = /interface\s+(\w+Props)\s+(?:extends\s+[\w<>,\s]+\s+)?\{/g;
|
|
25294
|
-
const interfaces = [];
|
|
25295
|
-
let im;
|
|
25296
|
-
while ((im = interfaceRegex.exec(source)) !== null) {
|
|
25297
|
-
interfaces.push({ name: im[1], index: im.index });
|
|
26188
|
+
function printTokens(tokens, jsonFlag2) {
|
|
26189
|
+
if (jsonFlag2) {
|
|
26190
|
+
console.log(JSON.stringify(tokens, null, 2));
|
|
26191
|
+
return;
|
|
25298
26192
|
}
|
|
25299
|
-
|
|
25300
|
-
|
|
25301
|
-
|
|
25302
|
-
if (!exportedNames.includes(componentName)) continue;
|
|
25303
|
-
const beforeInterface = source.slice(Math.max(0, iface.index - 300), iface.index);
|
|
25304
|
-
const jsdocMatch = beforeInterface.match(/\/\*\*\s*([\s\S]*?)\s*\*\/\s*$/);
|
|
25305
|
-
const description = jsdocMatch ? extractPropDescription(jsdocMatch[1]) : "";
|
|
25306
|
-
const props = extractPropsFromInterface(source, iface.index);
|
|
25307
|
-
subs.push({ name: componentName, description, props });
|
|
26193
|
+
if (tokens.length === 0) {
|
|
26194
|
+
console.log("No tokens found.");
|
|
26195
|
+
return;
|
|
25308
26196
|
}
|
|
25309
|
-
|
|
26197
|
+
const nameWidth = Math.max(25, ...tokens.map((t) => t.name.length + 4));
|
|
26198
|
+
const header = `${"NAME".padEnd(nameWidth)}VALUE`;
|
|
26199
|
+
console.log(header);
|
|
26200
|
+
console.log("-".repeat(header.length + 20));
|
|
26201
|
+
for (const t of tokens) {
|
|
26202
|
+
const name = `--${t.name}`;
|
|
26203
|
+
const dark = t.dark;
|
|
26204
|
+
const darkSuffix = dark ? ` (dark: ${dark})` : "";
|
|
26205
|
+
console.log(`${name.padEnd(nameWidth)}${t.value}${darkSuffix}`);
|
|
26206
|
+
}
|
|
26207
|
+
console.log(`
|
|
26208
|
+
${tokens.length} token(s)`);
|
|
25310
26209
|
}
|
|
25311
|
-
function
|
|
25312
|
-
|
|
25313
|
-
const
|
|
25314
|
-
|
|
25315
|
-
|
|
25316
|
-
|
|
25317
|
-
|
|
25318
|
-
|
|
25319
|
-
|
|
26210
|
+
async function run10(args2, ctx2) {
|
|
26211
|
+
let category;
|
|
26212
|
+
const catIdx = args2.indexOf("--category");
|
|
26213
|
+
if (catIdx >= 0 && args2[catIdx + 1]) {
|
|
26214
|
+
const val = args2[catIdx + 1];
|
|
26215
|
+
if (!CATEGORY_NAMES.includes(val)) {
|
|
26216
|
+
console.error(`Unknown category: ${val}`);
|
|
26217
|
+
console.error(`Available: ${CATEGORY_NAMES.join(", ")}`);
|
|
26218
|
+
process.exit(1);
|
|
25320
26219
|
}
|
|
26220
|
+
category = val;
|
|
25321
26221
|
}
|
|
25322
|
-
|
|
26222
|
+
const tokenSet = await loadTokenSet(ctx2);
|
|
26223
|
+
const tokens = flattenTokens(tokenSet, category);
|
|
26224
|
+
printTokens(tokens, ctx2.jsonFlag);
|
|
25323
26225
|
}
|
|
25324
|
-
|
|
25325
|
-
|
|
25326
|
-
|
|
25327
|
-
|
|
25328
|
-
|
|
25329
|
-
|
|
25330
|
-
|
|
25331
|
-
|
|
26226
|
+
var CATEGORY_NAMES;
|
|
26227
|
+
var init_tokens2 = __esm({
|
|
26228
|
+
"src/commands/tokens.ts"() {
|
|
26229
|
+
"use strict";
|
|
26230
|
+
init_tokens();
|
|
26231
|
+
CATEGORY_NAMES = [
|
|
26232
|
+
"typography",
|
|
26233
|
+
"spacing",
|
|
26234
|
+
"borderRadius",
|
|
26235
|
+
"transitions",
|
|
26236
|
+
"layout",
|
|
26237
|
+
"colors",
|
|
26238
|
+
"shadows"
|
|
26239
|
+
];
|
|
25332
26240
|
}
|
|
25333
|
-
|
|
25334
|
-
|
|
25335
|
-
|
|
25336
|
-
|
|
26241
|
+
});
|
|
26242
|
+
|
|
26243
|
+
// src/lib/scaffold.ts
|
|
26244
|
+
import { readFileSync as readFileSync8, existsSync as existsSync19 } from "fs";
|
|
26245
|
+
import path18 from "path";
|
|
26246
|
+
function loadMeta(metaDir, name) {
|
|
26247
|
+
const filePath = path18.join(metaDir, `${name}.json`);
|
|
26248
|
+
if (!existsSync19(filePath)) return null;
|
|
26249
|
+
return JSON.parse(readFileSync8(filePath, "utf-8"));
|
|
26250
|
+
}
|
|
26251
|
+
function toPascalCase2(kebab) {
|
|
26252
|
+
return kebab.split("-").map((w) => w[0].toUpperCase() + w.slice(1)).join("");
|
|
26253
|
+
}
|
|
26254
|
+
function toCamelCase(kebab) {
|
|
26255
|
+
const pascal = toPascalCase2(kebab);
|
|
26256
|
+
return pascal[0].toLowerCase() + pascal.slice(1);
|
|
26257
|
+
}
|
|
26258
|
+
function scaffold(componentName, useComponents, metaDir, componentsBasePath = "ui/components/ui", options = {}) {
|
|
26259
|
+
const metas = useComponents.map((name) => ({ name, meta: loadMeta(metaDir, name) }));
|
|
26260
|
+
const found = metas.filter((m) => m.meta !== null);
|
|
26261
|
+
const notFound = metas.filter((m) => m.meta === null).map((m) => m.name);
|
|
26262
|
+
const needsClient = found.some((m) => m.meta.stateful);
|
|
26263
|
+
const imports = buildImports(found);
|
|
26264
|
+
const componentCode = generateComponentCode(componentName, imports, needsClient, notFound);
|
|
26265
|
+
const testCode = generateTestCode(componentName, needsClient, options.testImportSource ?? "bun:test");
|
|
26266
|
+
const basePath = `${componentsBasePath}/${componentName}`;
|
|
25337
26267
|
return {
|
|
25338
|
-
|
|
25339
|
-
|
|
25340
|
-
|
|
26268
|
+
componentCode,
|
|
26269
|
+
testCode,
|
|
26270
|
+
componentPath: `${basePath}/index.tsx`,
|
|
26271
|
+
testPath: `${basePath}/index.test.tsx`
|
|
25341
26272
|
};
|
|
25342
26273
|
}
|
|
25343
|
-
function
|
|
25344
|
-
const
|
|
25345
|
-
const
|
|
25346
|
-
|
|
25347
|
-
|
|
25348
|
-
|
|
25349
|
-
|
|
25350
|
-
if (m[0].includes("import type")) continue;
|
|
25351
|
-
if (specifier.startsWith("./") || specifier.startsWith("../")) {
|
|
25352
|
-
const parts = specifier.split("/");
|
|
25353
|
-
const name = parts[parts.length - 1].replace(/\.tsx?$/, "");
|
|
25354
|
-
if (name !== "types" && name !== "index") {
|
|
25355
|
-
internal.push(name);
|
|
26274
|
+
function buildImports(components) {
|
|
26275
|
+
const imports = [];
|
|
26276
|
+
for (const { name, meta } of components) {
|
|
26277
|
+
const names = [toPascalCase2(name)];
|
|
26278
|
+
if (meta.subComponents) {
|
|
26279
|
+
for (const sub2 of meta.subComponents) {
|
|
26280
|
+
names.push(sub2.name);
|
|
25356
26281
|
}
|
|
25357
|
-
} else {
|
|
25358
|
-
external.push(specifier);
|
|
25359
26282
|
}
|
|
26283
|
+
imports.push({ from: `../${name}`, names });
|
|
25360
26284
|
}
|
|
25361
|
-
return
|
|
25362
|
-
internal: [...new Set(internal)],
|
|
25363
|
-
external: [...new Set(external)]
|
|
25364
|
-
};
|
|
26285
|
+
return imports;
|
|
25365
26286
|
}
|
|
25366
|
-
function
|
|
25367
|
-
const
|
|
25368
|
-
const
|
|
25369
|
-
|
|
25370
|
-
|
|
25371
|
-
|
|
25372
|
-
|
|
25373
|
-
if (trimmed.startsWith("type ")) return;
|
|
25374
|
-
seen.add(trimmed);
|
|
25375
|
-
names.push(trimmed);
|
|
25376
|
-
};
|
|
25377
|
-
const braceRegex = /export\s+\{([^}]+)\}/g;
|
|
25378
|
-
let bm;
|
|
25379
|
-
while ((bm = braceRegex.exec(source)) !== null) {
|
|
25380
|
-
for (const part of bm[1].split(",")) {
|
|
25381
|
-
const trimmed = part.trim();
|
|
25382
|
-
if (!trimmed || trimmed.startsWith("type ")) continue;
|
|
25383
|
-
const asMatch = trimmed.match(/\s+as\s+(\w+)$/);
|
|
25384
|
-
add(asMatch ? asMatch[1] : trimmed);
|
|
25385
|
-
}
|
|
26287
|
+
function generateComponentCode(componentName, imports, needsClient, notFound) {
|
|
26288
|
+
const pascalName = toPascalCase2(componentName);
|
|
26289
|
+
const lines = [];
|
|
26290
|
+
if (needsClient) {
|
|
26291
|
+
lines.push(`"use client"`);
|
|
26292
|
+
lines.push(``);
|
|
26293
|
+
lines.push(`import { createSignal } from '@barefootjs/client'`);
|
|
25386
26294
|
}
|
|
25387
|
-
|
|
25388
|
-
|
|
25389
|
-
|
|
25390
|
-
|
|
25391
|
-
|
|
25392
|
-
|
|
25393
|
-
|
|
25394
|
-
|
|
25395
|
-
|
|
25396
|
-
|
|
25397
|
-
}
|
|
25398
|
-
var init_parse_component = __esm({
|
|
25399
|
-
"src/lib/parse-component.ts"() {
|
|
25400
|
-
"use strict";
|
|
26295
|
+
lines.push(`import type { Child } from '../../../types'`);
|
|
26296
|
+
for (const imp of imports) {
|
|
26297
|
+
lines.push(`import { ${imp.names.join(", ")} } from '${imp.from}'`);
|
|
26298
|
+
}
|
|
26299
|
+
if (notFound.length > 0) {
|
|
26300
|
+
lines.push(``);
|
|
26301
|
+
lines.push(`// WARNING: These components were not found in ui/meta/:`);
|
|
26302
|
+
for (const name of notFound) {
|
|
26303
|
+
lines.push(`// - ${name}`);
|
|
26304
|
+
}
|
|
25401
26305
|
}
|
|
25402
|
-
});
|
|
25403
|
-
|
|
25404
|
-
// src/lib/test-template.ts
|
|
25405
|
-
import { readFileSync as readFileSync8 } from "fs";
|
|
25406
|
-
import path20 from "path";
|
|
25407
|
-
function generateTestTemplate(componentPath, options = {}) {
|
|
25408
|
-
const importSource = options.importSource ?? "bun:test";
|
|
25409
|
-
const source = readFileSync8(componentPath, "utf-8");
|
|
25410
|
-
const parsed = parseComponent(source);
|
|
25411
|
-
const fileName = path20.basename(componentPath);
|
|
25412
|
-
const baseName = fileName.replace(/\.tsx$/, "");
|
|
25413
|
-
const relativePath = fileName;
|
|
25414
|
-
const varName = toCamelCase2(baseName) + "Source";
|
|
25415
|
-
const exportedNames = parsed.exportedNames;
|
|
25416
|
-
const mainComponent = exportedNames[0];
|
|
25417
|
-
const hasSubComponents = exportedNames.length > 1;
|
|
25418
|
-
const lines = [];
|
|
25419
|
-
lines.push(`import { describe, test, expect } from '${importSource}'`);
|
|
25420
|
-
lines.push(`import { readFileSync } from 'fs'`);
|
|
25421
|
-
lines.push(`import { resolve } from 'path'`);
|
|
25422
|
-
lines.push(`import { renderToTest } from '@barefootjs/test'`);
|
|
25423
26306
|
lines.push(``);
|
|
25424
|
-
lines.push(`
|
|
26307
|
+
lines.push(`interface ${pascalName}Props {`);
|
|
26308
|
+
lines.push(` /** Additional CSS classes. */`);
|
|
26309
|
+
lines.push(` className?: string`);
|
|
26310
|
+
lines.push(` /** Children to render. */`);
|
|
26311
|
+
lines.push(` children?: Child`);
|
|
26312
|
+
lines.push(`}`);
|
|
25425
26313
|
lines.push(``);
|
|
25426
|
-
if (
|
|
25427
|
-
lines.push(
|
|
25428
|
-
|
|
25429
|
-
|
|
25430
|
-
|
|
25431
|
-
|
|
25432
|
-
|
|
25433
|
-
}
|
|
26314
|
+
if (needsClient) {
|
|
26315
|
+
lines.push(`function ${pascalName}(props: ${pascalName}Props) {`);
|
|
26316
|
+
lines.push(` // TODO: Add signals`);
|
|
26317
|
+
lines.push(` // const [value, setValue] = createSignal(...)`);
|
|
26318
|
+
lines.push(``);
|
|
26319
|
+
lines.push(` return (`);
|
|
26320
|
+
lines.push(` <div data-slot="${componentName}">`);
|
|
26321
|
+
lines.push(` {/* TODO: Compose components */}`);
|
|
26322
|
+
lines.push(` </div>`);
|
|
26323
|
+
lines.push(` )`);
|
|
26324
|
+
lines.push(`}`);
|
|
26325
|
+
} else {
|
|
26326
|
+
lines.push(`function ${pascalName}({`);
|
|
26327
|
+
lines.push(` className = '',`);
|
|
26328
|
+
lines.push(` children,`);
|
|
26329
|
+
lines.push(` ...props`);
|
|
26330
|
+
lines.push(`}: ${pascalName}Props) {`);
|
|
26331
|
+
lines.push(` return (`);
|
|
26332
|
+
lines.push(` <div data-slot="${componentName}" className={className}>`);
|
|
26333
|
+
lines.push(` {/* TODO: Compose components */}`);
|
|
26334
|
+
lines.push(` {children}`);
|
|
26335
|
+
lines.push(` </div>`);
|
|
26336
|
+
lines.push(` )`);
|
|
26337
|
+
lines.push(`}`);
|
|
25434
26338
|
}
|
|
25435
|
-
|
|
26339
|
+
lines.push(``);
|
|
26340
|
+
lines.push(`export { ${pascalName} }`);
|
|
26341
|
+
lines.push(`export type { ${pascalName}Props }`);
|
|
26342
|
+
lines.push(``);
|
|
26343
|
+
return lines.join("\n");
|
|
25436
26344
|
}
|
|
25437
|
-
function
|
|
26345
|
+
function generateTestCode(componentName, needsClient, importSource) {
|
|
26346
|
+
const pascalName = toPascalCase2(componentName);
|
|
26347
|
+
const varName = toCamelCase(componentName) + "Source";
|
|
25438
26348
|
const lines = [];
|
|
25439
|
-
|
|
25440
|
-
|
|
25441
|
-
lines.push(`
|
|
25442
|
-
lines.push(`
|
|
26349
|
+
lines.push(`import { describe, test, expect } from '${importSource}'`);
|
|
26350
|
+
lines.push(`import { readFileSync } from 'fs'`);
|
|
26351
|
+
lines.push(`import { resolve } from 'path'`);
|
|
26352
|
+
lines.push(`import { renderToTest } from '@barefootjs/test'`);
|
|
26353
|
+
lines.push(``);
|
|
26354
|
+
lines.push(`const ${varName} = readFileSync(resolve(__dirname, 'index.tsx'), 'utf-8')`);
|
|
26355
|
+
lines.push(``);
|
|
26356
|
+
lines.push(`describe('${pascalName}', () => {`);
|
|
26357
|
+
lines.push(` const result = renderToTest(${varName}, '${componentName}.tsx')`);
|
|
25443
26358
|
lines.push(``);
|
|
25444
26359
|
lines.push(` test('has no compiler errors', () => {`);
|
|
25445
26360
|
lines.push(` expect(result.errors).toEqual([])`);
|
|
25446
26361
|
lines.push(` })`);
|
|
25447
26362
|
lines.push(``);
|
|
25448
|
-
lines.push(` test('componentName is ${
|
|
25449
|
-
lines.push(` expect(result.componentName).toBe('${
|
|
26363
|
+
lines.push(` test('componentName is ${pascalName}', () => {`);
|
|
26364
|
+
lines.push(` expect(result.componentName).toBe('${pascalName}')`);
|
|
25450
26365
|
lines.push(` })`);
|
|
25451
26366
|
lines.push(``);
|
|
25452
|
-
if (
|
|
25453
|
-
lines.push(` test('
|
|
25454
|
-
|
|
25455
|
-
lines.push(` expect(result.signals).toContain('${sig}')`);
|
|
25456
|
-
}
|
|
25457
|
-
lines.push(` })`);
|
|
25458
|
-
} else {
|
|
25459
|
-
lines.push(` test('no signals (stateless)', () => {`);
|
|
25460
|
-
lines.push(` expect(result.signals).toEqual([])`);
|
|
26367
|
+
if (needsClient) {
|
|
26368
|
+
lines.push(` test('isClient is true', () => {`);
|
|
26369
|
+
lines.push(` expect(result.isClient).toBe(true)`);
|
|
25461
26370
|
lines.push(` })`);
|
|
25462
26371
|
}
|
|
25463
26372
|
lines.push(``);
|
|
25464
|
-
|
|
25465
|
-
|
|
25466
|
-
|
|
25467
|
-
|
|
25468
|
-
|
|
25469
|
-
|
|
25470
|
-
|
|
25471
|
-
|
|
25472
|
-
lines.push(` expect(result.root.componentName).toBe('${funcInfo.rootTag}')`);
|
|
25473
|
-
} else {
|
|
25474
|
-
lines.push(` expect(result.root.tag).toBe('${funcInfo.rootTag}')`);
|
|
25475
|
-
}
|
|
25476
|
-
lines.push(` })`);
|
|
25477
|
-
lines.push(``);
|
|
25478
|
-
}
|
|
25479
|
-
if (funcInfo.dataSlot) {
|
|
25480
|
-
lines.push(` test('has data-slot=${funcInfo.dataSlot}', () => {`);
|
|
25481
|
-
if (funcInfo.hasConditionalReturn) {
|
|
25482
|
-
lines.push(` const el = result.find({ tag: '${funcInfo.rootTag}' })!`);
|
|
25483
|
-
lines.push(` expect(el.props['data-slot']).toBe('${funcInfo.dataSlot}')`);
|
|
25484
|
-
} else {
|
|
25485
|
-
lines.push(` expect(result.root.props['data-slot']).toBe('${funcInfo.dataSlot}')`);
|
|
25486
|
-
}
|
|
25487
|
-
lines.push(` })`);
|
|
25488
|
-
lines.push(``);
|
|
25489
|
-
}
|
|
25490
|
-
if (funcInfo.role) {
|
|
25491
|
-
lines.push(` test('has role=${funcInfo.role}', () => {`);
|
|
25492
|
-
lines.push(` const el = result.find({ role: '${funcInfo.role}' })`);
|
|
25493
|
-
lines.push(` expect(el).not.toBeNull()`);
|
|
25494
|
-
lines.push(` })`);
|
|
25495
|
-
lines.push(``);
|
|
25496
|
-
}
|
|
25497
|
-
const ariaAttrs = funcInfo.ariaAttributes;
|
|
25498
|
-
if (ariaAttrs.length > 0) {
|
|
25499
|
-
lines.push(` test('has ARIA attributes', () => {`);
|
|
25500
|
-
const findTarget = funcInfo.role ? `result.find({ role: '${funcInfo.role}' })!` : funcInfo.rootTag ? `result.find({ tag: '${funcInfo.rootTag}' })!` : "result.root";
|
|
25501
|
-
lines.push(` const el = ${findTarget}`);
|
|
25502
|
-
for (const attr of ariaAttrs) {
|
|
25503
|
-
const shortName = attr.replace("aria-", "");
|
|
25504
|
-
lines.push(` expect(el.aria).toHaveProperty('${shortName}')`);
|
|
25505
|
-
}
|
|
25506
|
-
lines.push(` })`);
|
|
25507
|
-
lines.push(``);
|
|
25508
|
-
}
|
|
25509
|
-
if (funcInfo.hasDataState) {
|
|
25510
|
-
lines.push(` test('has data-state attribute', () => {`);
|
|
25511
|
-
if (funcInfo.hasConditionalReturn) {
|
|
25512
|
-
lines.push(` const el = result.find({ tag: '${funcInfo.rootTag}' })!`);
|
|
25513
|
-
lines.push(` expect(el.dataState).not.toBeNull()`);
|
|
25514
|
-
} else {
|
|
25515
|
-
lines.push(` expect(result.root.dataState).not.toBeNull()`);
|
|
25516
|
-
}
|
|
25517
|
-
lines.push(` })`);
|
|
25518
|
-
lines.push(``);
|
|
25519
|
-
}
|
|
25520
|
-
if (funcInfo.events.length > 0) {
|
|
25521
|
-
lines.push(` test('has event handlers', () => {`);
|
|
25522
|
-
lines.push(` const all = result.findAll({})`);
|
|
25523
|
-
for (const event of funcInfo.events) {
|
|
25524
|
-
const onProp = ON_PROP_BY_EVENT[event];
|
|
25525
|
-
lines.push(` expect(`);
|
|
25526
|
-
lines.push(` all.some(n => n.events.includes('${event}') || n.props['${onProp}'] != null),`);
|
|
25527
|
-
lines.push(` ).toBe(true)`);
|
|
25528
|
-
}
|
|
25529
|
-
lines.push(` })`);
|
|
25530
|
-
lines.push(``);
|
|
25531
|
-
}
|
|
25532
|
-
if (funcInfo.childComponents.length > 0) {
|
|
25533
|
-
lines.push(` test('contains child components', () => {`);
|
|
25534
|
-
for (const child of funcInfo.childComponents) {
|
|
25535
|
-
lines.push(` expect(result.find({ componentName: '${child}' })).not.toBeNull()`);
|
|
25536
|
-
}
|
|
25537
|
-
lines.push(` })`);
|
|
25538
|
-
lines.push(``);
|
|
25539
|
-
}
|
|
26373
|
+
lines.push(` test('renders as <div>', () => {`);
|
|
26374
|
+
lines.push(` expect(result.root.tag).toBe('div')`);
|
|
26375
|
+
lines.push(` })`);
|
|
26376
|
+
lines.push(``);
|
|
26377
|
+
lines.push(` test('has data-slot=${componentName}', () => {`);
|
|
26378
|
+
lines.push(` expect(result.root.props['data-slot']).toBe('${componentName}')`);
|
|
26379
|
+
lines.push(` })`);
|
|
26380
|
+
lines.push(``);
|
|
25540
26381
|
lines.push(` test('toStructure() shows expected tree', () => {`);
|
|
25541
26382
|
lines.push(` const structure = result.toStructure()`);
|
|
25542
26383
|
lines.push(` expect(structure.length).toBeGreaterThan(0)`);
|
|
25543
|
-
if (funcInfo.rootTag) {
|
|
25544
|
-
lines.push(` expect(structure).toContain('${funcInfo.rootTag}')`);
|
|
25545
|
-
}
|
|
25546
|
-
if (funcInfo.role) {
|
|
25547
|
-
lines.push(` expect(structure).toContain('[role=${funcInfo.role}]')`);
|
|
25548
|
-
}
|
|
25549
26384
|
lines.push(` })`);
|
|
25550
26385
|
lines.push(`})`);
|
|
25551
|
-
|
|
25552
|
-
|
|
25553
|
-
function analyzeFunction(source, componentName) {
|
|
25554
|
-
const funcRegex = new RegExp(`function\\s+${componentName}\\s*\\(`);
|
|
25555
|
-
const funcMatch = funcRegex.exec(source);
|
|
25556
|
-
let funcBody = source;
|
|
25557
|
-
if (!funcMatch) {
|
|
25558
|
-
const aliasMatch = source.match(new RegExp(`const\\s+${componentName}\\s*=\\s*(\\w+)`));
|
|
25559
|
-
if (aliasMatch) {
|
|
25560
|
-
return analyzeFunction(source, aliasMatch[1]);
|
|
25561
|
-
}
|
|
25562
|
-
}
|
|
25563
|
-
if (funcMatch) {
|
|
25564
|
-
const parenStart = source.indexOf("(", funcMatch.index);
|
|
25565
|
-
let parenDepth = 0;
|
|
25566
|
-
let parenEnd = parenStart;
|
|
25567
|
-
for (let i = parenStart; i < source.length; i++) {
|
|
25568
|
-
if (source[i] === "(") parenDepth++;
|
|
25569
|
-
else if (source[i] === ")") {
|
|
25570
|
-
parenDepth--;
|
|
25571
|
-
if (parenDepth === 0) {
|
|
25572
|
-
parenEnd = i;
|
|
25573
|
-
break;
|
|
25574
|
-
}
|
|
25575
|
-
}
|
|
25576
|
-
}
|
|
25577
|
-
let depth = 0;
|
|
25578
|
-
let bodyStart = -1;
|
|
25579
|
-
for (let i = parenEnd + 1; i < source.length; i++) {
|
|
25580
|
-
if (source[i] === "{") {
|
|
25581
|
-
if (bodyStart === -1) bodyStart = i;
|
|
25582
|
-
depth++;
|
|
25583
|
-
} else if (source[i] === "}") {
|
|
25584
|
-
depth--;
|
|
25585
|
-
if (depth === 0) {
|
|
25586
|
-
funcBody = source.slice(bodyStart, i + 1);
|
|
25587
|
-
break;
|
|
25588
|
-
}
|
|
25589
|
-
}
|
|
25590
|
-
}
|
|
25591
|
-
}
|
|
25592
|
-
const signals = [];
|
|
25593
|
-
const signalRegex = /const\s+\[(\w+),\s*\w+\]\s*=\s*createSignal/g;
|
|
25594
|
-
let sm;
|
|
25595
|
-
while ((sm = signalRegex.exec(funcBody)) !== null) {
|
|
25596
|
-
signals.push(sm[1]);
|
|
25597
|
-
}
|
|
25598
|
-
const returnMatches = [...funcBody.matchAll(/return\s*\(?\s*<(\w+)/g)];
|
|
25599
|
-
const rootTag = returnMatches.length > 0 ? returnMatches[returnMatches.length - 1][1] : null;
|
|
25600
|
-
const allSlots = [...funcBody.matchAll(/data-slot="([^"]+)"/g)].map((m) => m[1]);
|
|
25601
|
-
const expectedSlot = componentName.replace(/([a-z])([A-Z])/g, "$1-$2").toLowerCase();
|
|
25602
|
-
const dataSlot = allSlots.find((s) => s === expectedSlot) || allSlots[0] || null;
|
|
25603
|
-
const roleMatch = funcBody.match(/role="([^"]+)"/);
|
|
25604
|
-
const role = roleMatch ? roleMatch[1] : null;
|
|
25605
|
-
const ariaMatches = funcBody.match(/aria-[\w]+(?==)/g);
|
|
25606
|
-
const ariaAttributes = [...new Set(ariaMatches || [])].filter((a) => a !== "aria-invalid");
|
|
25607
|
-
const hasDataState = /data-state=/.test(funcBody);
|
|
25608
|
-
const hasConditionalReturn = /if\s*\(.*\)\s*\{?\s*return/.test(funcBody);
|
|
25609
|
-
const events = [];
|
|
25610
|
-
for (const [event, onProp] of Object.entries(ON_PROP_BY_EVENT)) {
|
|
25611
|
-
if (new RegExp(`${onProp}=`).test(funcBody)) events.push(event);
|
|
25612
|
-
}
|
|
25613
|
-
const childCompRegex = /<([A-Z][A-Za-z]+)[\s/>]/g;
|
|
25614
|
-
const childComponents = [];
|
|
25615
|
-
let cm;
|
|
25616
|
-
while ((cm = childCompRegex.exec(funcBody)) !== null) {
|
|
25617
|
-
if (!childComponents.includes(cm[1])) {
|
|
25618
|
-
childComponents.push(cm[1]);
|
|
25619
|
-
}
|
|
25620
|
-
}
|
|
25621
|
-
return {
|
|
25622
|
-
signals,
|
|
25623
|
-
rootTag,
|
|
25624
|
-
dataSlot,
|
|
25625
|
-
role,
|
|
25626
|
-
ariaAttributes,
|
|
25627
|
-
hasDataState,
|
|
25628
|
-
hasConditionalReturn,
|
|
25629
|
-
events,
|
|
25630
|
-
childComponents
|
|
25631
|
-
};
|
|
25632
|
-
}
|
|
25633
|
-
function toCamelCase2(kebab) {
|
|
25634
|
-
return kebab.replace(/-([a-z])/g, (_, c) => c.toUpperCase());
|
|
26386
|
+
lines.push(``);
|
|
26387
|
+
return lines.join("\n");
|
|
25635
26388
|
}
|
|
25636
|
-
var
|
|
25637
|
-
|
|
25638
|
-
"src/lib/test-template.ts"() {
|
|
26389
|
+
var init_scaffold = __esm({
|
|
26390
|
+
"src/lib/scaffold.ts"() {
|
|
25639
26391
|
"use strict";
|
|
25640
|
-
init_parse_component();
|
|
25641
|
-
ON_PROP_BY_EVENT = {
|
|
25642
|
-
click: "onClick",
|
|
25643
|
-
input: "onInput",
|
|
25644
|
-
change: "onChange",
|
|
25645
|
-
keydown: "onKeyDown"
|
|
25646
|
-
};
|
|
25647
26392
|
}
|
|
25648
26393
|
});
|
|
25649
26394
|
|
|
25650
|
-
// src/commands/gen-
|
|
25651
|
-
var
|
|
25652
|
-
__export(
|
|
25653
|
-
run: () =>
|
|
26395
|
+
// src/commands/gen-component.ts
|
|
26396
|
+
var gen_component_exports = {};
|
|
26397
|
+
__export(gen_component_exports, {
|
|
26398
|
+
run: () => run11
|
|
25654
26399
|
});
|
|
25655
|
-
import {
|
|
25656
|
-
import
|
|
25657
|
-
function
|
|
25658
|
-
|
|
25659
|
-
|
|
25660
|
-
|
|
25661
|
-
const force = flagSet.has("--force") || flagSet.has("-f");
|
|
25662
|
-
const componentName = positional[0];
|
|
25663
|
-
if (!componentName) {
|
|
25664
|
-
console.error("Error: Component name required. Usage: bf gen test <component> [--stdout] [--force]");
|
|
26400
|
+
import { writeFileSync as writeFileSync6, mkdirSync as mkdirSync4, existsSync as existsSync20 } from "fs";
|
|
26401
|
+
import path19 from "path";
|
|
26402
|
+
function run11(args2, ctx2) {
|
|
26403
|
+
if (args2.length < 1) {
|
|
26404
|
+
console.error("Usage: bf gen component <component-name> [use-component1] [use-component2] ...");
|
|
26405
|
+
console.error("Example: bf gen component settings-form input switch button");
|
|
25665
26406
|
process.exit(1);
|
|
25666
26407
|
}
|
|
25667
|
-
const
|
|
25668
|
-
const
|
|
25669
|
-
|
|
25670
|
-
|
|
25671
|
-
|
|
25672
|
-
|
|
26408
|
+
const [componentName, ...useComponents] = args2;
|
|
26409
|
+
const { writeRoot, componentsBasePath } = resolveScaffoldLayout(ctx2);
|
|
26410
|
+
const pm = detectPackageManager(ctx2.projectDir ?? ctx2.root);
|
|
26411
|
+
const runner = testRunnerFor(pm);
|
|
26412
|
+
const result = scaffold(componentName, useComponents, ctx2.metaDir, componentsBasePath, {
|
|
26413
|
+
testImportSource: runner.importSource
|
|
26414
|
+
});
|
|
26415
|
+
const componentAbsPath = path19.join(writeRoot, result.componentPath);
|
|
26416
|
+
if (existsSync20(componentAbsPath)) {
|
|
26417
|
+
console.error(`Error: ${result.componentPath} already exists. Delete it first or choose a different name.`);
|
|
25673
26418
|
process.exit(1);
|
|
25674
26419
|
}
|
|
25675
|
-
const
|
|
25676
|
-
const
|
|
25677
|
-
|
|
25678
|
-
|
|
25679
|
-
console.log(content);
|
|
25680
|
-
return;
|
|
25681
|
-
}
|
|
25682
|
-
const dir = path21.dirname(resolved.filePath);
|
|
25683
|
-
const base = path21.basename(resolved.filePath, path21.extname(resolved.filePath));
|
|
25684
|
-
const testPath = path21.join(dir, `${base}.test.tsx`);
|
|
25685
|
-
if (existsSync17(testPath) && !force) {
|
|
25686
|
-
const rel2 = path21.relative(ctx2.projectDir ?? ctx2.root, testPath);
|
|
25687
|
-
console.error(`Error: ${rel2} already exists. Pass --force to overwrite, or --stdout to preview.`);
|
|
25688
|
-
process.exit(1);
|
|
26420
|
+
const testAbsPath = path19.join(writeRoot, result.testPath);
|
|
26421
|
+
const testDir = path19.dirname(testAbsPath);
|
|
26422
|
+
if (!existsSync20(testDir)) {
|
|
26423
|
+
mkdirSync4(testDir, { recursive: true });
|
|
25689
26424
|
}
|
|
25690
|
-
writeFileSync6(
|
|
25691
|
-
|
|
25692
|
-
|
|
26425
|
+
writeFileSync6(componentAbsPath, result.componentCode);
|
|
26426
|
+
writeFileSync6(testAbsPath, result.testCode);
|
|
26427
|
+
const testCmd = commandsFor(pm).test(result.testPath);
|
|
26428
|
+
console.log(`Created:`);
|
|
26429
|
+
console.log(` ${result.componentPath}`);
|
|
26430
|
+
console.log(` ${result.testPath}`);
|
|
25693
26431
|
console.log(``);
|
|
25694
|
-
console.log(`Next
|
|
26432
|
+
console.log(`Next steps:`);
|
|
26433
|
+
console.log(` 1. Implement the component in ${result.componentPath}`);
|
|
26434
|
+
console.log(` 2. ${testCmd}`);
|
|
26435
|
+
console.log(` 3. bf gen test ${componentName} (regenerate richer test)`);
|
|
25695
26436
|
}
|
|
25696
|
-
var
|
|
25697
|
-
"src/commands/gen-
|
|
26437
|
+
var init_gen_component = __esm({
|
|
26438
|
+
"src/commands/gen-component.ts"() {
|
|
25698
26439
|
"use strict";
|
|
25699
|
-
|
|
25700
|
-
|
|
26440
|
+
init_scaffold();
|
|
26441
|
+
init_scaffold_layout();
|
|
25701
26442
|
init_pm();
|
|
25702
26443
|
}
|
|
25703
26444
|
});
|
|
25704
26445
|
|
|
25705
|
-
// src/lib/
|
|
25706
|
-
function
|
|
25707
|
-
return
|
|
26446
|
+
// src/lib/parse-component.ts
|
|
26447
|
+
function parseComponent(source) {
|
|
26448
|
+
return {
|
|
26449
|
+
useClient: detectUseClient(source),
|
|
26450
|
+
description: extractTopLevelDescription(source),
|
|
26451
|
+
examples: extractExamples2(source),
|
|
26452
|
+
props: extractMainProps2(source),
|
|
26453
|
+
subComponents: extractSubComponents2(source),
|
|
26454
|
+
variants: extractVariants2(source),
|
|
26455
|
+
accessibility: extractAccessibility2(source),
|
|
26456
|
+
dependencies: extractDependencies3(source),
|
|
26457
|
+
exportedNames: extractExportedNames(source)
|
|
26458
|
+
};
|
|
25708
26459
|
}
|
|
25709
|
-
function
|
|
25710
|
-
|
|
26460
|
+
function detectUseClient(source) {
|
|
26461
|
+
const firstLine = source.split("\n")[0].trim();
|
|
26462
|
+
return firstLine === '"use client"' || firstLine === "'use client'";
|
|
25711
26463
|
}
|
|
25712
|
-
function
|
|
25713
|
-
|
|
26464
|
+
function extractTopLevelDescription(source) {
|
|
26465
|
+
const match = source.match(/^(?:"use client"\n+)?\/\*\*\n([\s\S]*?)\*\//m);
|
|
26466
|
+
if (!match) return "";
|
|
26467
|
+
const block = match[1];
|
|
26468
|
+
const lines = block.split("\n");
|
|
26469
|
+
const descLines = [];
|
|
26470
|
+
for (const line of lines) {
|
|
26471
|
+
const cleaned = line.replace(/^\s*\*\s?/, "").trim();
|
|
26472
|
+
if (cleaned.startsWith("@")) break;
|
|
26473
|
+
descLines.push(cleaned);
|
|
26474
|
+
}
|
|
26475
|
+
return descLines.join(" ").replace(/\s+/g, " ").trim();
|
|
25714
26476
|
}
|
|
25715
|
-
function
|
|
25716
|
-
const match =
|
|
25717
|
-
|
|
26477
|
+
function extractExamples2(source) {
|
|
26478
|
+
const match = source.match(/^(?:"use client"\n+)?\/\*\*\n([\s\S]*?)\*\//m);
|
|
26479
|
+
if (!match) return [];
|
|
26480
|
+
const block = match[1];
|
|
26481
|
+
const examples = [];
|
|
26482
|
+
const exampleRegex = /@example\s+(.*?)(?:\n\s*\*\s*```tsx?\n([\s\S]*?)```)/g;
|
|
26483
|
+
let m;
|
|
26484
|
+
while ((m = exampleRegex.exec(block)) !== null) {
|
|
26485
|
+
const title = m[1].trim();
|
|
26486
|
+
const code = m[2].split("\n").map((l) => l.replace(/^\s*\*\s?/, "")).join("\n").trim();
|
|
26487
|
+
examples.push({ title, code });
|
|
26488
|
+
}
|
|
26489
|
+
return examples;
|
|
25718
26490
|
}
|
|
25719
|
-
function
|
|
25720
|
-
const
|
|
25721
|
-
|
|
25722
|
-
|
|
25723
|
-
|
|
26491
|
+
function extractMainProps2(source) {
|
|
26492
|
+
const match = source.match(/interface\s+(\w+Props)\s+(?:extends\s+[\w<>,\s]+\s+)?\{/);
|
|
26493
|
+
if (!match) return [];
|
|
26494
|
+
return extractPropsFromInterface(source, match.index);
|
|
26495
|
+
}
|
|
26496
|
+
function extractPropsFromInterface(source, startIndex) {
|
|
26497
|
+
const bodyStart = source.indexOf("{", startIndex);
|
|
26498
|
+
if (bodyStart === -1) return [];
|
|
26499
|
+
let depth = 0;
|
|
26500
|
+
let bodyEnd = bodyStart;
|
|
26501
|
+
for (let i = bodyStart; i < source.length; i++) {
|
|
26502
|
+
if (source[i] === "{") depth++;
|
|
26503
|
+
else if (source[i] === "}") {
|
|
26504
|
+
depth--;
|
|
26505
|
+
if (depth === 0) {
|
|
26506
|
+
bodyEnd = i;
|
|
26507
|
+
break;
|
|
26508
|
+
}
|
|
25724
26509
|
}
|
|
25725
26510
|
}
|
|
25726
|
-
|
|
26511
|
+
const body = source.slice(bodyStart + 1, bodyEnd);
|
|
26512
|
+
return parsePropsBody(body);
|
|
25727
26513
|
}
|
|
25728
|
-
function
|
|
25729
|
-
const
|
|
25730
|
-
const
|
|
25731
|
-
|
|
25732
|
-
|
|
26514
|
+
function parsePropsBody(body) {
|
|
26515
|
+
const props = [];
|
|
26516
|
+
const propRegex = /(?:\/\*\*\s*([\s\S]*?)\s*\*\/\s*)?([\w]+)(\??):\s*([^\n]+)/g;
|
|
26517
|
+
let m;
|
|
26518
|
+
while ((m = propRegex.exec(body)) !== null) {
|
|
26519
|
+
const jsdoc = m[1] || "";
|
|
26520
|
+
const name = m[2];
|
|
26521
|
+
const optional = m[3] === "?";
|
|
26522
|
+
const rawType = m[4].trim();
|
|
26523
|
+
if (name.startsWith("__")) continue;
|
|
26524
|
+
const type = rawType.replace(/;?\s*$/, "").trim();
|
|
26525
|
+
const description = extractPropDescription(jsdoc);
|
|
26526
|
+
const defaultMatch = jsdoc.match(/@default\s+(.+?)(?:\s*$|\s*\*)/m);
|
|
26527
|
+
const defaultValue = defaultMatch ? defaultMatch[1].trim().replace(/^['"]|['"]$/g, "") : void 0;
|
|
26528
|
+
props.push({
|
|
26529
|
+
name,
|
|
26530
|
+
type,
|
|
26531
|
+
required: !optional && defaultValue === void 0,
|
|
26532
|
+
default: defaultValue,
|
|
26533
|
+
description
|
|
26534
|
+
});
|
|
26535
|
+
}
|
|
26536
|
+
return props;
|
|
26537
|
+
}
|
|
26538
|
+
function extractPropDescription(jsdoc) {
|
|
26539
|
+
if (!jsdoc) return "";
|
|
26540
|
+
const lines = jsdoc.split("\n");
|
|
26541
|
+
const descLines = [];
|
|
26542
|
+
for (const line of lines) {
|
|
26543
|
+
const cleaned = line.replace(/^\s*\*?\s*/, "").trim();
|
|
26544
|
+
if (cleaned.startsWith("@")) break;
|
|
26545
|
+
if (cleaned) descLines.push(cleaned);
|
|
26546
|
+
}
|
|
26547
|
+
return descLines.join(" ").trim();
|
|
26548
|
+
}
|
|
26549
|
+
function extractSubComponents2(source) {
|
|
26550
|
+
const exportedNames = extractExportedNames(source);
|
|
26551
|
+
if (exportedNames.length <= 1) return [];
|
|
26552
|
+
const subs = [];
|
|
26553
|
+
const interfaceRegex = /interface\s+(\w+Props)\s+(?:extends\s+[\w<>,\s]+\s+)?\{/g;
|
|
26554
|
+
const interfaces = [];
|
|
26555
|
+
let im;
|
|
26556
|
+
while ((im = interfaceRegex.exec(source)) !== null) {
|
|
26557
|
+
interfaces.push({ name: im[1], index: im.index });
|
|
26558
|
+
}
|
|
26559
|
+
for (let i = 1; i < interfaces.length; i++) {
|
|
26560
|
+
const iface = interfaces[i];
|
|
26561
|
+
const componentName = iface.name.replace(/Props$/, "");
|
|
26562
|
+
if (!exportedNames.includes(componentName)) continue;
|
|
26563
|
+
const beforeInterface = source.slice(Math.max(0, iface.index - 300), iface.index);
|
|
26564
|
+
const jsdocMatch = beforeInterface.match(/\/\*\*\s*([\s\S]*?)\s*\*\/\s*$/);
|
|
26565
|
+
const description = jsdocMatch ? extractPropDescription(jsdocMatch[1]) : "";
|
|
26566
|
+
const props = extractPropsFromInterface(source, iface.index);
|
|
26567
|
+
subs.push({ name: componentName, description, props });
|
|
26568
|
+
}
|
|
26569
|
+
return subs;
|
|
26570
|
+
}
|
|
26571
|
+
function extractVariants2(source) {
|
|
26572
|
+
const variants = {};
|
|
26573
|
+
const typeRegex = /type\s+(\w+(?:Variant|Size|Orientation|Side|Position))\s*=\s*([^\n]+)/g;
|
|
26574
|
+
let m;
|
|
26575
|
+
while ((m = typeRegex.exec(source)) !== null) {
|
|
26576
|
+
const name = m[1];
|
|
26577
|
+
const values = m[2].match(/'([^']+)'/g);
|
|
26578
|
+
if (values) {
|
|
26579
|
+
variants[name] = values.map((v) => v.replace(/'/g, ""));
|
|
26580
|
+
}
|
|
26581
|
+
}
|
|
26582
|
+
return variants;
|
|
26583
|
+
}
|
|
26584
|
+
function extractAccessibility2(source) {
|
|
26585
|
+
const roleMatches = source.match(/role[={"]+([^"}\s]+)/g);
|
|
26586
|
+
const roles = /* @__PURE__ */ new Set();
|
|
26587
|
+
if (roleMatches) {
|
|
26588
|
+
for (const rm of roleMatches) {
|
|
26589
|
+
const val = rm.match(/role[={"]+([^"}\s]+)/);
|
|
26590
|
+
if (val) roles.add(val[1]);
|
|
26591
|
+
}
|
|
25733
26592
|
}
|
|
26593
|
+
const ariaMatches = source.match(/aria-[\w]+/g);
|
|
26594
|
+
const ariaAttrs = [...new Set(ariaMatches || [])];
|
|
26595
|
+
const dataMatches = source.match(/data-(?:state|slot|orientation|value|disabled|side)[\w-]*/g);
|
|
26596
|
+
const dataAttrs = [...new Set(dataMatches || [])];
|
|
25734
26597
|
return {
|
|
25735
|
-
|
|
25736
|
-
|
|
26598
|
+
role: roles.size > 0 ? [...roles].join(", ") : void 0,
|
|
26599
|
+
ariaAttributes: ariaAttrs,
|
|
26600
|
+
dataAttributes: dataAttrs
|
|
25737
26601
|
};
|
|
25738
26602
|
}
|
|
25739
|
-
function
|
|
25740
|
-
const
|
|
25741
|
-
|
|
26603
|
+
function extractDependencies3(source) {
|
|
26604
|
+
const internal = [];
|
|
26605
|
+
const external = [];
|
|
26606
|
+
const importRegex = /import\s+(?:type\s+)?(?:\{[^}]*\}|[\w]+)\s+from\s+['"]([^'"]+)['"]/g;
|
|
26607
|
+
let m;
|
|
26608
|
+
while ((m = importRegex.exec(source)) !== null) {
|
|
26609
|
+
const specifier = m[1];
|
|
26610
|
+
if (m[0].includes("import type")) continue;
|
|
26611
|
+
if (specifier.startsWith("./") || specifier.startsWith("../")) {
|
|
26612
|
+
const parts = specifier.split("/");
|
|
26613
|
+
const name = parts[parts.length - 1].replace(/\.tsx?$/, "");
|
|
26614
|
+
if (name !== "types" && name !== "index") {
|
|
26615
|
+
internal.push(name);
|
|
26616
|
+
}
|
|
26617
|
+
} else {
|
|
26618
|
+
external.push(specifier);
|
|
26619
|
+
}
|
|
26620
|
+
}
|
|
26621
|
+
return {
|
|
26622
|
+
internal: [...new Set(internal)],
|
|
26623
|
+
external: [...new Set(external)]
|
|
26624
|
+
};
|
|
26625
|
+
}
|
|
26626
|
+
function extractExportedNames(source) {
|
|
26627
|
+
const names = [];
|
|
26628
|
+
const seen = /* @__PURE__ */ new Set();
|
|
26629
|
+
const add = (n) => {
|
|
26630
|
+
if (!n) return;
|
|
26631
|
+
const trimmed = n.trim();
|
|
26632
|
+
if (!trimmed || seen.has(trimmed)) return;
|
|
26633
|
+
if (trimmed.startsWith("type ")) return;
|
|
26634
|
+
seen.add(trimmed);
|
|
26635
|
+
names.push(trimmed);
|
|
26636
|
+
};
|
|
26637
|
+
const braceRegex = /export\s+\{([^}]+)\}/g;
|
|
26638
|
+
let bm;
|
|
26639
|
+
while ((bm = braceRegex.exec(source)) !== null) {
|
|
26640
|
+
for (const part of bm[1].split(",")) {
|
|
26641
|
+
const trimmed = part.trim();
|
|
26642
|
+
if (!trimmed || trimmed.startsWith("type ")) continue;
|
|
26643
|
+
const asMatch = trimmed.match(/\s+as\s+(\w+)$/);
|
|
26644
|
+
add(asMatch ? asMatch[1] : trimmed);
|
|
26645
|
+
}
|
|
26646
|
+
}
|
|
26647
|
+
const fnRegex = /export\s+(?:default\s+)?(?:async\s+)?function\s+(\w+)/g;
|
|
26648
|
+
let fm;
|
|
26649
|
+
while ((fm = fnRegex.exec(source)) !== null) add(fm[1]);
|
|
26650
|
+
const varRegex = /export\s+(?:const|let|var)\s+(\w+)\s*[=:]/g;
|
|
26651
|
+
let vm;
|
|
26652
|
+
while ((vm = varRegex.exec(source)) !== null) add(vm[1]);
|
|
26653
|
+
const defaultRegex = /export\s+default\s+(\w+)\s*;?\s*$/m;
|
|
26654
|
+
const dm = defaultRegex.exec(source);
|
|
26655
|
+
if (dm) add(dm[1]);
|
|
26656
|
+
return names;
|
|
25742
26657
|
}
|
|
25743
|
-
|
|
25744
|
-
|
|
25745
|
-
|
|
25746
|
-
const hasSubComponents = meta.subComponents != null && meta.subComponents.length > 0;
|
|
25747
|
-
let needsClient = meta.stateful || meta.tags.includes("stateful");
|
|
25748
|
-
const exampleCode = hasSubComponents && meta.examples.length > 0 ? meta.examples[0].code : "";
|
|
25749
|
-
if (exampleCode.includes("createSignal")) {
|
|
25750
|
-
needsClient = true;
|
|
26658
|
+
var init_parse_component = __esm({
|
|
26659
|
+
"src/lib/parse-component.ts"() {
|
|
26660
|
+
"use strict";
|
|
25751
26661
|
}
|
|
25752
|
-
|
|
26662
|
+
});
|
|
26663
|
+
|
|
26664
|
+
// src/lib/test-template.ts
|
|
26665
|
+
import { readFileSync as readFileSync9 } from "fs";
|
|
26666
|
+
import path20 from "path";
|
|
26667
|
+
function generateTestTemplate(componentPath, options = {}) {
|
|
26668
|
+
const importSource = options.importSource ?? "bun:test";
|
|
26669
|
+
const source = readFileSync9(componentPath, "utf-8");
|
|
26670
|
+
const parsed = parseComponent(source);
|
|
26671
|
+
const fileName = path20.basename(componentPath);
|
|
26672
|
+
const baseName = fileName.replace(/\.tsx$/, "");
|
|
26673
|
+
const relativePath = fileName;
|
|
26674
|
+
const varName = toCamelCase2(baseName) + "Source";
|
|
26675
|
+
const exportedNames = parsed.exportedNames;
|
|
26676
|
+
const mainComponent = exportedNames[0];
|
|
26677
|
+
const hasSubComponents = exportedNames.length > 1;
|
|
25753
26678
|
const lines = [];
|
|
25754
|
-
|
|
25755
|
-
lines.push(
|
|
25756
|
-
|
|
25757
|
-
|
|
25758
|
-
|
|
25759
|
-
lines.push(
|
|
25760
|
-
|
|
25761
|
-
|
|
26679
|
+
lines.push(`import { describe, test, expect } from '${importSource}'`);
|
|
26680
|
+
lines.push(`import { readFileSync } from 'fs'`);
|
|
26681
|
+
lines.push(`import { resolve } from 'path'`);
|
|
26682
|
+
lines.push(`import { renderToTest } from '@barefootjs/test'`);
|
|
26683
|
+
lines.push(``);
|
|
26684
|
+
lines.push(`const ${varName} = readFileSync(resolve(__dirname, '${relativePath}'), 'utf-8')`);
|
|
26685
|
+
lines.push(``);
|
|
26686
|
+
if (mainComponent) {
|
|
26687
|
+
lines.push(...generateDescribeBlock(source, parsed, mainComponent, varName, fileName, hasSubComponents));
|
|
25762
26688
|
}
|
|
25763
|
-
const componentImports = [pascalName];
|
|
25764
|
-
const subNames = [];
|
|
25765
26689
|
if (hasSubComponents) {
|
|
25766
|
-
for (
|
|
25767
|
-
|
|
25768
|
-
|
|
26690
|
+
for (let i = 1; i < exportedNames.length; i++) {
|
|
26691
|
+
lines.push(``);
|
|
26692
|
+
lines.push(...generateDescribeBlock(source, parsed, exportedNames[i], varName, fileName, true));
|
|
25769
26693
|
}
|
|
25770
26694
|
}
|
|
25771
|
-
lines.
|
|
25772
|
-
|
|
25773
|
-
|
|
25774
|
-
|
|
25775
|
-
|
|
26695
|
+
return lines.join("\n") + "\n";
|
|
26696
|
+
}
|
|
26697
|
+
function generateDescribeBlock(source, parsed, componentName, varName, fileName, multiComponent) {
|
|
26698
|
+
const lines = [];
|
|
26699
|
+
const renderArg = multiComponent ? `, '${componentName}'` : "";
|
|
26700
|
+
const funcInfo = analyzeFunction(source, componentName);
|
|
26701
|
+
lines.push(`describe('${componentName}', () => {`);
|
|
26702
|
+
lines.push(` const result = renderToTest(${varName}, '${fileName}'${renderArg})`);
|
|
26703
|
+
lines.push(``);
|
|
26704
|
+
lines.push(` test('has no compiler errors', () => {`);
|
|
26705
|
+
lines.push(` expect(result.errors).toEqual([])`);
|
|
26706
|
+
lines.push(` })`);
|
|
26707
|
+
lines.push(``);
|
|
26708
|
+
lines.push(` test('componentName is ${componentName}', () => {`);
|
|
26709
|
+
lines.push(` expect(result.componentName).toBe('${componentName}')`);
|
|
26710
|
+
lines.push(` })`);
|
|
26711
|
+
lines.push(``);
|
|
26712
|
+
if (funcInfo.signals.length > 0) {
|
|
26713
|
+
lines.push(` test('has expected signals', () => {`);
|
|
26714
|
+
for (const sig of funcInfo.signals) {
|
|
26715
|
+
lines.push(` expect(result.signals).toContain('${sig}')`);
|
|
25776
26716
|
}
|
|
25777
|
-
}
|
|
25778
|
-
lines.push("");
|
|
25779
|
-
if (hasSubComponents) {
|
|
25780
|
-
generateMultiComponent(lines, previewNames, meta, pascalName, subNames);
|
|
25781
|
-
} else if (needsClient && hasVariants) {
|
|
25782
|
-
generateStatefulWithVariants(lines, previewNames, meta, pascalName);
|
|
25783
|
-
} else if (needsClient) {
|
|
25784
|
-
generateStateful(lines, previewNames, meta, pascalName);
|
|
25785
|
-
} else if (hasVariants) {
|
|
25786
|
-
generateStatelessWithVariants(lines, previewNames, meta, pascalName);
|
|
26717
|
+
lines.push(` })`);
|
|
25787
26718
|
} else {
|
|
25788
|
-
|
|
26719
|
+
lines.push(` test('no signals (stateless)', () => {`);
|
|
26720
|
+
lines.push(` expect(result.signals).toEqual([])`);
|
|
26721
|
+
lines.push(` })`);
|
|
25789
26722
|
}
|
|
25790
|
-
lines.push(
|
|
25791
|
-
|
|
25792
|
-
|
|
25793
|
-
|
|
25794
|
-
|
|
25795
|
-
|
|
25796
|
-
}
|
|
25797
|
-
|
|
25798
|
-
|
|
25799
|
-
|
|
25800
|
-
|
|
25801
|
-
|
|
25802
|
-
lines.push("");
|
|
25803
|
-
for (const [typeName, values] of Object.entries(meta.variants)) {
|
|
25804
|
-
const propName = inferVariantPropName(typeName, meta.props);
|
|
25805
|
-
if (!propName) continue;
|
|
25806
|
-
const funcName = capitalize2(propName) + "s";
|
|
25807
|
-
previewNames.push(funcName);
|
|
25808
|
-
lines.push(`export function ${funcName}() {`);
|
|
25809
|
-
lines.push(" return (");
|
|
25810
|
-
lines.push(' <div className="flex flex-wrap items-center gap-4">');
|
|
25811
|
-
for (const value of values) {
|
|
25812
|
-
lines.push(` <${pascalName} ${propName}="${value}">${capitalize2(value)}</${pascalName}>`);
|
|
26723
|
+
lines.push(``);
|
|
26724
|
+
if (funcInfo.rootTag) {
|
|
26725
|
+
const isComponentRoot = /^[A-Z]/.test(funcInfo.rootTag);
|
|
26726
|
+
lines.push(` test('renders as <${funcInfo.rootTag}>', () => {`);
|
|
26727
|
+
if (funcInfo.hasConditionalReturn) {
|
|
26728
|
+
lines.push(` // Component has conditional return (e.g., asChild branch)`);
|
|
26729
|
+
const finder = isComponentRoot ? `result.find({ componentName: '${funcInfo.rootTag}' })` : `result.find({ tag: '${funcInfo.rootTag}' })`;
|
|
26730
|
+
lines.push(` expect(${finder}).not.toBeNull()`);
|
|
26731
|
+
} else if (isComponentRoot) {
|
|
26732
|
+
lines.push(` expect(result.root.componentName).toBe('${funcInfo.rootTag}')`);
|
|
26733
|
+
} else {
|
|
26734
|
+
lines.push(` expect(result.root.tag).toBe('${funcInfo.rootTag}')`);
|
|
25813
26735
|
}
|
|
25814
|
-
lines.push(
|
|
25815
|
-
lines.push(
|
|
25816
|
-
lines.push("}");
|
|
25817
|
-
lines.push("");
|
|
26736
|
+
lines.push(` })`);
|
|
26737
|
+
lines.push(``);
|
|
25818
26738
|
}
|
|
25819
|
-
|
|
25820
|
-
|
|
25821
|
-
|
|
25822
|
-
|
|
25823
|
-
|
|
25824
|
-
if (simpleExample) {
|
|
25825
|
-
const jsxLines = simpleExample.code.split("\n");
|
|
25826
|
-
if (jsxLines.length === 1) {
|
|
25827
|
-
lines.push(` return ${simpleExample.code}`);
|
|
26739
|
+
if (funcInfo.dataSlot) {
|
|
26740
|
+
lines.push(` test('has data-slot=${funcInfo.dataSlot}', () => {`);
|
|
26741
|
+
if (funcInfo.hasConditionalReturn) {
|
|
26742
|
+
lines.push(` const el = result.find({ tag: '${funcInfo.rootTag}' })!`);
|
|
26743
|
+
lines.push(` expect(el.props['data-slot']).toBe('${funcInfo.dataSlot}')`);
|
|
25828
26744
|
} else {
|
|
25829
|
-
lines.push(
|
|
25830
|
-
for (const l of jsxLines) {
|
|
25831
|
-
lines.push(` ${l}`);
|
|
25832
|
-
}
|
|
25833
|
-
lines.push(" )");
|
|
26745
|
+
lines.push(` expect(result.root.props['data-slot']).toBe('${funcInfo.dataSlot}')`);
|
|
25834
26746
|
}
|
|
25835
|
-
}
|
|
25836
|
-
|
|
25837
|
-
|
|
25838
|
-
|
|
26747
|
+
lines.push(` })`);
|
|
26748
|
+
lines.push(``);
|
|
26749
|
+
}
|
|
26750
|
+
if (funcInfo.role) {
|
|
26751
|
+
lines.push(` test('has role=${funcInfo.role}', () => {`);
|
|
26752
|
+
lines.push(` const el = result.find({ role: '${funcInfo.role}' })`);
|
|
26753
|
+
lines.push(` expect(el).not.toBeNull()`);
|
|
26754
|
+
lines.push(` })`);
|
|
26755
|
+
lines.push(``);
|
|
26756
|
+
}
|
|
26757
|
+
const ariaAttrs = funcInfo.ariaAttributes;
|
|
26758
|
+
if (ariaAttrs.length > 0) {
|
|
26759
|
+
lines.push(` test('has ARIA attributes', () => {`);
|
|
26760
|
+
const findTarget = funcInfo.role ? `result.find({ role: '${funcInfo.role}' })!` : funcInfo.rootTag ? `result.find({ tag: '${funcInfo.rootTag}' })!` : "result.root";
|
|
26761
|
+
lines.push(` const el = ${findTarget}`);
|
|
26762
|
+
for (const attr of ariaAttrs) {
|
|
26763
|
+
const shortName = attr.replace("aria-", "");
|
|
26764
|
+
lines.push(` expect(el.aria).toHaveProperty('${shortName}')`);
|
|
26765
|
+
}
|
|
26766
|
+
lines.push(` })`);
|
|
26767
|
+
lines.push(``);
|
|
26768
|
+
}
|
|
26769
|
+
if (funcInfo.hasDataState) {
|
|
26770
|
+
lines.push(` test('has data-state attribute', () => {`);
|
|
26771
|
+
if (funcInfo.hasConditionalReturn) {
|
|
26772
|
+
lines.push(` const el = result.find({ tag: '${funcInfo.rootTag}' })!`);
|
|
26773
|
+
lines.push(` expect(el.dataState).not.toBeNull()`);
|
|
25839
26774
|
} else {
|
|
25840
|
-
lines.push(`
|
|
26775
|
+
lines.push(` expect(result.root.dataState).not.toBeNull()`);
|
|
25841
26776
|
}
|
|
26777
|
+
lines.push(` })`);
|
|
26778
|
+
lines.push(``);
|
|
25842
26779
|
}
|
|
25843
|
-
|
|
25844
|
-
|
|
25845
|
-
}
|
|
25846
|
-
|
|
25847
|
-
|
|
25848
|
-
|
|
25849
|
-
|
|
25850
|
-
|
|
25851
|
-
|
|
25852
|
-
|
|
25853
|
-
(
|
|
25854
|
-
);
|
|
25855
|
-
if (defaultStateProp) {
|
|
25856
|
-
lines.push(` <${pascalName} ${defaultStateProp.name} />`);
|
|
26780
|
+
if (funcInfo.events.length > 0) {
|
|
26781
|
+
lines.push(` test('has event handlers', () => {`);
|
|
26782
|
+
lines.push(` const all = result.findAll({})`);
|
|
26783
|
+
for (const event of funcInfo.events) {
|
|
26784
|
+
const onProp = ON_PROP_BY_EVENT[event];
|
|
26785
|
+
lines.push(` expect(`);
|
|
26786
|
+
lines.push(` all.some(n => n.events.includes('${event}') || n.props['${onProp}'] != null),`);
|
|
26787
|
+
lines.push(` ).toBe(true)`);
|
|
26788
|
+
}
|
|
26789
|
+
lines.push(` })`);
|
|
26790
|
+
lines.push(``);
|
|
25857
26791
|
}
|
|
25858
|
-
if (
|
|
25859
|
-
lines.push(`
|
|
26792
|
+
if (funcInfo.childComponents.length > 0) {
|
|
26793
|
+
lines.push(` test('contains child components', () => {`);
|
|
26794
|
+
for (const child of funcInfo.childComponents) {
|
|
26795
|
+
lines.push(` expect(result.find({ componentName: '${child}' })).not.toBeNull()`);
|
|
26796
|
+
}
|
|
26797
|
+
lines.push(` })`);
|
|
26798
|
+
lines.push(``);
|
|
25860
26799
|
}
|
|
25861
|
-
lines.push(
|
|
25862
|
-
lines.push(
|
|
25863
|
-
lines.push(
|
|
25864
|
-
|
|
26800
|
+
lines.push(` test('toStructure() shows expected tree', () => {`);
|
|
26801
|
+
lines.push(` const structure = result.toStructure()`);
|
|
26802
|
+
lines.push(` expect(structure.length).toBeGreaterThan(0)`);
|
|
26803
|
+
if (funcInfo.rootTag) {
|
|
26804
|
+
lines.push(` expect(structure).toContain('${funcInfo.rootTag}')`);
|
|
26805
|
+
}
|
|
26806
|
+
if (funcInfo.role) {
|
|
26807
|
+
lines.push(` expect(structure).toContain('[role=${funcInfo.role}]')`);
|
|
26808
|
+
}
|
|
26809
|
+
lines.push(` })`);
|
|
26810
|
+
lines.push(`})`);
|
|
26811
|
+
return lines;
|
|
25865
26812
|
}
|
|
25866
|
-
function
|
|
25867
|
-
|
|
25868
|
-
const
|
|
25869
|
-
|
|
25870
|
-
|
|
25871
|
-
|
|
25872
|
-
|
|
25873
|
-
|
|
25874
|
-
lines.push(`export function ${funcName}() {`);
|
|
25875
|
-
lines.push(" return (");
|
|
25876
|
-
lines.push(' <div className="flex flex-wrap items-center gap-4">');
|
|
25877
|
-
for (const value of values) {
|
|
25878
|
-
if (hasChildren) {
|
|
25879
|
-
lines.push(` <${pascalName} ${propName}="${value}">${capitalize2(value)}</${pascalName}>`);
|
|
25880
|
-
} else {
|
|
25881
|
-
lines.push(` <${pascalName} ${propName}="${value}" />`);
|
|
25882
|
-
}
|
|
26813
|
+
function analyzeFunction(source, componentName) {
|
|
26814
|
+
const funcRegex = new RegExp(`function\\s+${componentName}\\s*\\(`);
|
|
26815
|
+
const funcMatch = funcRegex.exec(source);
|
|
26816
|
+
let funcBody = source;
|
|
26817
|
+
if (!funcMatch) {
|
|
26818
|
+
const aliasMatch = source.match(new RegExp(`const\\s+${componentName}\\s*=\\s*(\\w+)`));
|
|
26819
|
+
if (aliasMatch) {
|
|
26820
|
+
return analyzeFunction(source, aliasMatch[1]);
|
|
25883
26821
|
}
|
|
25884
|
-
lines.push(" </div>");
|
|
25885
|
-
lines.push(" )");
|
|
25886
|
-
lines.push("}");
|
|
25887
|
-
lines.push("");
|
|
25888
26822
|
}
|
|
25889
|
-
|
|
25890
|
-
|
|
25891
|
-
|
|
25892
|
-
|
|
25893
|
-
|
|
25894
|
-
|
|
25895
|
-
|
|
25896
|
-
|
|
25897
|
-
|
|
25898
|
-
|
|
25899
|
-
|
|
25900
|
-
|
|
25901
|
-
);
|
|
25902
|
-
const handlerRefs = [...new Set([...jsx.matchAll(/\b(handle[A-Z]\w*)\b/g)].map((m) => m[1]))];
|
|
25903
|
-
for (const h of handlerRefs) {
|
|
25904
|
-
if (!definedNames.has(h)) {
|
|
25905
|
-
lines.push(` const ${h} = () => {}`);
|
|
26823
|
+
if (funcMatch) {
|
|
26824
|
+
const parenStart = source.indexOf("(", funcMatch.index);
|
|
26825
|
+
let parenDepth = 0;
|
|
26826
|
+
let parenEnd = parenStart;
|
|
26827
|
+
for (let i = parenStart; i < source.length; i++) {
|
|
26828
|
+
if (source[i] === "(") parenDepth++;
|
|
26829
|
+
else if (source[i] === ")") {
|
|
26830
|
+
parenDepth--;
|
|
26831
|
+
if (parenDepth === 0) {
|
|
26832
|
+
parenEnd = i;
|
|
26833
|
+
break;
|
|
26834
|
+
}
|
|
25906
26835
|
}
|
|
25907
26836
|
}
|
|
25908
|
-
|
|
25909
|
-
|
|
25910
|
-
|
|
25911
|
-
|
|
25912
|
-
|
|
25913
|
-
|
|
25914
|
-
|
|
25915
|
-
|
|
25916
|
-
|
|
25917
|
-
|
|
26837
|
+
let depth = 0;
|
|
26838
|
+
let bodyStart = -1;
|
|
26839
|
+
for (let i = parenEnd + 1; i < source.length; i++) {
|
|
26840
|
+
if (source[i] === "{") {
|
|
26841
|
+
if (bodyStart === -1) bodyStart = i;
|
|
26842
|
+
depth++;
|
|
26843
|
+
} else if (source[i] === "}") {
|
|
26844
|
+
depth--;
|
|
26845
|
+
if (depth === 0) {
|
|
26846
|
+
funcBody = source.slice(bodyStart, i + 1);
|
|
26847
|
+
break;
|
|
26848
|
+
}
|
|
25918
26849
|
}
|
|
25919
|
-
lines.push(" )");
|
|
25920
26850
|
}
|
|
25921
|
-
|
|
25922
|
-
|
|
25923
|
-
|
|
25924
|
-
|
|
25925
|
-
|
|
25926
|
-
|
|
25927
|
-
|
|
25928
|
-
|
|
25929
|
-
|
|
25930
|
-
|
|
25931
|
-
|
|
25932
|
-
|
|
25933
|
-
|
|
26851
|
+
}
|
|
26852
|
+
const signals = [];
|
|
26853
|
+
const signalRegex = /const\s+\[(\w+),\s*\w+\]\s*=\s*createSignal/g;
|
|
26854
|
+
let sm;
|
|
26855
|
+
while ((sm = signalRegex.exec(funcBody)) !== null) {
|
|
26856
|
+
signals.push(sm[1]);
|
|
26857
|
+
}
|
|
26858
|
+
const returnMatches = [...funcBody.matchAll(/return\s*\(?\s*<(\w+)/g)];
|
|
26859
|
+
const rootTag = returnMatches.length > 0 ? returnMatches[returnMatches.length - 1][1] : null;
|
|
26860
|
+
const allSlots = [...funcBody.matchAll(/data-slot="([^"]+)"/g)].map((m) => m[1]);
|
|
26861
|
+
const expectedSlot = componentName.replace(/([a-z])([A-Z])/g, "$1-$2").toLowerCase();
|
|
26862
|
+
const dataSlot = allSlots.find((s) => s === expectedSlot) || allSlots[0] || null;
|
|
26863
|
+
const roleMatch = funcBody.match(/role="([^"]+)"/);
|
|
26864
|
+
const role = roleMatch ? roleMatch[1] : null;
|
|
26865
|
+
const ariaMatches = funcBody.match(/aria-[\w]+(?==)/g);
|
|
26866
|
+
const ariaAttributes = [...new Set(ariaMatches || [])].filter((a) => a !== "aria-invalid");
|
|
26867
|
+
const hasDataState = /data-state=/.test(funcBody);
|
|
26868
|
+
const hasConditionalReturn = /if\s*\(.*\)\s*\{?\s*return/.test(funcBody);
|
|
26869
|
+
const events = [];
|
|
26870
|
+
for (const [event, onProp] of Object.entries(ON_PROP_BY_EVENT)) {
|
|
26871
|
+
if (new RegExp(`${onProp}=`).test(funcBody)) events.push(event);
|
|
26872
|
+
}
|
|
26873
|
+
const childCompRegex = /<([A-Z][A-Za-z]+)[\s/>]/g;
|
|
26874
|
+
const childComponents = [];
|
|
26875
|
+
let cm;
|
|
26876
|
+
while ((cm = childCompRegex.exec(funcBody)) !== null) {
|
|
26877
|
+
if (!childComponents.includes(cm[1])) {
|
|
26878
|
+
childComponents.push(cm[1]);
|
|
25934
26879
|
}
|
|
25935
|
-
lines.push(` </${pascalName}>`);
|
|
25936
|
-
lines.push(" )");
|
|
25937
|
-
lines.push("}");
|
|
25938
26880
|
}
|
|
25939
|
-
|
|
26881
|
+
return {
|
|
26882
|
+
signals,
|
|
26883
|
+
rootTag,
|
|
26884
|
+
dataSlot,
|
|
26885
|
+
role,
|
|
26886
|
+
ariaAttributes,
|
|
26887
|
+
hasDataState,
|
|
26888
|
+
hasConditionalReturn,
|
|
26889
|
+
events,
|
|
26890
|
+
childComponents
|
|
26891
|
+
};
|
|
25940
26892
|
}
|
|
25941
|
-
|
|
25942
|
-
|
|
26893
|
+
function toCamelCase2(kebab) {
|
|
26894
|
+
return kebab.replace(/-([a-z])/g, (_, c) => c.toUpperCase());
|
|
26895
|
+
}
|
|
26896
|
+
var ON_PROP_BY_EVENT;
|
|
26897
|
+
var init_test_template = __esm({
|
|
26898
|
+
"src/lib/test-template.ts"() {
|
|
26899
|
+
"use strict";
|
|
26900
|
+
init_parse_component();
|
|
26901
|
+
ON_PROP_BY_EVENT = {
|
|
26902
|
+
click: "onClick",
|
|
26903
|
+
input: "onInput",
|
|
26904
|
+
change: "onChange",
|
|
26905
|
+
keydown: "onKeyDown"
|
|
26906
|
+
};
|
|
26907
|
+
}
|
|
26908
|
+
});
|
|
26909
|
+
|
|
26910
|
+
// src/commands/gen-test.ts
|
|
26911
|
+
var gen_test_exports = {};
|
|
26912
|
+
__export(gen_test_exports, {
|
|
26913
|
+
run: () => run12
|
|
26914
|
+
});
|
|
26915
|
+
import { existsSync as existsSync21, writeFileSync as writeFileSync7 } from "fs";
|
|
26916
|
+
import path21 from "path";
|
|
26917
|
+
function run12(args2, ctx2) {
|
|
26918
|
+
const positional = args2.filter((a) => !a.startsWith("-"));
|
|
26919
|
+
const flagSet = new Set(args2.filter((a) => a.startsWith("-")));
|
|
26920
|
+
const writeToStdout = flagSet.has("--stdout");
|
|
26921
|
+
const force = flagSet.has("--force") || flagSet.has("-f");
|
|
26922
|
+
const componentName = positional[0];
|
|
26923
|
+
if (!componentName) {
|
|
26924
|
+
console.error("Error: Component name required. Usage: bf gen test <component> [--stdout] [--force]");
|
|
26925
|
+
process.exit(1);
|
|
26926
|
+
}
|
|
26927
|
+
const searched = [];
|
|
26928
|
+
const resolved = resolveComponentSource(componentName, ctx2, searched);
|
|
26929
|
+
if (!resolved) {
|
|
26930
|
+
console.error(`Error: Cannot find component "${componentName}".`);
|
|
26931
|
+
console.error("Looked in:");
|
|
26932
|
+
for (const p of searched) console.error(` - ${p}`);
|
|
26933
|
+
process.exit(1);
|
|
26934
|
+
}
|
|
26935
|
+
const pm = detectPackageManager(ctx2.projectDir ?? ctx2.root);
|
|
26936
|
+
const runner = testRunnerFor(pm);
|
|
26937
|
+
const content = generateTestTemplate(resolved.filePath, { importSource: runner.importSource });
|
|
26938
|
+
if (writeToStdout) {
|
|
26939
|
+
console.log(content);
|
|
26940
|
+
return;
|
|
26941
|
+
}
|
|
26942
|
+
const dir = path21.dirname(resolved.filePath);
|
|
26943
|
+
const base = path21.basename(resolved.filePath, path21.extname(resolved.filePath));
|
|
26944
|
+
const testPath = path21.join(dir, `${base}.test.tsx`);
|
|
26945
|
+
if (existsSync21(testPath) && !force) {
|
|
26946
|
+
const rel2 = path21.relative(ctx2.projectDir ?? ctx2.root, testPath);
|
|
26947
|
+
console.error(`Error: ${rel2} already exists. Pass --force to overwrite, or --stdout to preview.`);
|
|
26948
|
+
process.exit(1);
|
|
26949
|
+
}
|
|
26950
|
+
writeFileSync7(testPath, content);
|
|
26951
|
+
const rel = path21.relative(ctx2.projectDir ?? ctx2.root, testPath);
|
|
26952
|
+
console.log(`Created: ${rel}`);
|
|
26953
|
+
console.log(``);
|
|
26954
|
+
console.log(`Next: ${commandsFor(pm).test(rel)}`);
|
|
26955
|
+
}
|
|
26956
|
+
var init_gen_test = __esm({
|
|
26957
|
+
"src/commands/gen-test.ts"() {
|
|
25943
26958
|
"use strict";
|
|
26959
|
+
init_resolve_source();
|
|
26960
|
+
init_test_template();
|
|
26961
|
+
init_pm();
|
|
25944
26962
|
}
|
|
25945
26963
|
});
|
|
25946
26964
|
|
|
@@ -25949,7 +26967,7 @@ var gen_preview_exports = {};
|
|
|
25949
26967
|
__export(gen_preview_exports, {
|
|
25950
26968
|
run: () => run13
|
|
25951
26969
|
});
|
|
25952
|
-
import { existsSync as
|
|
26970
|
+
import { existsSync as existsSync22, writeFileSync as writeFileSync8, mkdirSync as mkdirSync5 } from "fs";
|
|
25953
26971
|
import path22 from "path";
|
|
25954
26972
|
async function run13(args2, ctx2) {
|
|
25955
26973
|
const force = args2.includes("--force");
|
|
@@ -25962,15 +26980,15 @@ async function run13(args2, ctx2) {
|
|
|
25962
26980
|
const { writeRoot, componentsBasePath } = resolveScaffoldLayout(ctx2);
|
|
25963
26981
|
const result = generatePreview(meta, componentsBasePath);
|
|
25964
26982
|
const absPath = path22.join(writeRoot, result.filePath);
|
|
25965
|
-
if (
|
|
26983
|
+
if (existsSync22(absPath) && !force) {
|
|
25966
26984
|
console.error(`Error: ${result.filePath} already exists. Use --force to overwrite.`);
|
|
25967
26985
|
process.exit(1);
|
|
25968
26986
|
}
|
|
25969
26987
|
const dir = path22.dirname(absPath);
|
|
25970
|
-
if (!
|
|
26988
|
+
if (!existsSync22(dir)) {
|
|
25971
26989
|
mkdirSync5(dir, { recursive: true });
|
|
25972
26990
|
}
|
|
25973
|
-
|
|
26991
|
+
writeFileSync8(absPath, result.code);
|
|
25974
26992
|
console.log(`Generated ${result.filePath}`);
|
|
25975
26993
|
console.log(`Previews: ${result.previewNames.join(", ")}`);
|
|
25976
26994
|
}
|
|
@@ -25988,7 +27006,7 @@ var debug_graph_exports = {};
|
|
|
25988
27006
|
__export(debug_graph_exports, {
|
|
25989
27007
|
run: () => run14
|
|
25990
27008
|
});
|
|
25991
|
-
import { readFileSync as
|
|
27009
|
+
import { readFileSync as readFileSync10 } from "fs";
|
|
25992
27010
|
async function run14(args2, ctx2) {
|
|
25993
27011
|
const componentName = args2[0];
|
|
25994
27012
|
if (!componentName) {
|
|
@@ -26005,7 +27023,7 @@ async function run14(args2, ctx2) {
|
|
|
26005
27023
|
for (const p of searched) console.error(` - ${p}`);
|
|
26006
27024
|
process.exit(1);
|
|
26007
27025
|
}
|
|
26008
|
-
const source =
|
|
27026
|
+
const source = readFileSync10(resolved.filePath, "utf-8");
|
|
26009
27027
|
const graph = buildComponentGraph2(source, resolved.filePath, resolved.componentName);
|
|
26010
27028
|
if (ctx2.jsonFlag) {
|
|
26011
27029
|
console.log(JSON.stringify(graphToJSON2(graph), null, 2));
|
|
@@ -26025,7 +27043,7 @@ var debug_trace_exports = {};
|
|
|
26025
27043
|
__export(debug_trace_exports, {
|
|
26026
27044
|
run: () => run15
|
|
26027
27045
|
});
|
|
26028
|
-
import { readFileSync as
|
|
27046
|
+
import { readFileSync as readFileSync11 } from "fs";
|
|
26029
27047
|
async function run15(args2, ctx2) {
|
|
26030
27048
|
const componentName = args2[0];
|
|
26031
27049
|
const targetName = args2[1];
|
|
@@ -26043,7 +27061,7 @@ async function run15(args2, ctx2) {
|
|
|
26043
27061
|
for (const p of searched) console.error(` - ${p}`);
|
|
26044
27062
|
process.exit(1);
|
|
26045
27063
|
}
|
|
26046
|
-
const source =
|
|
27064
|
+
const source = readFileSync11(resolved.filePath, "utf-8");
|
|
26047
27065
|
const graph = buildComponentGraph2(source, resolved.filePath, resolved.componentName);
|
|
26048
27066
|
const path23 = traceUpdatePath2(graph, targetName);
|
|
26049
27067
|
if (!path23) {
|
|
@@ -26075,7 +27093,7 @@ var debug_fallbacks_exports = {};
|
|
|
26075
27093
|
__export(debug_fallbacks_exports, {
|
|
26076
27094
|
run: () => run16
|
|
26077
27095
|
});
|
|
26078
|
-
import { readFileSync as
|
|
27096
|
+
import { readFileSync as readFileSync12 } from "fs";
|
|
26079
27097
|
async function run16(args2, ctx2) {
|
|
26080
27098
|
const componentName = args2[0];
|
|
26081
27099
|
if (!componentName) {
|
|
@@ -26083,7 +27101,7 @@ async function run16(args2, ctx2) {
|
|
|
26083
27101
|
console.error("Usage: bf debug fallbacks <component> [--json]");
|
|
26084
27102
|
process.exit(1);
|
|
26085
27103
|
}
|
|
26086
|
-
const { buildComponentGraph: buildComponentGraph2 } = await Promise.resolve().then(() => (init_src2(), src_exports));
|
|
27104
|
+
const { buildComponentGraph: buildComponentGraph2, describeFallback: describeFallback2, formatFallbackExplanations: formatFallbackExplanations2 } = await Promise.resolve().then(() => (init_src2(), src_exports));
|
|
26087
27105
|
const searched = [];
|
|
26088
27106
|
const resolved = resolveComponentSource(componentName, ctx2, searched);
|
|
26089
27107
|
if (!resolved) {
|
|
@@ -26092,45 +27110,35 @@ async function run16(args2, ctx2) {
|
|
|
26092
27110
|
for (const p of searched) console.error(` - ${p}`);
|
|
26093
27111
|
process.exit(1);
|
|
26094
27112
|
}
|
|
26095
|
-
const source =
|
|
27113
|
+
const source = readFileSync12(resolved.filePath, "utf-8");
|
|
26096
27114
|
const graph = buildComponentGraph2(source, resolved.filePath, resolved.componentName);
|
|
26097
|
-
const
|
|
27115
|
+
const isEventHandlerProp = (d) => d.type === "attribute" && /^on[A-Z]/.test(d.label.split(".").pop() ?? "");
|
|
27116
|
+
const fallbacks = graph.domBindings.filter((d) => d.classification === "fallback" && !isEventHandlerProp(d));
|
|
26098
27117
|
if (ctx2.jsonFlag) {
|
|
26099
27118
|
console.log(JSON.stringify({
|
|
26100
27119
|
componentName: graph.componentName,
|
|
26101
27120
|
sourceFile: graph.sourceFile,
|
|
26102
|
-
fallbacks: fallbacks.map((f) =>
|
|
26103
|
-
|
|
26104
|
-
|
|
26105
|
-
|
|
26106
|
-
|
|
26107
|
-
|
|
26108
|
-
|
|
26109
|
-
|
|
26110
|
-
|
|
27121
|
+
fallbacks: fallbacks.map((f) => {
|
|
27122
|
+
const ex = describeFallback2(f);
|
|
27123
|
+
return {
|
|
27124
|
+
label: f.label,
|
|
27125
|
+
slotId: f.slotId,
|
|
27126
|
+
deps: f.deps,
|
|
27127
|
+
type: f.type,
|
|
27128
|
+
classification: f.classification,
|
|
27129
|
+
...f.expression !== void 0 && { expression: f.expression },
|
|
27130
|
+
...f.wrapReason !== void 0 && { wrapReason: f.wrapReason },
|
|
27131
|
+
reason: ex.reason,
|
|
27132
|
+
runtimeDeps: ex.runtimeDeps,
|
|
27133
|
+
suggestion: ex.suggestion,
|
|
27134
|
+
isEventHandler: ex.isEventHandler,
|
|
27135
|
+
...ex.loc && { loc: ex.loc }
|
|
27136
|
+
};
|
|
27137
|
+
})
|
|
26111
27138
|
}, null, 2));
|
|
26112
27139
|
return;
|
|
26113
27140
|
}
|
|
26114
|
-
|
|
26115
|
-
console.log(`${graph.componentName} \u2014 no fallback-wrapped expressions.`);
|
|
26116
|
-
return;
|
|
26117
|
-
}
|
|
26118
|
-
console.log(`${graph.componentName} \u2014 ${fallbacks.length} fallback-wrapped expression(s)`);
|
|
26119
|
-
const cells = fallbacks.map((f) => {
|
|
26120
|
-
const id = f.type === "attribute" ? f.label : f.slotId;
|
|
26121
|
-
return { f, cell: `${f.type} "${id}"` };
|
|
26122
|
-
});
|
|
26123
|
-
const width = cells.reduce((w, c) => Math.max(w, c.cell.length), 0);
|
|
26124
|
-
for (const { f, cell } of cells) {
|
|
26125
|
-
const expr = f.expression ?? "(expression not captured)";
|
|
26126
|
-
const reason = f.wrapReason ? ` [${f.wrapReason}]` : "";
|
|
26127
|
-
console.log(` ${cell.padEnd(width)} ~ ${expr}${reason}`);
|
|
26128
|
-
}
|
|
26129
|
-
console.log();
|
|
26130
|
-
console.log("Fallback wraps run one createEffect per expression. Each subscribes to");
|
|
26131
|
-
console.log("whatever signals it happens to read at runtime (possibly none).");
|
|
26132
|
-
console.log("Rewrite the expression as a createMemo to make the dependency static,");
|
|
26133
|
-
console.log("or inline a known-reactive source so the emitter can prove it.");
|
|
27141
|
+
console.log(formatFallbackExplanations2(graph.componentName, fallbacks));
|
|
26134
27142
|
}
|
|
26135
27143
|
var init_debug_fallbacks = __esm({
|
|
26136
27144
|
"src/commands/debug-fallbacks.ts"() {
|
|
@@ -26144,7 +27152,7 @@ var debug_signals_exports = {};
|
|
|
26144
27152
|
__export(debug_signals_exports, {
|
|
26145
27153
|
run: () => run17
|
|
26146
27154
|
});
|
|
26147
|
-
import { readFileSync as
|
|
27155
|
+
import { readFileSync as readFileSync13 } from "fs";
|
|
26148
27156
|
async function run17(args2, ctx2) {
|
|
26149
27157
|
const componentName = args2[0];
|
|
26150
27158
|
if (!componentName) {
|
|
@@ -26161,7 +27169,7 @@ async function run17(args2, ctx2) {
|
|
|
26161
27169
|
for (const p of searched) console.error(` - ${p}`);
|
|
26162
27170
|
process.exit(1);
|
|
26163
27171
|
}
|
|
26164
|
-
const source =
|
|
27172
|
+
const source = readFileSync13(resolved.filePath, "utf-8");
|
|
26165
27173
|
const graph = buildComponentGraph2(source, resolved.filePath, resolved.componentName);
|
|
26166
27174
|
const trace = generateStaticTrace2(graph);
|
|
26167
27175
|
if (ctx2.jsonFlag) {
|
|
@@ -26184,7 +27192,7 @@ var debug_events_exports = {};
|
|
|
26184
27192
|
__export(debug_events_exports, {
|
|
26185
27193
|
run: () => run18
|
|
26186
27194
|
});
|
|
26187
|
-
import { readFileSync as
|
|
27195
|
+
import { readFileSync as readFileSync14 } from "fs";
|
|
26188
27196
|
async function run18(args2, ctx2) {
|
|
26189
27197
|
const componentName = args2[0];
|
|
26190
27198
|
if (!componentName) {
|
|
@@ -26201,7 +27209,7 @@ async function run18(args2, ctx2) {
|
|
|
26201
27209
|
for (const p of searched) console.error(` - ${p}`);
|
|
26202
27210
|
process.exit(1);
|
|
26203
27211
|
}
|
|
26204
|
-
const source =
|
|
27212
|
+
const source = readFileSync14(resolved.filePath, "utf-8");
|
|
26205
27213
|
const summary = buildEventSummary2(source, resolved.filePath, resolved.componentName);
|
|
26206
27214
|
if (ctx2.jsonFlag) {
|
|
26207
27215
|
console.log(JSON.stringify({ componentName: summary.componentName, sourceFile: summary.sourceFile, events: summary.events }, null, 2));
|
|
@@ -26221,7 +27229,7 @@ var debug_loops_exports = {};
|
|
|
26221
27229
|
__export(debug_loops_exports, {
|
|
26222
27230
|
run: () => run19
|
|
26223
27231
|
});
|
|
26224
|
-
import { readFileSync as
|
|
27232
|
+
import { readFileSync as readFileSync15 } from "fs";
|
|
26225
27233
|
async function run19(args2, ctx2) {
|
|
26226
27234
|
const componentName = args2[0];
|
|
26227
27235
|
if (!componentName) {
|
|
@@ -26238,7 +27246,7 @@ async function run19(args2, ctx2) {
|
|
|
26238
27246
|
for (const p of searched) console.error(` - ${p}`);
|
|
26239
27247
|
process.exit(1);
|
|
26240
27248
|
}
|
|
26241
|
-
const source =
|
|
27249
|
+
const source = readFileSync15(resolved.filePath, "utf-8");
|
|
26242
27250
|
const summary = buildLoopSummary2(source, resolved.filePath, resolved.componentName);
|
|
26243
27251
|
if (ctx2.jsonFlag) {
|
|
26244
27252
|
console.log(JSON.stringify(summary, null, 2));
|
|
@@ -26253,6 +27261,101 @@ var init_debug_loops = __esm({
|
|
|
26253
27261
|
}
|
|
26254
27262
|
});
|
|
26255
27263
|
|
|
27264
|
+
// src/commands/debug-why-update.ts
|
|
27265
|
+
var debug_why_update_exports = {};
|
|
27266
|
+
__export(debug_why_update_exports, {
|
|
27267
|
+
run: () => run20
|
|
27268
|
+
});
|
|
27269
|
+
import { readFileSync as readFileSync16 } from "fs";
|
|
27270
|
+
async function run20(args2, ctx2) {
|
|
27271
|
+
const componentName = args2[0];
|
|
27272
|
+
const bindingLabel = args2[1];
|
|
27273
|
+
if (!componentName || !bindingLabel) {
|
|
27274
|
+
console.error("Error: Component name and binding label required.");
|
|
27275
|
+
console.error("Usage: bf debug why-update <component> <binding> [--json]");
|
|
27276
|
+
console.error(' binding: attribute name (e.g. "style"), slot ID (e.g. "s0"),');
|
|
27277
|
+
console.error(' or component prop (e.g. "Button.disabled")');
|
|
27278
|
+
process.exit(1);
|
|
27279
|
+
}
|
|
27280
|
+
const { buildWhyUpdate: buildWhyUpdate2, formatWhyUpdate: formatWhyUpdate2, buildComponentGraph: buildComponentGraph2 } = await Promise.resolve().then(() => (init_src2(), src_exports));
|
|
27281
|
+
const searched = [];
|
|
27282
|
+
const resolved = resolveComponentSource(componentName, ctx2, searched);
|
|
27283
|
+
if (!resolved) {
|
|
27284
|
+
console.error(`Error: Cannot find component "${componentName}".`);
|
|
27285
|
+
console.error("Looked in:");
|
|
27286
|
+
for (const p of searched) console.error(` - ${p}`);
|
|
27287
|
+
process.exit(1);
|
|
27288
|
+
}
|
|
27289
|
+
const source = readFileSync16(resolved.filePath, "utf-8");
|
|
27290
|
+
const result = buildWhyUpdate2(source, resolved.filePath, bindingLabel, resolved.componentName);
|
|
27291
|
+
if (!result) {
|
|
27292
|
+
const graph = buildComponentGraph2(source, resolved.filePath, resolved.componentName);
|
|
27293
|
+
console.error(`Error: Binding "${bindingLabel}" not found in ${graph.componentName}.`);
|
|
27294
|
+
const available = graph.domBindings.map(
|
|
27295
|
+
(d) => d.type === "attribute" ? d.label : d.slotId
|
|
27296
|
+
);
|
|
27297
|
+
if (available.length > 0) {
|
|
27298
|
+
console.error(`Available bindings: ${[...new Set(available)].join(", ")}`);
|
|
27299
|
+
}
|
|
27300
|
+
process.exit(1);
|
|
27301
|
+
}
|
|
27302
|
+
if (result.ambiguous) {
|
|
27303
|
+
console.error(`Error: "${bindingLabel}" matches multiple bindings. Disambiguate with a slot ID:`);
|
|
27304
|
+
for (const m of result.ambiguous) {
|
|
27305
|
+
console.error(` ${m.slotId} (${m.label})`);
|
|
27306
|
+
}
|
|
27307
|
+
process.exit(1);
|
|
27308
|
+
}
|
|
27309
|
+
if (ctx2.jsonFlag) {
|
|
27310
|
+
console.log(JSON.stringify(result, null, 2));
|
|
27311
|
+
return;
|
|
27312
|
+
}
|
|
27313
|
+
console.log(formatWhyUpdate2(result));
|
|
27314
|
+
}
|
|
27315
|
+
var init_debug_why_update = __esm({
|
|
27316
|
+
"src/commands/debug-why-update.ts"() {
|
|
27317
|
+
"use strict";
|
|
27318
|
+
init_resolve_source();
|
|
27319
|
+
}
|
|
27320
|
+
});
|
|
27321
|
+
|
|
27322
|
+
// src/commands/debug-summary.ts
|
|
27323
|
+
var debug_summary_exports = {};
|
|
27324
|
+
__export(debug_summary_exports, {
|
|
27325
|
+
run: () => run21
|
|
27326
|
+
});
|
|
27327
|
+
import { readFileSync as readFileSync17 } from "fs";
|
|
27328
|
+
async function run21(args2, ctx2) {
|
|
27329
|
+
const componentName = args2[0];
|
|
27330
|
+
if (!componentName) {
|
|
27331
|
+
console.error("Error: Component name required.");
|
|
27332
|
+
console.error("Usage: bf debug summary <component> [--json]");
|
|
27333
|
+
process.exit(1);
|
|
27334
|
+
}
|
|
27335
|
+
const { buildComponentSummary: buildComponentSummary2, formatComponentSummary: formatComponentSummary2 } = await Promise.resolve().then(() => (init_src2(), src_exports));
|
|
27336
|
+
const searched = [];
|
|
27337
|
+
const resolved = resolveComponentSource(componentName, ctx2, searched);
|
|
27338
|
+
if (!resolved) {
|
|
27339
|
+
console.error(`Error: Cannot find component "${componentName}".`);
|
|
27340
|
+
console.error("Looked in:");
|
|
27341
|
+
for (const p of searched) console.error(` - ${p}`);
|
|
27342
|
+
process.exit(1);
|
|
27343
|
+
}
|
|
27344
|
+
const source = readFileSync17(resolved.filePath, "utf-8");
|
|
27345
|
+
const summary = buildComponentSummary2(source, resolved.filePath, resolved.componentName);
|
|
27346
|
+
if (ctx2.jsonFlag) {
|
|
27347
|
+
console.log(JSON.stringify(summary, null, 2));
|
|
27348
|
+
return;
|
|
27349
|
+
}
|
|
27350
|
+
console.log(formatComponentSummary2(summary));
|
|
27351
|
+
}
|
|
27352
|
+
var init_debug_summary = __esm({
|
|
27353
|
+
"src/commands/debug-summary.ts"() {
|
|
27354
|
+
"use strict";
|
|
27355
|
+
init_resolve_source();
|
|
27356
|
+
}
|
|
27357
|
+
});
|
|
27358
|
+
|
|
26256
27359
|
// src/context.ts
|
|
26257
27360
|
import { existsSync as existsSync2 } from "fs";
|
|
26258
27361
|
import path from "path";
|
|
@@ -26272,9 +27375,9 @@ function findProjectConfig(startDir) {
|
|
|
26272
27375
|
let dir = path.resolve(startDir);
|
|
26273
27376
|
const { root: fsRoot } = path.parse(dir);
|
|
26274
27377
|
while (true) {
|
|
26275
|
-
const
|
|
26276
|
-
if (existsSync2(
|
|
26277
|
-
return { dir, tsConfigPath:
|
|
27378
|
+
const ts20 = path.join(dir, "barefoot.config.ts");
|
|
27379
|
+
if (existsSync2(ts20)) {
|
|
27380
|
+
return { dir, tsConfigPath: ts20 };
|
|
26278
27381
|
}
|
|
26279
27382
|
if (dir === fsRoot) return null;
|
|
26280
27383
|
dir = path.dirname(dir);
|
|
@@ -26350,6 +27453,8 @@ Debug:
|
|
|
26350
27453
|
debug trace <component> <signal> Trace update propagation for a signal/memo
|
|
26351
27454
|
debug events <component> Show event handlers and their update paths
|
|
26352
27455
|
debug loops <component> Show loop bindings grouped by source collection
|
|
27456
|
+
debug why-update <component> <binding> Explain why a binding updates
|
|
27457
|
+
debug summary <component> Show hydration and size summary
|
|
26353
27458
|
debug fallbacks <component> Show wrap-by-default fallback bindings (#937)
|
|
26354
27459
|
debug signals <component> Show signal initialization trace
|
|
26355
27460
|
|
|
@@ -26369,60 +27474,60 @@ Workflow:
|
|
|
26369
27474
|
}
|
|
26370
27475
|
switch (command) {
|
|
26371
27476
|
case "build": {
|
|
26372
|
-
const { run:
|
|
26373
|
-
await
|
|
27477
|
+
const { run: run22 } = await Promise.resolve().then(() => (init_build2(), build_exports));
|
|
27478
|
+
await run22(filteredArgs.slice(1), ctx);
|
|
26374
27479
|
break;
|
|
26375
27480
|
}
|
|
26376
27481
|
case "init": {
|
|
26377
|
-
const { run:
|
|
26378
|
-
await
|
|
27482
|
+
const { run: run22 } = await Promise.resolve().then(() => (init_init(), init_exports));
|
|
27483
|
+
await run22(filteredArgs.slice(1), ctx);
|
|
26379
27484
|
break;
|
|
26380
27485
|
}
|
|
26381
27486
|
case "add": {
|
|
26382
|
-
const { run:
|
|
26383
|
-
await
|
|
27487
|
+
const { run: run22 } = await Promise.resolve().then(() => (init_add(), add_exports));
|
|
27488
|
+
await run22(filteredArgs.slice(1), ctx);
|
|
26384
27489
|
break;
|
|
26385
27490
|
}
|
|
26386
27491
|
case "search": {
|
|
26387
|
-
const { run:
|
|
26388
|
-
await
|
|
27492
|
+
const { run: run22 } = await Promise.resolve().then(() => (init_search(), search_exports));
|
|
27493
|
+
await run22(filteredArgs.slice(1), ctx);
|
|
26389
27494
|
break;
|
|
26390
27495
|
}
|
|
26391
27496
|
case "docs": {
|
|
26392
|
-
const { run:
|
|
26393
|
-
|
|
27497
|
+
const { run: run22 } = await Promise.resolve().then(() => (init_docs(), docs_exports));
|
|
27498
|
+
run22(filteredArgs.slice(1), ctx);
|
|
26394
27499
|
break;
|
|
26395
27500
|
}
|
|
26396
27501
|
case "guide": {
|
|
26397
|
-
const { run:
|
|
26398
|
-
|
|
27502
|
+
const { run: run22 } = await Promise.resolve().then(() => (init_guide(), guide_exports));
|
|
27503
|
+
run22(filteredArgs.slice(1), ctx);
|
|
26399
27504
|
break;
|
|
26400
27505
|
}
|
|
26401
27506
|
case "preview": {
|
|
26402
|
-
const { run:
|
|
26403
|
-
await
|
|
27507
|
+
const { run: run22 } = await Promise.resolve().then(() => (init_preview(), preview_exports));
|
|
27508
|
+
await run22(filteredArgs.slice(1), ctx);
|
|
26404
27509
|
break;
|
|
26405
27510
|
}
|
|
26406
27511
|
case "tokens": {
|
|
26407
27512
|
if (sub === "apply") {
|
|
26408
|
-
const { run:
|
|
26409
|
-
await
|
|
27513
|
+
const { run: run22 } = await Promise.resolve().then(() => (init_tokens_apply(), tokens_apply_exports));
|
|
27514
|
+
await run22(rest, ctx);
|
|
26410
27515
|
} else {
|
|
26411
|
-
const { run:
|
|
26412
|
-
await
|
|
27516
|
+
const { run: run22 } = await Promise.resolve().then(() => (init_tokens2(), tokens_exports));
|
|
27517
|
+
await run22(filteredArgs.slice(1), ctx);
|
|
26413
27518
|
}
|
|
26414
27519
|
break;
|
|
26415
27520
|
}
|
|
26416
27521
|
case "gen": {
|
|
26417
27522
|
if (sub === "component") {
|
|
26418
|
-
const { run:
|
|
26419
|
-
|
|
27523
|
+
const { run: run22 } = await Promise.resolve().then(() => (init_gen_component(), gen_component_exports));
|
|
27524
|
+
run22(rest, ctx);
|
|
26420
27525
|
} else if (sub === "test") {
|
|
26421
|
-
const { run:
|
|
26422
|
-
|
|
27526
|
+
const { run: run22 } = await Promise.resolve().then(() => (init_gen_test(), gen_test_exports));
|
|
27527
|
+
run22(rest, ctx);
|
|
26423
27528
|
} else if (sub === "preview") {
|
|
26424
|
-
const { run:
|
|
26425
|
-
await
|
|
27529
|
+
const { run: run22 } = await Promise.resolve().then(() => (init_gen_preview(), gen_preview_exports));
|
|
27530
|
+
await run22(rest, ctx);
|
|
26426
27531
|
} else {
|
|
26427
27532
|
console.error("Usage: bf gen <component|test|preview> ...");
|
|
26428
27533
|
process.exit(1);
|
|
@@ -26431,33 +27536,39 @@ switch (command) {
|
|
|
26431
27536
|
}
|
|
26432
27537
|
case "debug": {
|
|
26433
27538
|
if (sub === "graph") {
|
|
26434
|
-
const { run:
|
|
26435
|
-
await
|
|
27539
|
+
const { run: run22 } = await Promise.resolve().then(() => (init_debug_graph(), debug_graph_exports));
|
|
27540
|
+
await run22(rest, ctx);
|
|
26436
27541
|
} else if (sub === "trace") {
|
|
26437
|
-
const { run:
|
|
26438
|
-
await
|
|
27542
|
+
const { run: run22 } = await Promise.resolve().then(() => (init_debug_trace(), debug_trace_exports));
|
|
27543
|
+
await run22(rest, ctx);
|
|
26439
27544
|
} else if (sub === "fallbacks") {
|
|
26440
|
-
const { run:
|
|
26441
|
-
await
|
|
27545
|
+
const { run: run22 } = await Promise.resolve().then(() => (init_debug_fallbacks(), debug_fallbacks_exports));
|
|
27546
|
+
await run22(rest, ctx);
|
|
26442
27547
|
} else if (sub === "signals") {
|
|
26443
|
-
const { run:
|
|
26444
|
-
await
|
|
27548
|
+
const { run: run22 } = await Promise.resolve().then(() => (init_debug_signals(), debug_signals_exports));
|
|
27549
|
+
await run22(rest, ctx);
|
|
26445
27550
|
} else if (sub === "events") {
|
|
26446
|
-
const { run:
|
|
26447
|
-
await
|
|
27551
|
+
const { run: run22 } = await Promise.resolve().then(() => (init_debug_events(), debug_events_exports));
|
|
27552
|
+
await run22(rest, ctx);
|
|
26448
27553
|
} else if (sub === "loops") {
|
|
26449
|
-
const { run:
|
|
26450
|
-
await
|
|
27554
|
+
const { run: run22 } = await Promise.resolve().then(() => (init_debug_loops(), debug_loops_exports));
|
|
27555
|
+
await run22(rest, ctx);
|
|
27556
|
+
} else if (sub === "why-update") {
|
|
27557
|
+
const { run: run22 } = await Promise.resolve().then(() => (init_debug_why_update(), debug_why_update_exports));
|
|
27558
|
+
await run22(rest, ctx);
|
|
27559
|
+
} else if (sub === "summary") {
|
|
27560
|
+
const { run: run22 } = await Promise.resolve().then(() => (init_debug_summary(), debug_summary_exports));
|
|
27561
|
+
await run22(rest, ctx);
|
|
26451
27562
|
} else {
|
|
26452
|
-
console.error("Usage: bf debug <graph|trace|fallbacks|signals|events|loops> ...");
|
|
27563
|
+
console.error("Usage: bf debug <graph|trace|fallbacks|signals|events|loops|why-update|summary> ...");
|
|
26453
27564
|
process.exit(1);
|
|
26454
27565
|
}
|
|
26455
27566
|
break;
|
|
26456
27567
|
}
|
|
26457
27568
|
case "meta": {
|
|
26458
27569
|
if (sub === "extract") {
|
|
26459
|
-
const { run:
|
|
26460
|
-
await
|
|
27570
|
+
const { run: run22 } = await Promise.resolve().then(() => (init_meta_extract(), meta_extract_exports));
|
|
27571
|
+
await run22(rest, ctx);
|
|
26461
27572
|
} else {
|
|
26462
27573
|
console.error("Usage: bf meta extract");
|
|
26463
27574
|
process.exit(1);
|