@barefootjs/hono 0.5.0 → 0.5.2
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/adapter/hono-adapter.d.ts +9 -2
- package/dist/adapter/hono-adapter.d.ts.map +1 -1
- package/dist/adapter/index.js +21 -6
- package/dist/build.js +21 -6
- package/dist/index.js +21 -6
- package/dist/test-render.d.ts +10 -0
- package/dist/test-render.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/adapter/hono-adapter.ts +52 -6
- package/src/test-render.ts +116 -16
|
@@ -78,7 +78,7 @@ export declare class HonoAdapter extends JsxAdapter implements IRNodeEmitter<Hon
|
|
|
78
78
|
emitElement(node: IRElement, ctx: HonoRenderCtx, _emit: EmitIRNode<HonoRenderCtx>): string;
|
|
79
79
|
emitText(node: IRText): string;
|
|
80
80
|
emitExpression(node: IRExpression): string;
|
|
81
|
-
emitConditional(node: IRConditional,
|
|
81
|
+
emitConditional(node: IRConditional, ctx: HonoRenderCtx, _emit: EmitIRNode<HonoRenderCtx>): string;
|
|
82
82
|
emitLoop(node: IRLoop, _ctx: HonoRenderCtx, _emit: EmitIRNode<HonoRenderCtx>): string;
|
|
83
83
|
emitComponent(node: IRComponent, ctx: HonoRenderCtx, _emit: EmitIRNode<HonoRenderCtx>): string;
|
|
84
84
|
emitFragment(node: IRFragment, _ctx: HonoRenderCtx, _emit: EmitIRNode<HonoRenderCtx>): string;
|
|
@@ -91,7 +91,14 @@ export declare class HonoAdapter extends JsxAdapter implements IRNodeEmitter<Hon
|
|
|
91
91
|
}): string;
|
|
92
92
|
private renderText;
|
|
93
93
|
renderExpression(expr: IRExpression): string;
|
|
94
|
-
renderConditional(cond: IRConditional): string;
|
|
94
|
+
renderConditional(cond: IRConditional, ctx?: HonoRenderCtx): string;
|
|
95
|
+
/**
|
|
96
|
+
* Like the base `renderNodeRaw`, but threads a render ctx through to
|
|
97
|
+
* `renderNode` so a conditional branch can mark its element as a loop item
|
|
98
|
+
* root (#1665). The `null` / `undefined` expression branch carries no
|
|
99
|
+
* element, so it short-circuits exactly as the base helper does.
|
|
100
|
+
*/
|
|
101
|
+
private renderNodeRawCtx;
|
|
95
102
|
private wrapWithCondMarker;
|
|
96
103
|
renderLoop(loop: IRLoop): string;
|
|
97
104
|
private renderChildrenInLoop;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"hono-adapter.d.ts","sourceRoot":"","sources":["../../src/adapter/hono-adapter.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EACL,KAAK,WAAW,EAChB,KAAK,MAAM,EACX,KAAK,SAAS,EACd,KAAK,MAAM,EACX,KAAK,YAAY,EACjB,KAAK,aAAa,EAClB,KAAK,MAAM,EACX,KAAK,WAAW,EAChB,KAAK,UAAU,EACf,KAAK,aAAa,EAClB,KAAK,UAAU,EACf,KAAK,OAAO,EACZ,KAAK,MAAM,EAIX,KAAK,sBAAsB,EAC3B,KAAK,aAAa,EAElB,KAAK,gBAAgB,EACrB,KAAK,aAAa,EAClB,KAAK,UAAU,EAEf,UAAU,EAMX,MAAM,iBAAiB,CAAA;AAExB;;;;;GAKG;AACH,KAAK,aAAa,GAAG;IACnB,uBAAuB,CAAC,EAAE,OAAO,CAAA;IACjC,YAAY,CAAC,EAAE,OAAO,CAAA;IACtB,cAAc,CAAC,EAAE,OAAO,CAAA;CACzB,CAAA;AAGD,MAAM,WAAW,kBAAkB;IACjC;;;OAGG;IACH,gBAAgB,CAAC,EAAE,MAAM,CAAA;IAEzB;;OAEG;IACH,cAAc,CAAC,EAAE,MAAM,CAAA;IAEvB;;;;OAIG;IACH,gBAAgB,CAAC,EAAE,MAAM,CAAA;IAEzB;;;;;;OAMG;IACH,IAAI,CAAC,EAAE,MAAM,CAAA;CACd;AAuBD,qBAAa,WAAY,SAAQ,UAAW,YAAW,aAAa,CAAC,aAAa,CAAC;IACjF,IAAI,SAAS;IACb,SAAS,SAAS;IAClB,gBAAgB,SAAiC;IAGjD,kBAAkB,EAAG,WAAW,CAAS;IAmBzC,mBAAmB,QAAO,OAAO,CAAQ;IAEzC,SAAS,CAAC,SAAS,EAAE,gBAAgB,CAA0B;IAE/D,OAAO,CAAC,OAAO,CAAoB;IACnC,OAAO,CAAC,iBAAiB,CAAiB;IAC1C,OAAO,CAAC,sBAAsB,CAAiB;IAC/C,OAAO,CAAC,wBAAwB,CAAiB;IACjD;;;;;;;OAOG;IACH,OAAO,CAAC,qBAAqB,CAAC,CAAgC;IAC9D,uFAAuF;IACvF,OAAO,CAAC,YAAY,CAAmD;IAEvE,YAAY,OAAO,GAAE,kBAAuB,EAQ3C;IAED,QAAQ,CAAC,EAAE,EAAE,WAAW,EAAE,OAAO,CAAC,EAAE,sBAAsB,GAAG,aAAa,CAyCzE;IAED,OAAO,CAAC,kCAAkC;IAkB1C,OAAO,CAAC,eAAe;IAyDvB,aAAa,CAAC,EAAE,EAAE,WAAW,EAAE,aAAa,CAAC,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CA+EpE;IAED,OAAO,CAAC,gBAAgB;IAWxB,OAAO,CAAC,iBAAiB;
|
|
1
|
+
{"version":3,"file":"hono-adapter.d.ts","sourceRoot":"","sources":["../../src/adapter/hono-adapter.ts"],"names":[],"mappings":"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"}
|
package/dist/adapter/index.js
CHANGED
|
@@ -260,9 +260,13 @@ export default ${this.componentName}` : "";
|
|
|
260
260
|
}
|
|
261
261
|
fullPropsDestructure = `{ ${parts.join(", ")} }`;
|
|
262
262
|
}
|
|
263
|
+
const hasRequiredProps = ir.metadata.propsParams.some((p) => !p.optional && p.defaultValue === undefined && !p.isRest);
|
|
264
|
+
const wantsNoArgDefault = propsObjectName ? !propsTypeName : !hasRequiredProps;
|
|
265
|
+
const propsTypeExpr = typeAnnotation.replace(/^:\s*/, "");
|
|
266
|
+
const noArgDefault = wantsNoArgDefault ? ` = {} as ${propsTypeExpr}` : "";
|
|
263
267
|
const lines = [];
|
|
264
268
|
const exportPrefix = ir.metadata.isExported === false ? "" : "export ";
|
|
265
|
-
lines.push(`${exportPrefix}function ${name}(${fullPropsDestructure}${typeAnnotation}) {`);
|
|
269
|
+
lines.push(`${exportPrefix}function ${name}(${fullPropsDestructure}${typeAnnotation}${noArgDefault}) {`);
|
|
266
270
|
if (propsExtraction) {
|
|
267
271
|
lines.push(propsExtraction);
|
|
268
272
|
}
|
|
@@ -313,8 +317,8 @@ export default ${this.componentName}` : "";
|
|
|
313
317
|
emitExpression(node) {
|
|
314
318
|
return this.renderExpression(node);
|
|
315
319
|
}
|
|
316
|
-
emitConditional(node,
|
|
317
|
-
return this.renderConditional(node);
|
|
320
|
+
emitConditional(node, ctx, _emit) {
|
|
321
|
+
return this.renderConditional(node, ctx);
|
|
318
322
|
}
|
|
319
323
|
emitLoop(node, _ctx, _emit) {
|
|
320
324
|
return this.renderLoop(node);
|
|
@@ -401,12 +405,13 @@ export default ${this.componentName}` : "";
|
|
|
401
405
|
}
|
|
402
406
|
return `{${expr.expr}}`;
|
|
403
407
|
}
|
|
404
|
-
renderConditional(cond) {
|
|
408
|
+
renderConditional(cond, ctx) {
|
|
405
409
|
if (cond.clientOnly && cond.slotId) {
|
|
406
410
|
return `{bfComment("cond-start:${cond.slotId}")}{bfComment("cond-end:${cond.slotId}")}`;
|
|
407
411
|
}
|
|
408
|
-
const
|
|
409
|
-
|
|
412
|
+
const branchCtx = ctx?.isLoopItemRoot ? { isLoopItemRoot: true } : undefined;
|
|
413
|
+
const whenTrue = this.renderNodeRawCtx(cond.whenTrue, branchCtx);
|
|
414
|
+
let whenFalse = this.renderNodeRawCtx(cond.whenFalse, branchCtx);
|
|
410
415
|
if (!whenFalse || whenFalse === "" || whenFalse === "null") {
|
|
411
416
|
whenFalse = "null";
|
|
412
417
|
}
|
|
@@ -417,6 +422,14 @@ export default ${this.componentName}` : "";
|
|
|
417
422
|
}
|
|
418
423
|
return `{${cond.condition} ? ${whenTrue} : ${whenFalse}}`;
|
|
419
424
|
}
|
|
425
|
+
renderNodeRawCtx(node, ctx) {
|
|
426
|
+
if (node.type === "expression") {
|
|
427
|
+
if (node.expr === "null" || node.expr === "undefined")
|
|
428
|
+
return "null";
|
|
429
|
+
return node.expr;
|
|
430
|
+
}
|
|
431
|
+
return this.renderNode(node, ctx);
|
|
432
|
+
}
|
|
420
433
|
wrapWithCondMarker(node, content, condId) {
|
|
421
434
|
if (node.type === "component") {
|
|
422
435
|
return `<>{bfComment("cond-start:${condId}")}${content}{bfComment("cond-end:${condId}")}</>`;
|
|
@@ -447,6 +460,8 @@ export default ${this.componentName}` : "";
|
|
|
447
460
|
let safeChildren = children.startsWith("{") ? `<>${children}</>` : children;
|
|
448
461
|
if (loop.bodyIsMultiRoot) {
|
|
449
462
|
safeChildren = `<>{bfComment('bf-loop-i')}${children}</>`;
|
|
463
|
+
} else if (loop.bodyIsItemConditional && loop.key) {
|
|
464
|
+
safeChildren = `<>{bfComment('loop-i:' + String(${loop.key}))}${children}</>`;
|
|
450
465
|
}
|
|
451
466
|
let chainedArray = applyHonoLoopChain(loop);
|
|
452
467
|
const iterMethod = loop.method ?? "map";
|
package/dist/build.js
CHANGED
|
@@ -260,9 +260,13 @@ export default ${this.componentName}` : "";
|
|
|
260
260
|
}
|
|
261
261
|
fullPropsDestructure = `{ ${parts.join(", ")} }`;
|
|
262
262
|
}
|
|
263
|
+
const hasRequiredProps = ir.metadata.propsParams.some((p) => !p.optional && p.defaultValue === undefined && !p.isRest);
|
|
264
|
+
const wantsNoArgDefault = propsObjectName ? !propsTypeName : !hasRequiredProps;
|
|
265
|
+
const propsTypeExpr = typeAnnotation.replace(/^:\s*/, "");
|
|
266
|
+
const noArgDefault = wantsNoArgDefault ? ` = {} as ${propsTypeExpr}` : "";
|
|
263
267
|
const lines = [];
|
|
264
268
|
const exportPrefix = ir.metadata.isExported === false ? "" : "export ";
|
|
265
|
-
lines.push(`${exportPrefix}function ${name}(${fullPropsDestructure}${typeAnnotation}) {`);
|
|
269
|
+
lines.push(`${exportPrefix}function ${name}(${fullPropsDestructure}${typeAnnotation}${noArgDefault}) {`);
|
|
266
270
|
if (propsExtraction) {
|
|
267
271
|
lines.push(propsExtraction);
|
|
268
272
|
}
|
|
@@ -313,8 +317,8 @@ export default ${this.componentName}` : "";
|
|
|
313
317
|
emitExpression(node) {
|
|
314
318
|
return this.renderExpression(node);
|
|
315
319
|
}
|
|
316
|
-
emitConditional(node,
|
|
317
|
-
return this.renderConditional(node);
|
|
320
|
+
emitConditional(node, ctx, _emit) {
|
|
321
|
+
return this.renderConditional(node, ctx);
|
|
318
322
|
}
|
|
319
323
|
emitLoop(node, _ctx, _emit) {
|
|
320
324
|
return this.renderLoop(node);
|
|
@@ -401,12 +405,13 @@ export default ${this.componentName}` : "";
|
|
|
401
405
|
}
|
|
402
406
|
return `{${expr.expr}}`;
|
|
403
407
|
}
|
|
404
|
-
renderConditional(cond) {
|
|
408
|
+
renderConditional(cond, ctx) {
|
|
405
409
|
if (cond.clientOnly && cond.slotId) {
|
|
406
410
|
return `{bfComment("cond-start:${cond.slotId}")}{bfComment("cond-end:${cond.slotId}")}`;
|
|
407
411
|
}
|
|
408
|
-
const
|
|
409
|
-
|
|
412
|
+
const branchCtx = ctx?.isLoopItemRoot ? { isLoopItemRoot: true } : undefined;
|
|
413
|
+
const whenTrue = this.renderNodeRawCtx(cond.whenTrue, branchCtx);
|
|
414
|
+
let whenFalse = this.renderNodeRawCtx(cond.whenFalse, branchCtx);
|
|
410
415
|
if (!whenFalse || whenFalse === "" || whenFalse === "null") {
|
|
411
416
|
whenFalse = "null";
|
|
412
417
|
}
|
|
@@ -417,6 +422,14 @@ export default ${this.componentName}` : "";
|
|
|
417
422
|
}
|
|
418
423
|
return `{${cond.condition} ? ${whenTrue} : ${whenFalse}}`;
|
|
419
424
|
}
|
|
425
|
+
renderNodeRawCtx(node, ctx) {
|
|
426
|
+
if (node.type === "expression") {
|
|
427
|
+
if (node.expr === "null" || node.expr === "undefined")
|
|
428
|
+
return "null";
|
|
429
|
+
return node.expr;
|
|
430
|
+
}
|
|
431
|
+
return this.renderNode(node, ctx);
|
|
432
|
+
}
|
|
420
433
|
wrapWithCondMarker(node, content, condId) {
|
|
421
434
|
if (node.type === "component") {
|
|
422
435
|
return `<>{bfComment("cond-start:${condId}")}${content}{bfComment("cond-end:${condId}")}</>`;
|
|
@@ -447,6 +460,8 @@ export default ${this.componentName}` : "";
|
|
|
447
460
|
let safeChildren = children.startsWith("{") ? `<>${children}</>` : children;
|
|
448
461
|
if (loop.bodyIsMultiRoot) {
|
|
449
462
|
safeChildren = `<>{bfComment('bf-loop-i')}${children}</>`;
|
|
463
|
+
} else if (loop.bodyIsItemConditional && loop.key) {
|
|
464
|
+
safeChildren = `<>{bfComment('loop-i:' + String(${loop.key}))}${children}</>`;
|
|
450
465
|
}
|
|
451
466
|
let chainedArray = applyHonoLoopChain(loop);
|
|
452
467
|
const iterMethod = loop.method ?? "map";
|
package/dist/index.js
CHANGED
|
@@ -260,9 +260,13 @@ export default ${this.componentName}` : "";
|
|
|
260
260
|
}
|
|
261
261
|
fullPropsDestructure = `{ ${parts.join(", ")} }`;
|
|
262
262
|
}
|
|
263
|
+
const hasRequiredProps = ir.metadata.propsParams.some((p) => !p.optional && p.defaultValue === undefined && !p.isRest);
|
|
264
|
+
const wantsNoArgDefault = propsObjectName ? !propsTypeName : !hasRequiredProps;
|
|
265
|
+
const propsTypeExpr = typeAnnotation.replace(/^:\s*/, "");
|
|
266
|
+
const noArgDefault = wantsNoArgDefault ? ` = {} as ${propsTypeExpr}` : "";
|
|
263
267
|
const lines = [];
|
|
264
268
|
const exportPrefix = ir.metadata.isExported === false ? "" : "export ";
|
|
265
|
-
lines.push(`${exportPrefix}function ${name}(${fullPropsDestructure}${typeAnnotation}) {`);
|
|
269
|
+
lines.push(`${exportPrefix}function ${name}(${fullPropsDestructure}${typeAnnotation}${noArgDefault}) {`);
|
|
266
270
|
if (propsExtraction) {
|
|
267
271
|
lines.push(propsExtraction);
|
|
268
272
|
}
|
|
@@ -313,8 +317,8 @@ export default ${this.componentName}` : "";
|
|
|
313
317
|
emitExpression(node) {
|
|
314
318
|
return this.renderExpression(node);
|
|
315
319
|
}
|
|
316
|
-
emitConditional(node,
|
|
317
|
-
return this.renderConditional(node);
|
|
320
|
+
emitConditional(node, ctx, _emit) {
|
|
321
|
+
return this.renderConditional(node, ctx);
|
|
318
322
|
}
|
|
319
323
|
emitLoop(node, _ctx, _emit) {
|
|
320
324
|
return this.renderLoop(node);
|
|
@@ -401,12 +405,13 @@ export default ${this.componentName}` : "";
|
|
|
401
405
|
}
|
|
402
406
|
return `{${expr.expr}}`;
|
|
403
407
|
}
|
|
404
|
-
renderConditional(cond) {
|
|
408
|
+
renderConditional(cond, ctx) {
|
|
405
409
|
if (cond.clientOnly && cond.slotId) {
|
|
406
410
|
return `{bfComment("cond-start:${cond.slotId}")}{bfComment("cond-end:${cond.slotId}")}`;
|
|
407
411
|
}
|
|
408
|
-
const
|
|
409
|
-
|
|
412
|
+
const branchCtx = ctx?.isLoopItemRoot ? { isLoopItemRoot: true } : undefined;
|
|
413
|
+
const whenTrue = this.renderNodeRawCtx(cond.whenTrue, branchCtx);
|
|
414
|
+
let whenFalse = this.renderNodeRawCtx(cond.whenFalse, branchCtx);
|
|
410
415
|
if (!whenFalse || whenFalse === "" || whenFalse === "null") {
|
|
411
416
|
whenFalse = "null";
|
|
412
417
|
}
|
|
@@ -417,6 +422,14 @@ export default ${this.componentName}` : "";
|
|
|
417
422
|
}
|
|
418
423
|
return `{${cond.condition} ? ${whenTrue} : ${whenFalse}}`;
|
|
419
424
|
}
|
|
425
|
+
renderNodeRawCtx(node, ctx) {
|
|
426
|
+
if (node.type === "expression") {
|
|
427
|
+
if (node.expr === "null" || node.expr === "undefined")
|
|
428
|
+
return "null";
|
|
429
|
+
return node.expr;
|
|
430
|
+
}
|
|
431
|
+
return this.renderNode(node, ctx);
|
|
432
|
+
}
|
|
420
433
|
wrapWithCondMarker(node, content, condId) {
|
|
421
434
|
if (node.type === "component") {
|
|
422
435
|
return `<>{bfComment("cond-start:${condId}")}${content}{bfComment("cond-end:${condId}")}</>`;
|
|
@@ -447,6 +460,8 @@ export default ${this.componentName}` : "";
|
|
|
447
460
|
let safeChildren = children.startsWith("{") ? `<>${children}</>` : children;
|
|
448
461
|
if (loop.bodyIsMultiRoot) {
|
|
449
462
|
safeChildren = `<>{bfComment('bf-loop-i')}${children}</>`;
|
|
463
|
+
} else if (loop.bodyIsItemConditional && loop.key) {
|
|
464
|
+
safeChildren = `<>{bfComment('loop-i:' + String(${loop.key}))}${children}</>`;
|
|
450
465
|
}
|
|
451
466
|
let chainedArray = applyHonoLoopChain(loop);
|
|
452
467
|
const iterMethod = loop.method ?? "map";
|
package/dist/test-render.d.ts
CHANGED
|
@@ -14,6 +14,16 @@ export interface RenderOptions {
|
|
|
14
14
|
props?: Record<string, unknown>;
|
|
15
15
|
/** Additional component files (filename → source) */
|
|
16
16
|
components?: Record<string, string>;
|
|
17
|
+
/**
|
|
18
|
+
* Pre-compiled child component modules (import specifier → absolute
|
|
19
|
+
* module path) — #1467 Phase 2a. When the parent imports one of these
|
|
20
|
+
* specifiers, the import is *re-anchored* to the given module path
|
|
21
|
+
* (kept as a real ESM import) instead of having the child inlined via
|
|
22
|
+
* `components`. The module is a committed, export-intact marked
|
|
23
|
+
* template, so SSR loads it through the module system — no export
|
|
24
|
+
* stripping. Takes precedence over `components` for the same key.
|
|
25
|
+
*/
|
|
26
|
+
componentModules?: Record<string, string>;
|
|
17
27
|
/**
|
|
18
28
|
* Explicit component to render when the source declares multiple
|
|
19
29
|
* exports. When omitted, the first function-valued export in
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"test-render.d.ts","sourceRoot":"","sources":["../src/test-render.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAGH,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAA;
|
|
1
|
+
{"version":3,"file":"test-render.d.ts","sourceRoot":"","sources":["../src/test-render.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAGH,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAA;AAStD,MAAM,WAAW,aAAa;IAC5B,sBAAsB;IACtB,MAAM,EAAE,MAAM,CAAA;IACd,8BAA8B;IAC9B,OAAO,EAAE,eAAe,CAAA;IACxB,iCAAiC;IACjC,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;IAC/B,qDAAqD;IACrD,UAAU,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;IACnC;;;;;;;;OAQG;IACH,gBAAgB,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;IACzC;;;;;;;OAOG;IACH,aAAa,CAAC,EAAE,MAAM,CAAA;CACvB;AA2BD,wBAAsB,mBAAmB,CAAC,OAAO,EAAE,aAAa,GAAG,OAAO,CAAC,MAAM,CAAC,CA0KjF"}
|
package/package.json
CHANGED
|
@@ -510,11 +510,28 @@ export class HonoAdapter extends JsxAdapter implements IRNodeEmitter<HonoRenderC
|
|
|
510
510
|
fullPropsDestructure = `{ ${parts.join(', ')} }`
|
|
511
511
|
}
|
|
512
512
|
|
|
513
|
+
// Default the props param to `{}` when the component has no required
|
|
514
|
+
// props, so a bare no-arg call (`Foo()`) doesn't crash on destructuring
|
|
515
|
+
// `undefined`. This makes a JSX-returning arrow hoisted from an
|
|
516
|
+
// object-literal value (e.g. `THEME_LOGOS[id]()`) renderable at SSR
|
|
517
|
+
// (#1663). `hasRequiredProps` ignores props that carry a destructuring
|
|
518
|
+
// default, but the declared props type may still mark that field
|
|
519
|
+
// required — so a bare `= {}` would fail `tsc`. Assert the default to the
|
|
520
|
+
// param's own annotated type (`{} as T`); the destructuring defaults
|
|
521
|
+
// supply the values at runtime. The SolidJS-style (`propsObjectName`)
|
|
522
|
+
// branch opts in whenever the annotation is satisfiable by `{} as T`.
|
|
523
|
+
const hasRequiredProps = ir.metadata.propsParams.some(
|
|
524
|
+
(p: ParamInfo) => !p.optional && p.defaultValue === undefined && !p.isRest,
|
|
525
|
+
)
|
|
526
|
+
const wantsNoArgDefault = propsObjectName ? !propsTypeName : !hasRequiredProps
|
|
527
|
+
const propsTypeExpr = typeAnnotation.replace(/^:\s*/, '')
|
|
528
|
+
const noArgDefault = wantsNoArgDefault ? ` = {} as ${propsTypeExpr}` : ''
|
|
529
|
+
|
|
513
530
|
const lines: string[] = []
|
|
514
531
|
// Module-export keyword belongs to the adapter: it knows the target language
|
|
515
532
|
// and whether the source declared the component as exported.
|
|
516
533
|
const exportPrefix = ir.metadata.isExported === false ? '' : 'export '
|
|
517
|
-
lines.push(`${exportPrefix}function ${name}(${fullPropsDestructure}${typeAnnotation}) {`)
|
|
534
|
+
lines.push(`${exportPrefix}function ${name}(${fullPropsDestructure}${typeAnnotation}${noArgDefault}) {`)
|
|
518
535
|
|
|
519
536
|
// Add props extraction for SolidJS-style pattern
|
|
520
537
|
if (propsExtraction) {
|
|
@@ -600,8 +617,8 @@ export class HonoAdapter extends JsxAdapter implements IRNodeEmitter<HonoRenderC
|
|
|
600
617
|
return this.renderExpression(node)
|
|
601
618
|
}
|
|
602
619
|
|
|
603
|
-
emitConditional(node: IRConditional,
|
|
604
|
-
return this.renderConditional(node)
|
|
620
|
+
emitConditional(node: IRConditional, ctx: HonoRenderCtx, _emit: EmitIRNode<HonoRenderCtx>): string {
|
|
621
|
+
return this.renderConditional(node, ctx)
|
|
605
622
|
}
|
|
606
623
|
|
|
607
624
|
emitLoop(node: IRLoop, _ctx: HonoRenderCtx, _emit: EmitIRNode<HonoRenderCtx>): string {
|
|
@@ -721,14 +738,21 @@ export class HonoAdapter extends JsxAdapter implements IRNodeEmitter<HonoRenderC
|
|
|
721
738
|
return `{${expr.expr}}`
|
|
722
739
|
}
|
|
723
740
|
|
|
724
|
-
renderConditional(cond: IRConditional): string {
|
|
741
|
+
renderConditional(cond: IRConditional, ctx?: HonoRenderCtx): string {
|
|
725
742
|
// Handle @client directive - render comment markers for client-side evaluation
|
|
726
743
|
if (cond.clientOnly && cond.slotId) {
|
|
727
744
|
return `{bfComment("cond-start:${cond.slotId}")}{bfComment("cond-end:${cond.slotId}")}`
|
|
728
745
|
}
|
|
729
746
|
|
|
730
|
-
|
|
731
|
-
|
|
747
|
+
// A conditional that is itself a loop item root (#1665 whole-item
|
|
748
|
+
// conditional: `arr.map(t => cond && <li/>)`) makes its branch element the
|
|
749
|
+
// loop item's root, so the `data-key` that reconciliation/hydration expect
|
|
750
|
+
// belongs on that element — exactly like a non-conditional loop root. Pass
|
|
751
|
+
// the flag through so `renderElement` emits `data-key`, matching the Go /
|
|
752
|
+
// CSR adapters' generic `key`→`data-key` rewrite.
|
|
753
|
+
const branchCtx: HonoRenderCtx | undefined = ctx?.isLoopItemRoot ? { isLoopItemRoot: true } : undefined
|
|
754
|
+
const whenTrue = this.renderNodeRawCtx(cond.whenTrue, branchCtx)
|
|
755
|
+
let whenFalse = this.renderNodeRawCtx(cond.whenFalse, branchCtx)
|
|
732
756
|
|
|
733
757
|
// Handle empty/null whenFalse
|
|
734
758
|
if (!whenFalse || whenFalse === '' || whenFalse === 'null') {
|
|
@@ -749,6 +773,20 @@ export class HonoAdapter extends JsxAdapter implements IRNodeEmitter<HonoRenderC
|
|
|
749
773
|
return `{${cond.condition} ? ${whenTrue} : ${whenFalse}}`
|
|
750
774
|
}
|
|
751
775
|
|
|
776
|
+
/**
|
|
777
|
+
* Like the base `renderNodeRaw`, but threads a render ctx through to
|
|
778
|
+
* `renderNode` so a conditional branch can mark its element as a loop item
|
|
779
|
+
* root (#1665). The `null` / `undefined` expression branch carries no
|
|
780
|
+
* element, so it short-circuits exactly as the base helper does.
|
|
781
|
+
*/
|
|
782
|
+
private renderNodeRawCtx(node: IRNode, ctx?: HonoRenderCtx): string {
|
|
783
|
+
if (node.type === 'expression') {
|
|
784
|
+
if (node.expr === 'null' || node.expr === 'undefined') return 'null'
|
|
785
|
+
return node.expr
|
|
786
|
+
}
|
|
787
|
+
return this.renderNode(node, ctx)
|
|
788
|
+
}
|
|
789
|
+
|
|
752
790
|
private wrapWithCondMarker(node: IRNode, content: string, condId: string): string {
|
|
753
791
|
// Components don't reliably forward bf-c to their root element.
|
|
754
792
|
// Use comment markers so insert() can find them via TreeWalker.
|
|
@@ -816,6 +854,14 @@ export class HonoAdapter extends JsxAdapter implements IRNodeEmitter<HonoRenderC
|
|
|
816
854
|
// literal here to match the adapter's existing convention of
|
|
817
855
|
// emitting comment-marker strings directly.
|
|
818
856
|
safeChildren = `<>{bfComment('bf-loop-i')}${children}</>`
|
|
857
|
+
} else if (loop.bodyIsItemConditional && loop.key) {
|
|
858
|
+
// Whole-item conditional (#1665): a per-item `<!--bf-loop-i:KEY-->`
|
|
859
|
+
// anchor that is ALWAYS present (even when the item's conditional
|
|
860
|
+
// renders nothing), carrying the key so the client's
|
|
861
|
+
// `mapArrayAnchored` can hydrate every SSR-rendered item by its anchor.
|
|
862
|
+
// `bfComment(k)` emits `<!--bf-${k}-->`, so the `loop-i:` argument
|
|
863
|
+
// yields `<!--bf-loop-i:KEY-->`.
|
|
864
|
+
safeChildren = `<>{bfComment('loop-i:' + String(${loop.key}))}${children}</>`
|
|
819
865
|
}
|
|
820
866
|
// Apply chained `.sort()` / `.filter()` extracted to
|
|
821
867
|
// `loop.sortComparator` / `loop.filterPredicate` (#1448 Tier B).
|
package/src/test-render.ts
CHANGED
|
@@ -8,6 +8,7 @@
|
|
|
8
8
|
import { compileJSX } from '@barefootjs/jsx'
|
|
9
9
|
import type { TemplateAdapter } from '@barefootjs/jsx'
|
|
10
10
|
import { Hono } from 'hono'
|
|
11
|
+
import { readFileSync } from 'node:fs'
|
|
11
12
|
import { mkdir, rm } from 'node:fs/promises'
|
|
12
13
|
import { resolve } from 'node:path'
|
|
13
14
|
|
|
@@ -23,6 +24,16 @@ export interface RenderOptions {
|
|
|
23
24
|
props?: Record<string, unknown>
|
|
24
25
|
/** Additional component files (filename → source) */
|
|
25
26
|
components?: Record<string, string>
|
|
27
|
+
/**
|
|
28
|
+
* Pre-compiled child component modules (import specifier → absolute
|
|
29
|
+
* module path) — #1467 Phase 2a. When the parent imports one of these
|
|
30
|
+
* specifiers, the import is *re-anchored* to the given module path
|
|
31
|
+
* (kept as a real ESM import) instead of having the child inlined via
|
|
32
|
+
* `components`. The module is a committed, export-intact marked
|
|
33
|
+
* template, so SSR loads it through the module system — no export
|
|
34
|
+
* stripping. Takes precedence over `components` for the same key.
|
|
35
|
+
*/
|
|
36
|
+
componentModules?: Record<string, string>
|
|
26
37
|
/**
|
|
27
38
|
* Explicit component to render when the source declares multiple
|
|
28
39
|
* exports. When omitted, the first function-valued export in
|
|
@@ -34,14 +45,46 @@ export interface RenderOptions {
|
|
|
34
45
|
componentName?: string
|
|
35
46
|
}
|
|
36
47
|
|
|
48
|
+
/**
|
|
49
|
+
* Drop module-level exports from a compiled marked template so it can be
|
|
50
|
+
* inlined as plain declarations alongside other components. Specifier
|
|
51
|
+
* blocks (`export { … }`, `export type { … }`, with or without a
|
|
52
|
+
* trailing `from '…'` re-export source) are removed whole; declaration
|
|
53
|
+
* forms (`export function/const/let/type/interface`, `export default`)
|
|
54
|
+
* keep their body with only the leading keyword stripped.
|
|
55
|
+
*
|
|
56
|
+
* The set of forms is bounded by `generateModuleExports` in
|
|
57
|
+
* @barefootjs/jsx — see the caller for the enumeration. This stays a
|
|
58
|
+
* line-oriented text pass (rather than a real parse) because the input
|
|
59
|
+
* is compiler-generated with a stable, single-line-per-export shape.
|
|
60
|
+
*/
|
|
61
|
+
function stripModuleExports(code: string): string {
|
|
62
|
+
return code
|
|
63
|
+
// `export [type] { … } [from '…']` specifier / re-export blocks.
|
|
64
|
+
.replace(
|
|
65
|
+
/^[ \t]*export\s+(?:type\s+)?\{[^}]*\}(?:[ \t]*from[ \t]*['"][^'"]*['"])?[ \t]*;?[ \t]*$/gm,
|
|
66
|
+
'',
|
|
67
|
+
)
|
|
68
|
+
// Leading keyword on declaration forms (`export function`,
|
|
69
|
+
// `export const X = …`, `export default …`, etc.).
|
|
70
|
+
.replace(/\bexport\s+(default\s+)?/g, '')
|
|
71
|
+
}
|
|
72
|
+
|
|
37
73
|
export async function renderHonoComponent(options: RenderOptions): Promise<string> {
|
|
38
|
-
const { source, adapter, props, components, componentName: requestedName } = options
|
|
74
|
+
const { source, adapter, props, components, componentModules, componentName: requestedName } = options
|
|
39
75
|
|
|
40
|
-
//
|
|
76
|
+
// Child imports re-anchored to a pre-compiled module (#1467 Phase 2a):
|
|
77
|
+
// import specifier → absolute path. These are NOT inlined; the parent's
|
|
78
|
+
// matching import is rewritten to the path and loaded as a real module.
|
|
79
|
+
const moduleMap = new Map<string, string>(Object.entries(componentModules ?? {}))
|
|
80
|
+
|
|
81
|
+
// Compile child components first (inline path). Keys also present in
|
|
82
|
+
// `moduleMap` are skipped here — they load as real modules instead.
|
|
41
83
|
const childCodes: string[] = []
|
|
42
84
|
const componentKeys = new Set<string>()
|
|
43
85
|
if (components) {
|
|
44
86
|
for (const [filename, childSource] of Object.entries(components)) {
|
|
87
|
+
if (moduleMap.has(filename)) continue
|
|
45
88
|
componentKeys.add(filename)
|
|
46
89
|
const childResult = compileJSX(childSource, filename, { adapter })
|
|
47
90
|
const childErrors = childResult.errors.filter(e => e.severity === 'error')
|
|
@@ -50,8 +93,23 @@ export async function renderHonoComponent(options: RenderOptions): Promise<strin
|
|
|
50
93
|
}
|
|
51
94
|
const childTemplate = childResult.files.find(f => f.type === 'markedTemplate')
|
|
52
95
|
if (!childTemplate) throw new Error(`No marked template for ${filename}`)
|
|
53
|
-
// Strip
|
|
54
|
-
|
|
96
|
+
// Strip exports so only the parent component is exported, inlining
|
|
97
|
+
// the child as plain top-level declarations. The marked template's
|
|
98
|
+
// export forms are fixed by `generateModuleExports` (+ the
|
|
99
|
+
// component's own `export function`) in @barefootjs/jsx, each on
|
|
100
|
+
// its own line:
|
|
101
|
+
//
|
|
102
|
+
// export const/let X = … export function / async function …
|
|
103
|
+
// export type X = … export interface X { … }
|
|
104
|
+
// export { A, B } [from '…'] export type { A } [from '…']
|
|
105
|
+
//
|
|
106
|
+
// The `export { … }` / `export type { … }` *specifier* blocks
|
|
107
|
+
// (with or without a trailing `from '…'`) must be dropped whole —
|
|
108
|
+
// their bindings are already declared inline, and naively removing
|
|
109
|
+
// just the `export ` keyword leaves a bare `{ A }` / `type { A }`
|
|
110
|
+
// (the latter a syntax error). Declaration forms keep their body;
|
|
111
|
+
// only the leading `export `/`export default ` is removed.
|
|
112
|
+
const localCode = stripModuleExports(childTemplate.content)
|
|
55
113
|
childCodes.push(localCode)
|
|
56
114
|
}
|
|
57
115
|
}
|
|
@@ -67,22 +125,56 @@ export async function renderHonoComponent(options: RenderOptions): Promise<strin
|
|
|
67
125
|
const templateFile = result.files.find(f => f.type === 'markedTemplate')
|
|
68
126
|
if (!templateFile) throw new Error('No marked template in compile output')
|
|
69
127
|
|
|
128
|
+
// Pre-compiled child modules are committed under the adapter-tests
|
|
129
|
+
// fixtures tree, where `hono/jsx` is NOT resolvable (hono lives in
|
|
130
|
+
// this package's node_modules — the very reason render temp files go
|
|
131
|
+
// here). Copy each committed module verbatim into the render temp dir
|
|
132
|
+
// and re-anchor the parent import there. The committed file stays the
|
|
133
|
+
// reviewable source of truth; this is a byte copy, not export surgery.
|
|
134
|
+
const childModuleWrites: Array<{ path: string; content: string }> = []
|
|
135
|
+
const moduleTempPaths = new Map<string, string>()
|
|
136
|
+
for (const [key, modPath] of moduleMap) {
|
|
137
|
+
const safe = key.replace(/[^a-zA-Z0-9]+/g, '_')
|
|
138
|
+
const tempPath = resolve(
|
|
139
|
+
RENDER_TEMP_DIR,
|
|
140
|
+
`child-${safe}-${Date.now()}-${Math.random().toString(36).slice(2)}.tsx`,
|
|
141
|
+
)
|
|
142
|
+
moduleTempPaths.set(key, tempPath)
|
|
143
|
+
childModuleWrites.push({ path: tempPath, content: readFileSync(modPath, 'utf8') })
|
|
144
|
+
}
|
|
145
|
+
|
|
70
146
|
let parentCode = templateFile.content
|
|
71
|
-
//
|
|
72
|
-
|
|
147
|
+
// Resolve each child import: re-anchor to a pre-compiled module's temp
|
|
148
|
+
// copy (`moduleTempPaths`), strip it (inlined via `components`), or
|
|
149
|
+
// leave it. Both maps key on the import specifier; match the parent's
|
|
150
|
+
// import path with or without a `.tsx` extension (`./badge` ↔
|
|
151
|
+
// `./badge.tsx`).
|
|
152
|
+
//
|
|
153
|
+
// Assumes one import statement per line — the marked-template adapter
|
|
154
|
+
// emits single-line imports (`import { Slot } from '../slot'`), so the
|
|
155
|
+
// per-line scan is sufficient. A multi-line import would not match
|
|
156
|
+
// here; the unrewritten `../slot` then fails loudly at module
|
|
157
|
+
// resolution rather than rendering wrong output.
|
|
158
|
+
if (componentKeys.size > 0 || moduleTempPaths.size > 0) {
|
|
159
|
+
const matchKey = (importPath: string, keys: Iterable<string>): string | undefined => {
|
|
160
|
+
for (const key of keys) {
|
|
161
|
+
const keyWithoutExt = key.replace(/\.tsx?$/, '')
|
|
162
|
+
if (importPath === keyWithoutExt || importPath === key) return key
|
|
163
|
+
}
|
|
164
|
+
return undefined
|
|
165
|
+
}
|
|
73
166
|
parentCode = parentCode
|
|
74
167
|
.split('\n')
|
|
75
|
-
.
|
|
76
|
-
const importMatch = line.match(
|
|
77
|
-
if (!importMatch) return
|
|
78
|
-
const importPath = importMatch
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
}
|
|
84
|
-
return true
|
|
168
|
+
.map(line => {
|
|
169
|
+
const importMatch = line.match(/^(\s*import\s+.*from\s+['"])(.+?)(['"].*)$/)
|
|
170
|
+
if (!importMatch) return line
|
|
171
|
+
const [, prefix, importPath, suffix] = importMatch
|
|
172
|
+
const moduleKey = matchKey(importPath, moduleTempPaths.keys())
|
|
173
|
+
if (moduleKey) return `${prefix}${moduleTempPaths.get(moduleKey)}${suffix}`
|
|
174
|
+
if (matchKey(importPath, componentKeys)) return null
|
|
175
|
+
return line
|
|
85
176
|
})
|
|
177
|
+
.filter((line): line is string => line !== null)
|
|
86
178
|
.join('\n')
|
|
87
179
|
}
|
|
88
180
|
|
|
@@ -95,6 +187,11 @@ export async function renderHonoComponent(options: RenderOptions): Promise<strin
|
|
|
95
187
|
const code = codeParts.join('\n')
|
|
96
188
|
|
|
97
189
|
await mkdir(RENDER_TEMP_DIR, { recursive: true })
|
|
190
|
+
// Materialise the verbatim child-module copies next to the parent so
|
|
191
|
+
// their `hono/jsx` pragma resolves.
|
|
192
|
+
for (const { path, content } of childModuleWrites) {
|
|
193
|
+
await Bun.write(path, content)
|
|
194
|
+
}
|
|
98
195
|
// Unique filename per render to avoid Bun's process-level module cache
|
|
99
196
|
// (bun#12371: re-importing the same path returns stale module)
|
|
100
197
|
const tempFile = resolve(
|
|
@@ -139,5 +236,8 @@ export async function renderHonoComponent(options: RenderOptions): Promise<strin
|
|
|
139
236
|
return await res.text()
|
|
140
237
|
} finally {
|
|
141
238
|
await rm(tempFile, { force: true }).catch(() => {})
|
|
239
|
+
for (const { path } of childModuleWrites) {
|
|
240
|
+
await rm(path, { force: true }).catch(() => {})
|
|
241
|
+
}
|
|
142
242
|
}
|
|
143
243
|
}
|