Skip to content

Commit 45fb4e7

Browse files
committed
wip
1 parent 29b33e6 commit 45fb4e7

File tree

6 files changed

+40
-43
lines changed

6 files changed

+40
-43
lines changed

core/src/directives/component.ts

Lines changed: 11 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ export default (kire: Kire) => {
2929
description:
3030
"Loads a template as a reusable component, allowing content to be passed into named slots.",
3131
example: `@component('card', { title: 'My Card' })\n @slot('header')\n <h1>Card Header</h1>\n @end\n <p>Default content.</p>\n@end`,
32-
onCall(ctx) {
32+
async onCall(ctx) {
3333
const pathExpr = ctx.param("path");
3434
const varsExpr = ctx.param("variables") || "{}";
3535

@@ -43,25 +43,28 @@ export default (kire: Kire) => {
4343
ctx.res(` const $ctx = $bodyCtx;`); // Shadow $ctx
4444
ctx.res(` with($ctx) {`);
4545

46-
if (ctx.children) ctx.set(ctx.children);
46+
if (ctx.children) await ctx.set(ctx.children);
4747

4848
ctx.res(` }`);
4949
ctx.res(` })($ctx);`);
5050

51-
ctx.res(` $slots.default = $bodyCtx[Symbol.for('~response')];`);
51+
ctx.res(` if (!$slots.default) $slots.default = $bodyCtx[Symbol.for('~response')];`);
5252

5353
// Now load the component template
5454
ctx.res(` const path = $ctx.resolve(${JSON.stringify(pathExpr)});`);
5555
ctx.res(` const templateFn = await $ctx.load(path);`);
5656
ctx.res(` if (templateFn) {`);
57-
ctx.res(` const locals = ${varsExpr};`);
58-
ctx.res(` const componentCtx = $ctx.clone(locals);`);
59-
ctx.res(` componentCtx.slots = $slots;`); // Pass slots to component
60-
ctx.res(` await templateFn(componentCtx);`);
61-
ctx.res(` $ctx.res(componentCtx[Symbol.for('~response')]);`);
57+
ctx.res(` const locals = ${varsExpr};`);
58+
ctx.res(` const componentCtx = $ctx.clone(locals);`);
59+
ctx.res(` componentCtx[${JSON.stringify(kire.varLocals)}] = locals;`); // Expose locals under the configured name
60+
ctx.res(` if(typeof locals === 'object' && locals !== null) locals.slots = $slots;`); // Attach slots to locals for it.slots access
61+
ctx.res(` componentCtx.slots = $slots;`); // Pass slots to component
62+
ctx.res(` await templateFn(componentCtx);`);
63+
ctx.res(` $ctx.res(componentCtx[Symbol.for('~response')]);`);
6264
ctx.res(` }`);
6365

6466
ctx.res(`})();`);
6567
},
6668
});
6769
};
70+

core/src/directives/import.ts

