@collie-lang/compiler 1.0.0 → 2.0.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/index.cjs +134 -207
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +134 -207
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -334,175 +334,149 @@ function escapeText(value) {
|
|
|
334
334
|
}
|
|
335
335
|
|
|
336
336
|
// src/html-codegen.ts
|
|
337
|
-
function
|
|
337
|
+
function generateHtml(root, options = {}) {
|
|
338
|
+
const indent = options.indent ?? " ";
|
|
338
339
|
const aliasEnv = buildClassAliasEnvironment2(root.classAliases);
|
|
339
|
-
const
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
parts.push(`/** @returns {string} */`);
|
|
350
|
-
const lines = [`export default function ${options.componentName}(props = {}) {`];
|
|
351
|
-
if (propsDestructure) {
|
|
352
|
-
lines.push(` ${propsDestructure}`);
|
|
353
|
-
}
|
|
354
|
-
lines.push(` const __collie_html = ${htmlExpression};`);
|
|
355
|
-
lines.push(" return __collie_html;", "}");
|
|
356
|
-
parts.push(lines.join("\n"));
|
|
357
|
-
return parts.join("\n\n");
|
|
358
|
-
}
|
|
359
|
-
function emitNodesString(children, aliasEnv) {
|
|
360
|
-
if (children.length === 0) {
|
|
361
|
-
return '""';
|
|
340
|
+
const rendered = emitNodes(root.children, aliasEnv, indent, 0);
|
|
341
|
+
return rendered.trimEnd();
|
|
342
|
+
}
|
|
343
|
+
function emitNodes(children, aliasEnv, indent, depth) {
|
|
344
|
+
let html = "";
|
|
345
|
+
for (const child of children) {
|
|
346
|
+
const chunk = emitNode(child, aliasEnv, indent, depth);
|
|
347
|
+
if (chunk) {
|
|
348
|
+
html += chunk;
|
|
349
|
+
}
|
|
362
350
|
}
|
|
363
|
-
|
|
364
|
-
return concatSegments(segments);
|
|
351
|
+
return html;
|
|
365
352
|
}
|
|
366
|
-
function
|
|
353
|
+
function emitNode(node, aliasEnv, indent, depth) {
|
|
367
354
|
switch (node.type) {
|
|
368
|
-
case "Text":
|
|
369
|
-
return emitTextNode(node);
|
|
370
|
-
case "Expression":
|
|
371
|
-
return `__collie_escapeHtml(${node.value})`;
|
|
372
|
-
case "JSXPassthrough":
|
|
373
|
-
return `String(${node.expression})`;
|
|
374
355
|
case "Element":
|
|
375
|
-
return
|
|
376
|
-
case "
|
|
377
|
-
return
|
|
378
|
-
case "Conditional":
|
|
379
|
-
return emitConditional(node, aliasEnv);
|
|
380
|
-
case "For":
|
|
381
|
-
return emitFor(node, aliasEnv);
|
|
356
|
+
return emitElement2(node, aliasEnv, indent, depth);
|
|
357
|
+
case "Text":
|
|
358
|
+
return emitTextBlock(node, indent, depth);
|
|
382
359
|
default:
|
|
383
|
-
return
|
|
360
|
+
return "";
|
|
384
361
|
}
|
|
385
362
|
}
|
|
386
|
-
function emitElement2(node, aliasEnv) {
|
|
387
|
-
const
|
|
388
|
-
const
|
|
389
|
-
const
|
|
363
|
+
function emitElement2(node, aliasEnv, indent, depth) {
|
|
364
|
+
const indentText = indent.repeat(depth);
|
|
365
|
+
const classNames = expandClasses2(node.classes, aliasEnv);
|
|
366
|
+
const attrs = renderAttributes(node.attributes, classNames);
|
|
367
|
+
const openTag = `<${node.name}${attrs}>`;
|
|
390
368
|
if (node.children.length === 0) {
|
|
391
|
-
return
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
const closingToken = hasChildren ? ">" : " />";
|
|
401
|
-
const start = concatSegments([literal(`<${node.name}`), ...attributeSegments, literal(closingToken)]);
|
|
402
|
-
if (!hasChildren) {
|
|
403
|
-
return start;
|
|
404
|
-
}
|
|
405
|
-
const childSegments = [];
|
|
406
|
-
if (node.children.length) {
|
|
407
|
-
childSegments.push(emitNodesString(node.children, aliasEnv));
|
|
369
|
+
return `${indentText}${openTag}</${node.name}>
|
|
370
|
+
`;
|
|
371
|
+
}
|
|
372
|
+
if (node.children.length === 1 && node.children[0].type === "Text") {
|
|
373
|
+
const inline = emitInlineText(node.children[0]);
|
|
374
|
+
if (inline !== null) {
|
|
375
|
+
return `${indentText}${openTag}${inline}</${node.name}>
|
|
376
|
+
`;
|
|
377
|
+
}
|
|
408
378
|
}
|
|
409
|
-
|
|
410
|
-
|
|
379
|
+
const children = emitNodes(node.children, aliasEnv, indent, depth + 1);
|
|
380
|
+
if (!children) {
|
|
381
|
+
return `${indentText}${openTag}</${node.name}>
|
|
382
|
+
`;
|
|
411
383
|
}
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
}
|
|
416
|
-
function emitSlotTemplate(slot, aliasEnv) {
|
|
417
|
-
const start = literal(`<template slot="${slot.name}">`);
|
|
418
|
-
const body = emitNodesString(slot.children, aliasEnv);
|
|
419
|
-
const end = literal("</template>");
|
|
420
|
-
return concatSegments([start, body, end]);
|
|
384
|
+
return `${indentText}${openTag}
|
|
385
|
+
${children}${indentText}</${node.name}>
|
|
386
|
+
`;
|
|
421
387
|
}
|
|
422
|
-
function
|
|
423
|
-
|
|
424
|
-
|
|
388
|
+
function renderAttributes(attributes, classNames) {
|
|
389
|
+
const segments = [];
|
|
390
|
+
if (classNames.length) {
|
|
391
|
+
segments.push(`class="${escapeAttributeValue(classNames.join(" "))}"`);
|
|
425
392
|
}
|
|
426
|
-
const
|
|
427
|
-
|
|
428
|
-
|
|
393
|
+
for (const attr of attributes) {
|
|
394
|
+
if (attr.value === null) {
|
|
395
|
+
segments.push(attr.name);
|
|
396
|
+
continue;
|
|
397
|
+
}
|
|
398
|
+
const literal = extractStaticAttributeValue(attr.value);
|
|
399
|
+
if (literal === null) {
|
|
400
|
+
continue;
|
|
401
|
+
}
|
|
402
|
+
const name = attr.name === "className" ? "class" : attr.name;
|
|
403
|
+
segments.push(`${name}="${escapeAttributeValue(literal)}"`);
|
|
429
404
|
}
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
const limit = hasElse ? node.branches.length - 2 : node.branches.length - 1;
|
|
433
|
-
for (let i = limit; i >= 0; i--) {
|
|
434
|
-
const branch = node.branches[i];
|
|
435
|
-
const test = branch.test ?? "false";
|
|
436
|
-
fallback = `(${test}) ? ${emitBranch(branch, aliasEnv)} : ${fallback}`;
|
|
405
|
+
if (!segments.length) {
|
|
406
|
+
return "";
|
|
437
407
|
}
|
|
438
|
-
return
|
|
439
|
-
}
|
|
440
|
-
function emitBranch(branch, aliasEnv) {
|
|
441
|
-
return emitNodesString(branch.body, aliasEnv);
|
|
408
|
+
return " " + segments.join(" ");
|
|
442
409
|
}
|
|
443
|
-
function
|
|
444
|
-
const
|
|
445
|
-
|
|
410
|
+
function emitTextBlock(node, indent, depth) {
|
|
411
|
+
const inline = emitInlineText(node);
|
|
412
|
+
if (inline === null || inline.trim().length === 0) {
|
|
413
|
+
return "";
|
|
414
|
+
}
|
|
415
|
+
return `${indent.repeat(depth)}${inline}
|
|
416
|
+
`;
|
|
446
417
|
}
|
|
447
|
-
function
|
|
418
|
+
function emitInlineText(node) {
|
|
448
419
|
if (!node.parts.length) {
|
|
449
|
-
return
|
|
420
|
+
return "";
|
|
450
421
|
}
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
422
|
+
let text = "";
|
|
423
|
+
for (const part of node.parts) {
|
|
424
|
+
if (part.type !== "text") {
|
|
425
|
+
return null;
|
|
454
426
|
}
|
|
455
|
-
|
|
456
|
-
}
|
|
457
|
-
return
|
|
427
|
+
text += escapeStaticText(part.value);
|
|
428
|
+
}
|
|
429
|
+
return text;
|
|
458
430
|
}
|
|
459
|
-
function
|
|
460
|
-
const
|
|
461
|
-
if (
|
|
462
|
-
|
|
431
|
+
function extractStaticAttributeValue(raw) {
|
|
432
|
+
const trimmed = raw.trim();
|
|
433
|
+
if (trimmed.length < 2) {
|
|
434
|
+
return null;
|
|
463
435
|
}
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
436
|
+
const quote = trimmed[0];
|
|
437
|
+
if (quote !== '"' && quote !== "'" || trimmed[trimmed.length - 1] !== quote) {
|
|
438
|
+
return null;
|
|
439
|
+
}
|
|
440
|
+
const body = trimmed.slice(1, -1);
|
|
441
|
+
let result = "";
|
|
442
|
+
let escaping = false;
|
|
443
|
+
for (const char of body) {
|
|
444
|
+
if (escaping) {
|
|
445
|
+
result += unescapeChar(char, quote);
|
|
446
|
+
escaping = false;
|
|
467
447
|
continue;
|
|
468
448
|
}
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
` return __collie_attr == null ? "" : ${literal(` ${attr.name}="`)} + __collie_escapeAttr(__collie_attr) + ${literal(`"`)};`,
|
|
475
|
-
"})()"
|
|
476
|
-
].join(" ")
|
|
477
|
-
);
|
|
478
|
-
}
|
|
479
|
-
return segments;
|
|
480
|
-
}
|
|
481
|
-
function attributeExpression(raw) {
|
|
482
|
-
const trimmed = raw.trim();
|
|
483
|
-
if (trimmed.startsWith("{") && trimmed.endsWith("}")) {
|
|
484
|
-
return trimmed.slice(1, -1).trim();
|
|
449
|
+
if (char === "\\") {
|
|
450
|
+
escaping = true;
|
|
451
|
+
} else {
|
|
452
|
+
result += char;
|
|
453
|
+
}
|
|
485
454
|
}
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
function wrapWithGuard2(rendered, guard) {
|
|
489
|
-
if (!guard) {
|
|
490
|
-
return rendered;
|
|
455
|
+
if (escaping) {
|
|
456
|
+
result += "\\";
|
|
491
457
|
}
|
|
492
|
-
return
|
|
493
|
-
}
|
|
494
|
-
function literal(text) {
|
|
495
|
-
return JSON.stringify(text);
|
|
458
|
+
return result;
|
|
496
459
|
}
|
|
497
|
-
function
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
460
|
+
function unescapeChar(char, quote) {
|
|
461
|
+
switch (char) {
|
|
462
|
+
case "n":
|
|
463
|
+
return "\n";
|
|
464
|
+
case "r":
|
|
465
|
+
return "\r";
|
|
466
|
+
case "t":
|
|
467
|
+
return " ";
|
|
468
|
+
case "\\":
|
|
469
|
+
return "\\";
|
|
470
|
+
case '"':
|
|
471
|
+
return '"';
|
|
472
|
+
case "'":
|
|
473
|
+
return "'";
|
|
474
|
+
default:
|
|
475
|
+
if (char === quote) {
|
|
476
|
+
return quote;
|
|
477
|
+
}
|
|
478
|
+
return char;
|
|
504
479
|
}
|
|
505
|
-
return filtered.join(" + ");
|
|
506
480
|
}
|
|
507
481
|
function buildClassAliasEnvironment2(decl) {
|
|
508
482
|
const env = /* @__PURE__ */ new Map();
|
|
@@ -530,26 +504,6 @@ function expandClasses2(classes, aliasEnv) {
|
|
|
530
504
|
}
|
|
531
505
|
return result;
|
|
532
506
|
}
|
|
533
|
-
function emitJsDocPropsType2(props) {
|
|
534
|
-
if (!props) {
|
|
535
|
-
return "/** @typedef {any} Props */";
|
|
536
|
-
}
|
|
537
|
-
if (!props.fields.length) {
|
|
538
|
-
return "/** @typedef {{}} Props */";
|
|
539
|
-
}
|
|
540
|
-
const fields = props.fields.map((field) => {
|
|
541
|
-
const optional = field.optional ? "?" : "";
|
|
542
|
-
return `${field.name}${optional}: ${field.typeText}`;
|
|
543
|
-
}).join("; ");
|
|
544
|
-
return `/** @typedef {{ ${fields} }} Props */`;
|
|
545
|
-
}
|
|
546
|
-
function emitPropsDestructure2(props) {
|
|
547
|
-
if (!props || props.fields.length === 0) {
|
|
548
|
-
return null;
|
|
549
|
-
}
|
|
550
|
-
const names = props.fields.map((field) => field.name);
|
|
551
|
-
return `const { ${names.join(", ")} } = props;`;
|
|
552
|
-
}
|
|
553
507
|
function escapeStaticText(value) {
|
|
554
508
|
return value.replace(/[&<>{}]/g, (char) => {
|
|
555
509
|
switch (char) {
|
|
@@ -568,47 +522,21 @@ function escapeStaticText(value) {
|
|
|
568
522
|
}
|
|
569
523
|
});
|
|
570
524
|
}
|
|
571
|
-
function
|
|
572
|
-
return [
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
' return ">";',
|
|
587
|
-
" default:",
|
|
588
|
-
" return char;",
|
|
589
|
-
" }",
|
|
590
|
-
"}",
|
|
591
|
-
"function __collie_escapeAttr(value) {",
|
|
592
|
-
" if (value === null || value === undefined) {",
|
|
593
|
-
' return "";',
|
|
594
|
-
" }",
|
|
595
|
-
' return String(value).replace(/["&<>]/g, __collie_escapeAttrChar);',
|
|
596
|
-
"}",
|
|
597
|
-
"function __collie_escapeAttrChar(char) {",
|
|
598
|
-
" switch (char) {",
|
|
599
|
-
' case "&":',
|
|
600
|
-
' return "&";',
|
|
601
|
-
' case "<":',
|
|
602
|
-
' return "<";',
|
|
603
|
-
' case ">":',
|
|
604
|
-
' return ">";',
|
|
605
|
-
` case '"':`,
|
|
606
|
-
' return """;',
|
|
607
|
-
" default:",
|
|
608
|
-
" return char;",
|
|
609
|
-
" }",
|
|
610
|
-
"}"
|
|
611
|
-
];
|
|
525
|
+
function escapeAttributeValue(value) {
|
|
526
|
+
return value.replace(/["&<>]/g, (char) => {
|
|
527
|
+
switch (char) {
|
|
528
|
+
case "&":
|
|
529
|
+
return "&";
|
|
530
|
+
case "<":
|
|
531
|
+
return "<";
|
|
532
|
+
case ">":
|
|
533
|
+
return ">";
|
|
534
|
+
case '"':
|
|
535
|
+
return """;
|
|
536
|
+
default:
|
|
537
|
+
return char;
|
|
538
|
+
}
|
|
539
|
+
});
|
|
612
540
|
}
|
|
613
541
|
|
|
614
542
|
// src/diagnostics.ts
|
|
@@ -1969,10 +1897,9 @@ function compileToTsx(sourceOrAst, options = {}) {
|
|
|
1969
1897
|
function compileToHtml(sourceOrAst, options = {}) {
|
|
1970
1898
|
const document = normalizeDocument(sourceOrAst, options.filename);
|
|
1971
1899
|
const diagnostics = options.filename ? attachFilename(document.diagnostics, options.filename) : document.diagnostics;
|
|
1972
|
-
|
|
1973
|
-
let code = createStubHtml(componentName);
|
|
1900
|
+
let code = createStubHtml();
|
|
1974
1901
|
if (!hasErrors(diagnostics)) {
|
|
1975
|
-
code =
|
|
1902
|
+
code = generateHtml(document.root);
|
|
1976
1903
|
}
|
|
1977
1904
|
return { code, diagnostics, map: void 0 };
|
|
1978
1905
|
}
|
|
@@ -2014,8 +1941,8 @@ function createStubComponent(name, flavor) {
|
|
|
2014
1941
|
}
|
|
2015
1942
|
return [`export default function ${name}(props) {`, " return null;", "}"].join("\n");
|
|
2016
1943
|
}
|
|
2017
|
-
function createStubHtml(
|
|
2018
|
-
return
|
|
1944
|
+
function createStubHtml() {
|
|
1945
|
+
return "";
|
|
2019
1946
|
}
|
|
2020
1947
|
function attachFilename(diagnostics, filename) {
|
|
2021
1948
|
if (!filename) {
|