prettier-plugin-astro
Advanced tools
Comparing version 0.0.8 to 0.0.9
# prettier-plugin-astro | ||
## 0.0.9 | ||
### Patch Changes | ||
- 8820423: Fix test macro 'PrettierMarkdown' | ||
- a30ddcd: Bump @astrojs/parser from 0.15.0 to 0.20.2 | ||
- 695fc07: Add formatting for <Markdown> components | ||
- 1bf9f7c: Support arbitrary attributes in style tags | ||
- 395b3bd: Add basic support for indented sass | ||
- 672afef: Add new line at the end of the file | ||
- 20a298e: Add support for .sass formatting | ||
- 915a6e2: Add support for prettier-ignore comments | ||
## 0.0.8 | ||
@@ -4,0 +17,0 @@ |
{ | ||
"name": "prettier-plugin-astro", | ||
"version": "0.0.8", | ||
"version": "0.0.9", | ||
"description": "A Prettier Plugin for formatting Astro files", | ||
"main": "src/index.js", | ||
"files": [ | ||
"src/**" | ||
], | ||
"type": "commonjs", | ||
"engines": { | ||
"node": "^12.20.0 || ^14.13.1 || >=16.0.0", | ||
"npm": ">=6.14.0" | ||
}, | ||
"homepage": "https://github.com/snowpackjs/prettier-plugin-astro/", | ||
@@ -30,13 +37,15 @@ "issues": { | ||
"dependencies": { | ||
"@astrojs/parser": "^0.15.0", | ||
"prettier": "^2.4.1" | ||
"@astrojs/parser": "^0.20.2", | ||
"prettier": "^2.4.1", | ||
"sass-formatter": "^0.7.2" | ||
}, | ||
"devDependencies": { | ||
"@changesets/cli": "^2.16.0", | ||
"@types/prettier": "^2.2.1", | ||
"@types/prettier": "^2.4.1", | ||
"ava": "^4.0.0-alpha.2", | ||
"eslint": "^7.29.0", | ||
"eslint": "^8.0.0", | ||
"eslint-plugin-ava": "^13.0.0", | ||
"eslint-plugin-node": "^11.1.0", | ||
"eslint-plugin-prettier-doc": "^1.0.1" | ||
} | ||
} |
@@ -25,2 +25,3 @@ # Beta Prettier Plugin for [Astro](https://github.com/snowpackjs/astro) -- 🚧 Caution! May break your project 🚧 | ||
1. Run `yarn changeset` to add your changes to the changelog on version bump. | ||
Most changes to the plugin should be `patch` changes while we're before `1.0.0`. | ||
@@ -27,0 +28,0 @@ ## Resources for contributing |
const { | ||
builders: { join, fill, line, literalline, hardline, softline, group, conditionalGroup, breakParent, indent, dedent }, | ||
utils: { removeLines }, | ||
builders: { breakParent, dedent, fill, group, hardline, indent, join, line, literalline, softline }, | ||
utils: { removeLines, stripTrailingHardline }, | ||
} = require('prettier/doc'); | ||
const { SassFormatter } = require('sass-formatter'); | ||
const { parseSortOrder } = require('./options'); | ||
const { | ||
attachCommentsHTML, | ||
canOmitSoftlineBeforeClosingTag, | ||
dedent: manualDedent, | ||
endsWithLinebreak, | ||
forceIntoExpression, | ||
formattableAttributes, | ||
getMarkdownName, | ||
getText, | ||
getUnencodedText, | ||
indent: manualIndent, | ||
isASTNode, | ||
isEmptyDoc, | ||
isEmptyTextNode, | ||
isPreTagContent, | ||
isInlineElement, | ||
isEmptyDoc, | ||
isTextNodeStartingWithWhitespace, | ||
isTextNodeStartingWithLinebreak, | ||
isTextNodeEndingWithWhitespace, | ||
trimTextNodeLeft, | ||
trimTextNodeRight, | ||
isLine, | ||
isLoneMustacheTag, | ||
isAttributeShorthand, | ||
isNodeWithChildren, | ||
isOrCanBeConvertedToShorthand, | ||
isPreTagContent, | ||
isTextNodeEndingWithWhitespace, | ||
isTextNodeStartingWithLinebreak, | ||
isTextNodeStartingWithWhitespace, | ||
printRaw, | ||
replaceEndOfLineWith, | ||
selfClosingTags, | ||
formattableAttributes, | ||
getUnencodedText, | ||
replaceEndOfLineWith, | ||
forceIntoExpression, | ||
shouldHugEnd, | ||
shouldHugStart, | ||
shouldHugEnd, | ||
canOmitSoftlineBeforeClosingTag, | ||
startsWithLinebreak, | ||
endsWithLinebreak, | ||
printRaw, | ||
trim, | ||
isLine, | ||
trimChildren, | ||
flatten, | ||
getText, | ||
trimTextNodeLeft, | ||
trimTextNodeRight, | ||
} = require('./utils'); | ||
const supportedStyleLangValues = ['css', 'scss']; | ||
/** | ||
@@ -48,26 +50,38 @@ * | ||
*/ | ||
const printTopLevelParts = (node, path, opts, print) => { | ||
const parts = { | ||
frontmatter: [], | ||
markup: [], | ||
styles: [], | ||
}; | ||
function printTopLevelParts(node, path, opts, print) { | ||
let docs = []; | ||
const normalize = (doc) => [stripTrailingHardline(doc), hardline]; | ||
// frontmatter always comes first | ||
if (node.module) { | ||
parts.frontmatter.push(path.call(print, 'module')); | ||
const subDoc = normalize(path.call(print, 'module')); | ||
docs.push(subDoc); | ||
} | ||
if (node.css) { | ||
parts.styles.push(path.call(print, 'css')); | ||
// markup and styles follow, whichever the user prefers (default: markup, styles) | ||
for (const section of parseSortOrder(opts.astroSortOrder)) { | ||
switch (section) { | ||
case 'markup': { | ||
const subDoc = path.call(print, 'html'); | ||
if (!isEmptyDoc(subDoc)) docs.push(normalize(subDoc)); | ||
break; | ||
} | ||
case 'styles': { | ||
const subDoc = path.call(print, 'css'); | ||
if (!isEmptyDoc(subDoc)) docs.push(normalize(subDoc)); | ||
break; | ||
} | ||
} | ||
} | ||
if (node.html) { | ||
parts.markup.push(path.call(print, 'html')); | ||
} | ||
// 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(); | ||
const docs = flatten([parts.frontmatter, ...parseSortOrder(opts.astroSortOrder).map((p) => parts[p])]).filter((doc) => '' !== doc); | ||
return group([join(hardline, docs)]); | ||
}; | ||
return join(softline, docs); | ||
} | ||
const printAttributeNodeValue = (path, print, quotes, node) => { | ||
function printAttributeNodeValue(path, print, quotes, node) { | ||
const valueDocs = path.map((childPath) => childPath.call(print), 'value'); | ||
@@ -80,3 +94,3 @@ | ||
} | ||
}; | ||
} | ||
@@ -90,6 +104,14 @@ function printJS(path, print, name, { forceSingleQuote, forceSingleLine }) { | ||
/** @type {import('prettier').Printer['printComment']} */ | ||
function printComment(commentPath) { | ||
// note(drew): this isn’t doing anything currently, but Prettier requires it anyway | ||
return commentPath; | ||
} | ||
/** @type {import('prettier').Printer['print']} */ | ||
const print = (path, opts, print) => { | ||
function print(path, opts, print) { | ||
const node = path.getValue(); | ||
const isMarkdownSubDoc = opts.parentParser === 'markdown'; // is this a code block within .md? | ||
// 1. handle special node types | ||
switch (true) { | ||
@@ -106,2 +128,8 @@ case !node: | ||
// 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); | ||
} | ||
// 3. handle printing | ||
switch (node.type) { | ||
@@ -113,2 +141,10 @@ case 'Fragment': { | ||
} | ||
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 | ||
@@ -119,11 +155,5 @@ // and we can skip a bunch of work. Hooray! | ||
node.__isRawHTML = true; | ||
node.content = text; | ||
return path.call(print); | ||
return path.map(print, 'children'); | ||
} | ||
const children = node.children; | ||
if (children.length === 0 || children.every(isEmptyTextNode)) { | ||
return ''; | ||
} | ||
if (!isPreTagContent(path)) { | ||
@@ -148,29 +178,6 @@ trimChildren(node.children, path); | ||
} | ||
case 'Text': | ||
if (!isPreTagContent(path)) { | ||
if (isEmptyTextNode(node)) { | ||
const hasWhiteSpace = getUnencodedText(node).trim().length < getUnencodedText(node).length; | ||
const hasOneOrMoreNewlines = /\n/.test(getUnencodedText(node)); | ||
const hasTwoOrMoreNewlines = /\n\r?\s*\n\r?/.test(getUnencodedText(node)); | ||
if (hasTwoOrMoreNewlines) { | ||
return [hardline, hardline]; | ||
} | ||
if (hasOneOrMoreNewlines) { | ||
return hardline; | ||
} | ||
if (hasWhiteSpace) { | ||
return line; | ||
} | ||
return ''; | ||
} | ||
case 'Text': { | ||
const rawText = getUnencodedText(node); | ||
/** | ||
* For non-empty text nodes each sequence of non-whitespace characters (effectively, | ||
* each "word") is joined by a single `line`, which will be rendered as a single space | ||
* until this node's current line is out of room, at which `fill` will break at the | ||
* most convenient instance of `line`. | ||
*/ | ||
return fill(splitTextToDocs(node)); | ||
} else { | ||
const rawText = getUnencodedText(node); | ||
if (isPreTagContent(path)) { | ||
if (path.getParentNode().type === 'Attribute') { | ||
@@ -183,2 +190,28 @@ // Direct child of attribute value -> add literallines at end of lines | ||
} | ||
if (isEmptyTextNode(node)) { | ||
const hasWhiteSpace = rawText.trim().length < getUnencodedText(node).length; | ||
const hasOneOrMoreNewlines = /\n/.test(getUnencodedText(node)); | ||
const hasTwoOrMoreNewlines = /\n\r?\s*\n\r?/.test(getUnencodedText(node)); | ||
if (hasTwoOrMoreNewlines) { | ||
return [hardline, hardline]; | ||
} | ||
if (hasOneOrMoreNewlines) { | ||
return hardline; | ||
} | ||
if (hasWhiteSpace) { | ||
return line; | ||
} | ||
return ''; | ||
} | ||
/** | ||
* For non-empty text nodes each sequence of non-whitespace characters (effectively, | ||
* each "word") is joined by a single `line`, which will be rendered as a single space | ||
* until this node's current line is out of room, at which `fill` will break at the | ||
* most convenient instance of `line`. | ||
*/ | ||
return fill(splitTextToDocs(node)); | ||
} | ||
case 'Element': | ||
@@ -193,3 +226,3 @@ case 'InlineComponent': | ||
const isSelfClosingTag = isEmpty && (node.type !== 'Element' || selfClosingTags.indexOf(node.name) !== -1); | ||
const attributes = path.map((childPath) => childPath.call(print), 'attributes'); | ||
const attributes = path.map(print, 'attributes'); | ||
@@ -201,3 +234,3 @@ if (isSelfClosingTag) { | ||
if (node.name.toLowerCase() === '!doctype') { | ||
attributesWithLowercaseHTML = attributes.map((attribute) => { | ||
const attributesWithLowercaseHTML = attributes.map((attribute) => { | ||
if (attribute[0].type === 'line' && attribute[1].toLowerCase() === 'html') { | ||
@@ -220,7 +253,13 @@ attribute[1] = attribute[1].toLowerCase(); | ||
const hugStart = shouldHugStart(node, opts); | ||
const hugEnd = shouldHugEnd(node, opts); | ||
// No hugging of content means it's either a block element and/or there's whitespace at the start/end | ||
let noHugSeparatorStart = softline; | ||
let noHugSeparatorEnd = softline; | ||
let hugStart = shouldHugStart(node, opts); | ||
let hugEnd = shouldHugEnd(node, opts); | ||
let body; | ||
const isMarkdownComponent = | ||
node.type === 'InlineComponent' && opts.__astro && opts.__astro.markdownName && opts.__astro.markdownName.has(node.name) && isNodeWithChildren(node); | ||
if (isEmpty) { | ||
@@ -231,2 +270,11 @@ body = | ||
: () => (opts.jsxBracketNewLine ? '' : softline); | ||
} else if (isMarkdownComponent) { | ||
// collapse children into raw Markdown text | ||
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)) { | ||
@@ -248,8 +296,8 @@ body = () => printRaw(node, opts.originalText); | ||
// No hugging of content means it's either a block element and/or there's whitespace at the start/end | ||
let noHugSeparatorStart = softline; | ||
let noHugSeparatorEnd = softline; | ||
if (isPreTagContent(path)) { | ||
noHugSeparatorStart = ''; | ||
noHugSeparatorEnd = ''; | ||
} else if (isMarkdownComponent) { | ||
noHugSeparatorStart = softline; | ||
noHugSeparatorEnd = softline; | ||
} else { | ||
@@ -338,9 +386,12 @@ let didSetEndSeparator = false; | ||
case 'Comment': | ||
return [`<!--`, getUnencodedText(node), `-->`]; | ||
return ['<!--', getUnencodedText(node), '-->']; | ||
case 'CodeSpan': | ||
return getUnencodedText(node); | ||
case 'CodeFence': | ||
case 'CodeFence': { | ||
console.debug(node); | ||
return getUnencodedText(node); | ||
// We should use `node.metadata` to select a parser to embed with... something like return [node.metadata, hardline textToDoc(node.getMetadataLanguage()), hardline, `\`\`\``]; | ||
// const lang = node.metadata.slice(3); | ||
return [node.metadata, hardline, /** somehow call textToDoc(lang), */ node.data, hardline, '```', hardline]; | ||
// We should use `node.metadata` to select a parser to embed with... something like return [node.metadata, hardline textToDoc(node.getMetadataLanguage()), hardline, `\`\`\``]; | ||
} | ||
default: { | ||
@@ -350,3 +401,3 @@ throw new Error(`Unhandled node type "${node.type}"!`); | ||
} | ||
}; | ||
} | ||
@@ -389,3 +440,5 @@ /** | ||
/** @type {import('prettier').Printer['embed']} */ | ||
const embed = (path, print, textToDoc, opts) => { | ||
function embed(path, print, textToDoc, opts) { | ||
if (!opts.__astro) opts.__astro = {}; | ||
const node = path.getValue(); | ||
@@ -395,2 +448,11 @@ | ||
if (node.__isRawMarkdown) { | ||
const docs = textToDoc(getUnencodedText(node), { ...opts, parser: 'markdown' }); | ||
return stripTrailingHardline(docs); | ||
} | ||
if (node.type === 'Script' && !opts.__astro.markdownName) { | ||
opts.__astro.markdownName = getMarkdownName(node.content); | ||
} | ||
if (node.isJS) { | ||
@@ -420,3 +482,3 @@ try { | ||
if (parent.type === 'Element' && parent.name === 'script') { | ||
const [formatttedScript, _] = textToDoc(node.data, { ...opts, parser: 'typescript' }); | ||
const [formatttedScript, ,] = textToDoc(node.data, { ...opts, parser: 'typescript' }); | ||
return group(formatttedScript); | ||
@@ -427,41 +489,68 @@ } | ||
if (node.type === 'Style') { | ||
let styleLang = ''; | ||
let parserLang = ''; | ||
const supportedStyleLangValues = ['css', 'scss', 'sass']; | ||
let parserLang = 'css'; | ||
if ('attributes' in node) { | ||
const langAttribute = node.attributes.filter((x) => x.name === 'lang'); | ||
if (langAttribute.length === 0) styleLang = 'css'; | ||
else { | ||
styleLang = langAttribute[0].value[0].raw.toLowerCase(); | ||
if (langAttribute.length) { | ||
const styleLang = langAttribute[0].value[0].raw.toLowerCase(); | ||
if (supportedStyleLangValues.includes(styleLang)) parserLang = styleLang; | ||
} | ||
} | ||
if (styleLang in supportedStyleLangValues) parserLang = styleLang; | ||
// TODO(obnoxiousnerd): Provide error handling in case of unrecognized | ||
// styles language. | ||
else parserLang = 'css'; | ||
// the css parser appends an extra indented hardline, which we want outside of the `indent()`, | ||
// so we remove the last element of the array | ||
const [formatttedStyles, _] = textToDoc(node.content.styles, { ...opts, parser: parserLang }); | ||
return group([styleLang !== 'css' ? `<style lang="${styleLang}">` : '<style>', indent([hardline, formatttedStyles]), hardline, '</style>', hardline]); | ||
} | ||
switch (parserLang) { | ||
case 'css': | ||
case 'scss': { | ||
// the css parser appends an extra indented hardline, which we want outside of the `indent()`, | ||
// so we remove the last element of the array | ||
const [formattedStyles, ,] = textToDoc(node.content.styles, { ...opts, parser: parserLang }); | ||
if (node.__isRawHTML) { | ||
return textToDoc(node.content, { ...opts, parser: 'html' }); | ||
const attributes = path.map(print, 'attributes'); | ||
const openingTag = group(['<style', indent(group(attributes)), softline, '>']); | ||
return [openingTag, indent([hardline, formattedStyles]), hardline, '</style>']; | ||
} | ||
case 'sass': { | ||
const sassOptions = { | ||
tabSize: opts.tabWidth, | ||
insertSpaces: !opts.useTabs, | ||
lineEnding: opts.endOfLine.toUpperCase(), | ||
}; | ||
// dedent the .sass, otherwise SassFormatter gets indentation wrong | ||
const { result: raw, tabSize } = manualDedent(node.content.styles); | ||
// format + re-indent | ||
let formattedSass = SassFormatter.Format(raw, sassOptions).trim(); | ||
const indentChar = new Array(Math.max(tabSize, 2) + 1).join(opts.useTabs ? '\t' : ' '); | ||
formattedSass = manualIndent(formattedSass, indentChar); | ||
formattedSass = join(hardline, formattedSass.split('\n')); | ||
const attributes = path.map(print, 'attributes'); | ||
const openingTag = group(['<style', indent(group(attributes)), softline, '>']); | ||
return [openingTag, hardline, formattedSass, hardline, '</style>']; | ||
} | ||
} | ||
} | ||
return null; | ||
}; | ||
} | ||
/** @type {import('prettier').Printer['hasPrettierIgnore']} */ | ||
const hasPrettierIgnore = (path) => { | ||
function hasPrettierIgnore(path) { | ||
const node = path.getNode(); | ||
const isSimpleIgnore = (comment) => | ||
comment.value.includes('prettier-ignore') && !comment.value.includes('prettier-ignore-start') && !comment.value.includes('prettier-ignore-end'); | ||
return node && node.comments && node.comments.length > 0 && node.comments.some(isSimpleIgnore); | ||
}; | ||
if (!node || !Array.isArray(node.comments)) return false; | ||
const hasIgnore = node.comments.some( | ||
(comment) => comment.data.includes('prettier-ignore') && !comment.data.includes('prettier-ignore-start') && !comment.data.includes('prettier-ignore-end') | ||
); | ||
return hasIgnore; | ||
} | ||
/** @type {import('prettier').Printer} */ | ||
const printer = { | ||
print, | ||
printComment, | ||
embed, | ||
@@ -468,0 +557,0 @@ hasPrettierIgnore, |
160
src/utils.js
@@ -1,2 +0,2 @@ | ||
const { doc } = require('prettier'); | ||
const { doc, util } = require('prettier'); | ||
@@ -70,5 +70,4 @@ // @see http://xahlee.info/js/html5_non-closing_tag.html | ||
const isPreTagContent = (path) => { | ||
const stack = path.stack; | ||
return stack.some((node) => (node.type === 'Element' && node.name.toLowerCase() === 'pre') || (node.type === 'Attribute' && !formattableAttributes.includes(node.name))); | ||
if (!path || !path.stack || !Array.isArray(path.stack)) return false; | ||
return path.stack.some((node) => (node.type === 'Element' && node.name.toLowerCase() === 'pre') || (node.type === 'Attribute' && !formattableAttributes.includes(node.name))); | ||
}; | ||
@@ -175,3 +174,3 @@ | ||
function isNodeWithChildren(node) { | ||
return !!node.children; | ||
return node && Array.isArray(node.children); | ||
} | ||
@@ -399,3 +398,3 @@ | ||
for (var prop in x) { | ||
if (y.hasOwnProperty(prop)) { | ||
if (Object.prototype.hasOwnProperty.call(y, prop)) { | ||
if (!deepEqual(x[prop], y[prop])) return false; | ||
@@ -529,34 +528,139 @@ } else { | ||
const isObjEmpty = (obj) => { | ||
for (let i in obj) return false; | ||
return true; | ||
}; | ||
/** Shallowly attach comments to children */ | ||
function attachCommentsHTML(node) { | ||
if (!isNodeWithChildren(node) || !node.children.some(({ type }) => type === 'Comment')) return; | ||
const nodesToRemove = []; | ||
// note: the .length - 1 is because we don’t need to read the last node | ||
for (let n = 0; n < node.children.length - 1; n++) { | ||
if (!node.children[n]) continue; | ||
// attach comment to the next non-whitespace node | ||
if (node.children[n].type === 'Comment') { | ||
let next = n + 1; | ||
while (isEmptyTextNode(node.children[next])) { | ||
nodesToRemove.push(next); // if arbitrary whitespace between comment and node, remove | ||
next++; // skip to the next non-whitespace node | ||
} | ||
util.addLeadingComment(node.children[next], node.children[n]); | ||
} | ||
} | ||
// remove arbitrary whitespace nodes | ||
nodesToRemove.reverse(); // start at back so we aren’t changing indices | ||
nodesToRemove.forEach((index) => { | ||
node.children.splice(index, 1); | ||
}); | ||
} | ||
/** dedent string & return tabSize (the last part is what we need) */ | ||
function dedent(input) { | ||
let minTabSize = Infinity; | ||
let result = input; | ||
// 1. normalize | ||
result = result.replace(/\r\n/g, '\n'); | ||
// 2. count tabSize | ||
let char = ''; | ||
for (const line of result.split('\n')) { | ||
if (!line) continue; | ||
// if any line begins with a non-whitespace char, minTabSize is 0 | ||
if (line[0] && /^[^\s]/.test(line[0])) { | ||
minTabSize = 0; | ||
break; | ||
} | ||
const match = line.match(/^(\s+)\S+/); // \S ensures we don’t count lines of pure whitespace | ||
if (match) { | ||
if (match[1] && !char) char = match[1][0]; | ||
if (match[1].length < minTabSize) minTabSize = match[1].length; | ||
} | ||
} | ||
// 3. reformat string | ||
if (minTabSize > 0 && Number.isFinite(minTabSize)) { | ||
result = result.replace(new RegExp(`^${new Array(minTabSize + 1).join(char)}`, 'gm'), ''); | ||
} | ||
return { | ||
tabSize: minTabSize === Infinity ? 0 : minTabSize, | ||
char, | ||
result, | ||
}; | ||
} | ||
/** re-indent string by chars */ | ||
function indent(input, char = ' ') { | ||
return input.replace(/^(.)/gm, `${char}$1`); | ||
} | ||
/** scan code for Markdown name(s) */ | ||
function getMarkdownName(script) { | ||
// default import: could be named anything | ||
let defaultMatch; | ||
while ((defaultMatch = /import\s+([^\s]+)\s+from\s+['|"|`]astro\/components\/Markdown\.astro/g.exec(script))) { | ||
if (defaultMatch[1]) return new Set([defaultMatch[1].trim()]); | ||
} | ||
// named component: must have "Markdown" in specifier, but can be renamed via "as" | ||
let namedMatch; | ||
while ((namedMatch = /import\s+\{\s*([^}]+)\}\s+from\s+['|"|`]astro\/components/g.exec(script))) { | ||
if (namedMatch[1] && !namedMatch[1].includes('Markdown')) continue; | ||
// if "Markdown" was imported, find out whether or not it was renamed | ||
const rawImports = namedMatch[1].trim().replace(/^\{/, '').replace(/\}$/, '').trim(); | ||
let importName = 'Markdown'; | ||
for (const spec of rawImports.split(',')) { | ||
const [original, renamed] = spec.split(' as ').map((s) => s.trim()); | ||
if (original !== 'Markdown') continue; | ||
importName = renamed || original; | ||
break; | ||
} | ||
return new Set([importName]); | ||
} | ||
return new Set(['Markdown']); | ||
} | ||
module.exports = { | ||
attachCommentsHTML, | ||
canOmitSoftlineBeforeClosingTag, | ||
dedent, | ||
endsWithLinebreak, | ||
flatten, | ||
forceIntoExpression, | ||
formattableAttributes, | ||
getMarkdownName, | ||
getText, | ||
getUnencodedText, | ||
indent, | ||
isASTNode, | ||
isAttributeShorthand, | ||
isBlockElement, | ||
isEmptyDoc, | ||
isEmptyTextNode, | ||
isPreTagContent, | ||
isInlineElement, | ||
isLine, | ||
isLoneMustacheTag, | ||
isAttributeShorthand, | ||
isNodeWithChildren, | ||
isObjEmpty, | ||
isOrCanBeConvertedToShorthand, | ||
isPreTagContent, | ||
isTextNodeEndingWithLinebreak, | ||
isTextNodeEndingWithWhitespace, | ||
isTextNodeStartingWithLinebreak, | ||
isTextNodeStartingWithWhitespace, | ||
printRaw, | ||
replaceEndOfLineWith, | ||
getUnencodedText, | ||
selfClosingTags, | ||
formattableAttributes, | ||
forceIntoExpression, | ||
isInlineElement, | ||
isBlockElement, | ||
isTextNodeStartingWithWhitespace, | ||
isTextNodeEndingWithWhitespace, | ||
isTextNodeStartingWithLinebreak, | ||
shouldHugEnd, | ||
shouldHugStart, | ||
startsWithLinebreak, | ||
endsWithLinebreak, | ||
isTextNodeEndingWithLinebreak, | ||
canOmitSoftlineBeforeClosingTag, | ||
shouldHugStart, | ||
shouldHugEnd, | ||
trim, | ||
trimChildren, | ||
trimTextNodeLeft, | ||
trimTextNodeRight, | ||
trimChildren, | ||
flatten, | ||
printRaw, | ||
getText, | ||
trim, | ||
isLine, | ||
isEmptyDoc, | ||
}; |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Filesystem access
Supply chain riskAccesses the file system, and could potentially read sensitive data.
Found 1 instance in 1 package
1102
33
0
43894
3
7
10
+ Addedsass-formatter@^0.7.2
+ Added@astrojs/parser@0.20.3(transitive)
+ Addedacorn@7.4.1(transitive)
+ Addedlocate-character@2.0.5(transitive)
+ Addedmagic-string@0.25.9(transitive)
+ Addeds.color@0.0.15(transitive)
+ Addedsass-formatter@0.7.9(transitive)
+ Addedsourcemap-codec@1.4.8(transitive)
+ Addedsuf-log@2.5.3(transitive)
- Removed@astrojs/parser@0.15.4(transitive)
Updated@astrojs/parser@^0.20.2