Skip to content

Commit

Permalink
Format markdown component content (#63)
Browse files Browse the repository at this point in the history
  • Loading branch information
antonyfaris authored Nov 24, 2021
1 parent 643703c commit f7cf7c1
Show file tree
Hide file tree
Showing 5 changed files with 140 additions and 74 deletions.
5 changes: 5 additions & 0 deletions .changeset/quick-emus-whisper.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'prettier-plugin-astro': patch
---

Format markdown component content
96 changes: 25 additions & 71 deletions src/printer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,6 @@ import {
getMarkdownName,
getText,
getUnencodedText,
indent as manualIndent,
isASTNode,
isDocCommand,
isEmptyDoc,
Expand Down Expand Up @@ -76,12 +75,6 @@ function printTopLevelParts(node: Ast, path: AstPath, opts: ParserOptions, print
}
}

// remove trailing softline, if any
// const lastDoc = docs[docs.length - 1];
// const lastItem = lastDoc[lastDoc.length - 1];

// if (lastItem.type === 'line') docs[docs.length - 1].pop();

return join(softline, docs);
}

Expand Down Expand Up @@ -133,17 +126,6 @@ function print(path: AstPath, opts: ParserOptions, print: printFn): Doc {
return printTopLevelParts(node, path, opts, print);
}

// switch (true) {
// case !node:
// return '';
// case typeof node === 'string':
// return node;
// case Array.isArray(node):
// return path.map((childPath) => childPath.call(print));
// case isASTNode(node):
// return printTopLevelParts(node, path, opts, print);
// }

