prettier-plugin-marko
Version:
A prettier plugin for parsing and printing Marko files
1,491 lines (1,478 loc) • 51.9 kB
JavaScript
"use strict";
var __create = Object.create;
var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __getProtoOf = Object.getPrototypeOf;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __export = (target, all) => {
for (var name in all)
__defProp(target, name, { get: all[name], enumerable: true });
};
var __copyProps = (to, from, except, desc) => {
if (from && typeof from === "object" || typeof from === "function") {
for (let key of __getOwnPropNames(from))
if (!__hasOwnProp.call(to, key) && key !== except)
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
}
return to;
};
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
// If the importer is in node compatibility mode or this is not an ESM
// file that has been converted to a CommonJS file using a Babel-
// compatible transform (i.e. "__esModule" has not been set), then set
// "default" to the CommonJS "module.exports" for node compatibility.
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
mod
));
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
// src/index.ts
var index_exports = {};
__export(index_exports, {
languages: () => languages,
options: () => options,
parsers: () => parsers,
printers: () => printers,
setCompiler: () => setCompiler
});
module.exports = __toCommonJS(index_exports);
var import_path = require("path");
var import_module = require("module");
var import_prettier5 = require("prettier");
// src/constants.ts
var scriptParser = "babel-ts";
var expressionParser = "__ts_expression";
var enclosedNodeTypeReg = /^(?:Identifier|.*Literal|(?:Object|Array|Parenthesized|Record|Tuple)Expression)$/;
var styleReg = /^style((?:\.[^\s\\/:*?"<>|({]+)+)?\s*\{?/;
var voidHTMLReg = /^(?:area|b(?:ase|r)|col|embed|hr|i(?:mg|nput)|keygen|link|meta|param|source|track|wbr|const|debug|id|let|lifecycle|log|return)$/;
var shorthandIdOrClassReg = /^[a-zA-Z0-9_$][a-zA-Z0-9_$-]*(?:\s+[a-zA-Z0-9_$][a-zA-Z0-9_$-]*)*$/;
var preserveSpaceTagsReg = /^(?:textarea|pre|html-(?:comment|script|style))$/;
// src/utils/loc-to-pos.ts
function locToPos(loc, opts) {
const { markoLinePositions } = opts;
return markoLinePositions[loc.line - 1] + loc.column + (loc.line === 1 ? 0 : 1);
}
// src/utils/is-text-like.ts
function isTextLike(node, parent, opts) {
if (isText(node)) {
return true;
} else if (isInlineComment(node, opts)) {
const body = parent.type === "Program" ? parent.body : parent.body.body;
const i = body.indexOf(node);
let j = i;
while (j > 0) {
const check = body[--j];
if (isText(check)) return true;
else if (!isInlineComment(check, opts)) break;
}
j = i;
while (j < body.length - 1) {
const check = body[++j];
if (isText(check)) return true;
else if (!isInlineComment(check, opts)) break;
}
}
return false;
}
function getCommentType(node, opts) {
var _a;
const start = (_a = node.loc) == null ? void 0 : _a.start;
switch (start != null && opts.originalText[locToPos(start, opts) + 1]) {
case "/":
return "/";
case "*":
return "*";
default:
return "-";
}
}
function isInlineComment(node, opts) {
return node.type === "MarkoComment" && getCommentType(node, opts) !== "/";
}
function isText(node) {
return node.type === "MarkoText" || node.type === "MarkoPlaceholder";
}
// src/utils/with-line-if-needed.ts
var import_prettier = require("prettier");
var { builders: b } = import_prettier.doc;
function withLineIfNeeded(node, opts, doc4) {
const { originalText } = opts;
let pos = node.loc ? locToPos(node.loc.start, opts) : 0;
let count = 0;
while (--pos >= 0) {
let char = originalText[pos];
if (char === "\n") {
if (++count === 2) {
while (--pos >= 0) {
char = originalText[pos];
if (char !== "\n" && char !== " " && char !== "\r" && char !== " ") {
return [b.hardline, doc4];
}
}
return doc4;
}
} else if (char !== " " && char !== "\r" && char !== " ") {
break;
}
}
return doc4;
}
// src/utils/with-block-if-needed.ts
var import_prettier2 = require("prettier");
// src/utils/outer-code-matches.ts
var enclosedPatterns = [];
enclosedPatterns.push(
{
// Ignored
match: /[a-z0-9_$#@.]+/iy
},
{
// Line comments
match: /\/\/.*$/y
},
{
// Multi line comments
match: /\/\*.*?\*\//y
},
{
// Parens
match: /\s*\(/y,
patterns: enclosedPatterns,
until: /\)/y
},
{
// Braces
match: /\s*{/y,
patterns: enclosedPatterns,
until: /}/y
},
{
// Brackets
match: /\s*\[/y,
patterns: enclosedPatterns,
until: /]/y
},
{
// Single quote string
match: /'(?:\\.|[^'\\])*'/y
},
{
// Double quote string
match: /"(?:\\.|[^"\\])*"/y
},
{
// Template literal
match: /`/y,
patterns: [
{
// Content
match: /\\.|\$(?!{)|[^`\\$]+/y
},
{
// Expressions
match: /\${/y,
patterns: enclosedPatterns,
until: /}/y
}
],
until: /`/y
},
{
// RegExp
match: /\/(?:\\.|\[(?:\\.|[^\]\\]+)\]|[^[/\\])+\/[a-z]*/iy
}
);
var unenclosedPatterns = [
{
// Word operators
match: /\b\s*(?:as|async|await|class|function|in(?:stanceof)?|new|void|delete|keyof|typeof|satisfies|extends)(?:\s+|\b)/y
},
{
// Symbol operators
match: /\s*(?:[\^~%!]|\+{1,2}|\*{1,2}|-(?:-(?!\s))?|&{1,2}|\|{1,2}|!={0,2}|===?|<{1,3}|>{2,3}|<=?|=>)\s*/y
}
].concat(enclosedPatterns);
function outerCodeMatches(str, test, enclosed) {
const stack = [
{
until: test,
patterns: enclosed ? enclosedPatterns : unenclosedPatterns
}
];
let pos = 0;
do {
const { until, patterns } = stack[stack.length - 1];
outer: while (pos < str.length) {
for (const pattern of patterns) {
pattern.match.lastIndex = pos;
if (pattern.match.test(str)) {
pos = pattern.match.lastIndex;
if (pattern.until) {
stack.push(pattern);
break outer;
} else {
continue outer;
}
}
}
until.lastIndex = pos;
if (until.test(str)) {
pos = until.lastIndex;
if (stack.length === 1) return true;
stack.pop();
break;
}
pos++;
}
} while (pos < str.length && stack.length);
return false;
}
// src/utils/print-doc.ts
var DocCache = /* @__PURE__ */ new WeakMap();
function printDoc(doc4) {
switch (typeof doc4) {
case "string":
return doc4;
case "object":
if (doc4 !== null) {
let cached = DocCache.get(doc4);
if (cached !== void 0) return cached;
if (Array.isArray(doc4)) {
cached = "";
for (const item of doc4) {
cached += printDoc(item);
}
} else {
switch (doc4.type) {
case "align":
cached = `
${printDoc(doc4.contents)}
`;
break;
case "indent":
cached = ` ${printDoc(doc4.contents)} `;
break;
case "break-parent":
case "cursor":
case "line-suffix-boundary":
case "trim":
cached = "";
break;
case "fill":
cached = ` ${printDoc(doc4.parts)} `;
break;
case "group":
cached = printDoc(doc4.contents) + printDoc(doc4.expandedStates);
break;
case "if-break":
cached = printDoc(doc4.flatContents) + printDoc(doc4.breakContents);
break;
case "indent-if-break":
cached = " ";
break;
case "label":
case "line-suffix":
cached = printDoc(doc4.contents);
break;
case "line":
default:
cached = "\n";
break;
}
}
DocCache.set(doc4, cached);
return cached;
}
}
return "";
}
// src/utils/with-block-if-needed.ts
var { builders: b2 } = import_prettier2.doc;
function withBlockIfNeeded(node, doc4) {
if (!enclosedNodeTypeReg.test(node.type) && outerCodeMatches(printDoc(doc4).trim(), /[\n\r]/y) || node.leadingComments || node.trailingComments) {
return b2.group([
b2.indent([b2.ifBreak(["{", b2.line]), doc4]),
b2.ifBreak([b2.line, "}"])
]);
}
return doc4;
}
// src/utils/with-parens-if-needed.ts
var import_prettier3 = require("prettier");
var { builders: b3 } = import_prettier3.doc;
function withParensIfNeeded(node, doc4, enclosed) {
var _a, _b;
if (((_a = node.leadingComments) == null ? void 0 : _a.length) || ((_b = node.trailingComments) == null ? void 0 : _b.length) || !enclosedNodeTypeReg.test(node.type) && outerCodeMatches(printDoc(doc4).trim(), /\s|>/y, enclosed)) {
return b3.group(["(", b3.indent([b3.softline, doc4]), b3.softline, ")"]);
}
if (node.type === "LogicalExpression") {
return withParensIfBreak(node, doc4);
}
return doc4;
}
function withParensIfBreak(node, doc4) {
var _a, _b;
if (((_a = node.leadingComments) == null ? void 0 : _a.length) || ((_b = node.trailingComments) == null ? void 0 : _b.length) || !enclosedNodeTypeReg.test(node.type) && outerCodeMatches(printDoc(doc4).trim(), /\n/y, true)) {
return b3.group([
b3.ifBreak("(", ""),
b3.indent([b3.softline, doc4]),
b3.softline,
b3.ifBreak(")", "")
]);
}
return doc4;
}
// src/utils/as-literal-text-content.ts
var import_prettier4 = require("prettier");
var { builders: b4 } = import_prettier4.doc;
var temp = [""];
function asLiteralTextContent(val, escapeBackslashes = false) {
let charPos = 0;
let slotPos = 0;
for (let i = 0, len = val.length; i < len; i++) {
switch (val.charAt(i)) {
case (escapeBackslashes && "\\"):
temp.push("\\\\");
break;
case "\n":
temp.push(b4.literalline);
break;
default:
continue;
}
temp[slotPos] = val.slice(charPos, i);
slotPos = temp.push("") - 1;
charPos = i + 1;
}
if (charPos) {
const result = temp;
result[slotPos] = val.slice(charPos);
temp = [""];
return result;
} else {
return val;
}
}
function asFilledTextContent(val) {
const parts = val.split(/\s+/);
const len = parts.length;
switch (len) {
case 0:
return "";
case 1:
return asLiteralTextContent(parts[0], true);
}
const doc4 = [];
const last = len - 1;
for (let i = 0; i < last; i++) {
doc4.push(asLiteralTextContent(parts[i], true), b4.line);
}
doc4.push(asLiteralTextContent(parts[last], true));
return b4.fill(doc4);
}
// src/utils/get-original-code.ts
var import_generator = __toESM(require("@babel/generator"));
var generate = import_generator.default.default || import_generator.default;
function getOriginalCodeForNode(opts, node, path) {
var _a, _b, _c;
const hasLeadingComments = ((_a = node.leadingComments) == null ? void 0 : _a.length) && !(path && ((_b = path.getParentNode()) == null ? void 0 : _b.type) === "MarkoScriptlet" && !path.isFirst);
const hasTrailingComments = (_c = node.trailingComments) == null ? void 0 : _c.length;
if (!hasLeadingComments && !hasTrailingComments) {
switch (node.type) {
case "StringLiteral":
return JSON.stringify(node.value);
case "BooleanLiteral":
case "NumericLiteral":
return "" + node.value;
case "NullLiteral":
return "null";
}
}
const loc = node.loc;
if (!loc) {
return generate(node, {
filename: opts.filepath,
compact: false,
comments: true,
sourceMaps: false
}).code;
}
let start = loc.start;
if (hasLeadingComments) {
const commentStart = node.leadingComments[0].loc.start;
if (commentStart.line < start.line || commentStart.line === start.line && commentStart.column < start.column) {
start = commentStart;
}
}
let end = loc.end;
if (hasTrailingComments) {
const commentEnd = node.trailingComments[node.trailingComments.length - 1].loc.end;
if (commentEnd.line > end.line || commentEnd.line === end.line && commentEnd.column > end.column) {
end = commentEnd;
}
}
return opts.originalText.slice(locToPos(start, opts), locToPos(end, opts));
}
// src/index.ts
var defaultFilePath = (0, import_path.resolve)("index.marko");
var rootRequire = (0, import_module.createRequire)(defaultFilePath);
var { builders: b5, utils } = import_prettier5.doc;
var identity = (val) => val;
var emptyArr = [];
var embeddedPlaceholderReg = /__EMBEDDED_PLACEHOLDER_(\d+)__/g;
var currentCompiler;
var currentConfig;
var languages = [
{
name: "marko",
aceMode: "text",
parsers: ["marko"],
aliases: ["markojs"],
tmScope: "text.marko",
codemirrorMode: "htmlmixed",
vscodeLanguageIds: ["marko"],
linguistLanguageId: 932782397,
codemirrorMimeType: "text/html",
extensions: [".marko"]
}
];
var options = {
markoSyntax: {
type: "choice",
default: "auto",
category: "Marko",
description: "Change output syntax between HTML mode, concise mode and auto.",
choices: [
{
value: "auto",
description: "Determine output syntax by the input syntax used."
},
{
value: "html",
description: "Force the output to use the HTML syntax."
},
{
value: "concise",
description: "Force the output to use the concise syntax."
}
]
},
markoAttrParen: {
type: "boolean",
default: (() => {
try {
const compilerRequire = (0, import_module.createRequire)(
rootRequire.resolve("@marko/compiler")
);
const [major, minor] = compilerRequire("htmljs-parser/package.json").version.split(".").map((v) => parseInt(v, 10));
return major < 2 || major === 2 && minor < 11;
} catch {
return false;
}
})(),
category: "Marko",
description: "If enabled all attributes with unenclosed whitespace will be wrapped in parens."
}
};
var parsers = {
marko: {
astFormat: "marko-ast",
async parse(text, opts) {
ensureCompiler();
const { filepath = defaultFilePath } = opts;
const { compile, types: t } = currentCompiler;
const { ast } = await compile(`${text}
`, filepath, currentConfig);
opts.originalText = text;
opts.markoLinePositions = [0];
opts.markoPreservingSpace = false;
for (let i = 0; i < text.length; i++) {
if (text[i] === "\n") {
opts.markoLinePositions.push(i);
}
}
if (opts.markoSyntax === "auto") {
opts.markoSyntax = "html";
for (const childNode of ast.program.body) {
if (t.isMarkoTag(childNode)) {
if (t.isStringLiteral(childNode.name) && childNode.name.value === "style" && styleReg.exec(childNode.rawValue || "style")[0].endsWith("{")) {
continue;
}
if (opts.originalText[locToPos(childNode.loc.start, opts)] !== "<") {
opts.markoSyntax = "concise";
}
break;
}
}
}
t.traverseFast(ast, (node) => {
if (node.type === "MarkoAttribute") {
switch (node.name) {
case "class":
case "id":
switch (node.value.type) {
case "StringLiteral":
case "ArrayExpression":
case "TemplateLiteral":
case "ObjectExpression":
node.value.loc = null;
break;
}
break;
}
}
});
return ast;
},
locStart(node) {
var _a, _b;
return ((_b = (_a = node.loc) == null ? void 0 : _a.start) == null ? void 0 : _b.index) || 0;
},
locEnd(node) {
var _a, _b;
return ((_b = (_a = node.loc) == null ? void 0 : _a.end) == null ? void 0 : _b.index) || 0;
}
}
};
var printers = {
"marko-ast": {
print(path, opts, print) {
var _a, _b, _c, _d;
const node = path.getNode();
if (!node) return "";
const { types: t } = currentCompiler;
switch (node.type) {
case "File":
return path.call(print, "program");
case "Program": {
let text = [];
const lastIndex = node.body.length - 1;
const bodyDocs = [];
path.each((child, i) => {
const childNode = child.getNode();
const isText2 = isTextLike(childNode, node, opts);
if (isText2) {
text.push(print(child));
if (i !== lastIndex) return;
}
if (text.length) {
const textDoc = b5.group([
printDashes(node),
b5.indent([b5.line, b5.fill(text)])
]);
if (isText2) {
bodyDocs.push(textDoc);
} else {
text = [];
bodyDocs.push(textDoc, print(child));
}
} else {
bodyDocs.push(print(child));
}
}, "body");
return [b5.join(b5.hardline, bodyDocs), b5.hardline];
}
case "MarkoDocumentType":
return `<!${node.value.replace(/\s+/g, " ").trim()}>`;
case "MarkoDeclaration":
return asLiteralTextContent(`<?${node.value}?>`);
case "MarkoComment": {
switch (getCommentType(node, opts)) {
case "/":
return asLiteralTextContent(`//${node.value}`);
case "*":
return asLiteralTextContent(`/*${node.value}*/`);
default:
return asLiteralTextContent(`<!--${node.value}-->`);
}
}
case "MarkoCDATA":
return asLiteralTextContent(`<![CDATA[${node.value}]]>`);
case "MarkoTag": {
const tagPath = path;
const groupId = Symbol();
const doc4 = [opts.markoSyntax === "html" ? "<" : ""];
const { markoPreservingSpace } = opts;
const literalTagName = t.isStringLiteral(node.name) ? node.name.value : "";
const preserveSpace = markoPreservingSpace || (opts.markoPreservingSpace = preserveSpaceTagsReg.test(literalTagName));
if (literalTagName) {
doc4.push(literalTagName);
} else {
doc4.push(
b5.group([
"${",
b5.indent([b5.softline, tagPath.call(print, "name")]),
b5.softline,
"}"
])
);
}
if (node.typeArguments) {
doc4.push(
tagPath.call(print, "typeArguments")
);
}
if (node.body.typeParameters) {
if (!node.typeArguments) {
doc4.push(" ");
}
doc4.push(
tagPath.call(print, "body", "typeParameters")
);
}
const shorthandIndex = doc4.push("") - 1;
if (node.var) {
doc4.push(
"/",
tagPath.call(
print,
"var"
)
);
}
if ((_a = node.arguments) == null ? void 0 : _a.length) {
doc4.push(
b5.group([
"(",
b5.indent([
b5.softline,
b5.join(
[",", b5.line],
tagPath.map((it) => print(it), "arguments")
),
opts.trailingComma === "all" && !preventTrailingCommaTagArgs(literalTagName) ? b5.ifBreak(",") : ""
]),
b5.softline,
")"
])
);
}
if (node.body.params.length) {
doc4.push(
b5.group([
"|",
b5.indent([
b5.softline,
b5.join(
[",", b5.line],
tagPath.map((it) => print(it), "body", "params")
),
opts.trailingComma === "all" ? b5.ifBreak(",") : ""
]),
b5.softline,
"|"
])
);
}
if (node.attributes.length) {
const attrsDoc = [];
tagPath.each((attrPath) => {
const attrNode = attrPath.getNode();
if (t.isMarkoAttribute(attrNode) && (attrNode.name === "class" || attrNode.name === "id")) {
if (opts.markoSyntax === "concise" && t.isStringLiteral(attrNode.value) && !attrNode.modifier && shorthandIdOrClassReg.test(attrNode.value.value)) {
const symbol = attrNode.name === "class" ? "." : "#";
doc4[shorthandIndex] += symbol + attrNode.value.value.split(/ +/).join(symbol);
} else {
attrsDoc.push(print(attrPath));
}
} else if (attrNode.default) {
doc4.push(print(attrPath));
} else {
attrsDoc.push(print(attrPath));
}
}, "attributes");
if (attrsDoc.length) {
if (attrsDoc.length === 1) {
doc4.push(" ", attrsDoc[0]);
} else {
const attrSep = opts.markoSyntax === "concise" ? [b5.line, b5.ifBreak(",")] : b5.line;
doc4.push(
b5.group(b5.indent([attrSep, b5.join(attrSep, attrsDoc)]))
);
}
}
}
const hasAttrTags = !!((_b = node.attributeTags) == null ? void 0 : _b.length);
if (voidHTMLReg.test(literalTagName)) {
if (opts.markoSyntax === "html") doc4.push(">");
} else if (node.body.body.length || hasAttrTags) {
const lastIndex = node.body.body.length - 1;
const bodyDocs = hasAttrTags ? tagPath.map(print, "attributeTags") : [];
let textOnly = !hasAttrTags;
let textDocs = [];
tagPath.each(
(childPath, i) => {
const childNode = childPath.getNode();
const isText2 = isTextLike(childNode, node, opts);
const isLast = i === lastIndex;
if (isText2) {
if (preserveSpace && opts.markoSyntax === "concise") {
bodyDocs.push(
b5.group([printDashes(node), " ", print(childPath)])
);
} else {
textDocs.push(print(childPath));
}
if (textOnly || !isLast) return;
} else {
textOnly = false;
}
if (textDocs.length) {
if (opts.markoSyntax === "html") {
bodyDocs.push(textDocs);
} else if (!preserveSpace) {
const dashes = printDashes(node);
bodyDocs.push(
b5.group([
dashes,
b5.line,
textDocs,
b5.ifBreak([b5.line, dashes])
])
);
}
if (!isText2) {
textDocs = [];
bodyDocs.push(print(childPath));
}
} else {
bodyDocs.push(print(childPath));
}
},
"body",
"body"
);
if (opts.markoSyntax === "html") {
const joinSep = preserveSpace ? "" : textOnly ? b5.softline : b5.hardline;
const wrapSep = !preserveSpace && (node.var || node.body.params.length || ((_c = node.arguments) == null ? void 0 : _c.length) || node.attributes.length || node.body.body.some(
(child) => !isTextLike(child, node, opts)
)) ? b5.hardline : joinSep;
doc4.push(
">",
b5.indent([
wrapSep,
textOnly ? b5.group(textDocs) : b5.join(joinSep, bodyDocs)
]),
wrapSep,
`</${literalTagName}>`
);
} else {
if (!preserveSpace && textOnly) {
if (node.attributes.length) {
doc4.push(
b5.indent([
b5.line,
b5.group([
printDashes(node),
b5.indent([b5.line, textDocs])
])
])
);
} else {
doc4.push(
b5.group([
" " + printDashes(node),
b5.indent([b5.line, textDocs])
])
);
}
} else {
if (textOnly && bodyDocs.length === 1) {
doc4.push(" ", bodyDocs);
} else {
doc4.push(
b5.indent([b5.hardline, b5.join(b5.hardline, bodyDocs)])
);
}
}
}
} else if (opts.markoSyntax === "html") {
doc4.push("/>");
}
opts.markoPreservingSpace = markoPreservingSpace;
return withLineIfNeeded(node, opts, b5.group(doc4, { id: groupId }));
}
case "MarkoAttribute": {
const attrPath = path;
const doc4 = [];
const { value } = node;
if (!node.default) {
doc4.push(node.name);
if (node.modifier) {
doc4.push(`:${node.modifier}`);
}
if ((_d = node.arguments) == null ? void 0 : _d.length) {
doc4.push(
b5.group([
"(",
b5.indent([
b5.softline,
b5.join(
[",", b5.line],
attrPath.map((it) => print(it), "arguments")
),
opts.trailingComma === "all" && !preventTrailingCommaAttrArgs(node.name) ? b5.ifBreak(",") : ""
]),
b5.softline,
")"
])
);
}
}
if (node.default || !t.isBooleanLiteral(value, { value: true })) {
if (t.isFunctionExpression(value) && !(value.id || value.async || value.generator)) {
doc4.push(attrPath.call(print, "value"));
} else {
doc4.push(
node.bound ? ":=" : "=",
b5.group(
withParensIfNeeded(
value,
attrPath.call(print, "value"),
opts.markoAttrParen
)
)
);
}
}
return doc4;
}
case "MarkoSpreadAttribute": {
return ["..."].concat(
withParensIfNeeded(
node.value,
path.call(
print,
"value"
),
opts.markoAttrParen
)
);
}
case "MarkoPlaceholder":
return [
node.escape ? "${" : "$!{",
path.call(print, "value"),
"}"
];
case "MarkoScriptlet": {
const prefix = node.static ? node.target || "static" : "$";
if (node.body.filter((child) => !isEmpty(child)).length <= 1) {
let bodyDoc = [];
path.each((childPath) => {
const childNode = childPath.getNode();
if (childNode && !isEmpty(childNode)) {
bodyDoc = withLineIfNeeded(
childNode,
opts,
printSpecialDeclaration(childPath, prefix, opts, print) || [
prefix + " ",
withBlockIfNeeded(childNode, childPath.call(print))
]
);
}
}, "body");
return bodyDoc;
} else {
return [
prefix,
" {",
b5.indent([
b5.hardline,
b5.join(
b5.hardline,
path.map((childPath) => {
if (childPath.node.type !== "EmptyStatement") {
return childPath.call(print);
}
return [];
}, "body")
)
]),
b5.hardline,
"}"
];
}
}
case "MarkoText": {
const parent = getTextParent(path);
let { value } = node;
const isConcise = opts.markoSyntax === "concise";
if (isConcise && opts.markoPreservingSpace) {
return toPlaceholder(value, opts.singleQuote);
}
const dashMatch = isConcise && /---*/.exec(value);
if (dashMatch) {
minDashLookup.set(
parent,
Math.max(minDashLookup.get(parent) || 0, dashMatch[0].length)
);
}
if (opts.markoPreservingSpace) {
return asLiteralTextContent(value);
}
let prefix = "";
let suffix = "";
if (value[0] === " " && !(path.previous && isTextLike(path.previous, parent, opts))) {
prefix = opts.singleQuote ? "${' '}" : '${" "}';
value = value.slice(1);
}
const last = value.length - 1;
if (value[last] === " " && !(path.next && isTextLike(path.next, parent, opts))) {
suffix = opts.singleQuote ? "${' '}" : '${" "}';
value = value.slice(0, last);
}
return [prefix, asFilledTextContent(value), suffix];
}
default:
throw new Error(`Unknown node type in Marko template: ${node.type}`);
}
},
embed(path, opts) {
ensureCompiler();
const node = path.getNode();
const type = node == null ? void 0 : node.type;
const { types: t } = currentCompiler;
switch (type) {
case "File":
case "Program":
return null;
case "MarkoClass":
return (toDoc) => toDoc(
`class ${getOriginalCodeForNode(
opts,
node.body
)}`,
{ parser: expressionParser }
);
case "MarkoTag":
if (node.name.type === "StringLiteral") {
switch (node.name.value) {
case "script":
return async (toDoc, print) => {
var _a;
const placeholders = [];
const groupId = Symbol();
const parser = getScriptParser(node);
const doc4 = [
opts.markoSyntax === "html" ? "<" : "",
"script"
];
let placeholderId = 0;
if (node.var) {
doc4.push(
"/",
path.call(print, "var")
);
}
let bodyOverrideCode;
if (node.attributes.length) {
const attrsDoc = [];
path.each((attrPath) => {
const attrNode = attrPath.getNode();
if (attrNode.type === "MarkoAttribute" && attrNode.name === "value" && !node.body.body.length && (attrNode.value.type === "FunctionExpression" || attrNode.value.type === "ArrowFunctionExpression") && !(attrNode.value.generator || attrNode.value.returnType || attrNode.value.typeParameters)) {
bodyOverrideCode = getOriginalCodeForNode(
opts,
attrNode.value.body
).replace(/^\s*{\s*/, "").replace(/\s*}\s*$/, "");
} else if (attrNode.default) {
doc4.push(print(attrPath));
} else {
attrsDoc.push(print(attrPath));
}
}, "attributes");
if (attrsDoc.length) {
if (attrsDoc.length === 1) {
doc4.push(" ", attrsDoc[0]);
} else {
const attrSep = opts.markoSyntax === "concise" ? [b5.line, b5.ifBreak(",")] : b5.line;
doc4.push(
b5.group(
b5.indent([attrSep, b5.join(attrSep, attrsDoc)])
)
);
}
}
}
const bodyOverride = bodyOverrideCode !== void 0 && await toDoc(bodyOverrideCode, {
parser
}).catch(() => asLiteralTextContent(bodyOverrideCode));
if (bodyOverride || node.body.body.length) {
let embeddedCode = "";
if (!bodyOverride) {
path.each(
(childPath) => {
const childNode = childPath.getNode();
if (childNode.type === "MarkoText") {
embeddedCode += childNode.value;
} else {
embeddedCode += `__EMBEDDED_PLACEHOLDER_${placeholderId++}__`;
placeholders.push(print(childPath));
}
},
"body",
"body"
);
}
const bodyDoc = bodyOverride || replaceEmbeddedPlaceholders(
!parser ? asLiteralTextContent(embeddedCode.trim()) : await toDoc(embeddedCode, {
parser
}).catch(
() => asLiteralTextContent(embeddedCode.trim())
),
placeholders
);
if (opts.markoSyntax === "html") {
const wrapSep = node.var || node.body.params.length || ((_a = node.arguments) == null ? void 0 : _a.length) || node.attributes.length || !bodyOverride && node.body.body.some(
(child) => child.type === "MarkoScriptlet" || !isTextLike(child, node, opts)
) ? b5.hardline : b5.softline;
doc4.push(
">",
b5.indent([wrapSep, bodyDoc]),
wrapSep,
`</script>`
);
} else {
doc4.push(
b5.group([
" " + printDashes(node),
b5.indent([b5.line, bodyDoc])
])
);
}
} else if (opts.markoSyntax === "html") {
doc4.push("/>");
}
return withLineIfNeeded(
node,
opts,
b5.group(doc4, { id: groupId })
);
};
case "style": {
const rawValue = node.rawValue;
const [startContent, lang] = styleReg.exec(
rawValue || "style"
);
const parser = lang ? getParserNameFromExt(lang) : "css";
if (startContent.endsWith("{")) {
const codeSartOffset = startContent.length;
const codeEndOffset = node.rawValue.lastIndexOf("}");
const code = rawValue.slice(codeSartOffset, codeEndOffset).trim();
return async (toDoc) => {
try {
return withLineIfNeeded(
node,
opts,
b5.group([
"style",
!lang || lang === ".css" ? "" : lang,
" {",
b5.indent([
b5.line,
await toDoc(code, { parser }).catch(
() => asLiteralTextContent(code.trim())
)
]),
b5.line,
"}"
])
);
} catch {
return withLineIfNeeded(
node,
opts,
asLiteralTextContent(rawValue)
);
}
};
} else {
return async (toDoc, print) => {
var _a;
const placeholders = [];
const groupId = Symbol();
const doc4 = [
opts.markoSyntax === "html" ? "<" : "",
"style",
!lang || lang === ".css" ? "" : lang
];
let placeholderId = 0;
if (node.var) {
doc4.push(
"/",
path.call(print, "var")
);
}
if (!lang && node.attributes.length) {
const attrsDoc = [];
path.each((attrPath) => {
const attrNode = attrPath.getNode();
if (attrNode.default) {
doc4.push(print(attrPath));
} else {
attrsDoc.push(print(attrPath));
}
}, "attributes");
if (attrsDoc.length) {
if (attrsDoc.length === 1) {
doc4.push(" ", attrsDoc[0]);
} else {
const attrSep = opts.markoSyntax === "concise" ? [b5.line, b5.ifBreak(",")] : b5.line;
doc4.push(
b5.group(
b5.indent([attrSep, b5.join(attrSep, attrsDoc)])
)
);
}
}
}
if (node.body.body.length) {
let embeddedCode = "";
path.each(
(childPath) => {
const childNode = childPath.getNode();
if (childNode.type === "MarkoText") {
embeddedCode += childNode.value;
} else {
embeddedCode += `__EMBEDDED_PLACEHOLDER_${placeholderId++}__`;
placeholders.push(print(childPath));
}
},
"body",
"body"
);
const bodyDoc = replaceEmbeddedPlaceholders(
!parser ? asLiteralTextContent(embeddedCode.trim()) : await toDoc(embeddedCode, {
parser
}).catch(
() => asLiteralTextContent(embeddedCode.trim())
),
placeholders
);
if (opts.markoSyntax === "html") {
const wrapSep = node.var || node.body.params.length || ((_a = node.arguments) == null ? void 0 : _a.length) || node.attributes.length ? b5.hardline : b5.softline;
doc4.push(
">",
b5.indent([wrapSep, bodyDoc]),
wrapSep,
`</style>`
);
} else {
doc4.push(
b5.group([
" " + printDashes(node),
b5.indent([b5.line, bodyDoc])
])
);
}
} else if (opts.markoSyntax === "html") {
doc4.push("/>");
}
return withLineIfNeeded(
node,
opts,
b5.group(doc4, { id: groupId })
);
};
}
}
}
}
}
if (type.startsWith("Marko")) return null;
let parent = path.parent;
if (parent.type !== "Program") {
let parentIndex = 0;
while (!(parent.type === "ExportNamedDeclaration" || parent.type.startsWith("Marko"))) {
parent = path.getParentNode(++parentIndex);
if (!parent) return null;
}
}
return async (toDoc, print) => {
switch (node.type) {
case "EmptyStatement":
return void 0;
case "ExportNamedDeclaration":
if (node.declaration) {
const printedDeclaration = path.call(
(childPath) => printSpecialDeclaration(
childPath,
"export",
opts,
print
),
"declaration"
);
if (printedDeclaration) return printedDeclaration;
}
break;
}
const code = getOriginalCodeForNode(
opts,
node,
path
);
if (t.isStatement(node)) {
return tryPrintEmbed(code, scriptParser);
} else {
const parent2 = path.getParentNode();
const parentType = parent2 == null ? void 0 : parent2.type;
if (parentType === "MarkoTag" && path.key === "typeArguments") {
return tryPrintEmbed(
`_${code}`,
scriptParser,
(doc4) => {
const last = doc4.length - 1;
doc4[0] = doc4[0].replace(/^_/, "");
doc4[last] = doc4[last].replace(/;$/, "");
return doc4;
},
code
);
} else if (parentType === "MarkoTagBody" && path.key === "typeParameters") {
return tryPrintEmbed(
`function _${code}() {}`,
scriptParser,
(doc4) => {
return doc4[1];
},
code
);
} else if (parentType === "MarkoTagBody" || parentType === "VariableDeclarator" && path.key === "id" || parentType === "MarkoTag" && path.key === "var") {
return tryPrintEmbed(
`var ${code}=_`,
scriptParser,
(doc4) => {
const contents = doc4[0].contents[1].contents;
for (let i = contents.length; i--; ) {
const item = contents[i];
if (typeof item === "string") {
const match = /\s*=\s*$/.exec(item);
if (match) {
contents[i] = item.slice(0, -match[0].length);
contents.length = i + 1;
break;
}
}
}
return contents;
},
code
);
} else if (parentType === "MarkoAttribute" && path.key === "value" && node.type === "FunctionExpression" && !(node.async || node.generator || node.id)) {
return tryPrintEmbed(
`({_${code.replace(/^\s*function\s*/, "")}})`,
scriptParser,
(doc4) => {
return doc4[1].contents[1].contents[1].contents.slice(1);
},
code
);
}
return tryPrintEmbed(code, expressionParser);
}
async function tryPrintEmbed(code2, parser, normalize = identity, fallback = code2) {
try {
return normalize(await toDoc(code2, { parser }));
} catch {
return [asLiteralTextContent(fallback)];
}
}
};
},
getVisitorKeys(node) {
ensureCompiler();
return currentCompiler.types.VISITOR_KEYS[node.type] || emptyArr;
}
}
};
function setCompiler(compiler, config) {
currentCompiler = compiler;
setConfig(config);
}
function printSpecialDeclaration(path, prefix, opts, print) {
const node = path.getNode();
switch (node == null ? void 0 : node.type) {
case "TSTypeAliasDeclaration":
return [
prefix + " type ",
node.id.name,
node.typeParameters ? [
"<",
b5.group([
b5.indent([
b5.softline,
path.call(
(paramsPath) => b5.join(
[",", b5.line],
paramsPath.map((param) => param.call(print))
),
"typeParameters",
"params"
)
]),
b5.softline,
">"
])
] : "",
" = ",
withParensIfBreak(
node.typeAnnotation,
path.call(print, "typeAnnotation")
),
opts.semi ? ";" : ""
];
case "VariableDeclaration": {
if (path.node.leadingComments || path.node.trailingComments) {
break;
}
return b5.join(
b5.hardline,
path.map((declPath) => {
const decl = declPath.getNode();
return [
prefix + " " + (node.declare ? "declare " : "") + node.kind + " ",
declPath.call(print, "id"),
decl.init ? [
" = ",
withParensIfBreak(
decl.init,
declPath.call(print, "init")
)
] : "",
opts.semi ? ";" : ""
];
}, "declarations")
);
}
case "EmptyStatement": {
if (node.trailingComments) {
if (node.trailingComments.length > 1) {
return b5.join(b5.hardline, [
b5.indent(
b5.join(b5.hardline, [
prefix + " {",
...printComments(node.trailingComments)
])
),
"}"
]);
} else {
return prefix + " " + printComments(node.trailingComments)[0];
}
} else {
return [];
}
}
}
}
function printComments(comments) {
return comments.map(
(comment) => comment.type === "CommentBlock" ? `/*${comment.value}*/` : "//" + comment.value
);
}
function replaceEmbeddedPlaceholders(doc4, placeholders) {
if (!placeholders.length) return doc4;
return utils.mapDoc(doc4, (cur) => {
if (typeof cur === "string") {
let match = embeddedPlaceholderReg.exec(cur);
if (match) {
const replacementDocs = [];
let index = 0;
do {
const placeholderIndex = +match[1];
if (index !== match.index) {
replacementDocs.push(cur.slice(index, match.index));
}
replacementDocs.push(placeholders[placeholderIndex]);
index = match.index + match[0].length;
} while (match = embeddedPlaceholderReg.exec(cur));
if (index !== cur.length) {
replacementDocs.push(cur.slice(index));
}
if (replacementDocs.length === 1) {
return replacementDocs[0];
}
return replacementDocs;
}
}
return cur;
});
}
function getParserNameFromExt(ext) {
switch (ext) {
case ".css":
return "css";
case ".less":
return "less";
case ".scss":
return "scss";
case ".js":
case ".mjs":
case ".cjs":
return "babel";
case ".ts":
case ".mts":
case ".cts":
return "babel-ts";
}
}
function preventTrailingCommaTagArgs(tagName) {
switch (tagName) {
case "if":
case "else-if":
case "while":
return true;
default:
return false;
}
}
function preventTrailingCommaAttrArgs(attrName) {
switch (attrName) {
case "if":
case "while":
case "no-update-if":
case "no-update-body-if":
return true;
default:
return false;
}
}
function ensureCompiler() {
if (!currentConfig) {
let config;
try {
currentCompiler = rootRequire("@marko/compiler");
config = rootRequire("@marko/compiler/config").default;
} catch (cause) {
throw new Error(
"You must have @marko/compiler installed to use prettier-plugin-marko.",
{ cause }
);
}
setConfig(config);
}
}
function setConfig(config) {
let { translator } = config;
if (typeof translator === "string") {
try {
translator = rootRequire(translator);
} catch {
}
}
currentConfig = {
...config,
translator,
ast: true,
code: false,
optimize: false,
output: "source",
sourceMaps: false,
writeVersionComment: false,
babelConfig: {
caller: { name: "@marko/prettier" },
babelrc: false,
configFile: false,
parserOpts: {
allowUndeclare