diff --git a/src/language-markdown/embed.js b/src/language-markdown/embed.js index 53ed16c79..916176160 100644 --- a/src/language-markdown/embed.js +++ b/src/language-markdown/embed.js @@ -6,6 +6,7 @@ const { builders: { hardline, literalline, concat, markAsRoot }, utils: { mapDoc } } = require("../doc"); +const { getFencedCodeBlockValue } = require("./utils"); function embed(path, print, textToDoc, options) { const node = path.getValue(); @@ -20,7 +21,10 @@ function embed(path, print, textToDoc, options) { const style = styleUnit.repeat( Math.max(3, util.getMaxContinuousCount(node.value, styleUnit) + 1) ); - const doc = textToDoc(node.value, { parser }); + const doc = textToDoc( + getFencedCodeBlockValue(node, options.originalText), + { parser } + ); return markAsRoot( concat([ style, diff --git a/src/language-markdown/printer-markdown.js b/src/language-markdown/printer-markdown.js index a65a876f9..4a4090bb5 100644 --- a/src/language-markdown/printer-markdown.js +++ b/src/language-markdown/printer-markdown.js @@ -20,7 +20,7 @@ const { utils: { mapDoc }, printer: { printDocToString } } = require("../doc"); -const { getOrderedListItemInfo } = require("./utils"); +const { getOrderedListItemInfo, getFencedCodeBlockValue } = require("./utils"); const TRAILING_HARDLINE_NODES = ["importExport"]; @@ -221,7 +221,10 @@ function genericPrint(path, options, print) { style, node.lang || "", hardline, - join(hardline, node.value.split("\n")), + join( + hardline, + getFencedCodeBlockValue(node, options.originalText).split("\n") + ), hardline, style ]); diff --git a/src/language-markdown/utils.js b/src/language-markdown/utils.js index ecb431e0b..de2065777 100644 --- a/src/language-markdown/utils.js +++ b/src/language-markdown/utils.js @@ -11,6 +11,43 @@ function getOrderedListItemInfo(orderListItem, originalText) { return { numberText, marker, leadingSpaces }; } +// workaround for https://github.com/remarkjs/remark/issues/351 +// leading and trailing newlines are stripped by remark +function getFencedCodeBlockValue(node, originalText) { + const text = originalText.slice( + node.position.start.offset, + node.position.end.offset + ); + + const leadingSpaceCount = text.match(/^\s*/)[0].length; + const replaceRegex = new RegExp(`^\\s{0,${leadingSpaceCount}}`); + + const lineContents = text.split("\n"); + + const markerStyle = text[leadingSpaceCount]; // ` or ~ + const marker = text + .slice(leadingSpaceCount) + .match(new RegExp(`^[${markerStyle}]+`))[0]; + + // https://spec.commonmark.org/0.28/#example-104: Closing fences may be indented by 0-3 spaces + // https://spec.commonmark.org/0.28/#example-93: The closing code fence must be at least as long as the opening fence + const hasEndMarker = new RegExp(`^\\s{0,3}${marker}`).test( + lineContents[lineContents.length - 1].slice( + getIndent(lineContents.length - 1) + ) + ); + + return lineContents + .slice(1, hasEndMarker ? -1 : undefined) + .map((x, i) => x.slice(getIndent(i + 1)).replace(replaceRegex, "")) + .join("\n"); + + function getIndent(lineIndex) { + return node.position.indent[lineIndex - 1] - 1; + } +} + module.exports = { + getFencedCodeBlockValue, getOrderedListItemInfo }; diff --git a/tests/markdown_code/__snapshots__/jsfmt.spec.js.snap b/tests/markdown_code/__snapshots__/jsfmt.spec.js.snap index 5658e32d6..1c91ff3d6 100644 --- a/tests/markdown_code/__snapshots__/jsfmt.spec.js.snap +++ b/tests/markdown_code/__snapshots__/jsfmt.spec.js.snap @@ -109,9 +109,11 @@ console.log("hello world!"); \`\`\`\`\`\`\`\`\`\` ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ \`\`\`\` + \`\`\`js console.log("hello world!"); \`\`\` + \`\`\`\` `; @@ -195,6 +197,25 @@ console.log("hello world"); `; +exports[`leading-trailing-newlines.md - markdown-verify 1`] = ` +\`\`\` + + +123 + + +\`\`\` +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +\`\`\` + + +123 + + +\`\`\` + +`; + exports[`simple.md - markdown-verify 1`] = ` \`\`\` hello world diff --git a/tests/markdown_code/leading-trailing-newlines.md b/tests/markdown_code/leading-trailing-newlines.md new file mode 100644 index 000000000..bf7c4ae06 --- /dev/null +++ b/tests/markdown_code/leading-trailing-newlines.md @@ -0,0 +1,7 @@ +``` + + +123 + + +``` diff --git a/tests/markdown_spec/__snapshots__/jsfmt.spec.js.snap b/tests/markdown_spec/__snapshots__/jsfmt.spec.js.snap index 0a1ecc093..8c144ba5a 100644 --- a/tests/markdown_spec/__snapshots__/jsfmt.spec.js.snap +++ b/tests/markdown_spec/__snapshots__/jsfmt.spec.js.snap @@ -1000,6 +1000,7 @@ exports[`example-94.md - markdown-verify 1`] = ` aaa ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ \`\`\`\` + \`\`\` aaa \`\`\`\` @@ -1028,6 +1029,7 @@ exports[`example-96.md - markdown-verify 1`] = ` ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ \`\`\` + \`\`\` `; @@ -2545,6 +2547,7 @@ bar bar \`\`\`\` + - baz + \`\`\`