@marko/compiler
Version:
Marko template to JS compiler.
370 lines (320 loc) • 9.43 kB
JavaScript
;var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");require("../types/patch");
var _printer = _interopRequireDefault(require("@babel/generator/lib/printer"));
var t = _interopRequireWildcard(require("@babel/types"));
var _selfClosingTags = _interopRequireDefault(require("self-closing-tags"));function _getRequireWildcardCache(e) {if ("function" != typeof WeakMap) return null;var r = new WeakMap(),t = new WeakMap();return (_getRequireWildcardCache = function (e) {return e ? t : r;})(e);}function _interopRequireWildcard(e, r) {if (!r && e && e.__esModule) return e;if (null === e || "object" != typeof e && "function" != typeof e) return { default: e };var t = _getRequireWildcardCache(r);if (t && t.has(e)) return t.get(e);var n = { __proto__: null },a = Object.defineProperty && Object.getOwnPropertyDescriptor;for (var u in e) if ("default" !== u && {}.hasOwnProperty.call(e, u)) {var i = a ? Object.getOwnPropertyDescriptor(e, u) : null;i && (i.get || i.set) ? Object.defineProperty(n, u, i) : n[u] = e[u];}return n.default = e, t && t.set(e, n), n;}
Object.assign(_printer.default.prototype, {
MarkoParseError(node) {
this.token(node.source);
},
MarkoDocumentType(node) {
this.token("<!");
this.token(node.value);
this.token(">");
},
MarkoDeclaration(node) {
this.token("<?");
this.token(node.value);
this.token("?>");
},
MarkoCDATA(node) {
this.token("<![CDATA[");
this.token(node.value);
this.token("]]>");
},
MarkoComment(node) {
this.token("<!--");
this.token(node.value);
this.token("-->");
},
MarkoPlaceholder(node, parent) {
if (parent) {
const parentBody = getBody(parent);
const prev = parentBody[parentBody.indexOf(node) - 1];
if (prev && (t.isMarkoText(prev) || t.isMarkoPlaceholder(prev))) {
this.removeTrailingNewline();
}
}
this.token(node.escape ? "${" : "$!{");
this.print(node.value);
this.token("}");
},
MarkoScriptlet(node, parent) {
this.removeTrailingNewline();
if (!(t.isProgram(parent) && getBody(parent)[0] === node)) {
this.token("\n");
}
this.token(`${node.static ? node.target ?? "static" : "$"} `);
if (
node.body.length === 1 &&
!statementCouldHaveUnenclosedNewline(node.body[0]))
{
// TODO should determine if node has unenclosed newlines.
this.print(node.body[0]);
} else {
this.token("{");
this.newline();
this.indent();
this.printSequence(node.body);
this.dedent();
this.token("}");
}
},
MarkoClass(node) {
this.token("class");
this.token(" ");
this.print(node.body);
},
MarkoAttribute(node) {
const value = node.value;
if (!node.default) {
this.token(node.name);
if (node.modifier) {
this.token(":");
this.token(node.modifier);
}
if (node.arguments && node.arguments.length) {
this.token("(");
this.printList(node.arguments);
this.token(")");
}
}
if (node.default || !t.isBooleanLiteral(value, { value: true })) {
if (
t.isFunctionExpression(value) &&
!(value.id || value.async || value.generator))
{
this.token("(");
this.printList(value.params);
this.token(") ");
this.print(value.body);
} else {
this.token(node.bound ? ":=" : "=");
printWithParansIfNeeded.call(this, value);
}
}
},
MarkoSpreadAttribute(node) {
this.token("...");
printWithParansIfNeeded.call(this, node.value);
},
MarkoText(node, parent) {
const parentBody = getBody(parent);
const prev = parentBody[parentBody.indexOf(node) - 1];
const concatToPrev = prev && t.isMarkoPlaceholder(prev);
let { value } = node;
if (concatToPrev) {
this.removeTrailingNewline();
}
const isMultiLine = /[\r\n]/g.test(value);
const isRootLevel = !concatToPrev && t.isProgram(parent);
if (isRootLevel) {
if (isMultiLine) {
this.token("---\n");
} else {
this.token("-- ");
}
}
this.word(value);
if (isMultiLine && isRootLevel) {
this.token("\n---");
}
},
MarkoTagBody(node) {
this.printSequence(node.body, true);
},
MarkoTag(node) {
const isDynamicTag = !t.isStringLiteral(node.name);
const tagName = !isDynamicTag && node.name.value;
const rawValue = node.rawValue;
let bodyOverride;
if (
tagName === "style" &&
/^style(?:\.[^\s]+)?\s*\{[\s\S]*}$/.test(rawValue))
{
this.token(rawValue);
return;
}
this.token("<");
if (rawValue) {
this.token(rawValue);
} else {
if (isDynamicTag) {
this.token("${");
this.print(node.name);
this.token("}");
} else {
this.token(tagName);
}
if (node.typeArguments) {
this.token("<");
this.printList(node.typeArguments.params);
this.token(">");
}
if (node.var) {
this.token("/");
this.print(node.var);
if (node.var.typeAnnotation) {
this.print(node.var.typeAnnotation);
}
}
if (node.arguments && node.arguments.length) {
this.token("(");
this.printList(node.arguments);
this.token(")");
}
if (node.body.params.length) {
if (node.body.typeParameters) {
if (!node.typeArguments) {
this.token(" ");
}
this.token("<");
this.printList(node.body.typeParameters.params);
this.token(">");
}
this.token("|");
this.printList(node.body.params);
this.token("|");
}
let { attributes } = node;
if (attributes.length) {
if (tagName === "script") {
for (let i = attributes.length; i--;) {
const attr = attributes[i];
if (
attr.name === "value" && (
attr.value.type === "ArrowFunctionExpression" ||
attr.value.type === "FunctionExpression") &&
!(
attr.value.generator ||
attr.value.returnType ||
attr.value.typeParameters))
{
bodyOverride = attributes[i].value.body.body;
attributes = toSpliced(attributes, i);
break;
}
}
}
if (!(attributes && attributes[0] && attributes[0].default)) {
this.token(" ");
}
this.printJoin(attributes, undefined, undefined, spaceSeparator);
}
}
if (_selfClosingTags.default.voidElements.includes(tagName)) {
this.token(">");
} else if (
!(bodyOverride || node.body.body.length || node.attributeTags.length) ||
_selfClosingTags.default.svgElements.includes(tagName))
{
this.token("/>");
} else {
this.token(">");
this.newline();
this.printSequence(bodyOverride || zipAttributeTagsAndBody(node), true);
this.token("</");
if (!isDynamicTag) {
this.token(tagName);
}
this.token(">");
}
}
});
function spaceSeparator() {
this.token(" ");
}
function printWithParansIfNeeded(value) {
const needsParans = expressionCouldHaveUnenclosedWhitespace(value);
if (needsParans) {
this.token("(");
}
this.print(value);
if (needsParans) {
this.token(")");
}
}
function expressionCouldHaveUnenclosedWhitespace(node) {
switch (node.type) {
case "AssignmentExpression":
case "BinaryExpression":
case "ConditionalExpression":
case "Function":
case "LogicalExpression":
case "NewExpression":
return true;
default:
return false;
}
}
function statementCouldHaveUnenclosedNewline(node) {
switch (node.type) {
case "VariableDeclaration":
return node.declarations.length > 1;
default:
return false;
}
}
function zipAttributeTagsAndBody(tag) {
const {
attributeTags,
body: { body }
} = tag;
const bodyLen = body.length;
const attributeTagsLen = attributeTags.length;
if (!attributeTagsLen) return body;
if (!bodyLen) return attributeTags;
const result = [];
let i = 0;
let j = 0;
while (i < bodyLen && j < attributeTagsLen) {
const bodyNode = body[i];
const attributeTag = attributeTags[j];
if (bodyNode.loc != null && attributeTag.loc != null) {
if (compareStartLoc(bodyNode, attributeTag) < 0) {
result.push(bodyNode);
i++;
} else {
result.push(attributeTag);
j++;
}
} else if (j < attributeTagsLen) {
result.push(attributeTag);
j++;
} else {
result.push(bodyNode);
i++;
}
}
while (j < attributeTagsLen) {
result.push(attributeTags[j++]);
}
while (i < bodyLen) {
result.push(body[i++]);
}
return result;
}
function getBody(parent) {
switch (parent.type) {
case "MarkoTag":
return parent.body.body;
default:
return parent.body;
}
}
function compareStartLoc(a, b) {
return (
a.loc.start.line - b.loc.start.line ||
a.loc.start.column - b.loc.start.column);
}
function toSpliced(arr, index) {
const len = arr.length;
const result = new Array(len - 1);
let i = 0;
for (; i < index; i++) {
result[i] = arr[i];
}
for (i++; i < len; i++) {
result[i - 1] = arr[i];
}
return result;
}