From 99c4e41fa1bf71a99a34a734c9e4f7ce2e3ad900 Mon Sep 17 00:00:00 2001 From: Ika Date: Tue, 3 Jul 2018 09:54:40 +0800 Subject: [PATCH] feat: format yaml front matter (#4773) * refactor: front-matter * feat: format yaml front matter --- src/language-css/clean.js | 23 +++++++--- src/language-css/embed.js | 44 +++++++++++++++++++ src/language-css/parser-postcss.js | 5 +-- src/language-css/pragma.js | 3 +- src/language-css/printer-postcss.js | 13 ++++-- src/language-markdown/embed.js | 15 +++++++ src/language-markdown/parser-markdown.js | 5 +-- src/language-markdown/pragma.js | 2 +- src/language-markdown/printer-markdown.js | 14 ++++-- src/utils/front-matter.js | 31 ++++++++----- .../css_yaml/__snapshots__/jsfmt.spec.js.snap | 29 ++++++++++-- tests/css_yaml/dirty.css | 12 +++++ .../__snapshots__/jsfmt.spec.js.snap | 17 +++++++ tests/markdown_yaml/complex.md | 6 +++ 14 files changed, 180 insertions(+), 39 deletions(-) create mode 100644 src/language-css/embed.js create mode 100644 tests/css_yaml/dirty.css create mode 100644 tests/markdown_yaml/complex.md diff --git a/src/language-css/clean.js b/src/language-css/clean.js index 682eb7a7..bbfb3c88 100644 --- a/src/language-css/clean.js +++ b/src/language-css/clean.js @@ -3,11 +3,21 @@ const htmlTagNames = require("html-tag-names"); function clean(ast, newObj, parent) { - ["raws", "sourceIndex", "source", "before", "after", "trailingComma"].forEach( - name => { - delete newObj[name]; - } - ); + [ + "raw", // front-matter + "raws", + "sourceIndex", + "source", + "before", + "after", + "trailingComma" + ].forEach(name => { + delete newObj[name]; + }); + + if (ast.type === "yaml") { + delete newObj.value; + } // --insert-pragma if ( @@ -16,7 +26,8 @@ function clean(ast, newObj, parent) { parent.nodes.length !== 0 && // first non-front-matter comment (parent.nodes[0] === ast || - (parent.nodes[0].type === "front-matter" && parent.nodes[1] === ast)) + ((parent.nodes[0].type === "yaml" || parent.nodes[0].type === "toml") && + parent.nodes[1] === ast)) ) { /** * something diff --git a/src/language-css/embed.js b/src/language-css/embed.js new file mode 100644 index 00000000..b2648bc8 --- /dev/null +++ b/src/language-css/embed.js @@ -0,0 +1,44 @@ +"use strict"; + +const { + builders: { hardline, literalline, concat, markAsRoot }, + utils: { mapDoc } +} = require("../doc"); + +function embed(path, print, textToDoc /*, options */) { + const node = path.getValue(); + + if (node.type === "yaml") { + return markAsRoot( + concat([ + "---", + hardline, + node.value.trim() + ? replaceNewlinesWithLiterallines( + textToDoc(node.value, { parser: "yaml" }) + ) + : "", + "---", + hardline + ]) + ); + } + + return null; + + function replaceNewlinesWithLiterallines(doc) { + return mapDoc( + doc, + currentDoc => + typeof currentDoc === "string" && currentDoc.includes("\n") + ? concat( + currentDoc + .split(/(\n)/g) + .map((v, i) => (i % 2 === 0 ? v : literalline)) + ) + : currentDoc + ); + } +} + +module.exports = embed; diff --git a/src/language-css/parser-postcss.js b/src/language-css/parser-postcss.js index 4d961278..4d59a7d1 100644 --- a/src/language-css/parser-postcss.js +++ b/src/language-css/parser-postcss.js @@ -497,10 +497,7 @@ function parseWithParser(parser, text) { result = parseNestedCSS(addTypePrefix(result, "css-")); if (frontMatter) { - result.nodes.unshift({ - type: "front-matter", - value: frontMatter - }); + result.nodes.unshift(frontMatter); } return result; diff --git a/src/language-css/pragma.js b/src/language-css/pragma.js index 6e060b00..21f4b2c2 100644 --- a/src/language-css/pragma.js +++ b/src/language-css/pragma.js @@ -10,7 +10,8 @@ function hasPragma(text) { function insertPragma(text) { const { frontMatter, content } = parseFrontMatter(text); return ( - (frontMatter ? frontMatter + "\n\n" : "") + jsPragma.insertPragma(content) + (frontMatter ? frontMatter.raw + "\n\n" : "") + + jsPragma.insertPragma(content) ); } diff --git a/src/language-css/printer-postcss.js b/src/language-css/printer-postcss.js index d29d5746..904dcd9b 100644 --- a/src/language-css/printer-postcss.js +++ b/src/language-css/printer-postcss.js @@ -1,6 +1,7 @@ "use strict"; const clean = require("./clean"); +const embed = require("./embed"); const { insertPragma } = require("./pragma"); const { printNumber, @@ -93,8 +94,9 @@ function genericPrint(path, options, print) { } switch (node.type) { - case "front-matter": - return concat([node.value, hardline]); + case "yaml": + case "toml": + return concat([node.raw, hardline]); case "css-root": { const nodes = printNodeSequence(path, options, print); @@ -834,7 +836,8 @@ function printNodeSequence(path, options, print) { options.locStart(node.nodes[i + 1]), { backwards: true } ) && - node.nodes[i].type !== "front-matter") || + node.nodes[i].type !== "yaml" && + node.nodes[i].type !== "toml") || (node.nodes[i + 1].type === "css-atrule" && node.nodes[i + 1].name === "else" && node.nodes[i].type !== "css-comment") @@ -848,7 +851,8 @@ function printNodeSequence(path, options, print) { pathChild.getValue(), options ) && - node.nodes[i].type !== "front-matter" + node.nodes[i].type !== "yaml" && + node.nodes[i].type !== "toml" ) { parts.push(hardline); } @@ -906,6 +910,7 @@ function printCssNumber(rawNumber) { module.exports = { print: genericPrint, + embed, insertPragma, hasPrettierIgnore: hasIgnoreComment, massageAstNode: clean diff --git a/src/language-markdown/embed.js b/src/language-markdown/embed.js index 39883160..b27990d4 100644 --- a/src/language-markdown/embed.js +++ b/src/language-markdown/embed.js @@ -33,6 +33,21 @@ function embed(path, print, textToDoc, options) { } } + if (node.type === "yaml") { + return markAsRoot( + concat([ + "---", + hardline, + node.value.trim() + ? replaceNewlinesWithLiterallines( + textToDoc(node.value, { parser: "yaml" }) + ) + : "", + "---" + ]) + ); + } + return null; function getParserName(lang) { diff --git a/src/language-markdown/parser-markdown.js b/src/language-markdown/parser-markdown.js index 2b7f455f..47676d78 100644 --- a/src/language-markdown/parser-markdown.js +++ b/src/language-markdown/parser-markdown.js @@ -138,10 +138,7 @@ function frontMatter() { const parsed = parseFrontMatter(value); if (parsed.frontMatter) { - return eat(parsed.frontMatter)({ - type: "front-matter", - value: parsed.frontMatter - }); + return eat(parsed.frontMatter.raw)(parsed.frontMatter); } } tokenizer.onlyAtStart = true; diff --git a/src/language-markdown/pragma.js b/src/language-markdown/pragma.js index 7b844e45..d7018bd7 100644 --- a/src/language-markdown/pragma.js +++ b/src/language-markdown/pragma.js @@ -24,7 +24,7 @@ module.exports = { const extracted = parseFrontMatter(text); const pragma = ``; return extracted.frontMatter - ? `${extracted.frontMatter}\n\n${pragma}\n\n${extracted.content}` + ? `${extracted.frontMatter.raw}\n\n${pragma}\n\n${extracted.content}` : `${pragma}\n\n${extracted.content}`; } }; diff --git a/src/language-markdown/printer-markdown.js b/src/language-markdown/printer-markdown.js index 218c3833..c320a9d9 100644 --- a/src/language-markdown/printer-markdown.js +++ b/src/language-markdown/printer-markdown.js @@ -226,8 +226,12 @@ function genericPrint(path, options, print) { style ]); } - case "front-matter": - return node.value; + case "yaml": + case "toml": + return options.originalText.slice( + node.position.start.offset, + node.position.end.offset + ); case "html": { const parentNode = path.getParentNode(); const value = @@ -840,9 +844,10 @@ function clamp(value, min, max) { function clean(ast, newObj, parent) { delete newObj.position; + delete newObj.raw; // front-matter // for codeblock - if (ast.type === "code") { + if (ast.type === "code" || ast.type === "yaml") { delete newObj.value; } // for whitespace: "\n" and " " are considered the same @@ -855,7 +860,8 @@ function clean(ast, newObj, parent) { parent.type === "root" && parent.children.length > 0 && (parent.children[0] === ast || - (parent.children[0].type === "front-matter" && + ((parent.children[0].type === "yaml" || + parent.children[0].type === "toml") && parent.children[1] === ast)) && ast.type === "html" && pragma.startWithPragma(ast.value) diff --git a/src/utils/front-matter.js b/src/utils/front-matter.js index 4cb188ef..aded08bb 100644 --- a/src/utils/front-matter.js +++ b/src/utils/front-matter.js @@ -1,25 +1,32 @@ "use strict"; +const escape = require("escape-string-regexp"); + +const DELIMITER_MAP = { + "---": "yaml", + "+++": "toml" +}; + function parse(text) { - let delimiter; + const delimiterRegex = Object.keys(DELIMITER_MAP) + .map(escape) + .join("|"); - if (text.indexOf("---") === 0) { - delimiter = "---"; - } else if (text.indexOf("+++") === 0) { - delimiter = "+++"; - } + const match = text.match( + new RegExp(`^(${delimiterRegex})\\n(?:([\\s\\S]*?)\\n)?\\1(\\n|$)`) + ); - let end = -1; - - if (!delimiter || (end = text.indexOf(`\n${delimiter}`, 3)) === -1) { + if (match === null) { return { frontMatter: null, content: text }; } - end = end + 4; + const raw = match[0].trimRight(); + const delimiter = match[1]; + const value = match[2]; return { - frontMatter: text.slice(0, end), - content: text.slice(end) + frontMatter: { type: DELIMITER_MAP[delimiter], value, raw }, + content: text.slice(raw.length) }; } diff --git a/tests/css_yaml/__snapshots__/jsfmt.spec.js.snap b/tests/css_yaml/__snapshots__/jsfmt.spec.js.snap index 4d7c09dd..c13309b8 100644 --- a/tests/css_yaml/__snapshots__/jsfmt.spec.js.snap +++ b/tests/css_yaml/__snapshots__/jsfmt.spec.js.snap @@ -20,6 +20,32 @@ description: Description `; +exports[`dirty.css 1`] = ` +--- +hello: world +a: + - 123 + - 666 +--- + +.class { + + + +} +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +--- +hello: world +a: + - 123 + - 666 +--- + +.class { +} + +`; + exports[`empty.css 1`] = ` --- --- @@ -51,9 +77,6 @@ a { } ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ --- - - - --- a { diff --git a/tests/css_yaml/dirty.css b/tests/css_yaml/dirty.css new file mode 100644 index 00000000..e99a3c8b --- /dev/null +++ b/tests/css_yaml/dirty.css @@ -0,0 +1,12 @@ +--- +hello: world +a: + - 123 + - 666 +--- + +.class { + + + +} diff --git a/tests/markdown_yaml/__snapshots__/jsfmt.spec.js.snap b/tests/markdown_yaml/__snapshots__/jsfmt.spec.js.snap index 195f809f..a36df326 100644 --- a/tests/markdown_yaml/__snapshots__/jsfmt.spec.js.snap +++ b/tests/markdown_yaml/__snapshots__/jsfmt.spec.js.snap @@ -1,5 +1,22 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP +exports[`complex.md 1`] = ` +--- +- hello: world +- 123 +--- + +# something +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +--- +- hello: world +- 123 +--- + +# something + +`; + exports[`empty.md 1`] = ` --- --- diff --git a/tests/markdown_yaml/complex.md b/tests/markdown_yaml/complex.md new file mode 100644 index 00000000..3584e74e --- /dev/null +++ b/tests/markdown_yaml/complex.md @@ -0,0 +1,6 @@ +--- +- hello: world +- 123 +--- + +# something