@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.js
CHANGED
|
@@ -303,175 +303,149 @@ function escapeText(value) {
|
|
|
303
303
|
}
|
|
304
304
|
|
|
305
305
|
// src/html-codegen.ts
|
|
306
|
-
function
|
|
306
|
+
function generateHtml(root, options = {}) {
|
|
307
|
+
const indent = options.indent ?? " ";
|
|
307
308
|
const aliasEnv = buildClassAliasEnvironment2(root.classAliases);
|
|
308
|
-
const
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
parts.push(`/** @returns {string} */`);
|
|
319
|
-
const lines = [`export default function ${options.componentName}(props = {}) {`];
|
|
320
|
-
if (propsDestructure) {
|
|
321
|
-
lines.push(` ${propsDestructure}`);
|
|
322
|
-
}
|
|
323
|
-
lines.push(` const __collie_html = ${htmlExpression};`);
|
|
324
|
-
lines.push(" return __collie_html;", "}");
|
|
325
|
-
parts.push(lines.join("\n"));
|
|
326
|
-
return parts.join("\n\n");
|
|
327
|
-
}
|
|
328
|
-
function emitNodesString(children, aliasEnv) {
|
|
329
|
-
if (children.length === 0) {
|
|
330
|
-
return '""';
|
|
309
|
+
const rendered = emitNodes(root.children, aliasEnv, indent, 0);
|
|
310
|
+
return rendered.trimEnd();
|
|
311
|
+
}
|
|
312
|
+
function emitNodes(children, aliasEnv, indent, depth) {
|
|
313
|
+
let html = "";
|
|
314
|
+
for (const child of children) {
|
|
315
|
+
const chunk = emitNode(child, aliasEnv, indent, depth);
|
|
316
|
+
if (chunk) {
|
|
317
|
+
html += chunk;
|
|
318
|
+
}
|
|
331
319
|
}
|
|
332
|
-
|
|
333
|
-
return concatSegments(segments);
|
|
320
|
+
return html;
|
|
334
321
|
}
|
|
335
|
-
function
|
|
322
|
+
function emitNode(node, aliasEnv, indent, depth) {
|
|
336
323
|
switch (node.type) {
|
|
337
|
-
case "Text":
|
|
338
|
-
return emitTextNode(node);
|
|
339
|
-
case "Expression":
|
|
340
|
-
return `__collie_escapeHtml(${node.value})`;
|
|
341
|
-
case "JSXPassthrough":
|
|
342
|
-
return `String(${node.expression})`;
|
|
343
324
|
case "Element":
|
|
344
|
-
return
|
|
345
|
-
case "
|
|
346
|
-
return
|
|
347
|
-
case "Conditional":
|
|
348
|
-
return emitConditional(node, aliasEnv);
|
|
349
|
-
case "For":
|
|
350
|
-
return emitFor(node, aliasEnv);
|
|
325
|
+
return emitElement2(node, aliasEnv, indent, depth);
|
|
326
|
+
case "Text":
|
|
327
|
+
return emitTextBlock(node, indent, depth);
|
|
351
328
|
default:
|
|
352
|
-
return
|
|
329
|
+
return "";
|
|
353
330
|
}
|
|
354
331
|
}
|
|
355
|
-
function emitElement2(node, aliasEnv) {
|
|
356
|
-
const
|
|
357
|
-
const
|
|
358
|
-
const
|
|
332
|
+
function emitElement2(node, aliasEnv, indent, depth) {
|
|
333
|
+
const indentText = indent.repeat(depth);
|
|
334
|
+
const classNames = expandClasses2(node.classes, aliasEnv);
|
|
335
|
+
const attrs = renderAttributes(node.attributes, classNames);
|
|
336
|
+
const openTag = `<${node.name}${attrs}>`;
|
|
359
337
|
if (node.children.length === 0) {
|
|
360
|
-
return
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
const closingToken = hasChildren ? ">" : " />";
|
|
370
|
-
const start = concatSegments([literal(`<${node.name}`), ...attributeSegments, literal(closingToken)]);
|
|
371
|
-
if (!hasChildren) {
|
|
372
|
-
return start;
|
|
373
|
-
}
|
|
374
|
-
const childSegments = [];
|
|
375
|
-
if (node.children.length) {
|
|
376
|
-
childSegments.push(emitNodesString(node.children, aliasEnv));
|
|
338
|
+
return `${indentText}${openTag}</${node.name}>
|
|
339
|
+
`;
|
|
340
|
+
}
|
|
341
|
+
if (node.children.length === 1 && node.children[0].type === "Text") {
|
|
342
|
+
const inline = emitInlineText(node.children[0]);
|
|
343
|
+
if (inline !== null) {
|
|
344
|
+
return `${indentText}${openTag}${inline}</${node.name}>
|
|
345
|
+
`;
|
|
346
|
+
}
|
|
377
347
|
}
|
|
378
|
-
|
|
379
|
-
|
|
348
|
+
const children = emitNodes(node.children, aliasEnv, indent, depth + 1);
|
|
349
|
+
if (!children) {
|
|
350
|
+
return `${indentText}${openTag}</${node.name}>
|
|
351
|
+
`;
|
|
380
352
|
}
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
}
|
|
385
|
-
function emitSlotTemplate(slot, aliasEnv) {
|
|
386
|
-
const start = literal(`<template slot="${slot.name}">`);
|
|
387
|
-
const body = emitNodesString(slot.children, aliasEnv);
|
|
388
|
-
const end = literal("</template>");
|
|
389
|
-
return concatSegments([start, body, end]);
|
|
353
|
+
return `${indentText}${openTag}
|
|
354
|
+
${children}${indentText}</${node.name}>
|
|
355
|
+
`;
|
|
390
356
|
}
|
|
391
|
-
function
|
|
392
|
-
|
|
393
|
-
|
|
357
|
+
function renderAttributes(attributes, classNames) {
|
|
358
|
+
const segments = [];
|
|
359
|
+
if (classNames.length) {
|
|
360
|
+
segments.push(`class="${escapeAttributeValue(classNames.join(" "))}"`);
|
|
394
361
|
}
|
|
395
|
-
const
|
|
396
|
-
|
|
397
|
-
|
|
362
|
+
for (const attr of attributes) {
|
|
363
|
+
if (attr.value === null) {
|
|
364
|
+
segments.push(attr.name);
|
|
365
|
+
continue;
|
|
366
|
+
}
|
|
367
|
+
const literal = extractStaticAttributeValue(attr.value);
|
|
368
|
+
if (literal === null) {
|
|
369
|
+
continue;
|
|
370
|
+
}
|
|
371
|
+
const name = attr.name === "className" ? "class" : attr.name;
|
|
372
|
+
segments.push(`${name}="${escapeAttributeValue(literal)}"`);
|
|
398
373
|
}
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
const limit = hasElse ? node.branches.length - 2 : node.branches.length - 1;
|
|
402
|
-
for (let i = limit; i >= 0; i--) {
|
|
403
|
-
const branch = node.branches[i];
|
|
404
|
-
const test = branch.test ?? "false";
|
|
405
|
-
fallback = `(${test}) ? ${emitBranch(branch, aliasEnv)} : ${fallback}`;
|
|
374
|
+
if (!segments.length) {
|
|
375
|
+
return "";
|
|
406
376
|
}
|
|
407
|
-
return
|
|
408
|
-
}
|
|
409
|
-
function emitBranch(branch, aliasEnv) {
|
|
410
|
-
return emitNodesString(branch.body, aliasEnv);
|
|
377
|
+
return " " + segments.join(" ");
|
|
411
378
|
}
|
|
412
|
-
function
|
|
413
|
-
const
|
|
414
|
-
|
|
379
|
+
function emitTextBlock(node, indent, depth) {
|
|
380
|
+
const inline = emitInlineText(node);
|
|
381
|
+
if (inline === null || inline.trim().length === 0) {
|
|
382
|
+
return "";
|
|
383
|
+
}
|
|
384
|
+
return `${indent.repeat(depth)}${inline}
|
|
385
|
+
`;
|
|
415
386
|
}
|
|
416
|
-
function
|
|
387
|
+
function emitInlineText(node) {
|
|
417
388
|
if (!node.parts.length) {
|
|
418
|
-
return
|
|
389
|
+
return "";
|
|
419
390
|
}
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
391
|
+
let text = "";
|
|
392
|
+
for (const part of node.parts) {
|
|
393
|
+
if (part.type !== "text") {
|
|
394
|
+
return null;
|
|
423
395
|
}
|
|
424
|
-
|
|
425
|
-
}
|
|
426
|
-
return
|
|
396
|
+
text += escapeStaticText(part.value);
|
|
397
|
+
}
|
|
398
|
+
return text;
|
|
427
399
|
}
|
|
428
|
-
function
|
|
429
|
-
const
|
|
430
|
-
if (
|
|
431
|
-
|
|
400
|
+
function extractStaticAttributeValue(raw) {
|
|
401
|
+
const trimmed = raw.trim();
|
|
402
|
+
if (trimmed.length < 2) {
|
|
403
|
+
return null;
|
|
432
404
|
}
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
405
|
+
const quote = trimmed[0];
|
|
406
|
+
if (quote !== '"' && quote !== "'" || trimmed[trimmed.length - 1] !== quote) {
|
|
407
|
+
return null;
|
|
408
|
+
}
|
|
409
|
+
const body = trimmed.slice(1, -1);
|
|
410
|
+
let result = "";
|
|
411
|
+
let escaping = false;
|
|
412
|
+
for (const char of body) {
|
|
413
|
+
if (escaping) {
|
|
414
|
+
result += unescapeChar(char, quote);
|
|
415
|
+
escaping = false;
|
|
436
416
|
continue;
|
|
437
417
|
}
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
` return __collie_attr == null ? "" : ${literal(` ${attr.name}="`)} + __collie_escapeAttr(__collie_attr) + ${literal(`"`)};`,
|
|
444
|
-
"})()"
|
|
445
|
-
].join(" ")
|
|
446
|
-
);
|
|
447
|
-
}
|
|
448
|
-
return segments;
|
|
449
|
-
}
|
|
450
|
-
function attributeExpression(raw) {
|
|
451
|
-
const trimmed = raw.trim();
|
|
452
|
-
if (trimmed.startsWith("{") && trimmed.endsWith("}")) {
|
|
453
|
-
return trimmed.slice(1, -1).trim();
|
|
418
|
+
if (char === "\\") {
|
|
419
|
+
escaping = true;
|
|
420
|
+
} else {
|
|
421
|
+
result += char;
|
|
422
|
+
}
|
|
454
423
|
}
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
function wrapWithGuard2(rendered, guard) {
|
|
458
|
-
if (!guard) {
|
|
459
|
-
return rendered;
|
|
424
|
+
if (escaping) {
|
|
425
|
+
result += "\\";
|
|
460
426
|
}
|
|
461
|
-
return
|
|
462
|
-
}
|
|
463
|
-
function literal(text) {
|
|
464
|
-
return JSON.stringify(text);
|
|
427
|
+
return result;
|
|
465
428
|
}
|
|
466
|
-
function
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
429
|
+
function unescapeChar(char, quote) {
|
|
430
|
+
switch (char) {
|
|
431
|
+
case "n":
|
|
432
|
+
return "\n";
|
|
433
|
+
case "r":
|
|
434
|
+
return "\r";
|
|
435
|
+
case "t":
|
|
436
|
+
return " ";
|
|
437
|
+
case "\\":
|
|
438
|
+
return "\\";
|
|
439
|
+
case '"':
|
|
440
|
+
return '"';
|
|
441
|
+
case "'":
|
|
442
|
+
return "'";
|
|
443
|
+
default:
|
|
444
|
+
if (char === quote) {
|
|
445
|
+
return quote;
|
|
446
|
+
}
|
|
447
|
+
return char;
|
|
473
448
|
}
|
|
474
|
-
return filtered.join(" + ");
|
|
475
449
|
}
|
|
476
450
|
function buildClassAliasEnvironment2(decl) {
|
|
477
451
|
const env = /* @__PURE__ */ new Map();
|
|
@@ -499,26 +473,6 @@ function expandClasses2(classes, aliasEnv) {
|
|
|
499
473
|
}
|
|
500
474
|
return result;
|
|
501
475
|
}
|
|
502
|
-
function emitJsDocPropsType2(props) {
|
|
503
|
-
if (!props) {
|
|
504
|
-
return "/** @typedef {any} Props */";
|
|
505
|
-
}
|
|
506
|
-
if (!props.fields.length) {
|
|
507
|
-
return "/** @typedef {{}} Props */";
|
|
508
|
-
}
|
|
509
|
-
const fields = props.fields.map((field) => {
|
|
510
|
-
const optional = field.optional ? "?" : "";
|
|
511
|
-
return `${field.name}${optional}: ${field.typeText}`;
|
|
512
|
-
}).join("; ");
|
|
513
|
-
return `/** @typedef {{ ${fields} }} Props */`;
|
|
514
|
-
}
|
|
515
|
-
function emitPropsDestructure2(props) {
|
|
516
|
-
if (!props || props.fields.length === 0) {
|
|
517
|
-
return null;
|
|
518
|
-
}
|
|
519
|
-
const names = props.fields.map((field) => field.name);
|
|
520
|
-
return `const { ${names.join(", ")} } = props;`;
|
|
521
|
-
}
|
|
522
476
|
function escapeStaticText(value) {
|
|
523
477
|
return value.replace(/[&<>{}]/g, (char) => {
|
|
524
478
|
switch (char) {
|
|
@@ -537,47 +491,21 @@ function escapeStaticText(value) {
|
|
|
537
491
|
}
|
|
538
492
|
});
|
|
539
493
|
}
|
|
540
|
-
function
|
|
541
|
-
return [
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
' return ">";',
|
|
556
|
-
" default:",
|
|
557
|
-
" return char;",
|
|
558
|
-
" }",
|
|
559
|
-
"}",
|
|
560
|
-
"function __collie_escapeAttr(value) {",
|
|
561
|
-
" if (value === null || value === undefined) {",
|
|
562
|
-
' return "";',
|
|
563
|
-
" }",
|
|
564
|
-
' return String(value).replace(/["&<>]/g, __collie_escapeAttrChar);',
|
|
565
|
-
"}",
|
|
566
|
-
"function __collie_escapeAttrChar(char) {",
|
|
567
|
-
" switch (char) {",
|
|
568
|
-
' case "&":',
|
|
569
|
-
' return "&";',
|
|
570
|
-
' case "<":',
|
|
571
|
-
' return "<";',
|
|
572
|
-
' case ">":',
|
|
573
|
-
' return ">";',
|
|
574
|
-
` case '"':`,
|
|
575
|
-
' return """;',
|
|
576
|
-
" default:",
|
|
577
|
-
" return char;",
|
|
578
|
-
" }",
|
|
579
|
-
"}"
|
|
580
|
-
];
|
|
494
|
+
function escapeAttributeValue(value) {
|
|
495
|
+
return value.replace(/["&<>]/g, (char) => {
|
|
496
|
+
switch (char) {
|
|
497
|
+
case "&":
|
|
498
|
+
return "&";
|
|
499
|
+
case "<":
|
|
500
|
+
return "<";
|
|
501
|
+
case ">":
|
|
502
|
+
return ">";
|
|
503
|
+
case '"':
|
|
504
|
+
return """;
|
|
505
|
+
default:
|
|
506
|
+
return char;
|
|
507
|
+
}
|
|
508
|
+
});
|
|
581
509
|
}
|
|
582
510
|
|
|
583
511
|
// src/diagnostics.ts
|
|
@@ -1938,10 +1866,9 @@ function compileToTsx(sourceOrAst, options = {}) {
|
|
|
1938
1866
|
function compileToHtml(sourceOrAst, options = {}) {
|
|
1939
1867
|
const document = normalizeDocument(sourceOrAst, options.filename);
|
|
1940
1868
|
const diagnostics = options.filename ? attachFilename(document.diagnostics, options.filename) : document.diagnostics;
|
|
1941
|
-
|
|
1942
|
-
let code = createStubHtml(componentName);
|
|
1869
|
+
let code = createStubHtml();
|
|
1943
1870
|
if (!hasErrors(diagnostics)) {
|
|
1944
|
-
code =
|
|
1871
|
+
code = generateHtml(document.root);
|
|
1945
1872
|
}
|
|
1946
1873
|
return { code, diagnostics, map: void 0 };
|
|
1947
1874
|
}
|
|
@@ -1983,8 +1910,8 @@ function createStubComponent(name, flavor) {
|
|
|
1983
1910
|
}
|
|
1984
1911
|
return [`export default function ${name}(props) {`, " return null;", "}"].join("\n");
|
|
1985
1912
|
}
|
|
1986
|
-
function createStubHtml(
|
|
1987
|
-
return
|
|
1913
|
+
function createStubHtml() {
|
|
1914
|
+
return "";
|
|
1988
1915
|
}
|
|
1989
1916
|
function attachFilename(diagnostics, filename) {
|
|
1990
1917
|
if (!filename) {
|