Lines changed: 6 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,11 @@ export default (kire: Kire) => {
44
kire.directive({
55
name: "include",
66
params: ["path:string", "locals:object"],
7-
children: true,
7+
children: false,
88
type: "html",
99
description:
10-
'Includes and renders a template from a given path, optionally passing local variables. Can also wrap content passed as "content" variable.',
11-
example: `@include('partials/card')\n <p>Card content</p>\n@end`,
10+
"Includes and renders a template from a given path, optionally passing local variables.",
11+
example: `@include('partials/card')`,
1212
onCall(ctx) {
1313
const pathExpr = ctx.param("path");
1414
const localsExpr = ctx.param("locals") || "{}";
@@ -18,25 +18,13 @@ export default (kire: Kire) => {
1818
const templateFn = await $ctx.load(path);
1919
2020
if (templateFn) {
21-
let content = '';`);
22-
23-
if (ctx.children && ctx.children.length > 0) {
24-
ctx.res(`const $bodyCtx = $ctx.clone();
25-
await (async ($parentCtx) => {
26-
const $ctx = $bodyCtx;
27-
with($ctx) {`);
28-
ctx.set(ctx.children);
29-
ctx.res(`}
30-
})($ctx);
31-
content = $bodyCtx[Symbol.for('~response')];`);
32-
}
33-
34-
ctx.res(`const locals = Object.assign({ content }, ${localsExpr});
21+
const locals = ${localsExpr};
3522
const childCtx = $ctx.clone(locals);
23+
childCtx[${JSON.stringify(kire.varLocals)}] = locals;
3624
await templateFn(childCtx);
3725
$ctx.res(childCtx[Symbol.for('~response')]);
3826
}
3927
})();`);
4028
},
4129
});
42-
};
30+
};

core/src/kire.ts

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,12 +32,16 @@ export class Kire {
3232
public cacheFiles: Map<string, Function> = new Map();
3333
public parserConstructor: IParserConstructor;
3434
public compilerConstructor: ICompilerConstructor;
35+
public varLocals: string;
36+
public exposeLocals: boolean;
3537

3638
constructor(options: KireOptions = {}) {
3739
this.root = options.root ?? "./";
3840
this.cache = options.cache ?? true;
3941
this.alias = options.alias ?? { "~/": this.root };
4042
this.extension = options.extension ?? "kire";
43+
this.varLocals = options.varLocals ?? "it";
44+
this.exposeLocals = options.exposeLocals ?? true;
4145

4246
this.resolverFn =
4347
options.resolver ??
@@ -248,6 +252,10 @@ export class Kire {
248252
}
249253
}
250254

255+
if (content === null || content === undefined) {
256+
return null as any;
257+
}
258+
251259
const code = await this.compile(content);
252260
try {
253261
const AsyncFunction = Object.getPrototypeOf(async () => {}).constructor;
@@ -276,6 +284,11 @@ export class Kire {
276284
}
277285
Object.assign(rctx, locals);
278286

287+
// Expose locals under the configured varLocals name if exposeLocals is true
288+
if (this.exposeLocals) {
289+
rctx[this.varLocals] = locals;
290+
}
291+
279292
// Initialize the response and structure symbols on the runtime context
280293
rctx[RESPONSE_SYMBOL] = "";
281294
rctx[STRUCTURE_SYMBOL] = [];

core/src/types.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,8 @@ export interface KireOptions {
2727
parser?: IParserConstructor;
2828
compiler?: ICompilerConstructor;
2929
};
30+
varLocals?: string;
31+
exposeLocals?: boolean;
3032
}
3133

3234
export interface KireContext {

core/tests/defaults.test.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,5 +52,5 @@ test("Kire - Include", async () => {
5252

5353
const tpl2 = `@include('/some/path.kire')`;
5454
// Expect this to fail or return empty, not throw uncaught.
55-
await expect(await kire.render(tpl2)).resolves.toBe("");
55+
expect(await kire.render(tpl2)).toBe("");
5656
});

core/tests/directives.test.ts

Lines changed: 7 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -61,20 +61,17 @@ describe("Kire Native Directives", () => {
6161

6262
describe("Loops", () => {
6363
it("@for with array of", async () => {
64-
const tpl = `@for(item of it.items){{ item }},
65-
@end`;
64+
const tpl = `@for(item of it.items){{ item }},@end`;
6665
expect(await render(tpl, { items: [1, 2, 3] })).toBe("1,2,3,");
6766
});
6867

6968
it("@for with empty array", async () => {
70-
const tpl = `@for(item of it.items){{ item }},
71-
@end`;
69+
const tpl = `@for(item of it.items){{ item }},@end`;
7270
expect(await render(tpl, { items: [] })).toBe("");
7371
});
7472

7573
it("@for with object properties (for...in equivalent)", async () => {
76-
const tpl = `@for(key in it.obj){{ key }}:{{ it.obj[key] }},
77-
@end`;
74+
const tpl = `@for(key in it.obj){{ key }}:{{ it.obj[key] }},@end`;
7875
expect(await render(tpl, { obj: { a: 1, b: 2 } })).toBe("a:1,b:2,");
7976
});
8077
});
@@ -230,11 +227,10 @@ describe("Kire Component Directives", () => {
230227
describe("Kire Include Directive", () => {
231228
const kire = new Kire();
232229
kire.resolverFn = async (path) => {
233-
if (path.includes("child")) return `Child: {{ it.name }}`;
234-
if (path.includes("wrapper")) return `Wrapper: {{ it.content }}`;
235-
if (path.includes("grandchild"))
230+
if (path === "child.kire") return `Child: {{ it.name }}`;
231+
if (path === "grandchild.kire")
236232
return `Grandchild: {{ it.item.name }} and {{ it.item.value }}`;
237-
if (path.includes("nested"))
233+
if (path === "nested.kire")
238234
return `@include('grandchild', { item: it.n_item })`;
239235
return null;
240236
};
@@ -245,14 +241,9 @@ describe("Kire Include Directive", () => {
245241
expect(await render(tpl)).toBe("Child: Test");
246242
});
247243

248-
it("@include with content block", async () => {
249-
const tpl = `@include('wrapper')Inner@end`;
250-
expect(await render(tpl)).toBe("Wrapper: Inner");
251-
});
252-
253244
it("@include with non-existent path", async () => {
254245
const tpl = `Start @include('nonexistent') End`;
255-
expect(await render(tpl)).toBe("Start End");
246+
expect(await render(tpl)).toBe("Start End");
256247
});
257248

258249
it("@include with nested includes", async () => {

0 commit comments

Comments
 (0)