-
-
Notifications
You must be signed in to change notification settings - Fork 2.4k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Prettier support for .astro
files
#106
Merged
Merged
Changes from all commits
Commits
Show all changes
22 commits
Select commit
Hold shift + click to select a range
33ad3c7
docs: fix readme
d488fc9
chore: scaffold prettier plugin
c20e18f
chore(prettier): switch to cjs
06cdd0e
test(prettier): scaffold prettier tests
9178c1b
test(prettier): add simple prettier tests
268da14
feat(prettier): first pass
8f16a2f
refactor: expose parser as CJS export
7bf8170
test(prettier): add long expression
a22cb8e
refactor(prettier): use Astro parser + built-in prettier doc for pret…
73b7fc4
chore: remove parser from git
700b724
chore: add prettier-plugin-astro `build` to workflow
f98498b
chore: update package-lock
3d7ed70
chore: do not build prettier-plugin-astro
53bd8c8
fix: update engines
a4393e5
chore: remove NPM restriction
bfa66ee
chore: fix workflow paths
b5dfcc6
chore: update build script
27e6784
test: fix prettier expr test
817d844
chore: fix parser build on windows
74225fb
refactor: add parser tsconfig, extending base config
ff91e0e
chore: relax ban-ts-comment
c08c16b
chore: fix lint issue
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,5 +1,6 @@ | ||
node_modules/ | ||
lib/ | ||
parser/ | ||
dist/ | ||
*.tsbuildinfo | ||
.DS_Store | ||
|
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
module.exports = require('./parser/index.js'); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,154 @@ | ||
const { | ||
doc: { | ||
builders: { concat, hardline }, | ||
}, | ||
} = require('prettier'); | ||
const { parse } = require('astro/parser'); | ||
|
||
/** @type {Partial<import('prettier').SupportLanguage>[]} */ | ||
module.exports.languages = [ | ||
{ | ||
name: 'astro', | ||
parsers: ['astro'], | ||
extensions: ['.astro'], | ||
vscodeLanguageIds: ['astro'], | ||
}, | ||
]; | ||
|
||
/** @type {Record<string, import('prettier').Parser>} */ | ||
module.exports.parsers = { | ||
astro: { | ||
parse: (text) => { | ||
let { html, css, module: frontmatter } = parse(text); | ||
html = html ? { ...html, text: text.slice(html.start, html.end), isRoot: true } : null; | ||
return [frontmatter, html, css].filter((v) => v); | ||
}, | ||
locStart(node) { | ||
return node.start; | ||
}, | ||
locEnd(node) { | ||
return node.end; | ||
}, | ||
astFormat: 'astro-ast', | ||
}, | ||
'astro-expression': { | ||
parse: (text, parsers) => { | ||
return { text }; | ||
}, | ||
locStart(node) { | ||
return node.start; | ||
}, | ||
locEnd(node) { | ||
return node.end; | ||
}, | ||
astFormat: 'astro-expression', | ||
} | ||
}; | ||
|
||
const findExpressionsInAST = (node, collect = []) => { | ||
if (node.type === 'MustacheTag') { | ||
return collect.concat(node); | ||
} | ||
if (node.children) { | ||
collect.push(...[].concat(...node.children.map(child => findExpressionsInAST(child)))); | ||
} | ||
return collect; | ||
} | ||
|
||
const formatExpression = ({ expression: { codeStart, codeEnd, children }}, text, options) => { | ||
if (children.length === 0) { | ||
if ([`'`, `"`].includes(codeStart[0])) { | ||
return `<script $ lang="ts">${codeStart}${codeEnd}</script>` | ||
} | ||
return `{${codeStart}${codeEnd}}`; | ||
} | ||
|
||
return `<script $ lang="ts">${text}</script>`; | ||
} | ||
|
||
const isAstroScript = (node) => node.type === 'concat' && node.parts[0] === '<script' && node.parts[1].type === 'indent' && node.parts[1].contents.parts.find(v => v === '$'); | ||
|
||
const walkDoc = (doc) => { | ||
let inAstroScript = false; | ||
const recurse = (node, { parent }) => { | ||
if (node.type === 'concat') { | ||
if (isAstroScript(node)) { | ||
inAstroScript = true; | ||
parent.contents = { type: 'concat', parts: ['{'] }; | ||
} | ||
return node.parts.map(part => recurse(part, { parent: node })); | ||
} | ||
if (inAstroScript) { | ||
if (node.type === 'break-parent') { | ||
parent.parts = parent.parts.filter(part => !['break-parent', 'line'].includes(part.type)); | ||
} | ||
if (node.type === 'indent') { | ||
parent.parts = parent.parts.map(part => { | ||
if (part.type !== 'indent') return part; | ||
return { | ||
type: 'concat', | ||
parts: [part.contents] | ||
} | ||
}) | ||
} | ||
if (typeof node === 'string' && node.endsWith(';')) { | ||
parent.parts = parent.parts.map(part => { | ||
if (typeof part === 'string' && part.endsWith(';')) return part.slice(0, -1); | ||
return part; | ||
}); | ||
} | ||
if (node === '</script>') { | ||
parent.parts = parent.parts.map(part => part === '</script>' ? '}' : part); | ||
inAstroScript = false; | ||
} | ||
} | ||
if (['group', 'indent'].includes(node.type)) { | ||
return recurse(node.contents, { parent: node }); | ||
} | ||
} | ||
recurse(doc, { parent: null }); | ||
} | ||
|
||
/** @type {Record<string, import('prettier').Printer>} */ | ||
module.exports.printers = { | ||
'astro-ast': { | ||
print(path, opts, print) { | ||
const node = path.getValue(); | ||
|
||
if (Array.isArray(node)) return concat(path.map(print)); | ||
if (node.type === 'Fragment') return concat(path.map(print, 'children')); | ||
|
||
return node; | ||
}, | ||
embed(path, print, textToDoc, options) { | ||
const node = path.getValue(); | ||
if (node.type === 'Script' && node.context === 'setup') { | ||
return concat(['---', hardline, textToDoc(node.content, { ...options, parser: 'typescript' }), '---', hardline, hardline]); | ||
} | ||
if (node.type === 'Fragment' && node.isRoot) { | ||
const expressions = findExpressionsInAST(node); | ||
if (expressions.length > 0) { | ||
const parts = [].concat(...expressions.map((expr, i, all) => { | ||
const prev = all[i - 1]; | ||
const start = node.text.slice((prev?.end ?? node.start) - node.start, expr.start - node.start); | ||
const exprText = formatExpression(expr, node.text.slice(expr.start - node.start + 1, expr.end - node.start - 1), options); | ||
|
||
if (i === all.length - 1) { | ||
const end = node.text.slice(expr.end - node.start); | ||
return [start, exprText, end] | ||
} | ||
|
||
return [start, exprText] | ||
})); | ||
const html = parts.join('\n'); | ||
const doc = textToDoc(html, { parser: 'html' }); | ||
walkDoc(doc); | ||
return doc; | ||
} | ||
return textToDoc(node.text, { parser: 'html' }); | ||
} | ||
|
||
return null; | ||
}, | ||
}, | ||
}; |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Glad you made this change. Was thinking the same