Skip to content

Commit 000cd7b

Browse files
committed
fixes
1 parent 1dc5597 commit 000cd7b

File tree

6 files changed

+124
-168
lines changed

6 files changed

+124
-168
lines changed

core/src/directives/component.ts

Lines changed: 18 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,23 @@ import type { Kire } from '../kire';
33
export default (kire: Kire) => {
44
// @component('path', {vars}) ... @end
55
// Uses slots.
6+
7+
kire.directive({
8+
name: 'slot',
9+
params: ['name:string'],
10+
children: true,
11+
type: 'html',
12+
description: 'Defines a named content slot within a component.',
13+
example: `@slot('header')\n <h1>This is the header</h1>\n@end`,
14+
onCall(c) {
15+
const name = c.param('name');
16+
c.res(`$slots[${JSON.stringify(name)}] = await (async ($parentCtx) => {`);
17+
c.res(` const $ctx = $parentCtx.clone();`);
18+
if (c.children) c.set(c.children);
19+
c.res(` return $ctx[Symbol.for('~response')];`);
20+
c.res(`})($ctx);`);
21+
}
22+
});
623

724
kire.directive({
825
name: 'component',
@@ -11,78 +28,21 @@ export default (kire: Kire) => {
1128
type: 'html',
1229
description: 'Loads a template as a reusable component, allowing content to be passed into named slots.',
1330
example: `@component('card', { title: 'My Card' })\n @slot('header')\n <h1>Card Header</h1>\n @end\n <p>Default content.</p>\n@end`,
14-
parents: [
15-
{
16-
name: 'slot',
17-
params: ['name:string'],
18-
children: true,
19-
type: 'html',
20-
description: 'Defines a named content slot within a component.',
21-
example: `@slot('header')\n <h1>This is the header</h1>\n@end`,
22-
onCall(c) {
23-
const name = c.param('name');
24-
c.res(`$slots[${name}] = await (async ($parentCtx) => {`);
25-
c.res(` const $ctx = $parentCtx.clone();`);
26-
if (c.children) c.set(c.children);
27-
c.res(` return $ctx[Symbol.for('~response')];`);
28-
c.res(`})($ctx);`);
29-
}
30-
}
31-
],
3231
onCall(ctx) {
3332
const pathExpr = ctx.param('path');
3433
const varsExpr = ctx.param('variables') || '{}';
3534

3635
ctx.res(`await (async () => {`);
3736
ctx.res(` const $slots = {};`);
3837

39-
// Process children (which may contain @slot directives or default content)
40-
// Default content goes to $slot or 'default'?
41-
// In old component:
42-
// "if (compiledChildren.trim()) { ejb.builder.add('$slots.$slot = ...') }"
43-
// And slots are in `parents`? Wait.
44-
// In old logic: `parents` were used for `@slot`.
45-
// But `@slot` is usually nested INSIDE component block.
46-
// `@component(...) @slot(...) ... @end @end`?
47-
// Or `@component(...) <p>Default</p> @slot('x')...@end @end`.
48-
49-
// If `@slot` is a child directive, we need to capture it.
50-
// But normal content should be captured as default slot.
51-
52-
// We need to separate @slot children from other children?
53-
// Or just execute children. Normal content appends to `$ctx.res` of this IIFE?
54-
// No, we want to capture slots into `$slots` object.
55-
56-
// Strategy:
57-
// 1. Create a context for the component body (the block where slots are defined).
58-
// 2. In this context, text output goes to default slot?
59-
// 3. `@slot` directives write to `$slots`.
60-
6138
ctx.res(` const $bodyCtx = $ctx.clone();`);
62-
ctx.res(` $bodyCtx.slots = $slots;`); // Expose slots map to body context so @slot can write to it?
63-
// Actually, @slot needs to write to the LOCAL `$slots` variable we defined above.
64-
// Directives run in the scope of `onCall`'s generated code.
65-
// So `@slot` generated code will run inside this IIFE.
66-
// So `$slots` is available!
67-
68-
// But what about default content?
69-
// We can capture `$bodyCtx` response as default slot.
70-
71-
// We need to render children using `$bodyCtx`.
72-
// But `ctx.set(children)` compiles children using the CURRENT compilation context.
73-
// It emits code into the current stream.
74-
// Code emitted: `$ctx[~response] += ...`.
75-
// We want it to be `$bodyCtx[~response] += ...`.
76-
77-
// Compiler doesn't support changing the context variable name easily (it uses `$ctx` hardcoded in `with($ctx)`).
78-
// But we can shadow `$ctx`!
39+
ctx.res(` $bodyCtx.slots = $slots;`);
7940

8041
ctx.res(` await (async ($parentCtx) => {`);
8142
ctx.res(` const $ctx = $bodyCtx;`); // Shadow $ctx
8243
ctx.res(` with($ctx) {`);
8344

8445
if (ctx.children) ctx.set(ctx.children);
85-
// Note: `ctx.set` will emit code using `$ctx`. Since we shadowed it, it uses `$bodyCtx`.
8646

8747
ctx.res(` }`);
8848
ctx.res(` })($ctx);`);

core/src/directives/layout.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,7 @@ export default (kire: Kire) => {
7070
for (const key in $ctx.stacks) {
7171
const placeholder = "<!-- KIRE:stack(" + key + ") -->";
7272
if ($ctx[Symbol.for('~response')].includes(placeholder)) {
73-
const content = $ctx.stacks[key].join('\n');
73+
const content = $ctx.stacks[key].join('\\n');
7474
$ctx[Symbol.for('~response')] = $ctx[Symbol.for('~response')].split(placeholder).join(content);
7575
}
7676
}

core/src/directives/natives.ts

Lines changed: 60 additions & 88 deletions
Original file line numberDiff line numberDiff line change
@@ -9,40 +9,40 @@ export default (kire: Kire) => {
99
description: 'Conditionally renders a block of content if the expression is true.',
1010
example: `@if(user.isLoggedIn)\n Welcome, {{ user.name }}!\n@end`,
1111
parents: [
12-
{
12+
{
1313
name: 'elseif',
1414
params: ['cond:string'],
1515
children: true,
1616
type: 'js',
1717
description: 'Renders a block of content if the preceding @if/@elseif is false and the current expression is true.',
1818
example: `@elseif(user.isAdmin)\n Admin access granted.\n@end`,
19-
onCall(c) {
20-
c.res(`} else if (${c.param('cond')}) {`);
21-
if(c.children) c.set(c.children);
22-
}
19+
onCall(c) {
20+
c.res(`} else if (${c.param('cond')}) {`);
21+
if (c.children) c.set(c.children);
22+
}
2323
},
24-
{
24+
{
2525
name: 'elif', // alias for elseif
2626
params: ['cond:string'],
27-
children: true,
27+
children: true,
2828
type: 'js',
2929
description: 'Alias for @elseif.',
3030
example: `@elif(user.isAdmin)\n Admin access granted.\n@end`,
31-
onCall(c) {
32-
c.res(`} else if (${c.param('cond')}) {`);
33-
if(c.children) c.set(c.children);
34-
}
31+
onCall(c) {
32+
c.res(`} else if (${c.param('cond')}) {`);
33+
if (c.children) c.set(c.children);
34+
}
3535
},
36-
{
37-
name: 'else',
38-
children: true,
36+
{
37+
name: 'else',
38+
children: true,
3939
type: 'js',
4040
description: 'Renders a block of content if the preceding @if/@elseif expressions are all false.',
4141
example: `@else\n Please log in.\n@end`,
42-
onCall(c) {
43-
c.res(`} else {`);
44-
if(c.children) c.set(c.children);
45-
}
42+
onCall(c) {
43+
c.res(`} else {`);
44+
if (c.children) c.set(c.children);
45+
}
4646
}
4747
],
4848
onCall(ctx) {
@@ -61,30 +61,30 @@ export default (kire: Kire) => {
6161
description: 'Iterates over an array or object, similar to a JavaScript for...of loop.',
6262
example: `@for(user of users)\n <p>{{ user.name }}</p>\n@end`,
6363
onCall(ctx) {
64-
const expr = ctx.param('expr');
64+
const expr = ctx.param('expr');
6565
if (expr.includes(' in ')) {
6666
const [lhs, rhs] = expr.split(' in ');
6767
ctx.res(`for (const ${lhs} of ${rhs}) {`);
6868
} else if (expr.includes(' of ')) {
6969
const [lhs, rhs] = expr.split(' of ');
70-
ctx.res(`for (const ${lhs} of ${rhs}) {`);
70+
ctx.res(`for (const ${lhs} of ${rhs}) {`);
7171
} else {
7272
ctx.res(`for (${expr}) {`);
7373
}
74-
74+
7575
if (ctx.children) ctx.set(ctx.children);
7676
ctx.res(`}`);
7777
}
7878
});
79-
79+
8080
kire.directive({
8181
name: 'const',
8282
params: ['expr:string'],
8383
type: 'js',
8484
description: 'Declares a block-scoped constant, similar to JavaScript `const`.',
8585
example: `@const(myVar = 'hello world')`,
8686
onCall(ctx) {
87-
ctx.res(`const ${ctx.param('expr')};`);
87+
ctx.res(`const ${ctx.param('expr')};`);
8888
}
8989
});
9090

@@ -95,18 +95,24 @@ export default (kire: Kire) => {
9595
description: 'Declares a block-scoped local variable, similar to JavaScript `let`.',
9696
example: `@let(counter = 0)`,
9797
onCall(ctx) {
98-
ctx.res(`let ${ctx.param('expr')};`);
98+
ctx.res(`let ${ctx.param('expr')};`);
9999
}
100100
});
101-
101+
102102
kire.directive({
103103
name: 'code',
104104
children: true,
105105
type: 'js',
106106
description: 'Executes a block of raw JavaScript code on the server.',
107107
example: `@code\n console.log('This runs during template compilation.');\n@end`,
108108
onCall(ctx) {
109-
if (ctx.children) ctx.set(ctx.children);
109+
if (ctx.children) {
110+
for (const child of ctx.children) {
111+
if (child.type === 'text' && child.content) {
112+
ctx.res(child.content);
113+
}
114+
}
115+
}
110116
}
111117
});
112118

@@ -118,69 +124,35 @@ export default (kire: Kire) => {
118124
description: 'Provides a control flow statement similar to a JavaScript switch block.',
119125
example: `@switch(value)\n @case(1) ... @end\n @default ... @end\n@end`,
120126
parents: [
121-
{
122-
name: 'case',
123-
params: ['val:string'],
124-
children: true,
125-
type: 'js',
126-
description: 'A case clause for a @switch statement.',
127-
example: `@case('A')\n <p>Value is A</p>\n@end`,
128-
onCall(c) {
129-
c.res(`case ${c.param('val')}: {`);
130-
if(c.children) c.set(c.children);
131-
c.res(`break; }`);
132-
}
133-
},
134-
{
135-
name: 'default',
136-
children: true,
137-
type: 'js',
138-
description: 'The default clause for a @switch statement.',
139-
example: `@default\n <p>Value is something else</p>\n@end`,
140-
onCall(c) {
141-
c.res(`default: {`);
142-
if(c.children) c.set(c.children);
143-
c.res(`}`);
144-
}
145-
}
127+
{
128+
name: 'case',
129+
params: ['val:string'],
130+
children: true,
131+
type: 'js',
132+
description: 'A case clause for a @switch statement.',
133+
example: `@case('A')\n <p>Value is A</p>\n@end`,
134+
onCall(c) {
135+
c.res(`case ${JSON.stringify(c.param('val'))}: {`);
136+
if (c.children) c.set(c.children);
137+
c.res(`break; }`);
138+
}
139+
}, {
140+
name: 'default',
141+
children: true,
142+
type: 'js',
143+
description: 'The default clause for a @switch statement.',
144+
example: `@default\n <p>Value is something else</p>\n@end`,
145+
onCall(c) {
146+
c.res(`default: {`);
147+
if (c.children) c.set(c.children);
148+
c.res(`}`);
149+
}
150+
}
146151
],
147-
onCall(ctx) {
148-
ctx.res(`switch (${ctx.param('expr')}) {`);
149-
// Switch structure typically has case/default as parents (related nodes)
150-
// Wait, in `parents` array we define SUB-directives.
151-
// `case` and `default` ARE nested inside `switch`.
152-
// But typically they are direct children in AST?
153-
// OR they are siblings in the chain?
154-
// Switch syntax: @switch(x) @case(1)...@end @default...@end @end
155-
// No, usually: @switch(x) @case(1)... @break @default ... @end
156-
157-
// If we use the `parents` logic (sub-directives), then:
158-
// @switch(x)
159-
// @case(1) ... @end
160-
// @end
161-
162-
// If `case` is defined as a sub-directive in `parents` of `switch`,
163-
// the parser will treat it as a related node IF it is at the same level?
164-
// No, `parents` in parser logic handles "chaining" (if...elseif...else).
165-
// Switch cases are CHILDREN of the switch block.
166-
// So they should be normal directives or handled via children traversal?
167-
168-
// If `case` is a normal directive, it needs to be registered globally?
169-
// Or registered only inside `switch`.
170-
// But our `kire.directive` recurses `parents`.
171-
172-
// If `case` is in `parents` of `switch`, it is registered.
173-
// But parser only links it to `related` if it follows the pattern `@switch ... @case`.
174-
// Chained style: `@switch(...) ... @end`.
175-
// Inside `...`, we have `@case`.
176-
// That is NOT a chain. That is nesting.
177-
178-
// So `case` should just be processed as a child node.
179-
// But `case` needs to be a valid directive.
180-
// If we define it in `parents`, it gets registered.
181-
182-
if (ctx.children) ctx.set(ctx.children);
183-
ctx.res(`}`);
152+
async onCall(ctx) {
153+
ctx.res(`switch (${ctx.param('expr')}) {`);
154+
if (ctx.parents) await ctx.set(ctx.parents);
155+
ctx.res(`}`);
184156
}
185157
});
186158
};

core/src/kire.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -117,7 +117,6 @@ export class Kire {
117117
public async compile(template: string): Promise<string> {
118118
const parser = new this.parserConstructor(template, this);
119119
const nodes = parser.parse();
120-
121120
const compiler = new this.compilerConstructor(this);
122121
return compiler.compile(nodes);
123122
}

0 commit comments

Comments
 (0)