Add location information to parseFrontMatter() (#17948)

This commit is contained in:
fisker Cheung authored and GitHub committed 2025-09-24 04:15:01 +08:00
1 parent 6df61307be
commit 4ec2648e61
12 files changed
+93 -40

No files matched your search

+2 -3
View File
@@ -1,3 +1,4 @@
import cleanFrontMatter from "../utils/front-matter/clean.js";
import isFrontMatter from "../utils/front-matter/is-front-matter.js";
const ignoredProperties = new Set([
@@ -12,9 +13,7 @@ const ignoredProperties = new Set([
]);
function clean(original, cloned, parent) {
if (isFrontMatter(original) && original.language === "yaml") {
delete cloned.value;
}
cleanFrontMatter(original, cloned);
if (
original.type === "css-comment" &&
+2 -1
View File
@@ -1,10 +1,11 @@
import { hardline } from "../document/builders.js";
import isFrontMatter from "../utils/front-matter/is-front-matter.js";
import printFrontMatter from "../utils/front-matter/print.js";
function embed(path) {
const { node } = path;
if (node.type === "front-matter") {
if (isFrontMatter(node) && node.language === "yaml") {
return async (textToDoc) => {
const doc = await printFrontMatter(node, textToDoc);
return doc ? [doc, hardline] : undefined;
+8 -8
View File
@@ -377,14 +377,12 @@ function parseNestedCSS(node, options) {
}
function parseWithParser(parse, text, options) {
const parsed = parseFrontMatter(text);
const { frontMatter } = parsed;
text = parsed.content;
const { frontMatter, content: textToParse } = parseFrontMatter(text);
let result;
try {
result = parse(text, {
result = parse(textToParse, {
// Prevent file access https://github.com/postcss/postcss/blob/4f4e2932fc97e2c117e1a4b15f0272ed551ed59d/lib/previous-map.js#L18
map: false,
});
@@ -407,11 +405,13 @@ function parseWithParser(parse, text, options) {
calculateLoc(result, text);
if (frontMatter) {
frontMatter.source = {
startOffset: 0,
endOffset: frontMatter.raw.length,
result.frontMatter = {
...frontMatter,
source: {
startOffset: frontMatter.start.index,
endOffset: frontMatter.end.index,
},
};
result.frontMatter = frontMatter;
}
return result;
+4 -7
View File
@@ -1,4 +1,4 @@
import isFrontMatter from "../utils/front-matter/is-front-matter.js";
import cleanFrontMatter from "../utils/front-matter/clean.js";
const ignoredProperties = new Set([
"sourceSpan",
@@ -28,13 +28,10 @@ function clean(original, cloned) {
return null;
}
cleanFrontMatter(original, cloned);
// may be formatted by multiparser
if (
isFrontMatter(original) ||
original.type === "yaml" ||
original.type === "toml"
) {
delete cloned.raw;
if (original.type === "yaml") {
delete cloned.value;
}
+4 -1
View File
@@ -142,7 +142,10 @@ function embed(path, options) {
return printAttribute(path, options);
case "front-matter":
return (textToDoc) => printFrontMatter(node, textToDoc);
if (node.language === "yaml") {
return (textToDoc) => printFrontMatter(node, textToDoc);
}
break;
case "angularControlFlowBlockParameters":
if (!embeddedAngularControlFlowBlocks.has(path.parent.name)) {
+16 -7
View File
@@ -21,7 +21,7 @@ import HTML_TAGS from "./utils/html-tags.evaluate.js";
import isUnknownNamespace from "./utils/is-unknown-namespace.js";
/**
* @import AngularHtmlParser, {ParseOptions as AngularHtmlParserParseOptions} from "angular-html-parser"
* @import {ParseOptions as AngularHtmlParserParseOptions} from "angular-html-parser"
* @import {Node as AstNode, Attribute, Element} from "angular-html-parser/lib/compiler/src/ml_parser/ast.js"
* @import {ParseTreeResult} from "angular-html-parser/lib/compiler/src/ml_parser/parser.js"
*/
@@ -348,7 +348,7 @@ function parse(
options = {},
shouldParseFrontMatter = true,
) {
const { frontMatter, content } = shouldParseFrontMatter
const { frontMatter, content: textToParse } = shouldParseFrontMatter
? parseFrontMatter(text)
: { frontMatter: null, content: text };
@@ -358,15 +358,24 @@ function parse(
const rawAst = {
type: "root",
sourceSpan: new ParseSourceSpan(start, end),
children: ngHtmlParser(content, parseOptions, options),
children: ngHtmlParser(textToParse, parseOptions, options),
};
if (frontMatter) {
const start = new ParseLocation(file, 0, 0, 0);
const end = start.moveBy(frontMatter.raw.length);
frontMatter.sourceSpan = new ParseSourceSpan(start, end);
const [start, end] = [frontMatter.start, frontMatter.end].map(
(location) =>
new ParseLocation(
file,
location.index,
location.line - 1,
location.column,
),
);
// @ts-expect-error -- not a real AstNode
rawAst.children.unshift(frontMatter);
rawAst.children.unshift({
...frontMatter,
sourceSpan: new ParseSourceSpan(start, end),
});
}
const ast = new Node(rawAst);
+3 -1
View File
@@ -1,4 +1,5 @@
import collapseWhiteSpace from "collapse-white-space";
import cleanFrontMatter from "../utils/front-matter/clean.js";
import isFrontMatter from "../utils/front-matter/is-front-matter.js";
import { FORMAT_PRAGMAS } from "../utils/pragma/pragma.evaluate.js";
import { startWithPragma } from "./pragma.js";
@@ -10,7 +11,6 @@ const ignoredProperties = new Set([
function clean(original, cloned, parent) {
// for codeblock
if (
original.type === "front-matter" ||
original.type === "code" ||
original.type === "yaml" ||
original.type === "import" ||
@@ -20,6 +20,8 @@ function clean(original, cloned, parent) {
delete cloned.value;
}
cleanFrontMatter(original, cloned);
if (original.type === "list") {
delete cloned.isAligned;
}
+1 -4
View File
@@ -90,10 +90,7 @@ function genericPrint(path, options, print) {
switch (node.type) {
case "front-matter":
return options.originalText.slice(
node.position.start.offset,
node.position.end.offset,
);
return node.raw;
case "root":
/* c8 ignore next 3 */
if (node.children.length === 0) {
@@ -11,10 +11,10 @@ const frontMatter = function () {
proto.blockTokenizers.frontMatter = tokenizer;
function tokenizer(eat, value) {
const parsed = parseFrontMatter(value);
const { frontMatter } = parseFrontMatter(value);
if (parsed.frontMatter) {
return eat(parsed.frontMatter.raw)(parsed.frontMatter);
if (frontMatter) {
return eat(frontMatter.raw)(frontMatter);
}
}
tokenizer.onlyAtStart = true;
+11
View File
@@ -0,0 +1,11 @@
import isFrontMatter from "./is-front-matter.js";
function clean(original, cloned) {
if (isFrontMatter(original) && original.language === "yaml") {
delete cloned.end;
delete cloned.raw;
delete cloned.value;
}
}
export default clean;
+38 -4
View File
@@ -1,5 +1,24 @@
const DELIMITER_LENGTH = 3;
/**
@typedef {{index: number, line: number, column: number}} Position
@typedef {{
type: "front-matter",
language: string,
explicitLanguage: string | null,
value: string,
startDelimiter: string,
endDelimiter: string,
raw: string,
start: Position,
end: Position,
}} FrontMatter
*/
/**
@param {string} text
@returns {FrontMatter | undefined}
*/
function getFrontMatter(text) {
const startDelimiter = text.slice(0, DELIMITER_LENGTH);
@@ -48,15 +67,29 @@ function getFrontMatter(text) {
}
const raw = text.slice(0, frontMatterEndIndex);
/** @type {string[]} */
let lines;
return {
type: "front-matter",
language,
explicitLanguage,
explicitLanguage: explicitLanguage || null,
value: text.slice(firstLineBreakIndex + 1, endDelimiterIndex),
startDelimiter,
endDelimiter: raw.slice(-DELIMITER_LENGTH),
raw,
start: { line: 1, column: 0, index: 0 },
end: {
index: raw.length,
get line() {
lines ??= raw.split("\n");
return lines.length;
},
get column() {
lines ??= raw.split("\n");
return lines.at(-1).length;
},
},
};
}
@@ -67,11 +100,12 @@ function parse(text) {
return { content: text };
}
const { raw } = frontMatter;
return {
frontMatter,
content: raw.replaceAll(/[^\n]/gu, " ") + text.slice(raw.length),
get content() {
const { raw } = frontMatter;
return raw.replaceAll(/[^\n]/gu, " ") + text.slice(raw.length);
},
};
}
+1 -1
View File
@@ -6,7 +6,7 @@ async function print(node, textToDoc) {
const doc = value ? await textToDoc(value, { parser: "yaml" }) : "";
return markAsRoot([
node.startDelimiter,
node.explicitLanguage,
node.explicitLanguage ?? "",
hardline,
doc,
doc ? hardline : "",