coffeehaml 0.7.1 → 0.7.3

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.
@@ -1,791 +0,0 @@
1
- // Generated by CoffeeScript 2.7.0
2
- // emitter.coffee — AST → JSX/JS code generation
3
- var EmitState, VOID_ELEMENTS, attrNameToJs, buildAttributes, compileComponentBody, emitArrowOutput, emitArrowOutputToJs, emitChildToJs, emitComment, emitConditional, emitControlFlow, emitControlFlowToJs, emitDoctype, emitElement, emitElseBranch, emitFilter, emitFilterToJs, emitImplicitDiv, emitLoop, emitNode, emitNodes, emitOutput, emitStatement, emitText, escapeString, mapSourceLocation, splitJsStatements, tagToJs;
4
-
5
- import {
6
- Document,
7
- Element,
8
- ImplicitDiv,
9
- Text,
10
- Output,
11
- ControlFlow,
12
- Comment,
13
- Filter,
14
- Doctype,
15
- Node,
16
- Expression
17
- } from './ast.js';
18
-
19
- import {
20
- compileExpression,
21
- compileStatement
22
- } from './expressions.js';
23
-
24
- import {
25
- CompileWarning
26
- } from './types.js';
27
-
28
- import {
29
- SourceMapGenerator
30
- } from 'source-map';
31
-
32
- VOID_ELEMENTS = new Set(['area', 'base', 'br', 'col', 'embed', 'hr', 'img', 'input', 'link', 'meta', 'param', 'source', 'track', 'wbr']);
33
-
34
- EmitState = (function() {
35
- // ─── Emit State ────────────────────────────────────────────
36
- class EmitState {
37
- constructor(options1 = {}) {
38
- this.options = options1;
39
- if (this.options.sourceMap && this.options.filename) {
40
- this.sourceMapGenerator = new SourceMapGenerator({
41
- file: this.options.filename
42
- });
43
- }
44
- }
45
-
46
- emit(s) {
47
- if (s) {
48
- return this.output += ' '.repeat(this.indent * 2) + s;
49
- }
50
- }
51
-
52
- emitLine(s = '') {
53
- if (s) {
54
- this.output += ' '.repeat(this.indent * 2) + s;
55
- }
56
- this.output += '\n';
57
- return this.outputLine++;
58
- }
59
-
60
- mapSource(loc) {
61
- var outputCol;
62
- if (!(this.sourceMapGenerator && loc)) {
63
- return;
64
- }
65
- outputCol = this.indent * 2;
66
- return this.sourceMapGenerator.addMapping({
67
- generated: {
68
- line: this.outputLine + 1,
69
- column: outputCol
70
- },
71
- original: {
72
- line: loc.line + 1,
73
- column: loc.column
74
- },
75
- source: this.options.filename || 'source.coffeehaml'
76
- });
77
- }
78
-
79
- indentBlock(fn) {
80
- this.indent++;
81
- fn();
82
- return this.indent--;
83
- }
84
-
85
- };
86
-
87
- EmitState.prototype.indent = 0;
88
-
89
- EmitState.prototype.output = '';
90
-
91
- EmitState.prototype.needsFragment = false;
92
-
93
- EmitState.prototype.needsImport = false;
94
-
95
- EmitState.prototype.options = null;
96
-
97
- EmitState.prototype.sourceMapGenerator = null;
98
-
99
- EmitState.prototype.outputLine = 0;
100
-
101
- EmitState.prototype.warnings = [];
102
-
103
- EmitState.prototype.hoisted = [];
104
-
105
- EmitState.prototype.directEmit = false;
106
-
107
- return EmitState;
108
-
109
- }).call(this);
110
-
111
- // ─── Public API ────────────────────────────────────────────
112
- export var emit = function(ast, options = {}) {
113
- var bodyExpr, hocs, inner, j, len, line, name, ref, result, state, wrap, wrapped;
114
- state = new EmitState(options);
115
- state.emitLine(`import { jsx, jsxs, Fragment } from \"${options.jsxRuntime || 'react/jsx-runtime'}\";`);
116
- // Emit prologue lines
117
- if (ast.prologue && ast.prologue.length > 0) {
118
- ref = ast.prologue;
119
- for (j = 0, len = ref.length; j < len; j++) {
120
- line = ref[j];
121
- state.emitLine(line);
122
- }
123
- }
124
- state.emitLine();
125
- wrap = options.wrap;
126
- if (wrap && wrap !== 'none') {
127
- name = options.componentName || 'Component';
128
- hocs = (function() {
129
- switch (wrap) {
130
- case 'component':
131
- return [];
132
- case 'observer':
133
- return ['observer'];
134
- default:
135
- return wrap;
136
- }
137
- })();
138
- bodyExpr = compileComponentBody(ast, state);
139
- inner = `function ${name}(props) { return ${bodyExpr}; }`;
140
- wrapped = hocs.reduceRight((function(acc, hoc) {
141
- return `${hoc}(${acc})`;
142
- }), inner);
143
- state.emitLine(`export default ${wrapped}`);
144
- } else {
145
- state.directEmit = true;
146
- emitNodes(ast.children, state, true);
147
- }
148
- result = {
149
- code: state.output,
150
- warnings: state.warnings
151
- };
152
- if (state.sourceMapGenerator) {
153
- result.sourceMap = state.sourceMapGenerator.toString();
154
- }
155
- return result;
156
- };
157
-
158
- // ─── Component Body Compilation ─────────────────────────────
159
- compileComponentBody = function(ast, state) {
160
- var body, hoistedJs, jsx, stmts, tmpState;
161
- tmpState = new EmitState(state.options);
162
- tmpState.indent = state.indent;
163
- emitNodes(ast.children, tmpState, true);
164
- state.warnings.push(...tmpState.warnings);
165
- body = tmpState.output.trim();
166
- hoistedJs = tmpState.hoisted.length > 0 ? tmpState.hoisted.join('; ') + '; ' : '';
167
- if (!body) {
168
- if (hoistedJs) {
169
- return `(()-> { ${hoistedJs}return null; })()`;
170
- }
171
- return 'null';
172
- }
173
- stmts = splitJsStatements(body);
174
- jsx = stmts.length === 0 ? 'null' : stmts.length === 1 ? stmts[0] : `jsxs(Fragment, { children: [${stmts.join(', ')}] })`;
175
- if (hoistedJs) {
176
- return `(()-> { ${hoistedJs}return ${jsx}; })()`;
177
- }
178
- return jsx;
179
- };
180
-
181
- splitJsStatements = function(body) {
182
- var ch, depth, i, inString, last, start, stmt, stmts;
183
- stmts = [];
184
- depth = 0;
185
- inString = null;
186
- start = 0;
187
- i = 0;
188
- while (i < body.length) {
189
- ch = body[i];
190
- if (inString) {
191
- if (ch === '\\') {
192
- i++;
193
- continue;
194
- }
195
- if (ch === inString) {
196
- inString = null;
197
- }
198
- i++;
199
- continue;
200
- }
201
- if (ch === "'" || ch === '"' || ch === '`') {
202
- inString = ch;
203
- i++;
204
- continue;
205
- }
206
- if (ch === '{' || ch === '[' || ch === '(') {
207
- depth++;
208
- i++;
209
- continue;
210
- }
211
- if (ch === '}' || ch === ']' || ch === ')') {
212
- depth--;
213
- i++;
214
- continue;
215
- }
216
- if (ch === ';' && depth === 0 && body[i + 1] === '\n') {
217
- stmt = body.slice(start, i).trim();
218
- if (stmt) {
219
- stmts.push(stmt);
220
- }
221
- start = i + 2;
222
- i++;
223
- }
224
- i++;
225
- }
226
- last = body.slice(start).trim();
227
- if (last) {
228
- stmts.push(last.endsWith(';') ? last.slice(0, -1).trim() : last);
229
- }
230
- return stmts;
231
- };
232
-
233
- // ─── Node Emitters ─────────────────────────────────────────
234
- emitNodes = function(nodes, state, isRoot = false) {
235
- var i, node, remaining;
236
- i = 0;
237
- while (i < nodes.length) {
238
- node = nodes[i];
239
- if (node instanceof ControlFlow) {
240
- remaining = nodes.slice(i + 1);
241
- emitControlFlow(node, remaining, state, isRoot);
242
- return;
243
- } else {
244
- emitNode(node, state, isRoot);
245
- }
246
- i++;
247
- }
248
- };
249
-
250
- emitNode = function(node, state, isRoot) {
251
- if (node instanceof Element) {
252
- return emitElement(node, state);
253
- } else if (node instanceof ImplicitDiv) {
254
- return emitImplicitDiv(node, state);
255
- } else if (node instanceof Text) {
256
- return emitText(node, state);
257
- } else if (node instanceof Output) {
258
- return emitOutput(node, state);
259
- } else if (node instanceof ControlFlow) {
260
- return emitControlFlow(node, [], state, isRoot);
261
- } else if (node instanceof Comment) {
262
- return emitComment(node, state);
263
- } else if (node instanceof Filter) {
264
- return emitFilter(node, state);
265
- } else if (node instanceof Doctype) {
266
- return emitDoctype(node, state);
267
- }
268
- };
269
-
270
- // ─── Element ───────────────────────────────────────────────
271
- emitElement = function(el, state) {
272
- var allAttrs, attrResult, attrs, attrsJs, childJs, childParts, childrenAttr, hasChildren, isVoid, keyArg, tagJs;
273
- mapSourceLocation(el.location, state);
274
- isVoid = typeof el.tag === 'string' && VOID_ELEMENTS.has(el.tag);
275
- hasChildren = !isVoid && el.children.length > 0;
276
- tagJs = tagToJs(el);
277
- attrResult = buildAttributes(el);
278
- attrs = attrResult.parts;
279
- keyArg = attrResult.keyExpr ? `, ${attrResult.keyExpr}` : '';
280
- if (hasChildren) {
281
- if (el.children.length === 1) {
282
- childJs = emitChildToJs(el.children[0], state);
283
- childrenAttr = `children: ${childJs}`;
284
- allAttrs = attrs.length > 0 ? `{ ${attrs.join(', ')}, ${childrenAttr} }` : `{ ${childrenAttr} }`;
285
- return state.emitLine(`jsx(${tagJs}, ${allAttrs}${keyArg});`);
286
- } else {
287
- childParts = el.children.map(function(c) {
288
- return emitChildToJs(c, state);
289
- });
290
- childrenAttr = `children: [${childParts.join(', ')}]`;
291
- allAttrs = attrs.length > 0 ? `{ ${attrs.join(', ')}, ${childrenAttr} }` : `{ ${childrenAttr} }`;
292
- return state.emitLine(`jsxs(${tagJs}, ${allAttrs}${keyArg});`);
293
- }
294
- } else {
295
- attrsJs = attrs.length > 0 ? `{ ${attrs.join(', ')} }` : 'null';
296
- return state.emitLine(`jsx(${tagJs}, ${attrsJs}${keyArg});`);
297
- }
298
- };
299
-
300
- tagToJs = function(el) {
301
- if (el.tag instanceof Expression) {
302
- return compileExpression(el.tag, el.location);
303
- }
304
- if (el.isComponent) {
305
- return el.tag;
306
- }
307
- return `\"${el.tag}\"`;
308
- };
309
-
310
- buildAttributes = function(el) {
311
- var attr, classNames, filteredParts, j, joined, k, keyExpr, len, len1, name, p, parts, ref, unique, valJs;
312
- parts = [];
313
- if (el.id) {
314
- parts.push(`id: \"${el.id}\"`);
315
- }
316
- classNames = el.classes.slice();
317
- ref = el.attributes;
318
- for (j = 0, len = ref.length; j < len; j++) {
319
- attr = ref[j];
320
- if (attr.spread) {
321
- valJs = compileExpression(attr.expression);
322
- parts.push(`...${valJs}`);
323
- continue;
324
- }
325
- valJs = compileExpression(attr.value);
326
- name = attrNameToJs(attr.name);
327
- if (name === 'className' || name === 'class') {
328
- if (attr.shorthand) {
329
- classNames.push(`$\{${valJs}}`);
330
- } else {
331
- classNames.push(valJs.replace(/^['"]|['"]$/g, ''));
332
- }
333
- } else {
334
- parts.push(`${name}: ${valJs}`);
335
- }
336
- }
337
- if (classNames.length > 0) {
338
- unique = [...new Set(classNames)];
339
- if (unique.length === 1 && !unique[0].includes('${')) {
340
- parts.push(`className: \"${unique[0]}\"`);
341
- } else {
342
- joined = unique.map(function(c) {
343
- if (c.includes('${')) {
344
- return c;
345
- } else {
346
- return `\"${c}\"`;
347
- }
348
- }).join(' + " " + ');
349
- parts.push(`className: ${joined}`);
350
- }
351
- }
352
- // Extract key
353
- keyExpr = null;
354
- filteredParts = [];
355
- for (k = 0, len1 = parts.length; k < len1; k++) {
356
- p = parts[k];
357
- if (p.startsWith('key: ')) {
358
- keyExpr = p.slice(5).trim();
359
- } else {
360
- filteredParts.push(p);
361
- }
362
- }
363
- return {
364
- parts: filteredParts,
365
- keyExpr: keyExpr
366
- };
367
- };
368
-
369
- attrNameToJs = function(name) {
370
- if (name === 'class') {
371
- return 'className';
372
- }
373
- if (name === 'for') {
374
- return 'htmlFor';
375
- }
376
- if (name.includes('-')) {
377
- return `\"${name}\"`;
378
- }
379
- return name;
380
- };
381
-
382
- emitImplicitDiv = function(div, state) {
383
- var el;
384
- mapSourceLocation(div.location, state);
385
- el = new Element('div', {
386
- classes: div.classes,
387
- id: div.id,
388
- attributes: div.attributes,
389
- children: div.children,
390
- isComponent: false,
391
- isSelfClosing: false,
392
- location: div.location
393
- });
394
- return emitElement(el, state);
395
- };
396
-
397
- // ─── Child Emission ────────────────────────────────────────
398
- emitChildToJs = function(node, state) {
399
- var allAttrs, attrResult, attrs, attrsJs, c, childParts, childrenAttr, el, exprSource, fn, hasChildren, keyArg, tagJs, textContinuations;
400
- if (node instanceof Text) {
401
- return escapeString(node.value);
402
- }
403
- if (node instanceof Output) {
404
- // Arrow continuation
405
- if (node.children.length > 0 && /->\s*$/.test(node.expression.source.trim())) {
406
- return emitArrowOutputToJs(node.expression.source, node.children, state);
407
- }
408
- textContinuations = (function() {
409
- var j, len, ref, results;
410
- ref = node.children;
411
- results = [];
412
- for (j = 0, len = ref.length; j < len; j++) {
413
- c = ref[j];
414
- if (c instanceof Text) {
415
- results.push(c.value);
416
- }
417
- }
418
- return results;
419
- })();
420
- exprSource = textContinuations.length > 0 ? [node.expression.source, ...textContinuations].join(' ') : node.expression.source;
421
- return compileExpression(new Expression(exprSource), node.location);
422
- }
423
- if (node instanceof Element) {
424
- hasChildren = node.children.length > 0;
425
- tagJs = tagToJs(node);
426
- attrResult = buildAttributes(node);
427
- attrs = attrResult.parts;
428
- keyArg = attrResult.keyExpr ? `, ${attrResult.keyExpr}` : '';
429
- if (hasChildren) {
430
- childParts = node.children.map(function(c) {
431
- return emitChildToJs(c, state);
432
- });
433
- childrenAttr = childParts.length === 1 ? `children: ${childParts[0]}` : `children: [${childParts.join(', ')}]`;
434
- fn = childParts.length === 1 ? 'jsx' : 'jsxs';
435
- allAttrs = attrs.length > 0 ? `{ ${attrs.join(', ')}, ${childrenAttr} }` : `{ ${childrenAttr} }`;
436
- return `${fn}(${tagJs}, ${allAttrs}${keyArg})`;
437
- }
438
- attrsJs = attrs.length > 0 ? `{ ${attrs.join(', ')} }` : 'null';
439
- return `jsx(${tagJs}, ${attrsJs}${keyArg})`;
440
- }
441
- if (node instanceof ImplicitDiv) {
442
- el = new Element('div', {
443
- classes: node.classes,
444
- id: node.id,
445
- attributes: node.attributes,
446
- children: node.children,
447
- isComponent: false,
448
- isSelfClosing: false,
449
- location: node.location
450
- });
451
- return emitChildToJs(el, state);
452
- }
453
- if (node instanceof ControlFlow) {
454
- return emitControlFlowToJs(node, state);
455
- }
456
- if (node instanceof Filter) {
457
- return emitFilterToJs(node, state);
458
- }
459
- return 'null';
460
- };
461
-
462
- // ─── Control Flow ──────────────────────────────────────────
463
- emitControlFlow = function(cf, remaining, state, _isRoot) {
464
- if (cf.controlKind === 'statement') {
465
- return emitStatement(cf, remaining, state);
466
- } else if (cf.isLoop()) {
467
- return emitLoop(cf, remaining, state);
468
- } else {
469
- return emitConditional(cf, remaining, state);
470
- }
471
- };
472
-
473
- emitStatement = function(cf, remaining, state) {
474
- var child, j, js, k, len, len1, ref, s, stmts;
475
- js = compileStatement(cf.expression.source.trim(), cf.location);
476
- stmts = [js];
477
- ref = cf.children;
478
- for (j = 0, len = ref.length; j < len; j++) {
479
- child = ref[j];
480
- if (child instanceof Text) {
481
- stmts.push(compileStatement(child.value, child.location));
482
- } else {
483
- emitNode(child, state, false);
484
- }
485
- }
486
- if (state.directEmit) {
487
- for (k = 0, len1 = stmts.length; k < len1; k++) {
488
- s = stmts[k];
489
- state.emitLine(s + ';');
490
- }
491
- } else {
492
- state.hoisted.push(...stmts);
493
- }
494
- if (remaining.length > 0) {
495
- return emitNodes(remaining, state);
496
- }
497
- };
498
-
499
- emitLoop = function(cf, remaining, state) {
500
- var cond, expr, forMatch, iterable, vars;
501
- if (cf.controlKind === 'for') {
502
- expr = cf.expression.source.trim();
503
- forMatch = expr.match(/^(.+?)\s+in\s+(.+)$/);
504
- if (forMatch) {
505
- vars = forMatch[1].trim();
506
- iterable = forMatch[2].trim();
507
- state.emit(`(${compileExpression(new Expression(iterable), cf.location)}).map((${vars}) => `);
508
- if (cf.children.length === 0) {
509
- state.output += 'null';
510
- } else if (cf.children.length === 1) {
511
- state.output += emitChildToJs(cf.children[0], state);
512
- } else {
513
- state.output += `jsxs(Fragment, { children: [${cf.children.map(function(c) {
514
- return emitChildToJs(c, state);
515
- }).join(', ')}] })`;
516
- }
517
- state.output += ')';
518
- if (remaining.length > 0) {
519
- state.output += ',\n';
520
- emitNodes(remaining, state);
521
- } else {
522
- state.output += ';\n';
523
- }
524
- return;
525
- }
526
- }
527
- if (cf.controlKind === 'while') {
528
- cond = compileExpression(cf.expression, cf.location);
529
- state.emitLine('(() => {');
530
- state.indentBlock(function() {
531
- state.emitLine('const __result = [];');
532
- state.emitLine(`while (${cond}) {`);
533
- state.indentBlock(function() {
534
- if (cf.children.length === 1 && cf.children[0] instanceof Element) {
535
- return state.emit(`__result.push(${emitChildToJs(cf.children[0], state)});\n`);
536
- } else {
537
- return state.emit(`__result.push(jsxs(Fragment, { children: [${cf.children.map(function(c) {
538
- return emitChildToJs(c, state);
539
- }).join(', ')}] }));\n`);
540
- }
541
- });
542
- state.emitLine('}');
543
- return state.emitLine('return __result;');
544
- });
545
- state.emit('})()');
546
- if (remaining.length > 0) {
547
- state.output += ',\n';
548
- emitNodes(remaining, state);
549
- } else {
550
- state.output += ';\n';
551
- }
552
- return;
553
- }
554
- return state.emitLine(`/* TODO: emit loop ${cf.controlKind} */`);
555
- };
556
-
557
- emitConditional = function(cf, remaining, state) {
558
- var condition;
559
- if (cf.controlKind === 'else') {
560
- emitElseBranch(cf, state);
561
- return;
562
- }
563
- condition = compileExpression(cf.expression, cf.location);
564
- if (cf.controlKind === 'unless') {
565
- state.emit(`!(${condition}) ? `);
566
- } else {
567
- state.emit(`${condition} ? `);
568
- }
569
- // Then branch
570
- if (cf.children.length === 0) {
571
- state.output += 'null';
572
- } else if (cf.children.length === 1) {
573
- state.output += emitChildToJs(cf.children[0], state);
574
- } else {
575
- state.output += `jsxs(Fragment, { children: [${cf.children.map(function(c) {
576
- return emitChildToJs(c, state);
577
- }).join(', ')}] })`;
578
- }
579
- state.output += ' : ';
580
- if (cf.next) {
581
- emitConditional(cf.next, [], state);
582
- } else if (remaining.length > 0) {
583
- if (remaining.length === 1) {
584
- state.output += emitChildToJs(remaining[0], state);
585
- } else {
586
- state.output += `jsxs(Fragment, { children: [${remaining.map(function(c) {
587
- return emitChildToJs(c, state);
588
- }).join(', ')}] })`;
589
- }
590
- } else {
591
- state.output += 'null';
592
- }
593
- return state.output += ';\n';
594
- };
595
-
596
- emitElseBranch = function(cf, state) {
597
- if (cf.children.length === 0) {
598
- return state.output += 'null';
599
- } else if (cf.children.length === 1) {
600
- return state.output += emitChildToJs(cf.children[0], state);
601
- } else {
602
- return state.output += `jsxs(Fragment, {}, ${cf.children.map(function(c) {
603
- return emitChildToJs(c, state);
604
- }).join(', ')})`;
605
- }
606
- };
607
-
608
- emitControlFlowToJs = function(cf, state) {
609
- var alternate, body, compiledIterable, condExpr, condition, consequent, expr, forMatch, iterable, js, vars;
610
- if (cf.isLoop() && cf.controlKind === 'for') {
611
- expr = cf.expression.source.trim();
612
- forMatch = expr.match(/^(.+?)\s+in\s+(.+)$/);
613
- if (forMatch) {
614
- vars = forMatch[1].trim();
615
- iterable = forMatch[2].trim();
616
- compiledIterable = compileExpression(new Expression(iterable), cf.location);
617
- body = cf.children.length === 1 ? emitChildToJs(cf.children[0], state) : `jsxs(Fragment, { children: [${cf.children.map(function(c) {
618
- return emitChildToJs(c, state);
619
- }).join(', ')}] })`;
620
- return `(${compiledIterable}).map((${vars}) => ${body})`;
621
- }
622
- }
623
- if (cf.controlKind === 'else') {
624
- if (cf.children.length === 1) {
625
- return emitChildToJs(cf.children[0], state);
626
- } else if (cf.children.length > 0) {
627
- return `jsxs(Fragment, {}, ${cf.children.map(function(c) {
628
- return emitChildToJs(c, state);
629
- }).join(', ')})`;
630
- } else {
631
- return 'null';
632
- }
633
- }
634
- if (cf.isConditional()) {
635
- condition = compileExpression(cf.expression, cf.location);
636
- condExpr = cf.controlKind === 'unless' ? `!(${condition})` : condition;
637
- consequent = cf.children.length === 1 ? emitChildToJs(cf.children[0], state) : cf.children.length > 0 ? `jsxs(Fragment, {}, ${cf.children.map(function(c) {
638
- return emitChildToJs(c, state);
639
- }).join(', ')})` : 'null';
640
- alternate = cf.next ? emitControlFlowToJs(cf.next, state) : 'null';
641
- return `${condExpr} ? ${consequent} : ${alternate}`;
642
- }
643
- if (cf.controlKind === 'statement') {
644
- js = compileExpression(cf.expression, cf.location);
645
- return `/* - ${js} */ null`;
646
- }
647
- return 'null';
648
- };
649
-
650
- // ─── Text / Output / Comment / Filter / Doctype ────────────
651
- emitText = function(text, state) {
652
- return state.emitLine(`\"${escapeString(text.value)}\";`);
653
- };
654
-
655
- emitOutput = function(out, state) {
656
- var child, elementChildren, exprSource, j, js, k, len, len1, ref, result, textLines;
657
- textLines = [];
658
- elementChildren = [];
659
- ref = out.children;
660
- for (j = 0, len = ref.length; j < len; j++) {
661
- child = ref[j];
662
- if (child instanceof Text) {
663
- textLines.push(child.value);
664
- } else {
665
- elementChildren.push(child);
666
- }
667
- }
668
- exprSource = out.expression.source;
669
- if (textLines.length > 0) {
670
- exprSource = [exprSource, ...textLines].join(' ');
671
- }
672
- // Arrow continuation
673
- if (elementChildren.length > 0 && /->\s*$/.test(exprSource.trim())) {
674
- emitArrowOutput(exprSource, elementChildren, out, state);
675
- return;
676
- }
677
- js = compileExpression(new Expression(exprSource), out.location);
678
- result = out.outputKind === 'escaped' ? `jsx(Fragment, { children: ${js} })` : `jsx(Fragment, {}, ${js})`;
679
- for (k = 0, len1 = elementChildren.length; k < len1; k++) {
680
- child = elementChildren[k];
681
- emitNode(child, state, false);
682
- }
683
- return state.emitLine(result + ';');
684
- };
685
-
686
- emitArrowOutput = function(exprSource, elementChildren, out, state) {
687
- var arrowArgs, arrowFn, arrowMatch, body, callTarget, compiledTarget, result, stripped;
688
- stripped = exprSource.replace(/\s*->\s*$/, '').trim();
689
- arrowMatch = stripped.match(/^(.+?)\s*\(([^)]*)\)\s*$/);
690
- if (arrowMatch) {
691
- callTarget = arrowMatch[1].trim();
692
- arrowArgs = arrowMatch[2].trim();
693
- } else {
694
- callTarget = stripped;
695
- arrowArgs = '';
696
- }
697
- compiledTarget = compileExpression(new Expression(callTarget), out.location);
698
- body = elementChildren.length === 1 ? emitChildToJs(elementChildren[0], state) : `jsxs(Fragment, { children: [${elementChildren.map(function(c) {
699
- return emitChildToJs(c, state);
700
- }).join(', ')}] })`;
701
- arrowFn = arrowArgs ? `(${arrowArgs}) => ${body}` : `() => ${body}`;
702
- result = out.outputKind === 'escaped' ? `jsx(Fragment, { children: ${compiledTarget}(${arrowFn}) })` : `jsx(Fragment, {}, ${compiledTarget}(${arrowFn}))`;
703
- return state.emitLine(result + ';');
704
- };
705
-
706
- emitArrowOutputToJs = function(exprSource, elementChildren, state) {
707
- var arrowArgs, arrowFn, arrowMatch, body, callTarget, compiledTarget, stripped;
708
- stripped = exprSource.replace(/\s*->\s*$/, '').trim();
709
- arrowMatch = stripped.match(/^(.+?)\s*\(([^)]*)\)\s*$/);
710
- if (arrowMatch) {
711
- callTarget = arrowMatch[1].trim();
712
- arrowArgs = arrowMatch[2].trim();
713
- } else {
714
- callTarget = stripped;
715
- arrowArgs = '';
716
- }
717
- compiledTarget = compileExpression(new Expression(callTarget));
718
- body = elementChildren.length === 1 ? emitChildToJs(elementChildren[0], state) : `jsxs(Fragment, { children: [${elementChildren.map(function(c) {
719
- return emitChildToJs(c, state);
720
- }).join(', ')}] })`;
721
- arrowFn = arrowArgs ? `(${arrowArgs}) => ${body}` : `() => ${body}`;
722
- return `${compiledTarget}(${arrowFn})`;
723
- };
724
-
725
- emitComment = function(comment, state) {
726
- if (comment.commentKind === 'html') {
727
- return state.emitLine(`/* <!-- ${comment.text} --> */`);
728
- }
729
- };
730
-
731
- emitFilter = function(filter, state) {
732
- var content, filterName, handler, html, ref;
733
- ({filterName, content} = filter);
734
- handler = (ref = state.options.filters) != null ? ref[filterName] : void 0;
735
- if (handler) {
736
- html = handler(content, filterName);
737
- state.emitLine(`jsx(Fragment, { dangerouslySetInnerHTML: { __html: ${JSON.stringify(html)} } })`);
738
- return;
739
- }
740
- switch (filterName) {
741
- case 'css':
742
- state.warnings.push({
743
- message: ':css filter emits raw CSS string — not usable as a <style> tag in React. Use a plugin handler to inject CSS properly.',
744
- location: filter.location,
745
- phase: 'emitter'
746
- });
747
- return state.emitLine(`\"${escapeString(content)}\";`);
748
- case 'javascript':
749
- state.warnings.push({
750
- message: ':javascript filter emits raw JS at module level — an XSS risk with untrusted input. Ensure content is trusted.',
751
- location: filter.location,
752
- phase: 'emitter'
753
- });
754
- return state.emitLine(content);
755
- case 'coffee':
756
- return state.emitLine(content);
757
- default:
758
- state.warnings.push({
759
- message: `:${filterName} filter used but no handler registered. Add one to compilerOptions.filters.`,
760
- location: filter.location,
761
- phase: 'emitter'
762
- });
763
- return state.emitLine(`\"${escapeString(content)}\";`);
764
- }
765
- };
766
-
767
- emitFilterToJs = function(filter, state) {
768
- var content, filterName, handler, html, ref;
769
- ({filterName, content} = filter);
770
- handler = (ref = state.options.filters) != null ? ref[filterName] : void 0;
771
- if (handler) {
772
- html = handler(content, filterName);
773
- return `jsx(Fragment, { dangerouslySetInnerHTML: { __html: ${JSON.stringify(html)} } })`;
774
- }
775
- return escapeString(content);
776
- };
777
-
778
- emitDoctype = function(doctype, state) {
779
- return state.emitLine(`// DOCTYPE ${doctype.value || 'html'}`);
780
- };
781
-
782
- // ─── Helpers ───────────────────────────────────────────────
783
- escapeString = function(s) {
784
- return JSON.stringify(s);
785
- };
786
-
787
- mapSourceLocation = function(loc, state) {
788
- if (loc) {
789
- return state.mapSource(loc.start);
790
- }
791
- };