// 2. attach comments shallowly to children, if any (https://prettier.io/docs/en/plugins.html#manually-attaching-a-comment)
if (!isPreTagContent(path) && !isMarkdownSubDoc) {
attachCommentsHTML(node);
Expand All @@ -159,11 +141,6 @@ function print(path: AstPath, opts: ParserOptions, print: printFn): Doc {

if (!isNodeWithChildren(node) || node.children.every(isEmptyTextNode)) return '';

// If this is the start of a markdown code block, remove arbitrary beginning whitespace
if (isMarkdownSubDoc) {
if (isEmptyTextNode(node.children[0])) node.children.shift();
}

// If we don't see any JSX expressions, this is just embedded HTML
// and we can skip a bunch of work. Hooray!
const hasInlineComponent = node.children.filter((x) => x.type === 'InlineComponent').length > 0;
Expand Down Expand Up @@ -231,10 +208,6 @@ function print(path: AstPath, opts: ParserOptions, print: printFn): Doc {
return fill(splitTextToDocs(node));
}

// case 'SlotTemplate':
// case 'Window':
// case 'Head':
// case 'Title':
case 'Element':
case 'InlineComponent':
case 'Slot': {
Expand Down Expand Up @@ -284,28 +257,12 @@ function print(path: AstPath, opts: ParserOptions, print: printFn): Doc {

let body;

const isMarkdownComponent =
// TODO: opts.__astro & opts.__astro.markdownName
// @ts-ignore
node.type === 'InlineComponent' && opts.__astro && opts.__astro.markdownName && opts.__astro.markdownName.has(node.name) && isNodeWithChildren(node);

if (isEmpty) {
body =
isInlineElement(path, opts, node) && node.children.length && isTextNodeStartingWithWhitespace(node.children[0]) && !isPreTagContent(path)
? () => line
: // () => (opts.jsxBracketNewLine ? '' : softline);
() => softline;
} else if (isMarkdownComponent) {
// collapse children into raw Markdown text
// TODO: CHECK TYPE
// @ts-ignore
const text = node.children.map(getUnencodedText).join('').trim();
node.children = [{ start: firstChild.start, end: lastChild.end - 2, type: 'Text', data: text, raw: text, __isRawMarkdown: true }];
body = () => path.map(print, 'children');

// set hugEnd
hugStart = false;
hugEnd = false;
} else if (isPreTagContent(path)) {
body = () => printRaw(node, opts.originalText);
} else if (isInlineElement(path, opts, node) && !isPreTagContent(path)) {
Expand All @@ -328,9 +285,6 @@ function print(path: AstPath, opts: ParserOptions, print: printFn): Doc {
if (isPreTagContent(path)) {
noHugSeparatorStart = '';
noHugSeparatorEnd = '';
} else if (isMarkdownComponent) {
noHugSeparatorStart = softline;
noHugSeparatorEnd = softline;
} else {
let didSetEndSeparator = false;

Expand Down Expand Up @@ -463,6 +417,8 @@ function expressionParser(text: string, parsers: any, opts: ParserOptions) {
return { ...ast, program: ast.program.body[0].expression };
}

let markdownComponentName = new Set();

function embed(path: AstPath, print: printFn, textToDoc: (text: string, options: object) => Doc, opts: ParserOptions) {
// TODO: ADD TYPES OR FIND ANOTHER WAY TO ACHIVE THIS
// @ts-ignore
Expand All @@ -472,23 +428,6 @@ function embed(path: AstPath, print: printFn, textToDoc: (text: string, options:

if (!node) return null;

// TODO: ADD TYPES OR FIND ANOTHER WAY TO ACHIVE THIS
// @ts-ignore
if (node.__isRawMarkdown) {
// TODO: CHECK TYPE
// @ts-ignore
const docs = textToDoc(getUnencodedText(node), { ...opts, parser: 'markdown' });
return stripTrailingHardline(docs);
}

// TODO: ADD TYPES OR FIND ANOTHER WAY TO ACHIVE THIS
// @ts-ignore
if (node.type === 'Script' && !opts.__astro.markdownName) {
// TODO: ADD TYPES OR FIND ANOTHER WAY TO ACHIVE THIS
// @ts-ignore
opts.__astro.markdownName = getMarkdownName(node.content);
}

// TODO: ADD TYPES OR FIND ANOTHER WAY TO ACHIVE THIS
// @ts-ignore
if (node.isJS) {
Expand All @@ -514,6 +453,7 @@ function embed(path: AstPath, print: printFn, textToDoc: (text: string, options:
}

if (node.type === 'Script' && node.context === 'setup') {
markdownComponentName = getMarkdownName(node.content);
return group(['---', hardline, textToDoc(node.content, { ...opts, parser: 'typescript' }), '---', hardline]);
}

Expand All @@ -523,11 +463,7 @@ function embed(path: AstPath, print: printFn, textToDoc: (text: string, options:

if (parent && parent.type === 'Element' && parent.name === 'script') {
const formatttedScript = textToDoc(node.data, { ...opts, parser: 'typescript' });
if (typeof formatttedScript === 'string') return formatttedScript;
if (isDocCommand(formatttedScript)) return formatttedScript;
return group(formatttedScript[0]);
// const [formatttedScript, ,] = textToDoc(node.data, { ...opts, parser: 'typescript' });
// return group(formatttedScript);
return stripTrailingHardline(formatttedScript);
}
}

Expand Down Expand Up @@ -559,9 +495,8 @@ function embed(path: AstPath, print: printFn, textToDoc: (text: string, options:
// the css parser appends an extra indented hardline, which we want outside of the `indent()`,
// so we remove the last element of the array
let formattedStyles = textToDoc(styleTagContent, { ...opts, parser: parserLang });
if (typeof formattedStyles === 'string') return formattedStyles;
if (isDocCommand(formattedStyles)) return formattedStyles;
formattedStyles = formattedStyles[0];

formattedStyles = stripTrailingHardline(formattedStyles);

// print
const attributes = path.map(print, 'attributes');
Expand Down Expand Up @@ -591,6 +526,25 @@ function embed(path: AstPath, print: printFn, textToDoc: (text: string, options:
}
}

// MARKDOWN COMPONENT
if (node.type === 'InlineComponent' && markdownComponentName.has(node.name)) {
let content = printRaw(node, opts.originalText);

// dedent the content
content = content.replace(/\r\n/g, '\n');
const contentArr = content.split('\n').map((s) => s.trimStart());
content = contentArr.join('\n');

// format
let formatttedMarkdown = textToDoc(content, { ...opts, parser: 'markdown' });
formatttedMarkdown = stripTrailingHardline(formatttedMarkdown);

// return formatttedMarkdown;
const attributes = path.map(print, 'attributes');
const openingTag = group([`<${node.name}`, indent(group(attributes)), softline, '>']);
return [openingTag, indent(group([hardline, formatttedMarkdown])), hardline, `</${node.name}>`];
}

// TODO: ADD TYPES OR FIND ANOTHER WAY TO ACHIVE THIS
// @ts-ignore
if (node.__isRawHTML) {
Expand Down
13 changes: 10 additions & 3 deletions test/astro-prettier.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -147,8 +147,15 @@ test('Can format an Astro file with prettier "astroAllowShorthand: true" option'

// astro option: astroAllowShorthand
test('Can format an Astro file with prettier "astroAllowShorthand: false" option', Prettier, 'option-astro-allow-shorthand-false');
test('Format nested style tag content', Prettier, 'format-nested-style-tag-content');

test('Format nested sass style tag content', Prettier, 'format-nested-sass-style-tag-content');

test('Format spread operator', Prettier, 'spread-operator');

test('Can format nested style tag content', Prettier, 'format-nested-style-tag-content');

test('Can format nested sass style tag content', Prettier, 'format-nested-sass-style-tag-content');

test('Can format the content of a markdown component as markdown', Prettier, 'markdown-component-content');

test.todo("Don't escape '*' inside markdown");

test.todo('Format jsx inside markdown');
47 changes: 47 additions & 0 deletions test/fixtures/markdown-component-content/input.astro
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
---
const text = "lorem";
import MarkdownComponent from "astro/components/Markdown.astro"
---
<div>
<MarkdownComponent>
# Header

## lorem lorem
lorem
<Fragment>test</Fragment>
<div>lorem</div>
lorem

{text + "lorem"}_Look,_ code blocks are formatted *too!*

``` js
function identity(x) { return x }
```

```css
.bd-examples {margin-right: -.75rem;margin-left: -.75rem
}

.bd-examples>[class^="col-"] {
padding-right: .75rem;
}
```

Pilot|Airport|Hours
--|:--:|--:
John Doe|SKG|1338
Jane Roe|JFK|314

- - - - - - - - - - - - - - -

+ List
+ with a [link] (/to/somewhere)
+ and [another one]


[another one]: http://example.com 'Example title'

Lorem ipsum dolor sit amet, consectetur adipiscing elit.
Curabitur consectetur maximus risus, sed maximus tellus tincidunt et.
</MarkdownComponent>
</div>
53 changes: 53 additions & 0 deletions test/fixtures/markdown-component-content/output.astro
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
---
const text = "lorem";
import MarkdownComponent from "astro/components/Markdown.astro";
---

<div>
<MarkdownComponent>
# Header

## lorem lorem

lorem
<Fragment>test</Fragment>

<div>lorem</div>
lorem

{text + "lorem"}_Look,_ code blocks are formatted _too!_

```js
function identity(x) {
return x;
}
```

```css
.bd-examples {
margin-right: -0.75rem;
margin-left: -0.75rem;
}

.bd-examples > [class^="col-"] {
padding-right: 0.75rem;
}
```

| Pilot | Airport | Hours |
| -------- | :-----: | ----: |
| John Doe | SKG | 1338 |
| Jane Roe | JFK | 314 |

---

- List
- with a [link] (/to/somewhere)
- and [another one]

[another one]: http://example.com "Example title"

Lorem ipsum dolor sit amet, consectetur adipiscing elit.
Curabitur consectetur maximus risus, sed maximus tellus tincidunt et.
</MarkdownComponent>
</div>

0 comments on commit f7cf7c1

Please sign in to comment.