@kitschpatrol/prettier-plugin-astro 0.14.1-fork.1
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.d.ts +22 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +925 -0
- package/dist/index.js.map +1 -0
- package/license.txt +78 -0
- package/package.json +80 -0
- package/readme.md +59 -0
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { Parser, Printer, SupportLanguage, SupportOption } from "prettier";
|
|
2
|
+
|
|
3
|
+
//#region src/options.d.ts
|
|
4
|
+
interface PluginOptions {
|
|
5
|
+
astroAllowShorthand: boolean;
|
|
6
|
+
astroSkipFrontmatter: boolean;
|
|
7
|
+
}
|
|
8
|
+
declare module 'prettier' {
|
|
9
|
+
interface RequiredOptions extends PluginOptions {}
|
|
10
|
+
}
|
|
11
|
+
declare const options: Record<keyof PluginOptions, SupportOption>;
|
|
12
|
+
//#endregion
|
|
13
|
+
//#region src/index.d.ts
|
|
14
|
+
declare const languages: Partial<SupportLanguage>[];
|
|
15
|
+
declare const parsers: Record<string, Parser>;
|
|
16
|
+
declare const printers: Record<string, Printer>;
|
|
17
|
+
declare const defaultOptions: {
|
|
18
|
+
tabWidth: number;
|
|
19
|
+
};
|
|
20
|
+
//#endregion
|
|
21
|
+
export { defaultOptions, languages, options, parsers, printers };
|
|
22
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","names":[],"sources":["../src/options.ts","../src/index.ts"],"mappings":";;;UAEU,aAAA;EACT,mBAAA;EACA,oBAAA;AAAA;AAAA;EAAA,UAKU,eAAA,SAAwB,aAAA;AAAA;AAAA,cAItB,OAAA,EAAS,MAAA,OAAa,aAAA,EAAe,aAAA;;;cCFrC,SAAA,EAAW,OAAA,CAAQ,eAAA;AAAA,cAUnB,OAAA,EAAS,MAAA,SAAe,MAAA;AAAA,cA0BxB,QAAA,EAAU,MAAA,SAAe,OAAA;AAAA,cAQhC,cAAA;EAEL,QAAA;AAAA"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,925 @@
|
|
|
1
|
+
import { parse } from "@astrojs/compiler/sync";
|
|
2
|
+
import * as prettierPluginBabel from "prettier/plugins/babel";
|
|
3
|
+
import { serialize } from "@astrojs/compiler/utils";
|
|
4
|
+
import _doc from "prettier/doc";
|
|
5
|
+
import { Buffer } from "node:buffer";
|
|
6
|
+
import { SassFormatter } from "sass-formatter";
|
|
7
|
+
//#region src/options.ts
|
|
8
|
+
const options = {
|
|
9
|
+
astroAllowShorthand: {
|
|
10
|
+
category: "Astro",
|
|
11
|
+
type: "boolean",
|
|
12
|
+
default: false,
|
|
13
|
+
description: "Enable/disable attribute shorthand if attribute name and expression are the same"
|
|
14
|
+
},
|
|
15
|
+
astroSkipFrontmatter: {
|
|
16
|
+
category: "Astro",
|
|
17
|
+
type: "boolean",
|
|
18
|
+
default: false,
|
|
19
|
+
description: "Skips the formatting of the frontmatter."
|
|
20
|
+
}
|
|
21
|
+
};
|
|
22
|
+
//#endregion
|
|
23
|
+
//#region src/printer/elements.ts
|
|
24
|
+
const selfClosingTags = [
|
|
25
|
+
"area",
|
|
26
|
+
"base",
|
|
27
|
+
"basefont",
|
|
28
|
+
"bgsound",
|
|
29
|
+
"br",
|
|
30
|
+
"col",
|
|
31
|
+
"command",
|
|
32
|
+
"embed",
|
|
33
|
+
"frame",
|
|
34
|
+
"hr",
|
|
35
|
+
"image",
|
|
36
|
+
"img",
|
|
37
|
+
"input",
|
|
38
|
+
"isindex",
|
|
39
|
+
"keygen",
|
|
40
|
+
"link",
|
|
41
|
+
"menuitem",
|
|
42
|
+
"meta",
|
|
43
|
+
"nextid",
|
|
44
|
+
"param",
|
|
45
|
+
"slot",
|
|
46
|
+
"source",
|
|
47
|
+
"track",
|
|
48
|
+
"wbr"
|
|
49
|
+
];
|
|
50
|
+
const blockElements = [
|
|
51
|
+
"address",
|
|
52
|
+
"article",
|
|
53
|
+
"aside",
|
|
54
|
+
"blockquote",
|
|
55
|
+
"details",
|
|
56
|
+
"dialog",
|
|
57
|
+
"dd",
|
|
58
|
+
"div",
|
|
59
|
+
"dl",
|
|
60
|
+
"dt",
|
|
61
|
+
"fieldset",
|
|
62
|
+
"figcaption",
|
|
63
|
+
"figure",
|
|
64
|
+
"footer",
|
|
65
|
+
"form",
|
|
66
|
+
"h1",
|
|
67
|
+
"h2",
|
|
68
|
+
"h3",
|
|
69
|
+
"h4",
|
|
70
|
+
"h5",
|
|
71
|
+
"h6",
|
|
72
|
+
"header",
|
|
73
|
+
"hgroup",
|
|
74
|
+
"hr",
|
|
75
|
+
"li",
|
|
76
|
+
"main",
|
|
77
|
+
"nav",
|
|
78
|
+
"ol",
|
|
79
|
+
"p",
|
|
80
|
+
"pre",
|
|
81
|
+
"section",
|
|
82
|
+
"table",
|
|
83
|
+
"ul",
|
|
84
|
+
"title",
|
|
85
|
+
"html"
|
|
86
|
+
];
|
|
87
|
+
/**
|
|
88
|
+
* HTML attributes that we may safely reformat (trim whitespace, add or remove newlines)
|
|
89
|
+
*/
|
|
90
|
+
const formattableAttributes = [];
|
|
91
|
+
//#endregion
|
|
92
|
+
//#region src/printer/utils.ts
|
|
93
|
+
const openingBracketReplace = "_Pé";
|
|
94
|
+
const closingBracketReplace = "èP_";
|
|
95
|
+
const atSignReplace = "ΩP_";
|
|
96
|
+
const dotReplace = "ωP_";
|
|
97
|
+
const interrogationReplace = "ΔP_";
|
|
98
|
+
function isInlineElement(path, opts, node) {
|
|
99
|
+
return node && isTagLikeNode(node) && !isBlockElement(node, opts) && !isPreTagContent(path);
|
|
100
|
+
}
|
|
101
|
+
function isBlockElement(node, opts) {
|
|
102
|
+
if (!node) return false;
|
|
103
|
+
if (opts.htmlWhitespaceSensitivity === "ignore") return true;
|
|
104
|
+
return node.type === "element" && opts.htmlWhitespaceSensitivity !== "strict" && blockElements.includes(node.name);
|
|
105
|
+
}
|
|
106
|
+
function isIgnoreDirective(node) {
|
|
107
|
+
return node.type === "comment" && node.value.trim() === "prettier-ignore";
|
|
108
|
+
}
|
|
109
|
+
/**
|
|
110
|
+
* Returns the content of the node
|
|
111
|
+
*/
|
|
112
|
+
function printRaw(node, stripLeadingAndTrailingNewline = false) {
|
|
113
|
+
if (!isNodeWithChildren(node)) return "";
|
|
114
|
+
if (node.children.length === 0) return "";
|
|
115
|
+
let raw = node.children.reduce((prev, curr) => prev + serialize(curr), "");
|
|
116
|
+
if (!stripLeadingAndTrailingNewline) return raw;
|
|
117
|
+
if (startsWithLinebreak(raw)) raw = raw.substring(raw.indexOf("\n") + 1);
|
|
118
|
+
if (endsWithLinebreak(raw)) {
|
|
119
|
+
raw = raw.substring(0, raw.lastIndexOf("\n"));
|
|
120
|
+
if (raw.charAt(raw.length - 1) === "\r") raw = raw.substring(0, raw.length - 1);
|
|
121
|
+
}
|
|
122
|
+
return raw;
|
|
123
|
+
}
|
|
124
|
+
function isNodeWithChildren(node) {
|
|
125
|
+
return node && "children" in node && Array.isArray(node.children);
|
|
126
|
+
}
|
|
127
|
+
const isEmptyTextNode = (node) => {
|
|
128
|
+
return !!node && node.type === "text" && getUnencodedText(node).trim() === "";
|
|
129
|
+
};
|
|
130
|
+
function getUnencodedText(node) {
|
|
131
|
+
return node.value;
|
|
132
|
+
}
|
|
133
|
+
function isTextNodeStartingWithLinebreak(node, nrLines = 1) {
|
|
134
|
+
return startsWithLinebreak(getUnencodedText(node), nrLines);
|
|
135
|
+
}
|
|
136
|
+
function startsWithLinebreak(text, nrLines = 1) {
|
|
137
|
+
return new RegExp(`^([\\t\\f\\r ]*\\n){${nrLines}}`).test(text);
|
|
138
|
+
}
|
|
139
|
+
function endsWithLinebreak(text, nrLines = 1) {
|
|
140
|
+
return new RegExp(`(\\n[\\t\\f\\r ]*){${nrLines}}$`).test(text);
|
|
141
|
+
}
|
|
142
|
+
function isTextNodeStartingWithWhitespace(node) {
|
|
143
|
+
return isTextNode(node) && /^\s/.test(getUnencodedText(node));
|
|
144
|
+
}
|
|
145
|
+
function endsWithWhitespace(text) {
|
|
146
|
+
return /\s$/.test(text);
|
|
147
|
+
}
|
|
148
|
+
function isTextNodeEndingWithWhitespace(node) {
|
|
149
|
+
return isTextNode(node) && endsWithWhitespace(getUnencodedText(node));
|
|
150
|
+
}
|
|
151
|
+
function hasSetDirectives(node) {
|
|
152
|
+
return Array.from(node.attributes, (attr) => attr.name).some((attr) => ["set:html", "set:text"].includes(attr));
|
|
153
|
+
}
|
|
154
|
+
/**
|
|
155
|
+
* Check if given node's start tag should hug its first child. This is the case for inline elements when there's
|
|
156
|
+
* no whitespace between the `>` and the first child.
|
|
157
|
+
*/
|
|
158
|
+
function shouldHugStart(node, opts) {
|
|
159
|
+
if (isBlockElement(node, opts)) return false;
|
|
160
|
+
if (!isNodeWithChildren(node)) return false;
|
|
161
|
+
const children = node.children;
|
|
162
|
+
if (children.length === 0) return true;
|
|
163
|
+
const firstChild = children[0];
|
|
164
|
+
return !isTextNodeStartingWithWhitespace(firstChild);
|
|
165
|
+
}
|
|
166
|
+
/**
|
|
167
|
+
* Check if given node's end tag should hug its last child. This is the case for inline elements when there's
|
|
168
|
+
* no whitespace between the last child and the `</`.
|
|
169
|
+
*/
|
|
170
|
+
function shouldHugEnd(node, opts) {
|
|
171
|
+
if (isBlockElement(node, opts)) return false;
|
|
172
|
+
if (!isNodeWithChildren(node)) return false;
|
|
173
|
+
const children = node.children;
|
|
174
|
+
if (children.length === 0) return true;
|
|
175
|
+
const lastChild = children[children.length - 1];
|
|
176
|
+
if (isExpressionNode(lastChild)) return true;
|
|
177
|
+
if (isTagLikeNode(lastChild)) return true;
|
|
178
|
+
return !isTextNodeEndingWithWhitespace(lastChild);
|
|
179
|
+
}
|
|
180
|
+
/**
|
|
181
|
+
* Returns true if the softline between `</tagName` and `>` can be omitted.
|
|
182
|
+
*/
|
|
183
|
+
function canOmitSoftlineBeforeClosingTag(path, opts) {
|
|
184
|
+
return isLastChildWithinParentBlockElement(path, opts);
|
|
185
|
+
}
|
|
186
|
+
function getChildren(node) {
|
|
187
|
+
return isNodeWithChildren(node) ? node.children : [];
|
|
188
|
+
}
|
|
189
|
+
function isLastChildWithinParentBlockElement(path, opts) {
|
|
190
|
+
const parent = path.getParentNode();
|
|
191
|
+
if (!parent || !isBlockElement(parent, opts)) return false;
|
|
192
|
+
const children = getChildren(parent);
|
|
193
|
+
return children[children.length - 1] === path.getNode();
|
|
194
|
+
}
|
|
195
|
+
function trimTextNodeLeft(node) {
|
|
196
|
+
node.value = node.value && node.value.trimStart();
|
|
197
|
+
}
|
|
198
|
+
function trimTextNodeRight(node) {
|
|
199
|
+
node.value = node.value && node.value.trimEnd();
|
|
200
|
+
}
|
|
201
|
+
function printClassNames(value) {
|
|
202
|
+
return value.trim().split(/[\r\n]+/).map((line) => {
|
|
203
|
+
const spaces = /^\s+/.exec(line);
|
|
204
|
+
return (spaces ? spaces[0] : "") + line.trim().split(/\s+/).join(" ");
|
|
205
|
+
}).join("\n");
|
|
206
|
+
}
|
|
207
|
+
/** dedent string & return tabSize (the last part is what we need) */
|
|
208
|
+
function manualDedent(input) {
|
|
209
|
+
let minTabSize = Infinity;
|
|
210
|
+
let result = input;
|
|
211
|
+
result = result.replace(/\r\n/g, "\n");
|
|
212
|
+
let char = "";
|
|
213
|
+
for (const line of result.split("\n")) {
|
|
214
|
+
if (!line) continue;
|
|
215
|
+
if (line[0] && /^\S/.test(line[0])) {
|
|
216
|
+
minTabSize = 0;
|
|
217
|
+
break;
|
|
218
|
+
}
|
|
219
|
+
const match = /^(\s+)\S+/.exec(line);
|
|
220
|
+
if (match) {
|
|
221
|
+
if (match[1] && !char) char = match[1][0];
|
|
222
|
+
if (match[1].length < minTabSize) minTabSize = match[1].length;
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
if (minTabSize > 0 && Number.isFinite(minTabSize)) result = result.replace(new RegExp(`^${new Array(minTabSize + 1).join(char)}`, "gm"), "");
|
|
226
|
+
return {
|
|
227
|
+
tabSize: minTabSize === Infinity ? 0 : minTabSize,
|
|
228
|
+
char,
|
|
229
|
+
result
|
|
230
|
+
};
|
|
231
|
+
}
|
|
232
|
+
/** True if the node is of type text */
|
|
233
|
+
function isTextNode(node) {
|
|
234
|
+
return node.type === "text";
|
|
235
|
+
}
|
|
236
|
+
function isExpressionNode(node) {
|
|
237
|
+
return node.type === "expression";
|
|
238
|
+
}
|
|
239
|
+
/** True if the node is TagLikeNode:
|
|
240
|
+
*
|
|
241
|
+
* ElementNode | ComponentNode | CustomElementNode | FragmentNode */
|
|
242
|
+
function isTagLikeNode(node) {
|
|
243
|
+
return node.type === "element" || node.type === "component" || node.type === "custom-element" || node.type === "fragment";
|
|
244
|
+
}
|
|
245
|
+
/**
|
|
246
|
+
* Returns siblings, that is, the children of the parent.
|
|
247
|
+
*/
|
|
248
|
+
function getSiblings(path) {
|
|
249
|
+
const parent = path.getParentNode();
|
|
250
|
+
if (!parent) return [];
|
|
251
|
+
return getChildren(parent);
|
|
252
|
+
}
|
|
253
|
+
function getNextNode(path) {
|
|
254
|
+
const node = path.getNode();
|
|
255
|
+
if (node) {
|
|
256
|
+
const siblings = getSiblings(path);
|
|
257
|
+
if (node.position?.start === siblings[siblings.length - 1].position?.start) return null;
|
|
258
|
+
for (let i = 0; i < siblings.length; i++) if (siblings[i].position?.start === node.position?.start && i !== siblings.length - 1) return siblings[i + 1];
|
|
259
|
+
}
|
|
260
|
+
return null;
|
|
261
|
+
}
|
|
262
|
+
const isPreTagContent = (path) => {
|
|
263
|
+
if (!path || !path.stack || !Array.isArray(path.stack)) return false;
|
|
264
|
+
return path.stack.some((node) => node.type === "element" && node.name.toLowerCase() === "pre" || node.type === "attribute" && !formattableAttributes.includes(node.name));
|
|
265
|
+
};
|
|
266
|
+
function getPreferredQuote(rawContent, preferredQuote) {
|
|
267
|
+
const double = {
|
|
268
|
+
quote: "\"",
|
|
269
|
+
regex: /"/g,
|
|
270
|
+
escaped: """
|
|
271
|
+
};
|
|
272
|
+
const single = {
|
|
273
|
+
quote: "'",
|
|
274
|
+
regex: /'/g,
|
|
275
|
+
escaped: "'"
|
|
276
|
+
};
|
|
277
|
+
const preferred = preferredQuote === "'" ? single : double;
|
|
278
|
+
const alternate = preferred === single ? double : single;
|
|
279
|
+
let result = preferred;
|
|
280
|
+
if (rawContent.includes(preferred.quote) || rawContent.includes(alternate.quote)) result = (preferred.regex.exec(rawContent) || []).length > (alternate.regex.exec(rawContent) || []).length ? alternate : preferred;
|
|
281
|
+
return result;
|
|
282
|
+
}
|
|
283
|
+
function inferParserByTypeAttribute(type) {
|
|
284
|
+
if (!type) return "babel-ts";
|
|
285
|
+
switch (type) {
|
|
286
|
+
case "module":
|
|
287
|
+
case "text/javascript":
|
|
288
|
+
case "text/babel":
|
|
289
|
+
case "application/javascript": return "babel";
|
|
290
|
+
case "application/x-typescript": return "babel-ts";
|
|
291
|
+
case "text/markdown": return "markdown";
|
|
292
|
+
case "text/html": return "html";
|
|
293
|
+
case "text/x-handlebars-template": return "glimmer";
|
|
294
|
+
default:
|
|
295
|
+
if (type.endsWith("json") || type.endsWith("importmap") || type === "speculationrules") return "json";
|
|
296
|
+
return "babel-ts";
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
//#endregion
|
|
300
|
+
//#region src/printer/index.ts
|
|
301
|
+
const { builders: { breakParent, dedent, fill, group: group$1, indent: indent$1, join: join$1, line: line$1, softline: softline$1, hardline: hardline$1, literalline }, utils: { stripTrailingHardline: stripTrailingHardline$1 } } = _doc;
|
|
302
|
+
let ignoreNext = false;
|
|
303
|
+
function print(path, opts, print) {
|
|
304
|
+
const node = path.node;
|
|
305
|
+
if (!node) return "";
|
|
306
|
+
if (ignoreNext && !isEmptyTextNode(node)) {
|
|
307
|
+
ignoreNext = false;
|
|
308
|
+
return [opts.originalText.slice(opts.locStart(node), opts.locEnd(node)).split("\n").map((lineContent, i) => i == 0 ? [lineContent] : [literalline, lineContent]).flat()];
|
|
309
|
+
}
|
|
310
|
+
if (typeof node === "string") return node;
|
|
311
|
+
switch (node.type) {
|
|
312
|
+
case "root": return [stripTrailingHardline$1(path.map(print, "children")), hardline$1];
|
|
313
|
+
case "text": {
|
|
314
|
+
const rawText = getUnencodedText(node);
|
|
315
|
+
if (isEmptyTextNode(node)) {
|
|
316
|
+
const hasWhiteSpace = rawText.trim().length < getUnencodedText(node).length;
|
|
317
|
+
const hasOneOrMoreNewlines = getUnencodedText(node).includes("\n");
|
|
318
|
+
if (/\n\s*\n\r?/.test(getUnencodedText(node))) return [hardline$1, hardline$1];
|
|
319
|
+
if (hasOneOrMoreNewlines) return hardline$1;
|
|
320
|
+
if (hasWhiteSpace) return line$1;
|
|
321
|
+
return "";
|
|
322
|
+
}
|
|
323
|
+
/**
|
|
324
|
+
* For non-empty text nodes each sequence of non-whitespace characters (effectively,
|
|
325
|
+
* each "word") is joined by a single `line`, which will be rendered as a single space
|
|
326
|
+
* until this node's current line is out of room, at which `fill` will break at the
|
|
327
|
+
* most convenient instance of `line`.
|
|
328
|
+
*/
|
|
329
|
+
return fill(splitTextToDocs(node));
|
|
330
|
+
}
|
|
331
|
+
case "component":
|
|
332
|
+
case "fragment":
|
|
333
|
+
case "custom-element":
|
|
334
|
+
case "element": {
|
|
335
|
+
let isEmpty;
|
|
336
|
+
if (!node.children) isEmpty = true;
|
|
337
|
+
else isEmpty = node.children.every((child) => isEmptyTextNode(child));
|
|
338
|
+
/**
|
|
339
|
+
* An element is allowed to self close only if:
|
|
340
|
+
* It is empty AND
|
|
341
|
+
* It's a component OR
|
|
342
|
+
* It's in the HTML spec as a void element OR
|
|
343
|
+
* It has a `set:*` directive
|
|
344
|
+
*/
|
|
345
|
+
const isSelfClosingTag = isEmpty && (node.type === "component" || selfClosingTags.includes(node.name) || hasSetDirectives(node));
|
|
346
|
+
const isSingleLinePerAttribute = opts.singleAttributePerLine && node.attributes.length > 1;
|
|
347
|
+
const attributes = join$1(isSingleLinePerAttribute ? breakParent : "", path.map(print, "attributes"));
|
|
348
|
+
if (isSelfClosingTag) return group$1([
|
|
349
|
+
"<",
|
|
350
|
+
node.name,
|
|
351
|
+
indent$1(attributes),
|
|
352
|
+
line$1,
|
|
353
|
+
`/>`
|
|
354
|
+
]);
|
|
355
|
+
if (node.children) {
|
|
356
|
+
const children = node.children;
|
|
357
|
+
const firstChild = children[0];
|
|
358
|
+
const lastChild = children[children.length - 1];
|
|
359
|
+
let noHugSeparatorStart = softline$1;
|
|
360
|
+
let noHugSeparatorEnd = softline$1;
|
|
361
|
+
const hugStart = shouldHugStart(node, opts);
|
|
362
|
+
const hugEnd = shouldHugEnd(node, opts);
|
|
363
|
+
let body;
|
|
364
|
+
if (isEmpty) body = isInlineElement(path, opts, node) && node.children.length && isTextNodeStartingWithWhitespace(node.children[0]) && !isPreTagContent(path) ? () => line$1 : () => node.children.length > 0 ? softline$1 : "";
|
|
365
|
+
else if (isPreTagContent(path)) body = () => printRaw(node);
|
|
366
|
+
else if (isInlineElement(path, opts, node) && !isPreTagContent(path)) body = () => path.map(print, "children");
|
|
367
|
+
else body = () => path.map(print, "children");
|
|
368
|
+
const openingTag = [
|
|
369
|
+
"<",
|
|
370
|
+
node.name,
|
|
371
|
+
indent$1(group$1([attributes, hugStart ? "" : !isPreTagContent(path) && !opts.bracketSameLine ? dedent(softline$1) : ""]))
|
|
372
|
+
];
|
|
373
|
+
if (hugStart && hugEnd) {
|
|
374
|
+
const huggedContent = [isSingleLinePerAttribute ? hardline$1 : softline$1, group$1([
|
|
375
|
+
">",
|
|
376
|
+
body(),
|
|
377
|
+
`</${node.name}`
|
|
378
|
+
])];
|
|
379
|
+
const omitSoftlineBeforeClosingTag = isEmpty || canOmitSoftlineBeforeClosingTag(path, opts);
|
|
380
|
+
return group$1([
|
|
381
|
+
...openingTag,
|
|
382
|
+
isEmpty ? group$1(huggedContent) : group$1(indent$1(huggedContent)),
|
|
383
|
+
omitSoftlineBeforeClosingTag ? "" : softline$1,
|
|
384
|
+
">"
|
|
385
|
+
]);
|
|
386
|
+
}
|
|
387
|
+
if (isPreTagContent(path)) {
|
|
388
|
+
noHugSeparatorStart = "";
|
|
389
|
+
noHugSeparatorEnd = "";
|
|
390
|
+
} else {
|
|
391
|
+
let didSetEndSeparator = false;
|
|
392
|
+
if (!hugStart && firstChild && isTextNode(firstChild)) {
|
|
393
|
+
if (isTextNodeStartingWithLinebreak(firstChild) && firstChild !== lastChild && (!isInlineElement(path, opts, node) || isTextNodeEndingWithWhitespace(lastChild))) {
|
|
394
|
+
noHugSeparatorStart = hardline$1;
|
|
395
|
+
noHugSeparatorEnd = hardline$1;
|
|
396
|
+
didSetEndSeparator = true;
|
|
397
|
+
} else if (isInlineElement(path, opts, node)) noHugSeparatorStart = line$1;
|
|
398
|
+
trimTextNodeLeft(firstChild);
|
|
399
|
+
}
|
|
400
|
+
if (!hugEnd && lastChild && isTextNode(lastChild)) {
|
|
401
|
+
if (isInlineElement(path, opts, node) && !didSetEndSeparator) noHugSeparatorEnd = line$1;
|
|
402
|
+
trimTextNodeRight(lastChild);
|
|
403
|
+
}
|
|
404
|
+
}
|
|
405
|
+
if (hugStart) return group$1([
|
|
406
|
+
...openingTag,
|
|
407
|
+
indent$1([softline$1, group$1([">", body()])]),
|
|
408
|
+
noHugSeparatorEnd,
|
|
409
|
+
`</${node.name}>`
|
|
410
|
+
]);
|
|
411
|
+
if (hugEnd) return group$1([
|
|
412
|
+
...openingTag,
|
|
413
|
+
">",
|
|
414
|
+
indent$1([noHugSeparatorStart, group$1([body(), `</${node.name}`])]),
|
|
415
|
+
canOmitSoftlineBeforeClosingTag(path, opts) ? "" : softline$1,
|
|
416
|
+
">"
|
|
417
|
+
]);
|
|
418
|
+
if (isEmpty) return group$1([
|
|
419
|
+
...openingTag,
|
|
420
|
+
">",
|
|
421
|
+
body(),
|
|
422
|
+
`</${node.name}>`
|
|
423
|
+
]);
|
|
424
|
+
return group$1([
|
|
425
|
+
...openingTag,
|
|
426
|
+
">",
|
|
427
|
+
indent$1([noHugSeparatorStart, body()]),
|
|
428
|
+
noHugSeparatorEnd,
|
|
429
|
+
`</${node.name}>`
|
|
430
|
+
]);
|
|
431
|
+
}
|
|
432
|
+
return "";
|
|
433
|
+
}
|
|
434
|
+
case "attribute": {
|
|
435
|
+
const name = node.name.trim();
|
|
436
|
+
switch (node.kind) {
|
|
437
|
+
case "empty": return [line$1, name];
|
|
438
|
+
case "expression": return "";
|
|
439
|
+
case "quoted":
|
|
440
|
+
let value = node.value;
|
|
441
|
+
if (node.name === "class") value = printClassNames(value);
|
|
442
|
+
const unescapedValue = value.replace(/'/g, "'").replace(/"/g, "\"");
|
|
443
|
+
const { escaped, quote, regex } = getPreferredQuote(unescapedValue, opts.jsxSingleQuote ? "'" : "\"");
|
|
444
|
+
return [
|
|
445
|
+
line$1,
|
|
446
|
+
name,
|
|
447
|
+
"=",
|
|
448
|
+
quote,
|
|
449
|
+
unescapedValue.replace(regex, escaped),
|
|
450
|
+
quote
|
|
451
|
+
];
|
|
452
|
+
case "shorthand": return [
|
|
453
|
+
line$1,
|
|
454
|
+
"{",
|
|
455
|
+
name,
|
|
456
|
+
"}"
|
|
457
|
+
];
|
|
458
|
+
case "spread": return [
|
|
459
|
+
line$1,
|
|
460
|
+
"{...",
|
|
461
|
+
name,
|
|
462
|
+
"}"
|
|
463
|
+
];
|
|
464
|
+
case "template-literal": return [
|
|
465
|
+
line$1,
|
|
466
|
+
name,
|
|
467
|
+
"=",
|
|
468
|
+
"`",
|
|
469
|
+
node.value,
|
|
470
|
+
"`"
|
|
471
|
+
];
|
|
472
|
+
default: break;
|
|
473
|
+
}
|
|
474
|
+
return "";
|
|
475
|
+
}
|
|
476
|
+
case "doctype": return ["<!doctype html>", hardline$1];
|
|
477
|
+
case "comment":
|
|
478
|
+
if (isIgnoreDirective(node)) ignoreNext = true;
|
|
479
|
+
const nextNode = getNextNode(path);
|
|
480
|
+
let trailingLine = "";
|
|
481
|
+
if (nextNode && isTagLikeNode(nextNode)) trailingLine = hardline$1;
|
|
482
|
+
return [
|
|
483
|
+
"<!--",
|
|
484
|
+
getUnencodedText(node),
|
|
485
|
+
"-->",
|
|
486
|
+
trailingLine
|
|
487
|
+
];
|
|
488
|
+
default: throw new Error(`Unhandled node type "${node.type}"!`);
|
|
489
|
+
}
|
|
490
|
+
}
|
|
491
|
+
/**
|
|
492
|
+
* Split the text into words separated by whitespace. Replace the whitespaces by lines,
|
|
493
|
+
* collapsing multiple whitespaces into a single line.
|
|
494
|
+
*
|
|
495
|
+
* If the text starts or ends with multiple newlines, two of those should be kept.
|
|
496
|
+
*/
|
|
497
|
+
function splitTextToDocs(node) {
|
|
498
|
+
const text = getUnencodedText(node);
|
|
499
|
+
let docs = join$1(line$1, text.split(/[\t\n\f\r ]+/)).filter((doc) => doc !== "");
|
|
500
|
+
if (startsWithLinebreak(text)) docs[0] = hardline$1;
|
|
501
|
+
if (startsWithLinebreak(text, 2)) docs = [hardline$1, ...docs];
|
|
502
|
+
if (endsWithLinebreak(text)) docs[docs.length - 1] = hardline$1;
|
|
503
|
+
if (endsWithLinebreak(text, 2)) docs = [...docs, hardline$1];
|
|
504
|
+
return docs;
|
|
505
|
+
}
|
|
506
|
+
//#endregion
|
|
507
|
+
//#region src/printer/embed.ts
|
|
508
|
+
const { builders: { group, indent, join, line, softline, hardline, lineSuffixBoundary }, utils: { stripTrailingHardline, mapDoc } } = _doc;
|
|
509
|
+
const supportedStyleLangValues = [
|
|
510
|
+
"css",
|
|
511
|
+
"scss",
|
|
512
|
+
"sass",
|
|
513
|
+
"less"
|
|
514
|
+
];
|
|
515
|
+
const embed = ((path, options) => {
|
|
516
|
+
const parserOption = options;
|
|
517
|
+
return async (textToDoc, print) => {
|
|
518
|
+
const node = path.node;
|
|
519
|
+
if (!node) return void 0;
|
|
520
|
+
if (node.type === "expression") {
|
|
521
|
+
const rawTagPlaceholders = [];
|
|
522
|
+
const textContent = printRaw(makeNodeJSXCompatible(replaceRawTagChildren(node, rawTagPlaceholders)));
|
|
523
|
+
let content;
|
|
524
|
+
content = await wrapParserTryCatch(textToDoc, textContent, {
|
|
525
|
+
...options,
|
|
526
|
+
parser: "astroExpressionParser"
|
|
527
|
+
});
|
|
528
|
+
content = stripTrailingHardline(content);
|
|
529
|
+
for (const entry of rawTagPlaceholders) {
|
|
530
|
+
let formattedContent;
|
|
531
|
+
if (entry.tagName === "script") {
|
|
532
|
+
const parser = inferParserByTypeAttribute(entry.typeAttr || "");
|
|
533
|
+
formattedContent = await wrapParserTryCatch(textToDoc, entry.content, {
|
|
534
|
+
...options,
|
|
535
|
+
parser
|
|
536
|
+
});
|
|
537
|
+
} else {
|
|
538
|
+
const langValue = entry.langAttr?.toLowerCase();
|
|
539
|
+
if (langValue === "sass") {
|
|
540
|
+
const lineEnding = parserOption?.endOfLine?.toUpperCase() === "CRLF" ? "CRLF" : "LF";
|
|
541
|
+
const sassOptions = {
|
|
542
|
+
tabSize: parserOption.tabWidth,
|
|
543
|
+
insertSpaces: !parserOption.useTabs,
|
|
544
|
+
lineEnding
|
|
545
|
+
};
|
|
546
|
+
const { result: raw } = manualDedent(entry.content);
|
|
547
|
+
formattedContent = join(hardline, SassFormatter.Format(raw, sassOptions).trim().split("\n"));
|
|
548
|
+
} else {
|
|
549
|
+
const styleParser = langValue === "scss" || langValue === "less" ? langValue : "css";
|
|
550
|
+
formattedContent = await wrapParserTryCatch(textToDoc, entry.content, {
|
|
551
|
+
...options,
|
|
552
|
+
parser: styleParser
|
|
553
|
+
});
|
|
554
|
+
}
|
|
555
|
+
}
|
|
556
|
+
formattedContent = stripTrailingHardline(formattedContent);
|
|
557
|
+
const isEmpty = /^\s*$/.test(entry.content);
|
|
558
|
+
const fullTagDoc = [
|
|
559
|
+
entry.openingTag,
|
|
560
|
+
indent([isEmpty ? "" : hardline, formattedContent]),
|
|
561
|
+
isEmpty ? "" : hardline,
|
|
562
|
+
`</${entry.tagName}>`
|
|
563
|
+
];
|
|
564
|
+
content = mapDoc(content, (doc) => {
|
|
565
|
+
if (typeof doc === "string" && doc.includes(entry.placeholder)) {
|
|
566
|
+
const parts = doc.split(entry.placeholder);
|
|
567
|
+
if (parts.length === 2) {
|
|
568
|
+
if (entry.isDirectChild) return [
|
|
569
|
+
parts[0],
|
|
570
|
+
fullTagDoc,
|
|
571
|
+
parts[1]
|
|
572
|
+
];
|
|
573
|
+
if (isEmpty) return [parts[0], parts[1]];
|
|
574
|
+
return [
|
|
575
|
+
parts[0],
|
|
576
|
+
indent([hardline, formattedContent]),
|
|
577
|
+
hardline,
|
|
578
|
+
parts[1]
|
|
579
|
+
];
|
|
580
|
+
}
|
|
581
|
+
}
|
|
582
|
+
return doc;
|
|
583
|
+
});
|
|
584
|
+
}
|
|
585
|
+
const strings = [];
|
|
586
|
+
mapDoc(content, (doc) => {
|
|
587
|
+
if (typeof doc === "string") strings.push(doc);
|
|
588
|
+
});
|
|
589
|
+
if (strings.every((value) => value.startsWith("//"))) return group([
|
|
590
|
+
"{",
|
|
591
|
+
content,
|
|
592
|
+
softline,
|
|
593
|
+
lineSuffixBoundary,
|
|
594
|
+
"}"
|
|
595
|
+
]);
|
|
596
|
+
const astroDoc = mapDoc(content, (doc) => {
|
|
597
|
+
if (typeof doc === "string") {
|
|
598
|
+
doc = doc.replaceAll(openingBracketReplace, "{");
|
|
599
|
+
doc = doc.replaceAll(closingBracketReplace, "}");
|
|
600
|
+
doc = doc.replaceAll(atSignReplace, "@");
|
|
601
|
+
doc = doc.replaceAll(dotReplace, ".");
|
|
602
|
+
doc = doc.replaceAll(interrogationReplace, "?");
|
|
603
|
+
}
|
|
604
|
+
return doc;
|
|
605
|
+
});
|
|
606
|
+
if (rawTagPlaceholders.length > 0) return [
|
|
607
|
+
"{",
|
|
608
|
+
indent([hardline, astroDoc]),
|
|
609
|
+
hardline,
|
|
610
|
+
lineSuffixBoundary,
|
|
611
|
+
"}"
|
|
612
|
+
];
|
|
613
|
+
return group([
|
|
614
|
+
"{",
|
|
615
|
+
indent([softline, astroDoc]),
|
|
616
|
+
softline,
|
|
617
|
+
lineSuffixBoundary,
|
|
618
|
+
"}"
|
|
619
|
+
]);
|
|
620
|
+
}
|
|
621
|
+
if (node.type === "attribute" && node.kind === "expression") {
|
|
622
|
+
const value = node.value.trim();
|
|
623
|
+
const name = node.name.trim();
|
|
624
|
+
const attrNodeValue = await wrapParserTryCatch(textToDoc, value, {
|
|
625
|
+
...options,
|
|
626
|
+
parser: "astroExpressionParser"
|
|
627
|
+
});
|
|
628
|
+
if (name === value && options.astroAllowShorthand) return [
|
|
629
|
+
line,
|
|
630
|
+
"{",
|
|
631
|
+
attrNodeValue,
|
|
632
|
+
"}"
|
|
633
|
+
];
|
|
634
|
+
return [
|
|
635
|
+
line,
|
|
636
|
+
name,
|
|
637
|
+
"=",
|
|
638
|
+
"{",
|
|
639
|
+
attrNodeValue,
|
|
640
|
+
"}"
|
|
641
|
+
];
|
|
642
|
+
}
|
|
643
|
+
if (node.type === "attribute" && node.kind === "spread") return [
|
|
644
|
+
line,
|
|
645
|
+
"{...",
|
|
646
|
+
await wrapParserTryCatch(textToDoc, node.name, {
|
|
647
|
+
...options,
|
|
648
|
+
parser: "astroExpressionParser"
|
|
649
|
+
}),
|
|
650
|
+
"}"
|
|
651
|
+
];
|
|
652
|
+
if (node.type === "frontmatter") {
|
|
653
|
+
if (options.astroSkipFrontmatter) return [group([
|
|
654
|
+
"---",
|
|
655
|
+
node.value,
|
|
656
|
+
"---",
|
|
657
|
+
hardline
|
|
658
|
+
]), hardline];
|
|
659
|
+
return [group([
|
|
660
|
+
"---",
|
|
661
|
+
hardline,
|
|
662
|
+
await wrapParserTryCatch(textToDoc, node.value, {
|
|
663
|
+
...options,
|
|
664
|
+
parser: "babel-ts"
|
|
665
|
+
}),
|
|
666
|
+
hardline,
|
|
667
|
+
"---",
|
|
668
|
+
hardline
|
|
669
|
+
]), hardline];
|
|
670
|
+
}
|
|
671
|
+
if (node.type === "element" && node.name === "script" && node.children.length) {
|
|
672
|
+
const typeAttribute = node.attributes.find((attr) => attr.name === "type")?.value;
|
|
673
|
+
let parser = "babel-ts";
|
|
674
|
+
if (typeAttribute) parser = inferParserByTypeAttribute(typeAttribute);
|
|
675
|
+
const scriptContent = printRaw(node);
|
|
676
|
+
let formattedScript = await wrapParserTryCatch(textToDoc, scriptContent, {
|
|
677
|
+
...options,
|
|
678
|
+
parser
|
|
679
|
+
});
|
|
680
|
+
formattedScript = stripTrailingHardline(formattedScript);
|
|
681
|
+
const isEmpty = /^\s*$/.test(scriptContent);
|
|
682
|
+
return [
|
|
683
|
+
group([
|
|
684
|
+
"<script",
|
|
685
|
+
indent(group(path.map(print, "attributes"))),
|
|
686
|
+
options.bracketSameLine ? "" : softline,
|
|
687
|
+
">"
|
|
688
|
+
]),
|
|
689
|
+
indent([isEmpty ? "" : hardline, formattedScript]),
|
|
690
|
+
isEmpty ? "" : hardline,
|
|
691
|
+
"<\/script>"
|
|
692
|
+
];
|
|
693
|
+
}
|
|
694
|
+
if (node.type === "element" && node.name === "style") {
|
|
695
|
+
const content = printRaw(node);
|
|
696
|
+
let parserLang = "css";
|
|
697
|
+
if (node.attributes) {
|
|
698
|
+
const langAttribute = node.attributes.filter((x) => x.name === "lang");
|
|
699
|
+
if (langAttribute.length) {
|
|
700
|
+
const styleLang = langAttribute[0].value.toLowerCase();
|
|
701
|
+
parserLang = supportedStyleLangValues.includes(styleLang) ? styleLang : void 0;
|
|
702
|
+
}
|
|
703
|
+
}
|
|
704
|
+
return await embedStyle(parserLang, content, path, print, textToDoc, parserOption);
|
|
705
|
+
}
|
|
706
|
+
};
|
|
707
|
+
});
|
|
708
|
+
async function wrapParserTryCatch(cb, text, options) {
|
|
709
|
+
try {
|
|
710
|
+
return await cb(text, options);
|
|
711
|
+
} catch (e) {
|
|
712
|
+
process.env.PRETTIER_DEBUG = "true";
|
|
713
|
+
throw e;
|
|
714
|
+
}
|
|
715
|
+
}
|
|
716
|
+
/**
|
|
717
|
+
* Due to the differences between Astro and JSX, Prettier's TypeScript parsers (be it `typescript` or `babel-ts`) are not
|
|
718
|
+
* able to parse all expressions. So we need to first make the expression compatible before passing it to the parser
|
|
719
|
+
*
|
|
720
|
+
* A list of the difference that matters here:
|
|
721
|
+
* - Astro allows a shorthand syntax for props. ex: `<Component {props} />`
|
|
722
|
+
* - Astro allows multiple root elements. ex: `<div></div><div></div>`
|
|
723
|
+
* - Astro allows attributes to include at signs (@) and dots (.)
|
|
724
|
+
*/
|
|
725
|
+
function makeNodeJSXCompatible(node) {
|
|
726
|
+
const newNode = { ...node };
|
|
727
|
+
const childBundle = [];
|
|
728
|
+
let childBundleIndex = 0;
|
|
729
|
+
if (isNodeWithChildren(newNode)) newNode.children = newNode.children.reduce((result, child, index) => {
|
|
730
|
+
const previousChildren = newNode.children[index - 1];
|
|
731
|
+
const nextChildren = newNode.children[index + 1];
|
|
732
|
+
if (isTagLikeNode(child)) {
|
|
733
|
+
child.attributes = child.attributes.map(makeAttributeJSXCompatible);
|
|
734
|
+
if (!childBundle[childBundleIndex]) childBundle[childBundleIndex] = [];
|
|
735
|
+
if (isNodeWithChildren(child)) child = makeNodeJSXCompatible(child);
|
|
736
|
+
if ((!previousChildren || isTextNode(previousChildren)) && nextChildren && isTagLikeNode(nextChildren)) {
|
|
737
|
+
childBundle[childBundleIndex].push(child);
|
|
738
|
+
return result;
|
|
739
|
+
}
|
|
740
|
+
if (previousChildren && isTagLikeNode(previousChildren) && nextChildren && isTagLikeNode(nextChildren)) {
|
|
741
|
+
childBundle[childBundleIndex].push(child);
|
|
742
|
+
return result;
|
|
743
|
+
}
|
|
744
|
+
if ((!nextChildren || isTextNode(nextChildren)) && childBundle[childBundleIndex].length > 0) {
|
|
745
|
+
childBundle[childBundleIndex].push(child);
|
|
746
|
+
const parentNode = {
|
|
747
|
+
type: "fragment",
|
|
748
|
+
name: "",
|
|
749
|
+
attributes: [],
|
|
750
|
+
children: childBundle[childBundleIndex]
|
|
751
|
+
};
|
|
752
|
+
childBundleIndex += 1;
|
|
753
|
+
result.push(parentNode);
|
|
754
|
+
return result;
|
|
755
|
+
}
|
|
756
|
+
} else childBundleIndex += 1;
|
|
757
|
+
result.push(child);
|
|
758
|
+
return result;
|
|
759
|
+
}, []);
|
|
760
|
+
return newNode;
|
|
761
|
+
function makeAttributeJSXCompatible(attr) {
|
|
762
|
+
if (attr.kind === "shorthand") {
|
|
763
|
+
attr.kind = "empty";
|
|
764
|
+
attr.name = openingBracketReplace + attr.name + closingBracketReplace;
|
|
765
|
+
}
|
|
766
|
+
if (attr.kind !== "spread") {
|
|
767
|
+
if (attr.name.includes("@")) attr.name = attr.name.replaceAll("@", atSignReplace);
|
|
768
|
+
if (attr.name.includes(".")) attr.name = attr.name.replaceAll(".", dotReplace);
|
|
769
|
+
if (attr.name.includes("?")) attr.name = attr.name.replaceAll("?", interrogationReplace);
|
|
770
|
+
}
|
|
771
|
+
return attr;
|
|
772
|
+
}
|
|
773
|
+
}
|
|
774
|
+
/** Tags whose content is raw text (not JSX) and must be extracted before Babel parsing */
|
|
775
|
+
const rawContentTags = ["script", "style"];
|
|
776
|
+
/**
|
|
777
|
+
* Replace the children of any raw-content elements (script, style) in an expression
|
|
778
|
+
* node with placeholder text nodes. This prevents Babel's JSX parser from trying to
|
|
779
|
+
* parse their content as JSX when they appear inside an expression.
|
|
780
|
+
*
|
|
781
|
+
* See: https://github.com/withastro/prettier-plugin-astro/issues/452
|
|
782
|
+
* See: https://github.com/withastro/prettier-plugin-astro/issues/454
|
|
783
|
+
*/
|
|
784
|
+
function replaceRawTagChildren(node, placeholders, isTopLevel = true) {
|
|
785
|
+
const newNode = { ...node };
|
|
786
|
+
if (isNodeWithChildren(newNode)) newNode.children = newNode.children.map((child) => {
|
|
787
|
+
if (child.type === "element" && rawContentTags.includes(child.name) && child.children.length) {
|
|
788
|
+
const placeholder = `__ASTRO_RAW_TAG_PLACEHOLDER_${placeholders.length}__`;
|
|
789
|
+
const content = printRaw(child);
|
|
790
|
+
const attrs = (child.attributes || []).map((a) => {
|
|
791
|
+
if (a.kind === "empty") return a.name;
|
|
792
|
+
if (a.kind === "expression") return `${a.name}={${a.value}}`;
|
|
793
|
+
if (a.kind === "spread") return `{...${a.name}}`;
|
|
794
|
+
return `${a.name}="${a.value}"`;
|
|
795
|
+
}).join(" ");
|
|
796
|
+
const openingTag = attrs ? `<${child.name} ${attrs}>` : `<${child.name}>`;
|
|
797
|
+
placeholders.push({
|
|
798
|
+
placeholder,
|
|
799
|
+
content,
|
|
800
|
+
tagName: child.name,
|
|
801
|
+
openingTag,
|
|
802
|
+
isDirectChild: isTopLevel,
|
|
803
|
+
typeAttr: child.attributes?.find((a) => a.name === "type")?.value,
|
|
804
|
+
langAttr: child.attributes?.find((a) => a.name === "lang")?.value
|
|
805
|
+
});
|
|
806
|
+
if (isTopLevel) return {
|
|
807
|
+
type: "text",
|
|
808
|
+
value: placeholder
|
|
809
|
+
};
|
|
810
|
+
return {
|
|
811
|
+
...child,
|
|
812
|
+
children: [{
|
|
813
|
+
type: "text",
|
|
814
|
+
value: placeholder
|
|
815
|
+
}]
|
|
816
|
+
};
|
|
817
|
+
}
|
|
818
|
+
if (isNodeWithChildren(child)) return replaceRawTagChildren(child, placeholders, false);
|
|
819
|
+
return child;
|
|
820
|
+
});
|
|
821
|
+
return newNode;
|
|
822
|
+
}
|
|
823
|
+
/**
|
|
824
|
+
* Format the content of a style tag and print the entire element
|
|
825
|
+
*/
|
|
826
|
+
async function embedStyle(lang, content, path, print, textToDoc, options) {
|
|
827
|
+
const isEmpty = /^\s*$/.test(content);
|
|
828
|
+
switch (lang) {
|
|
829
|
+
case "less":
|
|
830
|
+
case "css":
|
|
831
|
+
case "scss": {
|
|
832
|
+
let formattedStyles = await wrapParserTryCatch(textToDoc, content, {
|
|
833
|
+
...options,
|
|
834
|
+
parser: lang
|
|
835
|
+
});
|
|
836
|
+
formattedStyles = stripTrailingHardline(formattedStyles);
|
|
837
|
+
return [
|
|
838
|
+
group([
|
|
839
|
+
"<style",
|
|
840
|
+
indent(group(path.map(print, "attributes"))),
|
|
841
|
+
softline,
|
|
842
|
+
">"
|
|
843
|
+
]),
|
|
844
|
+
indent([isEmpty ? "" : hardline, formattedStyles]),
|
|
845
|
+
isEmpty ? "" : hardline,
|
|
846
|
+
"</style>"
|
|
847
|
+
];
|
|
848
|
+
}
|
|
849
|
+
case "sass": {
|
|
850
|
+
const lineEnding = options?.endOfLine?.toUpperCase() === "CRLF" ? "CRLF" : "LF";
|
|
851
|
+
const sassOptions = {
|
|
852
|
+
tabSize: options.tabWidth,
|
|
853
|
+
insertSpaces: !options.useTabs,
|
|
854
|
+
lineEnding
|
|
855
|
+
};
|
|
856
|
+
const { result: raw } = manualDedent(content);
|
|
857
|
+
const formattedSass = join(hardline, SassFormatter.Format(raw, sassOptions).trim().split("\n"));
|
|
858
|
+
return [
|
|
859
|
+
group([
|
|
860
|
+
"<style",
|
|
861
|
+
indent(group(path.map(print, "attributes"))),
|
|
862
|
+
softline,
|
|
863
|
+
">"
|
|
864
|
+
]),
|
|
865
|
+
indent([isEmpty ? "" : hardline, formattedSass]),
|
|
866
|
+
isEmpty ? "" : hardline,
|
|
867
|
+
"</style>"
|
|
868
|
+
];
|
|
869
|
+
}
|
|
870
|
+
case void 0: {
|
|
871
|
+
const node = path.getNode();
|
|
872
|
+
if (node) return Buffer.from(options.originalText).subarray(options.locStart(node), options.locEnd(node)).toString();
|
|
873
|
+
return;
|
|
874
|
+
}
|
|
875
|
+
}
|
|
876
|
+
}
|
|
877
|
+
//#endregion
|
|
878
|
+
//#region src/get-visitor-keys.ts
|
|
879
|
+
function getVisitorKeys(node) {
|
|
880
|
+
if (!node || typeof node !== "object") return [];
|
|
881
|
+
return Object.keys(node).filter((key) => {
|
|
882
|
+
const value = node[key];
|
|
883
|
+
return typeof value === "object" && value !== null && key !== "position" && key !== "type" && !Array.isArray(value) || Array.isArray(value) && value.length > 0 && typeof value[0] === "object";
|
|
884
|
+
});
|
|
885
|
+
}
|
|
886
|
+
//#endregion
|
|
887
|
+
//#region src/index.ts
|
|
888
|
+
const babelParser = prettierPluginBabel.parsers["babel-ts"];
|
|
889
|
+
const languages = [{
|
|
890
|
+
name: "astro",
|
|
891
|
+
parsers: ["astro"],
|
|
892
|
+
extensions: [".astro"],
|
|
893
|
+
vscodeLanguageIds: ["astro"]
|
|
894
|
+
}];
|
|
895
|
+
const parsers = {
|
|
896
|
+
astro: {
|
|
897
|
+
parse: (source) => parse(source, { position: true }).ast,
|
|
898
|
+
astFormat: "astro",
|
|
899
|
+
locStart: (node) => node.position.start.offset,
|
|
900
|
+
locEnd: (node) => node.position.end?.offset
|
|
901
|
+
},
|
|
902
|
+
astroExpressionParser: {
|
|
903
|
+
...babelParser,
|
|
904
|
+
preprocess(text) {
|
|
905
|
+
return `<>{${text}\n}</>`;
|
|
906
|
+
},
|
|
907
|
+
parse(text, opts) {
|
|
908
|
+
const ast = babelParser.parse(text, opts);
|
|
909
|
+
return {
|
|
910
|
+
...ast,
|
|
911
|
+
program: ast.program.body[0].expression.children[0].expression
|
|
912
|
+
};
|
|
913
|
+
}
|
|
914
|
+
}
|
|
915
|
+
};
|
|
916
|
+
const printers = { astro: {
|
|
917
|
+
print,
|
|
918
|
+
embed,
|
|
919
|
+
getVisitorKeys
|
|
920
|
+
} };
|
|
921
|
+
const defaultOptions = { tabWidth: 2 };
|
|
922
|
+
//#endregion
|
|
923
|
+
export { defaultOptions, languages, options, parsers, printers };
|
|
924
|
+
|
|
925
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","names":["stripTrailingHardline","hardline","line","join","group","indent","softline"],"sources":["../src/options.ts","../src/printer/elements.ts","../src/printer/utils.ts","../src/printer/index.ts","../src/printer/embed.ts","../src/get-visitor-keys.ts","../src/index.ts"],"sourcesContent":["import type { SupportOption } from 'prettier';\n\ninterface PluginOptions {\n\tastroAllowShorthand: boolean;\n\tastroSkipFrontmatter: boolean;\n}\n\ndeclare module 'prettier' {\n\t \n\tinterface RequiredOptions extends PluginOptions {}\n}\n\n// https://prettier.io/docs/en/plugins.html#options\nexport const options: Record<keyof PluginOptions, SupportOption> = {\n\tastroAllowShorthand: {\n\t\tcategory: 'Astro',\n\t\ttype: 'boolean',\n\t\tdefault: false,\n\t\tdescription: 'Enable/disable attribute shorthand if attribute name and expression are the same',\n\t},\n\tastroSkipFrontmatter: {\n\t\tcategory: 'Astro',\n\t\ttype: 'boolean',\n\t\tdefault: false,\n\t\tdescription: 'Skips the formatting of the frontmatter.',\n\t},\n};\n","export type TagName = keyof HTMLElementTagNameMap | 'svg';\n\n// https://github.com/prettier/prettier/blob/b77d912c0c1a5df85e3e9b5b192fc92523e411ee/vendors/html-void-elements.json\nexport const selfClosingTags = [\n\t'area',\n\t'base',\n\t'basefont',\n\t'bgsound',\n\t'br',\n\t'col',\n\t'command',\n\t'embed',\n\t'frame',\n\t'hr',\n\t'image',\n\t'img',\n\t'input',\n\t'isindex',\n\t'keygen',\n\t'link',\n\t'menuitem',\n\t'meta',\n\t'nextid',\n\t'param',\n\t'slot',\n\t'source',\n\t'track',\n\t'wbr',\n];\n\n// https://web.archive.org/web/20230108213516/https://developer.mozilla.org/en-US/docs/Web/HTML/Block-level_elements#elements\nexport const blockElements: TagName[] = [\n\t'address',\n\t'article',\n\t'aside',\n\t'blockquote',\n\t'details',\n\t'dialog',\n\t'dd',\n\t'div',\n\t'dl',\n\t'dt',\n\t'fieldset',\n\t'figcaption',\n\t'figure',\n\t'footer',\n\t'form',\n\t'h1',\n\t'h2',\n\t'h3',\n\t'h4',\n\t'h5',\n\t'h6',\n\t'header',\n\t'hgroup',\n\t'hr',\n\t'li',\n\t'main',\n\t'nav',\n\t'ol',\n\t'p',\n\t'pre',\n\t'section',\n\t'table',\n\t'ul',\n\t// TODO: WIP\n\t'title',\n\t'html',\n];\n\n/**\n * HTML attributes that we may safely reformat (trim whitespace, add or remove newlines)\n */\nexport const formattableAttributes: string[] = [\n\t// None at the moment\n\t// Prettier HTML does not format attributes at all\n\t// and to be consistent we leave this array empty for now\n];\n","import { serialize } from '@astrojs/compiler/utils';\nimport {\n\ttype AstPath as AstP,\n\ttype BuiltInParserName,\n\ttype Doc,\n\ttype ParserOptions as ParserOpts,\n} from 'prettier';\nimport { blockElements, formattableAttributes, type TagName } from './elements';\nimport type {\n\tCommentNode,\n\tExpressionNode,\n\tNode,\n\tParentLikeNode,\n\tTagLikeNode,\n\tTextNode,\n\tanyNode,\n} from './nodes';\n\nexport type printFn = (path: AstPath) => Doc;\nexport type ParserOptions = ParserOpts<anyNode>;\nexport type AstPath = AstP<anyNode>;\n\nexport const openingBracketReplace = '_Pé';\nexport const closingBracketReplace = 'èP_';\nexport const atSignReplace = 'ΩP_';\nexport const dotReplace = 'ωP_';\nexport const interrogationReplace = 'ΔP_';\n\nexport function isInlineElement(path: AstPath, opts: ParserOptions, node: anyNode): boolean {\n\treturn node && isTagLikeNode(node) && !isBlockElement(node, opts) && !isPreTagContent(path);\n}\n\nexport function isBlockElement(node: anyNode, opts: ParserOptions): boolean {\n\tif (!node) {\n\t\treturn false;\n\t}\n\n\t// All tags (element, custom-element, component, fragment) are considered\n\t// block elements when htmlWhitespaceSensitivity is set to \"ignore\".\n\tif (opts.htmlWhitespaceSensitivity === 'ignore') {\n\t\treturn true;\n\t}\n\n\treturn (\n\t\tnode.type === 'element' &&\n\t\topts.htmlWhitespaceSensitivity !== 'strict' &&\n\t\tblockElements.includes(node.name as TagName)\n\t);\n}\n\nexport function isIgnoreDirective(node: Node): boolean {\n\treturn node.type === 'comment' && node.value.trim() === 'prettier-ignore';\n}\n\n/**\n * Returns the content of the node\n */\nexport function printRaw(node: anyNode, stripLeadingAndTrailingNewline = false): string {\n\tif (!isNodeWithChildren(node)) {\n\t\treturn '';\n\t}\n\n\tif (node.children.length === 0) {\n\t\treturn '';\n\t}\n\n\tlet raw = node.children.reduce((prev: string, curr: Node) => prev + serialize(curr), '');\n\n\tif (!stripLeadingAndTrailingNewline) {\n\t\treturn raw;\n\t}\n\n\tif (startsWithLinebreak(raw)) {\n\t\traw = raw.substring(raw.indexOf('\\n') + 1);\n\t}\n\tif (endsWithLinebreak(raw)) {\n\t\traw = raw.substring(0, raw.lastIndexOf('\\n'));\n\t\tif (raw.charAt(raw.length - 1) === '\\r') {\n\t\t\traw = raw.substring(0, raw.length - 1);\n\t\t}\n\t}\n\n\treturn raw;\n}\n\nexport function isNodeWithChildren(node: anyNode): node is anyNode & ParentLikeNode {\n\treturn node && 'children' in node && Array.isArray(node.children);\n}\n\nexport const isEmptyTextNode = (node: anyNode): boolean => {\n\treturn !!node && node.type === 'text' && getUnencodedText(node).trim() === '';\n};\n\nexport function getUnencodedText(node: TextNode | CommentNode): string {\n\treturn node.value;\n}\n\nexport function isTextNodeStartingWithLinebreak(node: TextNode, nrLines = 1): node is TextNode {\n\treturn startsWithLinebreak(getUnencodedText(node), nrLines);\n}\n\nexport function startsWithLinebreak(text: string, nrLines = 1): boolean {\n\treturn new RegExp(`^([\\\\t\\\\f\\\\r ]*\\\\n){${nrLines}}`).test(text);\n}\n\nexport function endsWithLinebreak(text: string, nrLines = 1): boolean {\n\treturn new RegExp(`(\\\\n[\\\\t\\\\f\\\\r ]*){${nrLines}}$`).test(text);\n}\n\nexport function isTextNodeStartingWithWhitespace(node: Node): node is TextNode {\n\treturn isTextNode(node) && /^\\s/.test(getUnencodedText(node));\n}\n\nfunction endsWithWhitespace(text: string) {\n\treturn /\\s$/.test(text);\n}\n\nexport function isTextNodeEndingWithWhitespace(node: Node): node is TextNode {\n\treturn isTextNode(node) && endsWithWhitespace(getUnencodedText(node));\n}\n\nexport function hasSetDirectives(node: TagLikeNode) {\n\tconst attributes = Array.from(node.attributes, (attr) => attr.name);\n\treturn attributes.some((attr) => ['set:html', 'set:text'].includes(attr));\n}\n\n/**\n * Check if given node's start tag should hug its first child. This is the case for inline elements when there's\n * no whitespace between the `>` and the first child.\n */\nexport function shouldHugStart(node: anyNode, opts: ParserOptions): boolean {\n\tif (isBlockElement(node, opts)) {\n\t\treturn false;\n\t}\n\n\tif (!isNodeWithChildren(node)) {\n\t\treturn false;\n\t}\n\n\tconst children = node.children;\n\tif (children.length === 0) {\n\t\treturn true;\n\t}\n\n\tconst firstChild = children[0];\n\treturn !isTextNodeStartingWithWhitespace(firstChild);\n}\n\n/**\n * Check if given node's end tag should hug its last child. This is the case for inline elements when there's\n * no whitespace between the last child and the `</`.\n */\nexport function shouldHugEnd(node: anyNode, opts: ParserOptions): boolean {\n\tif (isBlockElement(node, opts)) {\n\t\treturn false;\n\t}\n\n\tif (!isNodeWithChildren(node)) {\n\t\treturn false;\n\t}\n\n\tconst children = node.children;\n\tif (children.length === 0) {\n\t\treturn true;\n\t}\n\n\tconst lastChild = children[children.length - 1];\n\tif (isExpressionNode(lastChild)) return true;\n\tif (isTagLikeNode(lastChild)) return true;\n\treturn !isTextNodeEndingWithWhitespace(lastChild);\n}\n\n/**\n * Returns true if the softline between `</tagName` and `>` can be omitted.\n */\nexport function canOmitSoftlineBeforeClosingTag(path: AstPath, opts: ParserOptions): boolean {\n\treturn isLastChildWithinParentBlockElement(path, opts);\n}\n\nfunction getChildren(node: anyNode): Node[] {\n\treturn isNodeWithChildren(node) ? node.children : [];\n}\n\nfunction isLastChildWithinParentBlockElement(path: AstPath, opts: ParserOptions): boolean {\n\tconst parent = path.getParentNode();\n\tif (!parent || !isBlockElement(parent, opts)) {\n\t\treturn false;\n\t}\n\n\tconst children = getChildren(parent);\n\tconst lastChild = children[children.length - 1];\n\treturn lastChild === path.getNode();\n}\n\nexport function trimTextNodeLeft(node: TextNode): void {\n\tnode.value = node.value && node.value.trimStart();\n}\n\nexport function trimTextNodeRight(node: TextNode): void {\n\tnode.value = node.value && node.value.trimEnd();\n}\n\nexport function printClassNames(value: string) {\n\tconst lines = value.trim().split(/[\\r\\n]+/);\n\tconst formattedLines = lines.map((line) => {\n\t\tconst spaces = /^\\s+/.exec(line);\n\t\treturn (spaces ? spaces[0] : '') + line.trim().split(/\\s+/).join(' ');\n\t});\n\treturn formattedLines.join('\\n');\n}\n\n/** dedent string & return tabSize (the last part is what we need) */\nexport function manualDedent(input: string): {\n\ttabSize: number;\n\tchar: string;\n\tresult: string;\n} {\n\tlet minTabSize = Infinity;\n\tlet result = input;\n\t// 1. normalize\n\tresult = result.replace(/\\r\\n/g, '\\n');\n\n\t// 2. count tabSize\n\tlet char = '';\n\tfor (const line of result.split('\\n')) {\n\t\tif (!line) continue;\n\t\t// if any line begins with a non-whitespace char, minTabSize is 0\n\t\tif (line[0] && /^\\S/.test(line[0])) {\n\t\t\tminTabSize = 0;\n\t\t\tbreak;\n\t\t}\n\t\tconst match = /^(\\s+)\\S+/.exec(line); // \\S ensures we don’t count lines of pure whitespace\n\t\tif (match) {\n\t\t\tif (match[1] && !char) char = match[1][0];\n\t\t\tif (match[1].length < minTabSize) minTabSize = match[1].length;\n\t\t}\n\t}\n\n\t// 3. reformat string\n\tif (minTabSize > 0 && Number.isFinite(minTabSize)) {\n\t\tresult = result.replace(new RegExp(`^${new Array(minTabSize + 1).join(char)}`, 'gm'), '');\n\t}\n\n\treturn {\n\t\ttabSize: minTabSize === Infinity ? 0 : minTabSize,\n\t\tchar,\n\t\tresult,\n\t};\n}\n\n/** True if the node is of type text */\nexport function isTextNode(node: anyNode): node is TextNode {\n\treturn node.type === 'text';\n}\n\nexport function isExpressionNode(node: anyNode): node is ExpressionNode {\n\treturn node.type === 'expression';\n}\n\n/** True if the node is TagLikeNode:\n *\n * ElementNode | ComponentNode | CustomElementNode | FragmentNode */\nexport function isTagLikeNode(node: anyNode): node is TagLikeNode {\n\treturn (\n\t\tnode.type === 'element' ||\n\t\tnode.type === 'component' ||\n\t\tnode.type === 'custom-element' ||\n\t\tnode.type === 'fragment'\n\t);\n}\n\n/**\n * Returns siblings, that is, the children of the parent.\n */\nexport function getSiblings(path: AstPath): anyNode[] {\n\tconst parent = path.getParentNode();\n\tif (!parent) return [];\n\n\treturn getChildren(parent);\n}\n\nexport function getNextNode(path: AstPath): anyNode | null {\n\tconst node = path.getNode();\n\tif (node) {\n\t\tconst siblings = getSiblings(path);\n\t\tif (node.position?.start === siblings[siblings.length - 1].position?.start) return null;\n\t\tfor (let i = 0; i < siblings.length; i++) {\n\t\t\tconst sibling = siblings[i];\n\t\t\tif (sibling.position?.start === node.position?.start && i !== siblings.length - 1) {\n\t\t\t\treturn siblings[i + 1];\n\t\t\t}\n\t\t}\n\t}\n\treturn null;\n}\n\nexport const isPreTagContent = (path: AstPath): boolean => {\n\tif (!path || !path.stack || !Array.isArray(path.stack)) return false;\n\treturn path.stack.some(\n\t\t(node: anyNode) =>\n\t\t\t(node.type === 'element' && node.name.toLowerCase() === 'pre') ||\n\t\t\t(node.type === 'attribute' && !formattableAttributes.includes(node.name)),\n\t);\n};\n\ninterface QuoteResult {\n\tquote: '\"' | \"'\";\n\tregex: RegExp;\n\tescaped: string;\n}\n\n// Adapted from Prettier's source code as it's unfortunately not exported\n// https://github.com/prettier/prettier/blob/237e681936fc533c27d7ce8577d3fc98838a3314/src/common/util.js#L238\nexport function getPreferredQuote(rawContent: string, preferredQuote: string): QuoteResult {\n\tconst double: QuoteResult = { quote: '\"', regex: /\"/g, escaped: '"' };\n\tconst single: QuoteResult = { quote: \"'\", regex: /'/g, escaped: ''' };\n\n\tconst preferred = preferredQuote === \"'\" ? single : double;\n\tconst alternate = preferred === single ? double : single;\n\n\tlet result = preferred;\n\n\t// If `rawContent` contains at least one of the quote preferred for enclosing\n\t// the string, we might want to enclose with the alternate quote instead, to\n\t// minimize the number of escaped quotes.\n\tif (rawContent.includes(preferred.quote) || rawContent.includes(alternate.quote)) {\n\t\tconst numPreferredQuotes = (preferred.regex.exec(rawContent) || []).length;\n\t\tconst numAlternateQuotes = (alternate.regex.exec(rawContent) || []).length;\n\n\t\tresult = numPreferredQuotes > numAlternateQuotes ? alternate : preferred;\n\t}\n\n\treturn result;\n}\n\n// Adapted from: https://github.com/prettier/prettier/blob/20ab6d6f1c5bd774621230b493a3b71d39383a2c/src/language-html/utils/index.js#LL336C1-L369C2\nexport function inferParserByTypeAttribute(type: string): BuiltInParserName {\n\tif (!type) {\n\t\treturn 'babel-ts';\n\t}\n\n\tswitch (type) {\n\t\tcase 'module':\n\t\tcase 'text/javascript':\n\t\tcase 'text/babel':\n\t\tcase 'application/javascript':\n\t\t\treturn 'babel';\n\n\t\tcase 'application/x-typescript':\n\t\t\treturn 'babel-ts';\n\n\t\tcase 'text/markdown':\n\t\t\treturn 'markdown';\n\n\t\tcase 'text/html':\n\t\t\treturn 'html';\n\n\t\tcase 'text/x-handlebars-template':\n\t\t\treturn 'glimmer';\n\n\t\tdefault:\n\t\t\tif (type.endsWith('json') || type.endsWith('importmap') || type === 'speculationrules') {\n\t\t\t\treturn 'json';\n\t\t\t}\n\t\t\treturn 'babel-ts';\n\t}\n}\n","import { type Doc } from 'prettier';\nimport { selfClosingTags } from './elements';\nimport { type TextNode } from './nodes';\nimport {\n\tcanOmitSoftlineBeforeClosingTag,\n\tendsWithLinebreak,\n\tgetNextNode,\n\tgetPreferredQuote,\n\tgetUnencodedText,\n\thasSetDirectives,\n\tisEmptyTextNode,\n\tisIgnoreDirective,\n\tisInlineElement,\n\tisPreTagContent,\n\tisTagLikeNode,\n\tisTextNode,\n\tisTextNodeEndingWithWhitespace,\n\tisTextNodeStartingWithLinebreak,\n\tisTextNodeStartingWithWhitespace,\n\tprintClassNames,\n\tprintRaw,\n\tshouldHugEnd,\n\tshouldHugStart,\n\tstartsWithLinebreak,\n\ttrimTextNodeLeft,\n\ttrimTextNodeRight,\n\ttype AstPath,\n\ttype ParserOptions,\n\ttype printFn,\n} from './utils';\n\nimport _doc from 'prettier/doc';\nconst {\n\tbuilders: {\n\t\tbreakParent,\n\t\tdedent,\n\t\tfill,\n\t\tgroup,\n\t\tindent,\n\t\tjoin,\n\t\tline,\n\t\tsoftline,\n\t\thardline,\n\t\tliteralline,\n\t},\n\tutils: { stripTrailingHardline },\n} = _doc;\n\nlet ignoreNext = false;\n\n// https://prettier.io/docs/en/plugins.html#print \nexport function print(path: AstPath, opts: ParserOptions, print: printFn): Doc {\n\tconst node = path.node;\n\n\t// 1. handle special node types\n\tif (!node) {\n\t\treturn '';\n\t}\n\n\tif (ignoreNext && !isEmptyTextNode(node)) {\n\t\tignoreNext = false;\n\t\treturn [\n\t\t\topts.originalText\n\t\t\t\t.slice(opts.locStart(node), opts.locEnd(node))\n\t\t\t\t.split('\\n')\n\t\t\t\t.map((lineContent, i) => (i == 0 ? [lineContent] : [literalline, lineContent]))\n\t\t\t\t.flat(),\n\t\t];\n\t}\n\n\tif (typeof node === 'string') {\n\t\treturn node;\n\t}\n\n\t// 2. handle printing\n\tswitch (node.type) {\n\t\tcase 'root': {\n\t\t\treturn [stripTrailingHardline(path.map(print, 'children')), hardline];\n\t\t}\n\n\t\tcase 'text': {\n\t\t\tconst rawText = getUnencodedText(node);\n\n\t\t\t// TODO: TEST PRE TAGS\n\t\t\t// if (isPreTagContent(path)) {\n\t\t\t// if (path.getParentNode()?.type === 'Attribute') {\n\t\t\t// // Direct child of attribute value -> add literallines at end of lines\n\t\t\t// // so that other things don't break in unexpected places\n\t\t\t// return replaceEndOfLineWith(rawText, literalline);\n\t\t\t// }\n\t\t\t// return rawText;\n\t\t\t// }\n\n\t\t\tif (isEmptyTextNode(node)) {\n\t\t\t\tconst hasWhiteSpace = rawText.trim().length < getUnencodedText(node).length;\n\t\t\t\tconst hasOneOrMoreNewlines = getUnencodedText(node).includes('\\n');\n\t\t\t\tconst hasTwoOrMoreNewlines = /\\n\\s*\\n\\r?/.test(getUnencodedText(node));\n\t\t\t\tif (hasTwoOrMoreNewlines) {\n\t\t\t\t\treturn [hardline, hardline];\n\t\t\t\t}\n\t\t\t\tif (hasOneOrMoreNewlines) {\n\t\t\t\t\treturn hardline;\n\t\t\t\t}\n\t\t\t\tif (hasWhiteSpace) {\n\t\t\t\t\treturn line;\n\t\t\t\t}\n\t\t\t\treturn '';\n\t\t\t}\n\n\t\t\t/**\n\t\t\t * For non-empty text nodes each sequence of non-whitespace characters (effectively,\n\t\t\t * each \"word\") is joined by a single `line`, which will be rendered as a single space\n\t\t\t * until this node's current line is out of room, at which `fill` will break at the\n\t\t\t * most convenient instance of `line`.\n\t\t\t */\n\t\t\treturn fill(splitTextToDocs(node));\n\t\t}\n\n\t\tcase 'component':\n\t\tcase 'fragment':\n\t\tcase 'custom-element':\n\t\tcase 'element': {\n\t\t\tlet isEmpty: boolean;\n\t\t\tif (!node.children) {\n\t\t\t\tisEmpty = true;\n\t\t\t} else {\n\t\t\t\tisEmpty = node.children.every((child) => isEmptyTextNode(child));\n\t\t\t}\n\n\t\t\t/**\n\t\t\t * An element is allowed to self close only if:\n\t\t\t * It is empty AND\n\t\t\t * It's a component OR\n\t\t\t * It's in the HTML spec as a void element OR\n\t\t\t * It has a `set:*` directive\n\t\t\t */\n\t\t\tconst isSelfClosingTag =\n\t\t\t\tisEmpty &&\n\t\t\t\t(node.type === 'component' ||\n\t\t\t\t\tselfClosingTags.includes(node.name) ||\n\t\t\t\t\thasSetDirectives(node));\n\n\t\t\tconst isSingleLinePerAttribute = opts.singleAttributePerLine && node.attributes.length > 1;\n\t\t\tconst attributeLine = isSingleLinePerAttribute ? breakParent : '';\n\t\t\tconst attributes = join(attributeLine, path.map(print, 'attributes'));\n\n\t\t\tif (isSelfClosingTag) {\n\t\t\t\treturn group(['<', node.name, indent(attributes), line, `/>`]);\n\t\t\t}\n\n\t\t\tif (node.children) {\n\t\t\t\tconst children = node.children;\n\t\t\t\tconst firstChild = children[0];\n\t\t\t\tconst lastChild = children[children.length - 1];\n\n\t\t\t\t// No hugging of content means it's either a block element and/or there's whitespace at the start/end\n\t\t\t\tlet noHugSeparatorStart:\n\t\t\t\t\t| _doc.builders.Line\n\t\t\t\t\t| _doc.builders.Softline\n\t\t\t\t\t| _doc.builders.Hardline\n\t\t\t\t\t| string = softline;\n\t\t\t\tlet noHugSeparatorEnd:\n\t\t\t\t\t| _doc.builders.Line\n\t\t\t\t\t| _doc.builders.Softline\n\t\t\t\t\t| _doc.builders.Hardline\n\t\t\t\t\t| string = softline;\n\t\t\t\tconst hugStart = shouldHugStart(node, opts);\n\t\t\t\tconst hugEnd = shouldHugEnd(node, opts);\n\n\t\t\t\tlet body;\n\n\t\t\t\tif (isEmpty) {\n\t\t\t\t\tbody =\n\t\t\t\t\t\tisInlineElement(path, opts, node) &&\n\t\t\t\t\t\tnode.children.length &&\n\t\t\t\t\t\tisTextNodeStartingWithWhitespace(node.children[0]) &&\n\t\t\t\t\t\t!isPreTagContent(path)\n\t\t\t\t\t\t\t? () => line\n\t\t\t\t\t\t\t: () => (node.children.length > 0 ? softline : '');\n\t\t\t\t} else if (isPreTagContent(path)) {\n\t\t\t\t\tbody = () => printRaw(node);\n\t\t\t\t} else if (isInlineElement(path, opts, node) && !isPreTagContent(path)) {\n\t\t\t\t\tbody = () => path.map(print, 'children');\n\t\t\t\t} else {\n\t\t\t\t\tbody = () => path.map(print, 'children');\n\t\t\t\t}\n\n\t\t\t\tconst openingTag = [\n\t\t\t\t\t'<',\n\t\t\t\t\tnode.name,\n\t\t\t\t\tindent(\n\t\t\t\t\t\tgroup([\n\t\t\t\t\t\t\tattributes,\n\t\t\t\t\t\t\thugStart\n\t\t\t\t\t\t\t\t? ''\n\t\t\t\t\t\t\t\t: !isPreTagContent(path) && !opts.bracketSameLine\n\t\t\t\t\t\t\t\t\t? dedent(softline)\n\t\t\t\t\t\t\t\t\t: '',\n\t\t\t\t\t\t]),\n\t\t\t\t\t),\n\t\t\t\t];\n\n\t\t\t\tif (hugStart && hugEnd) {\n\t\t\t\t\tconst huggedContent = [\n\t\t\t\t\t\tisSingleLinePerAttribute ? hardline : softline,\n\t\t\t\t\t\tgroup(['>', body(), `</${node.name}`]),\n\t\t\t\t\t];\n\n\t\t\t\t\tconst omitSoftlineBeforeClosingTag =\n\t\t\t\t\t\tisEmpty || canOmitSoftlineBeforeClosingTag(path, opts);\n\t\t\t\t\treturn group([\n\t\t\t\t\t\t...openingTag,\n\t\t\t\t\t\tisEmpty ? group(huggedContent) : group(indent(huggedContent)),\n\t\t\t\t\t\tomitSoftlineBeforeClosingTag ? '' : softline,\n\t\t\t\t\t\t'>',\n\t\t\t\t\t]);\n\t\t\t\t}\n\n\t\t\t\tif (isPreTagContent(path)) {\n\t\t\t\t\tnoHugSeparatorStart = '';\n\t\t\t\t\tnoHugSeparatorEnd = '';\n\t\t\t\t} else {\n\t\t\t\t\tlet didSetEndSeparator = false;\n\n\t\t\t\t\tif (!hugStart && firstChild && isTextNode(firstChild)) {\n\t\t\t\t\t\tif (\n\t\t\t\t\t\t\tisTextNodeStartingWithLinebreak(firstChild) &&\n\t\t\t\t\t\t\tfirstChild !== lastChild &&\n\t\t\t\t\t\t\t(!isInlineElement(path, opts, node) || isTextNodeEndingWithWhitespace(lastChild))\n\t\t\t\t\t\t) {\n\t\t\t\t\t\t\tnoHugSeparatorStart = hardline;\n\t\t\t\t\t\t\tnoHugSeparatorEnd = hardline;\n\t\t\t\t\t\t\tdidSetEndSeparator = true;\n\t\t\t\t\t\t} else if (isInlineElement(path, opts, node)) {\n\t\t\t\t\t\t\tnoHugSeparatorStart = line;\n\t\t\t\t\t\t}\n\t\t\t\t\t\ttrimTextNodeLeft(firstChild);\n\t\t\t\t\t}\n\t\t\t\t\tif (!hugEnd && lastChild && isTextNode(lastChild)) {\n\t\t\t\t\t\tif (isInlineElement(path, opts, node) && !didSetEndSeparator) {\n\t\t\t\t\t\t\tnoHugSeparatorEnd = line;\n\t\t\t\t\t\t}\n\t\t\t\t\t\ttrimTextNodeRight(lastChild);\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tif (hugStart) {\n\t\t\t\t\treturn group([\n\t\t\t\t\t\t...openingTag,\n\t\t\t\t\t\tindent([softline, group(['>', body()])]),\n\t\t\t\t\t\tnoHugSeparatorEnd,\n\t\t\t\t\t\t`</${node.name}>`,\n\t\t\t\t\t]);\n\t\t\t\t}\n\n\t\t\t\tif (hugEnd) {\n\t\t\t\t\treturn group([\n\t\t\t\t\t\t...openingTag,\n\t\t\t\t\t\t'>',\n\t\t\t\t\t\tindent([noHugSeparatorStart, group([body(), `</${node.name}`])]),\n\t\t\t\t\t\tcanOmitSoftlineBeforeClosingTag(path, opts) ? '' : softline,\n\t\t\t\t\t\t'>',\n\t\t\t\t\t]);\n\t\t\t\t}\n\n\t\t\t\tif (isEmpty) {\n\t\t\t\t\treturn group([...openingTag, '>', body(), `</${node.name}>`]);\n\t\t\t\t}\n\n\t\t\t\treturn group([\n\t\t\t\t\t...openingTag,\n\t\t\t\t\t'>',\n\t\t\t\t\tindent([noHugSeparatorStart, body()]),\n\t\t\t\t\tnoHugSeparatorEnd,\n\t\t\t\t\t`</${node.name}>`,\n\t\t\t\t]);\n\t\t\t}\n\n\t\t\t// TODO: WIP\n\t\t\treturn '';\n\t\t}\n\n\t\tcase 'attribute': {\n\t\t\tconst name = node.name.trim();\n\t\t\tswitch (node.kind) {\n\t\t\t\tcase 'empty':\n\t\t\t\t\treturn [line, name];\n\t\t\t\tcase 'expression':\n\t\t\t\t\t// Handled in the `embed` function\n\t\t\t\t\t// See embed.ts\n\t\t\t\t\treturn '';\n\t\t\t\tcase 'quoted':\n\t\t\t\t\tlet value = node.value;\n\n\t\t\t\t\tif (node.name === 'class') {\n\t\t\t\t\t\tvalue = printClassNames(value);\n\t\t\t\t\t}\n\n\t\t\t\t\tconst unescapedValue = value.replace(/'/g, \"'\").replace(/"/g, '\"');\n\t\t\t\t\tconst { escaped, quote, regex } = getPreferredQuote(\n\t\t\t\t\t\tunescapedValue,\n\t\t\t\t\t\topts.jsxSingleQuote ? \"'\" : '\"',\n\t\t\t\t\t);\n\n\t\t\t\t\tconst result = unescapedValue.replace(regex, escaped);\n\t\t\t\t\treturn [line, name, '=', quote, result, quote];\n\t\t\t\tcase 'shorthand':\n\t\t\t\t\treturn [line, '{', name, '}'];\n\t\t\t\tcase 'spread':\n\t\t\t\t\treturn [line, '{...', name, '}'];\n\t\t\t\tcase 'template-literal':\n\t\t\t\t\treturn [line, name, '=', '`', node.value, '`'];\n\t\t\t\tdefault:\n\t\t\t\t\tbreak;\n\t\t\t}\n\t\t\treturn '';\n\t\t}\n\n\t\tcase 'doctype': {\n\t\t\t// https://www.w3.org/wiki/Doctypes_and_markup_styles\n\t\t\treturn ['<!doctype html>', hardline];\n\t\t}\n\n\t\tcase 'comment':\n\t\t\tif (isIgnoreDirective(node)) {\n\t\t\t\tignoreNext = true;\n\t\t\t}\n\n\t\t\tconst nextNode = getNextNode(path);\n\t\t\tlet trailingLine: string | _doc.builders.Hardline = '';\n\t\t\tif (nextNode && isTagLikeNode(nextNode)) {\n\t\t\t\ttrailingLine = hardline;\n\t\t\t}\n\t\t\treturn ['<!--', getUnencodedText(node), '-->', trailingLine];\n\n\t\tdefault: {\n\t\t\tthrow new Error(`Unhandled node type \"${node.type}\"!`);\n\t\t}\n\t}\n}\n\n/**\n * Split the text into words separated by whitespace. Replace the whitespaces by lines,\n * collapsing multiple whitespaces into a single line.\n *\n * If the text starts or ends with multiple newlines, two of those should be kept.\n */\nfunction splitTextToDocs(node: TextNode): Doc[] {\n\tconst text = getUnencodedText(node);\n\n\tconst textLines = text.split(/[\\t\\n\\f\\r ]+/);\n\n\tlet docs = join(line, textLines).filter((doc) => doc !== '');\n\n\tif (startsWithLinebreak(text)) {\n\t\tdocs[0] = hardline;\n\t}\n\tif (startsWithLinebreak(text, 2)) {\n\t\tdocs = [hardline, ...docs];\n\t}\n\n\tif (endsWithLinebreak(text)) {\n\t\tdocs[docs.length - 1] = hardline;\n\t}\n\tif (endsWithLinebreak(text, 2)) {\n\t\tdocs = [...docs, hardline];\n\t}\n\n\treturn docs;\n}\n","import { Buffer } from 'node:buffer';\nimport type { BuiltInParserName, Doc, Options } from 'prettier';\nimport _doc from 'prettier/doc';\nimport { SassFormatter, type SassFormatterConfig } from 'sass-formatter';\nimport type { AttributeNode, ExpressionNode, FragmentNode, Node } from './nodes';\nimport {\n\tatSignReplace,\n\tclosingBracketReplace,\n\tdotReplace,\n\tinferParserByTypeAttribute,\n\tinterrogationReplace,\n\tisNodeWithChildren,\n\tisTagLikeNode,\n\tisTextNode,\n\tmanualDedent,\n\topeningBracketReplace,\n\tprintRaw,\n\ttype AstPath,\n\ttype ParserOptions,\n\ttype printFn,\n} from './utils';\n\nconst {\n\tbuilders: { group, indent, join, line, softline, hardline, lineSuffixBoundary },\n\tutils: { stripTrailingHardline, mapDoc },\n} = _doc;\n\nconst supportedStyleLangValues = ['css', 'scss', 'sass', 'less'] as const;\ntype supportedStyleLang = (typeof supportedStyleLangValues)[number];\n\n// https://prettier.io/docs/en/plugins.html#optional-embed\ntype TextToDoc = (text: string, options: Options) => Promise<Doc>;\n\ntype Embed =\n\t| ((\n\t\t\tpath: AstPath,\n\t\t\toptions: Options,\n\t ) =>\n\t\t\t| ((\n\t\t\t\t\ttextToDoc: TextToDoc,\n\t\t\t\t\tprint: (selector?: string | number | Array<string | number> | AstPath) => Doc,\n\t\t\t\t\tpath: AstPath,\n\t\t\t\t\toptions: Options,\n\t\t\t ) => Promise<Doc | undefined> | Doc | undefined)\n\t\t\t| Doc\n\t\t\t| null)\n\t| undefined;\n\nexport const embed = ((path: AstPath, options: Options) => {\n\tconst parserOption = options as ParserOptions;\n\treturn async (textToDoc, print) => {\n\t\tconst node = path.node;\n\n\t\tif (!node) return undefined;\n\n\t\tif (node.type === 'expression') {\n\t\t\t// Extract script and style elements and replace with self-closing\n\t\t\t// placeholder components so Babel's JSX parser doesn't try to parse\n\t\t\t// their content as JSX.\n\t\t\t// See: https://github.com/withastro/prettier-plugin-astro/issues/452\n\t\t\t// See: https://github.com/withastro/prettier-plugin-astro/issues/454\n\t\t\tconst rawTagPlaceholders: RawTagPlaceholder[] = [];\n\t\t\tconst nodeWithPlaceholders = replaceRawTagChildren(node, rawTagPlaceholders);\n\n\t\t\tconst jsxNode = makeNodeJSXCompatible<ExpressionNode>(nodeWithPlaceholders);\n\t\t\tconst textContent = printRaw(jsxNode);\n\n\t\t\tlet content: Doc;\n\n\t\t\tcontent = await wrapParserTryCatch(textToDoc, textContent, {\n\t\t\t\t...options,\n\t\t\t\tparser: 'astroExpressionParser',\n\t\t\t});\n\n\t\t\tcontent = stripTrailingHardline(content);\n\n\t\t\t// Replace self-closing placeholder components with fully-rendered\n\t\t\t// script/style tags, matching the format of the top-level handlers.\n\t\t\tfor (const entry of rawTagPlaceholders) {\n\t\t\t\tlet formattedContent: Doc;\n\n\t\t\t\tif (entry.tagName === 'script') {\n\t\t\t\t\tconst parser = inferParserByTypeAttribute(entry.typeAttr || '');\n\t\t\t\t\tformattedContent = await wrapParserTryCatch(textToDoc, entry.content, {\n\t\t\t\t\t\t...options,\n\t\t\t\t\t\tparser,\n\t\t\t\t\t});\n\t\t\t\t} else {\n\t\t\t\t\t// style tag\n\t\t\t\t\tconst langValue = entry.langAttr?.toLowerCase();\n\t\t\t\t\tif (langValue === 'sass') {\n\t\t\t\t\t\tconst lineEnding = parserOption?.endOfLine?.toUpperCase() === 'CRLF' ? 'CRLF' : 'LF';\n\t\t\t\t\t\tconst sassOptions: Partial<SassFormatterConfig> = {\n\t\t\t\t\t\t\ttabSize: parserOption.tabWidth,\n\t\t\t\t\t\t\tinsertSpaces: !parserOption.useTabs,\n\t\t\t\t\t\t\tlineEnding,\n\t\t\t\t\t\t};\n\t\t\t\t\t\tconst { result: raw } = manualDedent(entry.content);\n\t\t\t\t\t\tconst formatted = SassFormatter.Format(raw, sassOptions).trim();\n\t\t\t\t\t\tformattedContent = join(hardline, formatted.split('\\n'));\n\t\t\t\t\t} else {\n\t\t\t\t\t\t// css, scss, less, or default to css\n\t\t\t\t\t\tconst styleParser: BuiltInParserName =\n\t\t\t\t\t\t\tlangValue === 'scss' || langValue === 'less' ? langValue : 'css';\n\t\t\t\t\t\tformattedContent = await wrapParserTryCatch(textToDoc, entry.content, {\n\t\t\t\t\t\t\t...options,\n\t\t\t\t\t\t\tparser: styleParser,\n\t\t\t\t\t\t});\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tformattedContent = stripTrailingHardline(formattedContent);\n\t\t\t\tconst isEmpty = /^\\s*$/.test(entry.content);\n\n\t\t\t\t// Build the full tag Doc matching top-level script/style formatting:\n\t\t\t\t// <tag attrs>\\n content\\n</tag>\n\t\t\t\tconst fullTagDoc: Doc = [\n\t\t\t\t\tentry.openingTag,\n\t\t\t\t\tindent([isEmpty ? '' : hardline, formattedContent]),\n\t\t\t\t\tisEmpty ? '' : hardline,\n\t\t\t\t\t`</${entry.tagName}>`,\n\t\t\t\t];\n\n\t\t\t\tcontent = mapDoc(content, (doc) => {\n\t\t\t\t\tif (typeof doc === 'string' && doc.includes(entry.placeholder)) {\n\t\t\t\t\t\tconst parts = doc.split(entry.placeholder);\n\t\t\t\t\t\tif (parts.length === 2) {\n\t\t\t\t\t\t\tif (entry.isDirectChild) {\n\t\t\t\t\t\t\t\t// Direct children: placeholder replaced the entire element,\n\t\t\t\t\t\t\t\t// so insert the fully-rendered tag Doc\n\t\t\t\t\t\t\t\treturn [parts[0], fullTagDoc, parts[1]];\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t// Nested children: placeholder is inside the tag, the tag\n\t\t\t\t\t\t\t// structure is preserved in the doc. Replace content only.\n\t\t\t\t\t\t\tif (isEmpty) {\n\t\t\t\t\t\t\t\treturn [parts[0], parts[1]];\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\treturn [parts[0], indent([hardline, formattedContent]), hardline, parts[1]];\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\treturn doc;\n\t\t\t\t});\n\t\t\t}\n\n\t\t\t// HACK: Bit of a weird hack to get if a document is exclusively comments\n\t\t\t// Using `mapDoc` directly to build the array for some reason caused it to always be undefined? Not sure why\n\t\t\tconst strings: string[] = [];\n\t\t\tmapDoc(content, (doc) => {\n\t\t\t\tif (typeof doc === 'string') {\n\t\t\t\t\tstrings.push(doc);\n\t\t\t\t}\n\t\t\t});\n\n\t\t\tif (strings.every((value) => value.startsWith('//'))) {\n\t\t\t\treturn group(['{', content, softline, lineSuffixBoundary, '}']);\n\t\t\t}\n\n\t\t\t// Create a Doc without the things we had to add to make the expression compatible with Babel\n\t\t\tconst astroDoc = mapDoc(content, (doc) => {\n\t\t\t\tif (typeof doc === 'string') {\n\t\t\t\t\tdoc = doc.replaceAll(openingBracketReplace, '{');\n\t\t\t\t\tdoc = doc.replaceAll(closingBracketReplace, '}');\n\t\t\t\t\tdoc = doc.replaceAll(atSignReplace, '@');\n\t\t\t\t\tdoc = doc.replaceAll(dotReplace, '.');\n\t\t\t\t\tdoc = doc.replaceAll(interrogationReplace, '?');\n\t\t\t\t}\n\n\t\t\t\treturn doc;\n\t\t\t});\n\n\t\t\t// Force multi-line format for expressions containing raw content tags\n\t\t\t// (script/style), since their content has hardlines that need proper\n\t\t\t// indentation context. Without this, babel may keep the expression on\n\t\t\t// one line, causing misaligned content after placeholder replacement.\n\t\t\tif (rawTagPlaceholders.length > 0) {\n\t\t\t\treturn ['{', indent([hardline, astroDoc]), hardline, lineSuffixBoundary, '}'];\n\t\t\t}\n\n\t\t\treturn group(['{', indent([softline, astroDoc]), softline, lineSuffixBoundary, '}']);\n\t\t}\n\n\t\t// Attribute using an expression as value\n\t\tif (node.type === 'attribute' && node.kind === 'expression') {\n\t\t\tconst value = node.value.trim();\n\t\t\tconst name = node.name.trim();\n\n\t\t\tconst attrNodeValue = await wrapParserTryCatch(textToDoc, value, {\n\t\t\t\t...options,\n\t\t\t\tparser: 'astroExpressionParser',\n\t\t\t});\n\n\t\t\tif (name === value && options.astroAllowShorthand) {\n\t\t\t\treturn [line, '{', attrNodeValue, '}'];\n\t\t\t}\n\n\t\t\treturn [line, name, '=', '{', attrNodeValue, '}'];\n\t\t}\n\n\t\tif (node.type === 'attribute' && node.kind === 'spread') {\n\t\t\tconst spreadContent = await wrapParserTryCatch(textToDoc, node.name, {\n\t\t\t\t...options,\n\t\t\t\tparser: 'astroExpressionParser',\n\t\t\t});\n\n\t\t\treturn [line, '{...', spreadContent, '}'];\n\t\t}\n\n\t\t// Frontmatter\n\t\tif (node.type === 'frontmatter') {\n\t\t\tif (options.astroSkipFrontmatter) {\n\t\t\t\treturn [group(['---', node.value, '---', hardline]), hardline];\n\t\t\t}\n\n\t\t\tconst frontmatterContent = await wrapParserTryCatch(textToDoc, node.value, {\n\t\t\t\t...options,\n\t\t\t\tparser: 'babel-ts',\n\t\t\t});\n\n\t\t\treturn [group(['---', hardline, frontmatterContent, hardline, '---', hardline]), hardline];\n\t\t}\n\n\t\t// Script tags\n\t\tif (node.type === 'element' && node.name === 'script' && node.children.length) {\n\t\t\tconst typeAttribute = node.attributes.find((attr) => attr.name === 'type')?.value;\n\n\t\t\tlet parser: BuiltInParserName = 'babel-ts';\n\t\t\tif (typeAttribute) {\n\t\t\t\tparser = inferParserByTypeAttribute(typeAttribute);\n\t\t\t}\n\n\t\t\tconst scriptContent = printRaw(node);\n\t\t\tlet formattedScript = await wrapParserTryCatch(textToDoc, scriptContent, {\n\t\t\t\t...options,\n\t\t\t\tparser: parser,\n\t\t\t});\n\n\t\t\tformattedScript = stripTrailingHardline(formattedScript);\n\t\t\tconst isEmpty = /^\\s*$/.test(scriptContent);\n\n\t\t\t// print\n\t\t\tconst attributes = path.map(print, 'attributes');\n\t\t\tconst openingTag = group([\n\t\t\t\t'<script',\n\t\t\t\tindent(group(attributes)),\n\t\t\t\toptions.bracketSameLine ? '' : softline,\n\t\t\t\t'>',\n\t\t\t]);\n\t\t\treturn [\n\t\t\t\topeningTag,\n\t\t\t\tindent([isEmpty ? '' : hardline, formattedScript]),\n\t\t\t\tisEmpty ? '' : hardline,\n\t\t\t\t'</script>',\n\t\t\t];\n\t\t}\n\n\t\t// Style tags\n\t\tif (node.type === 'element' && node.name === 'style') {\n\t\t\tconst content = printRaw(node);\n\t\t\tlet parserLang: supportedStyleLang | undefined = 'css';\n\n\t\t\tif (node.attributes) {\n\t\t\t\tconst langAttribute = node.attributes.filter((x) => x.name === 'lang');\n\t\t\t\tif (langAttribute.length) {\n\t\t\t\t\tconst styleLang = langAttribute[0].value.toLowerCase() as supportedStyleLang;\n\t\t\t\t\tparserLang = supportedStyleLangValues.includes(styleLang) ? styleLang : undefined;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\treturn await embedStyle(parserLang, content, path, print, textToDoc, parserOption);\n\t\t}\n\n\t\treturn undefined;\n\t};\n}) satisfies Embed;\n\nasync function wrapParserTryCatch(cb: TextToDoc, text: string, options: Options) {\n\ttry {\n\t\treturn await cb(text, options);\n\t} catch (e) {\n\t\t// If we couldn't parse the expression (ex: syntax error) and we throw here, Prettier fallback to `print` and we'll\n\t\t// get a totally useless error message (ex: unhandled node type). An undocumented way to work around this is to set\n\t\t// `PRETTIER_DEBUG=1`, but nobody know that exists / want to do that just to get useful error messages. So we force it on\n\t\tprocess.env.PRETTIER_DEBUG = 'true';\n\t\tthrow e;\n\t}\n}\n\n/**\n * Due to the differences between Astro and JSX, Prettier's TypeScript parsers (be it `typescript` or `babel-ts`) are not\n * able to parse all expressions. So we need to first make the expression compatible before passing it to the parser\n *\n * A list of the difference that matters here:\n * - Astro allows a shorthand syntax for props. ex: `<Component {props} />`\n * - Astro allows multiple root elements. ex: `<div></div><div></div>`\n * - Astro allows attributes to include at signs (@) and dots (.)\n */\nfunction makeNodeJSXCompatible<T>(node: any): T {\n\tconst newNode = { ...node };\n\tconst childBundle: Node[][] = [];\n\tlet childBundleIndex = 0;\n\n\tif (isNodeWithChildren(newNode)) {\n\t\tnewNode.children = newNode.children.reduce((result: Node[], child, index) => {\n\t\t\tconst previousChildren = newNode.children[index - 1];\n\t\t\tconst nextChildren = newNode.children[index + 1];\n\t\t\tif (isTagLikeNode(child)) {\n\t\t\t\tchild.attributes = child.attributes.map(makeAttributeJSXCompatible);\n\n\t\t\t\tif (!childBundle[childBundleIndex]) {\n\t\t\t\t\tchildBundle[childBundleIndex] = [];\n\t\t\t\t}\n\n\t\t\t\tif (isNodeWithChildren(child)) {\n\t\t\t\t\tchild = makeNodeJSXCompatible<typeof child>(child);\n\t\t\t\t}\n\n\t\t\t\t// If we don't have a previous children, or it's not an element AND\n\t\t\t\t// we have a next children, and it's an element. Add the current children to the bundle\n\t\t\t\tif (\n\t\t\t\t\t(!previousChildren || isTextNode(previousChildren)) &&\n\t\t\t\t\tnextChildren &&\n\t\t\t\t\tisTagLikeNode(nextChildren)\n\t\t\t\t) {\n\t\t\t\t\tchildBundle[childBundleIndex].push(child);\n\t\t\t\t\treturn result;\n\t\t\t\t}\n\n\t\t\t\t// If we have a previous children, and it's an element AND\n\t\t\t\t// we have a next children, and it's also an element. Add the current children to the bundle\n\t\t\t\tif (\n\t\t\t\t\tpreviousChildren &&\n\t\t\t\t\tisTagLikeNode(previousChildren) &&\n\t\t\t\t\tnextChildren &&\n\t\t\t\t\tisTagLikeNode(nextChildren)\n\t\t\t\t) {\n\t\t\t\t\tchildBundle[childBundleIndex].push(child);\n\t\t\t\t\treturn result;\n\t\t\t\t}\n\n\t\t\t\t// If we have elements in our bundle, and there's no next children, or it's a text node\n\t\t\t\t// Create a fake parent, and add all the previous encountered elements as children of it\n\t\t\t\tif (\n\t\t\t\t\t(!nextChildren || isTextNode(nextChildren)) &&\n\t\t\t\t\tchildBundle[childBundleIndex].length > 0\n\t\t\t\t) {\n\t\t\t\t\tchildBundle[childBundleIndex].push(child);\n\n\t\t\t\t\tconst parentNode: FragmentNode = {\n\t\t\t\t\t\ttype: 'fragment',\n\t\t\t\t\t\tname: '',\n\t\t\t\t\t\tattributes: [],\n\t\t\t\t\t\tchildren: childBundle[childBundleIndex],\n\t\t\t\t\t};\n\n\t\t\t\t\tchildBundleIndex += 1;\n\t\t\t\t\tresult.push(parentNode);\n\t\t\t\t\treturn result;\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tchildBundleIndex += 1;\n\t\t\t}\n\n\t\t\tresult.push(child);\n\t\t\treturn result;\n\t\t}, []);\n\t}\n\n\treturn newNode;\n\n\tfunction makeAttributeJSXCompatible(attr: AttributeNode): AttributeNode {\n\t\t// Transform shorthand attributes into an empty attribute, ex: `{shorthand}` becomes `shorthand` and wrap it\n\t\t// so we can transform it back into {}\n\t\tif (attr.kind === 'shorthand') {\n\t\t\tattr.kind = 'empty';\n\t\t\tattr.name = openingBracketReplace + attr.name + closingBracketReplace;\n\t\t}\n\n\t\t// For spreads, we don't need to do anything because it should already be JSX compatible\n\t\tif (attr.kind !== 'spread') {\n\t\t\tif (attr.name.includes('@')) {\n\t\t\t\tattr.name = attr.name.replaceAll('@', atSignReplace);\n\t\t\t}\n\n\t\t\tif (attr.name.includes('.')) {\n\t\t\t\tattr.name = attr.name.replaceAll('.', dotReplace);\n\t\t\t}\n\n\t\t\tif (attr.name.includes('?')) {\n\t\t\t\tattr.name = attr.name.replaceAll('?', interrogationReplace);\n\t\t\t}\n\t\t}\n\n\t\treturn attr;\n\t}\n}\n\n/** Tags whose content is raw text (not JSX) and must be extracted before Babel parsing */\nconst rawContentTags = ['script', 'style'] as const;\n\ninterface RawTagPlaceholder {\n\tplaceholder: string;\n\tcontent: string;\n\ttagName: (typeof rawContentTags)[number];\n\topeningTag: string; // the serialized opening tag, e.g. '<script is:inline>'\n\tisDirectChild: boolean; // true if direct child of expression (vs nested in fragment)\n\ttypeAttr?: string; // for script\n\tlangAttr?: string; // for style\n}\n\n/**\n * Replace the children of any raw-content elements (script, style) in an expression\n * node with placeholder text nodes. This prevents Babel's JSX parser from trying to\n * parse their content as JSX when they appear inside an expression.\n *\n * See: https://github.com/withastro/prettier-plugin-astro/issues/452\n * See: https://github.com/withastro/prettier-plugin-astro/issues/454\n */\nfunction replaceRawTagChildren(\n\tnode: any,\n\tplaceholders: RawTagPlaceholder[],\n\tisTopLevel = true,\n): any {\n\tconst newNode = { ...node };\n\tif (isNodeWithChildren(newNode)) {\n\t\tnewNode.children = newNode.children.map((child: any) => {\n\t\t\tif (\n\t\t\t\tchild.type === 'element' &&\n\t\t\t\trawContentTags.includes(child.name) &&\n\t\t\t\tchild.children.length\n\t\t\t) {\n\t\t\t\tconst placeholder = `__ASTRO_RAW_TAG_PLACEHOLDER_${placeholders.length}__`;\n\t\t\t\tconst content = printRaw(child);\n\n\t\t\t\t// Build the opening tag string from the original element\n\t\t\t\tconst attrs = (child.attributes || [])\n\t\t\t\t\t.map((a: AttributeNode) => {\n\t\t\t\t\t\tif (a.kind === 'empty') return a.name;\n\t\t\t\t\t\tif (a.kind === 'expression') return `${a.name}={${a.value}}`;\n\t\t\t\t\t\tif (a.kind === 'spread') return `{...${a.name}}`;\n\t\t\t\t\t\treturn `${a.name}=\"${a.value}\"`;\n\t\t\t\t\t})\n\t\t\t\t\t.join(' ');\n\t\t\t\tconst openingTag = attrs ? `<${child.name} ${attrs}>` : `<${child.name}>`;\n\n\t\t\t\tplaceholders.push({\n\t\t\t\t\tplaceholder,\n\t\t\t\t\tcontent,\n\t\t\t\t\ttagName: child.name,\n\t\t\t\t\topeningTag,\n\t\t\t\t\t// Whether this tag is a direct child of the expression (not nested\n\t\t\t\t\t// inside a fragment/element). Direct children are replaced entirely\n\t\t\t\t\t// so babel formats the expression around a simple identifier. Nested\n\t\t\t\t\t// children keep their tag structure for proper fragment formatting.\n\t\t\t\t\tisDirectChild: isTopLevel,\n\t\t\t\t\ttypeAttr: child.attributes?.find((a: AttributeNode) => a.name === 'type')?.value,\n\t\t\t\t\tlangAttr: child.attributes?.find((a: AttributeNode) => a.name === 'lang')?.value,\n\t\t\t\t});\n\n\t\t\t\tif (isTopLevel) {\n\t\t\t\t\t// Replace the entire element with a text placeholder.\n\t\t\t\t\t// Babel sees it as a simple identifier, keeping it on its own\n\t\t\t\t\t// line when the expression handler forces multi-line format.\n\t\t\t\t\treturn { type: 'text', value: placeholder };\n\t\t\t\t}\n\t\t\t\t// Nested: replace only children, preserving the tag structure so\n\t\t\t\t// babel can format it properly within fragments/wrappers.\n\t\t\t\treturn {\n\t\t\t\t\t...child,\n\t\t\t\t\tchildren: [{ type: 'text', value: placeholder }],\n\t\t\t\t};\n\t\t\t}\n\t\t\tif (isNodeWithChildren(child)) {\n\t\t\t\treturn replaceRawTagChildren(child, placeholders, false);\n\t\t\t}\n\t\t\treturn child;\n\t\t});\n\t}\n\treturn newNode;\n}\n\n/**\n * Format the content of a style tag and print the entire element\n */\nasync function embedStyle(\n\tlang: supportedStyleLang | undefined,\n\tcontent: string,\n\tpath: AstPath,\n\tprint: printFn,\n\ttextToDoc: TextToDoc,\n\toptions: ParserOptions,\n): Promise<_doc.builders.Doc | undefined> {\n\tconst isEmpty = /^\\s*$/.test(content);\n\n\tswitch (lang) {\n\t\tcase 'less':\n\t\tcase 'css':\n\t\tcase 'scss': {\n\t\t\tlet formattedStyles = await wrapParserTryCatch(textToDoc, content, {\n\t\t\t\t...options,\n\t\t\t\tparser: lang,\n\t\t\t});\n\n\t\t\t// The css parser appends an extra indented hardline, which we want outside of the `indent()`,\n\t\t\t// so we remove the last element of the array\n\t\t\tformattedStyles = stripTrailingHardline(formattedStyles);\n\n\t\t\t// print\n\t\t\tconst attributes = path.map(print, 'attributes');\n\t\t\tconst openingTag = group(['<style', indent(group(attributes)), softline, '>']);\n\t\t\treturn [\n\t\t\t\topeningTag,\n\t\t\t\tindent([isEmpty ? '' : hardline, formattedStyles]),\n\t\t\t\tisEmpty ? '' : hardline,\n\t\t\t\t'</style>',\n\t\t\t];\n\t\t}\n\t\tcase 'sass': {\n\t\t\tconst lineEnding = options?.endOfLine?.toUpperCase() === 'CRLF' ? 'CRLF' : 'LF';\n\t\t\tconst sassOptions: Partial<SassFormatterConfig> = {\n\t\t\t\ttabSize: options.tabWidth,\n\t\t\t\tinsertSpaces: !options.useTabs,\n\t\t\t\tlineEnding,\n\t\t\t};\n\n\t\t\t// dedent the .sass, otherwise SassFormatter gets indentation wrong\n\t\t\tconst { result: raw } = manualDedent(content);\n\n\t\t\t// format\n\t\t\tconst formattedSassIndented = SassFormatter.Format(raw, sassOptions).trim();\n\n\t\t\t// print\n\t\t\tconst formattedSass = join(hardline, formattedSassIndented.split('\\n'));\n\t\t\tconst attributes = path.map(print, 'attributes');\n\t\t\tconst openingTag = group(['<style', indent(group(attributes)), softline, '>']);\n\t\t\treturn [\n\t\t\t\topeningTag,\n\t\t\t\tindent([isEmpty ? '' : hardline, formattedSass]),\n\t\t\t\tisEmpty ? '' : hardline,\n\t\t\t\t'</style>',\n\t\t\t];\n\t\t}\n\t\tcase undefined: {\n\t\t\tconst node = path.getNode();\n\n\t\t\tif (node) {\n\t\t\t\treturn Buffer.from(options.originalText)\n\t\t\t\t\t.subarray(options.locStart(node), options.locEnd(node))\n\t\t\t\t\t.toString();\n\t\t\t}\n\n\t\t\treturn undefined;\n\t\t}\n\t}\n}\n","export function getVisitorKeys(node: any): string[] {\n\tif (!node || typeof node !== 'object') return [];\n\treturn Object.keys(node).filter((key) => {\n\t\tconst value = node[key];\n\t\treturn (\n\t\t\t(typeof value === 'object' &&\n\t\t\t\tvalue !== null &&\n\t\t\t\tkey !== 'position' && // skip position metadata\n\t\t\t\tkey !== 'type' && // skip node type itself\n\t\t\t\t!Array.isArray(value)) ||\n\t\t\t(Array.isArray(value) && value.length > 0 && typeof value[0] === 'object')\n\t\t);\n\t});\n}\n","import { parse } from '@astrojs/compiler/sync';\nimport type { Parser, Printer, SupportLanguage } from 'prettier';\nimport * as prettierPluginBabel from 'prettier/plugins/babel';\nimport { options } from './options';\nimport { print } from './printer';\nimport { embed } from './printer/embed';\nimport { getVisitorKeys } from './get-visitor-keys';\n\nconst babelParser = prettierPluginBabel.parsers['babel-ts'];\n\n// https://prettier.io/docs/en/plugins.html#languages\nexport const languages: Partial<SupportLanguage>[] = [\n\t{\n\t\tname: 'astro',\n\t\tparsers: ['astro'],\n\t\textensions: ['.astro'],\n\t\tvscodeLanguageIds: ['astro'],\n\t},\n];\n\n// https://prettier.io/docs/en/plugins.html#parsers\nexport const parsers: Record<string, Parser> = {\n\tastro: {\n\t\tparse: (source) => parse(source, { position: true }).ast,\n\t\tastFormat: 'astro',\n\t\tlocStart: (node) => node.position.start.offset,\n\t\tlocEnd: (node) => node.position.end?.offset,\n\t},\n\tastroExpressionParser: {\n\t\t...babelParser,\n\t\tpreprocess(text) {\n\t\t\t// note the trailing newline: if the statement ends in a // comment,\n\t\t\t// we can't add the closing bracket right afterwards\n\t\t\treturn `<>{${text}\\n}</>`;\n\t\t},\n\t\tparse(text, opts) {\n\t\t\tconst ast = babelParser.parse(text, opts);\n\n\t\t\treturn {\n\t\t\t\t...ast,\n\t\t\t\tprogram: ast.program.body[0].expression.children[0].expression,\n\t\t\t};\n\t\t},\n\t},\n};\n\n// https://prettier.io/docs/en/plugins.html#printers\nexport const printers: Record<string, Printer> = {\n\tastro: {\n\t\tprint,\n\t\tembed,\n\t\tgetVisitorKeys,\n\t},\n};\n\nconst defaultOptions = {\n\ttabWidth: 2,\n};\n\nexport { defaultOptions, options };\n"],"mappings":"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"}
|
package/license.txt
ADDED
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
---
|
|
2
|
+
|
|
3
|
+
Fork modifications:
|
|
4
|
+
|
|
5
|
+
MIT License
|
|
6
|
+
|
|
7
|
+
Copyright (c) 2026 Eric Mika
|
|
8
|
+
|
|
9
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
10
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
11
|
+
in the Software without restriction, including without limitation the rights
|
|
12
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
13
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
14
|
+
furnished to do so, subject to the following conditions:
|
|
15
|
+
|
|
16
|
+
The above copyright notice and this permission notice shall be included in all
|
|
17
|
+
copies or substantial portions of the Software.
|
|
18
|
+
|
|
19
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
20
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
21
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
22
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
23
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
24
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
25
|
+
SOFTWARE.
|
|
26
|
+
|
|
27
|
+
---
|
|
28
|
+
|
|
29
|
+
Original plugin:
|
|
30
|
+
|
|
31
|
+
MIT License
|
|
32
|
+
|
|
33
|
+
Copyright (c) 2021 Nate Moore
|
|
34
|
+
|
|
35
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
36
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
37
|
+
in the Software without restriction, including without limitation the rights
|
|
38
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
39
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
40
|
+
furnished to do so, subject to the following conditions:
|
|
41
|
+
|
|
42
|
+
The above copyright notice and this permission notice shall be included in all
|
|
43
|
+
copies or substantial portions of the Software.
|
|
44
|
+
|
|
45
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
46
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
47
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
48
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
49
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
50
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
51
|
+
SOFTWARE.
|
|
52
|
+
|
|
53
|
+
---
|
|
54
|
+
|
|
55
|
+
Substantial portions of this code are adopted from https://github.com/sveltejs/prettier-plugin-svelte
|
|
56
|
+
under the following license:
|
|
57
|
+
|
|
58
|
+
MIT License
|
|
59
|
+
|
|
60
|
+
Copyright (c) 2019 [Contributors](https://github.com/sveltejs/prettier-plugin-svelte/graphs/contributors)
|
|
61
|
+
|
|
62
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
63
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
64
|
+
in the Software without restriction, including without limitation the rights
|
|
65
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
66
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
67
|
+
furnished to do so, subject to the following conditions:
|
|
68
|
+
|
|
69
|
+
The above copyright notice and this permission notice shall be included in all
|
|
70
|
+
copies or substantial portions of the Software.
|
|
71
|
+
|
|
72
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
73
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
74
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
75
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
76
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
77
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
78
|
+
SOFTWARE.
|
package/package.json
ADDED
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@kitschpatrol/prettier-plugin-astro",
|
|
3
|
+
"version": "0.14.1-fork.1",
|
|
4
|
+
"description": "A fork of Prettier Plugin Astro with minor fixes.",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"prettier-plugin",
|
|
7
|
+
"prettier",
|
|
8
|
+
"astro",
|
|
9
|
+
"formatter"
|
|
10
|
+
],
|
|
11
|
+
"homepage": "https://github.com/kitschpatrol/prettier-plugin-astro",
|
|
12
|
+
"bugs": "https://github.com/kitschpatrol/prettier-plugin-astro/issues",
|
|
13
|
+
"repository": {
|
|
14
|
+
"type": "git",
|
|
15
|
+
"url": "git+https://github.com/kitschpatrol/prettier-plugin-astro.git"
|
|
16
|
+
},
|
|
17
|
+
"license": "MIT",
|
|
18
|
+
"contributors": [
|
|
19
|
+
{
|
|
20
|
+
"name": "Eric Mika",
|
|
21
|
+
"email": "eric@ericmika.com",
|
|
22
|
+
"url": "https://ericmika.com"
|
|
23
|
+
}
|
|
24
|
+
],
|
|
25
|
+
"sideEffects": false,
|
|
26
|
+
"type": "module",
|
|
27
|
+
"exports": {
|
|
28
|
+
".": {
|
|
29
|
+
"types": "./dist/index.d.ts",
|
|
30
|
+
"import": "./dist/index.js"
|
|
31
|
+
}
|
|
32
|
+
},
|
|
33
|
+
"main": "./dist/index.js",
|
|
34
|
+
"module": "./dist/index.js",
|
|
35
|
+
"files": [
|
|
36
|
+
"dist/*"
|
|
37
|
+
],
|
|
38
|
+
"dependencies": {
|
|
39
|
+
"@astrojs/compiler": "^3.0.1",
|
|
40
|
+
"sass-formatter": "^0.8.0"
|
|
41
|
+
},
|
|
42
|
+
"devDependencies": {
|
|
43
|
+
"@arethetypeswrong/core": "^0.18.2",
|
|
44
|
+
"@kitschpatrol/shared-config": "^7.3.0",
|
|
45
|
+
"@types/node": "~22.18.13",
|
|
46
|
+
"bumpp": "^11.0.1",
|
|
47
|
+
"prettier": "^3.8.2",
|
|
48
|
+
"publint": "^0.3.18",
|
|
49
|
+
"shx": "^0.4.0",
|
|
50
|
+
"tsdown": "^0.21.7",
|
|
51
|
+
"typescript": "~6.0.2",
|
|
52
|
+
"vite": "^8.0.8",
|
|
53
|
+
"vitest": "^4.1.4"
|
|
54
|
+
},
|
|
55
|
+
"peerDependencies": {
|
|
56
|
+
"prettier": "^3.7.0"
|
|
57
|
+
},
|
|
58
|
+
"engines": {
|
|
59
|
+
"node": ">=18.0.0"
|
|
60
|
+
},
|
|
61
|
+
"devEngines": {
|
|
62
|
+
"runtime": {
|
|
63
|
+
"name": "node",
|
|
64
|
+
"version": ">=22.18.0"
|
|
65
|
+
}
|
|
66
|
+
},
|
|
67
|
+
"publishConfig": {
|
|
68
|
+
"access": "public"
|
|
69
|
+
},
|
|
70
|
+
"scripts": {
|
|
71
|
+
"bench": "vitest bench --run --compare test/benchmarks/baseline.json",
|
|
72
|
+
"bench:baseline": "vitest bench --run --outputJson test/benchmarks/baseline.json",
|
|
73
|
+
"build": "tsdown",
|
|
74
|
+
"clean": "shx rm -f pnpm-lock.yaml && git clean -fdX -e !.claude/",
|
|
75
|
+
"fix": "ksc fix",
|
|
76
|
+
"lint": "ksc lint",
|
|
77
|
+
"release": "bumpp --preid fork --commit 'Release: %s' && pnpm build && NPM_AUTH_TOKEN=$(op read 'op://Personal/npm/token') && pnpm publish --tag latest",
|
|
78
|
+
"test": "vitest run"
|
|
79
|
+
}
|
|
80
|
+
}
|
package/readme.md
ADDED
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
<!-- title -->
|
|
2
|
+
|
|
3
|
+
# @kitschpatrol/prettier-plugin-astro
|
|
4
|
+
|
|
5
|
+
<!-- /title -->
|
|
6
|
+
|
|
7
|
+
<!-- badges -->
|
|
8
|
+
|
|
9
|
+
[](https://npmjs.com/package/@kitschpatrol/prettier-plugin-astro)
|
|
10
|
+
[](https://opensource.org/licenses/MIT)
|
|
11
|
+
[](https://github.com/kitschpatrol/prettier-plugin-astro/actions/workflows/ci.yml)
|
|
12
|
+
|
|
13
|
+
<!-- /badges -->
|
|
14
|
+
|
|
15
|
+
<!-- short-description -->
|
|
16
|
+
|
|
17
|
+
**A fork of Prettier Plugin Astro with minor fixes.**
|
|
18
|
+
|
|
19
|
+
<!-- /short-description -->
|
|
20
|
+
|
|
21
|
+
## Changes
|
|
22
|
+
|
|
23
|
+
This is a reluctant fork of the official [Prettier Plugin for Astro](https://github.com/withastro/prettier-plugin-astro) with fixes for the following issues that I don't have time to tend PRs for at the moment:
|
|
24
|
+
|
|
25
|
+
- Fix for: [Conditional inline script with triangle bracket causes syntax error with prettier@3.7.0 and up · Issue #452](https://github.com/withastro/prettier-plugin-astro/issues/452)
|
|
26
|
+
|
|
27
|
+
- Fix for: [Prettier fails / misparses \<script> when conditionally rendered in .astro template expressions · Issue #454](https://github.com/withastro/prettier-plugin-astro/issues/454)
|
|
28
|
+
|
|
29
|
+
- Merge of: [fix: implement elements whose children should always have lines between them by Princesseuh · PR #399](https://github.com/withastro/prettier-plugin-astro/pull/399)
|
|
30
|
+
|
|
31
|
+
- Merge of: [Add failing test · angelikatyborska/prettier-plugin-astro@73bdf9c](https://github.com/angelikatyborska/prettier-plugin-astro/commit/73bdf9ca634338e5e673130e5832136d29e5bd83)
|
|
32
|
+
|
|
33
|
+
- Aggressive dependency updates, which might be breaking changes for users of older Astro versions < v6.
|
|
34
|
+
|
|
35
|
+
- Package is ESM-only.
|
|
36
|
+
|
|
37
|
+
- Repository project template aligned with [kitschpatrol/create-project](https://github.com/kitschpatrol/create-project). (Massive diff, but simplifies management on my end.)
|
|
38
|
+
|
|
39
|
+
## Branches
|
|
40
|
+
|
|
41
|
+
- [main](https://github.com/kitschpatrol/prettier-plugin-astro/tree/main) Tracks upstream without modifications.
|
|
42
|
+
- [fix-nested-script-tags](https://github.com/kitschpatrol/prettier-plugin-astro/tree/fix-nested-script-tags) Clean merge-able branch of changes.
|
|
43
|
+
- [fork-release](https://github.com/kitschpatrol/prettier-plugin-astro/tree/fork-release) Branch with additional project template and readme changes for NPM releases.
|
|
44
|
+
|
|
45
|
+
## Availability
|
|
46
|
+
|
|
47
|
+
This package is periodically published from its [fork-release branch](https://github.com/kitschpatrol/prettier-plugin-astro/tree/fork-release) to NPM as @kitschpatrol/prettier-plugin-astro since I need it in some other public projects.
|
|
48
|
+
|
|
49
|
+
To install:
|
|
50
|
+
|
|
51
|
+
```sh
|
|
52
|
+
pnpm add -D @kitschpatrol/prettier-plugin-astro
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
It will be deprecated when fixes are available upstream.
|
|
56
|
+
|
|
57
|
+
## More information
|
|
58
|
+
|
|
59
|
+
Please see the [upstream readme](https://github.com/withastro/prettier-plugin-astro/blob/main/README.md) for more details and documentation on the plugin itself.
|