prettier-plugin-svelte
Advanced tools
Comparing version 0.4.2 to 0.5.0
{ | ||
"name": "prettier-plugin-svelte", | ||
"version": "0.4.2", | ||
"version": "0.5.0", | ||
"description": "Svelte plugin for prettier", | ||
@@ -5,0 +5,0 @@ "main": "plugin.js", |
235
plugin.js
@@ -53,3 +53,3 @@ 'use strict'; | ||
const { concat, join, line, group, indent, softline, hardline } = prettier.doc.builders; | ||
const { concat, join, line, group, indent, dedent, softline, hardline, fill, breakParent, } = prettier.doc.builders; | ||
function print(path, options, print) { | ||
@@ -77,3 +77,6 @@ const n = path.getValue(); | ||
} | ||
parts.push(path.call(print, 'html')); | ||
const htmlDoc = path.call(print, 'html'); | ||
if (htmlDoc) { | ||
parts.push(htmlDoc); | ||
} | ||
return group(join(hardline, parts)); | ||
@@ -86,27 +89,26 @@ } | ||
const children = node.children; | ||
return concat([ | ||
printChildren(path, print, { | ||
skipFirst: true, | ||
filter: (node, i) => { | ||
if (i === 0 && node.type === 'Text' && node.data.trim() === '') { | ||
return false; | ||
} | ||
let include = false; | ||
for (let j = i; j < children.length; j++) { | ||
const child = children[j]; | ||
if (!(child.type === 'Text' && child.data.trim() === '')) { | ||
include = true; | ||
break; | ||
} | ||
} | ||
return include; | ||
}, | ||
}), | ||
hardline, | ||
]); | ||
if (children.length === 0 || children.every(isEmptyNode)) { | ||
return ''; | ||
} | ||
return concat([printChildren(path, print, false), hardline]); | ||
case 'Text': | ||
return join(hardline, node.data | ||
.replace(/\n(?!\n)/g, '') | ||
.replace(/[ \t]+/g, ' ') | ||
.split(/\n/g)); | ||
if (isEmptyNode(node)) { | ||
return Object.assign({}, line, { | ||
/** | ||
* A text node is considered lonely if it is in a group without other inline | ||
* elements, such as the line breaks between otherwise consecutive HTML tags. | ||
* Text nodes that are both empty and lonely are discarded unless they have at | ||
* least one empty line (i.e. at least two linebreak sequences). This is to | ||
* allow for flexible grouping of HTML tags in a particular indentation level, | ||
* and is similar to how vanilla HTML is handled in Prettier core. | ||
*/ | ||
keepIfLonely: /\n\r?\s*\n\r?/.test(node.data) }); | ||
} | ||
/** | ||
* 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 convienient instance of `line`. | ||
*/ | ||
return fill(join(line, node.data.split(/[\t\n\f\r ]+/)).parts); | ||
case 'Element': | ||
@@ -118,2 +120,3 @@ case 'InlineComponent': | ||
case 'Title': | ||
const notEmpty = node.children.some(child => !isEmptyNode(child)); | ||
return group(concat([ | ||
@@ -133,5 +136,5 @@ '<', | ||
]))), | ||
node.children.length ? '>' : ' />', | ||
indent(printChildren(path, print)), | ||
node.children.length ? concat([softline, '</', node.name, '>']) : '', | ||
notEmpty ? '>' : ' />', | ||
notEmpty ? indent(printChildren(path, print)) : '', | ||
notEmpty ? concat(['</', node.name, '>']) : '', | ||
])); | ||
@@ -174,3 +177,2 @@ case 'Identifier': | ||
indent(printChildren(path, print)), | ||
line, | ||
]; | ||
@@ -192,3 +194,2 @@ if (node.else) { | ||
indent(path.map(ifPath => printChildren(ifPath, print), 'children')[0]), | ||
line, | ||
]; | ||
@@ -200,3 +201,3 @@ if (ifNode.else) { | ||
} | ||
return group(concat(['{:else}', indent(printChildren(path, print)), line])); | ||
return group(concat(['{:else}', indent(printChildren(path, print))])); | ||
} | ||
@@ -216,3 +217,3 @@ case 'EachBlock': { | ||
} | ||
def.push('}', indent(printChildren(path, print)), line); | ||
def.push('}', indent(printChildren(path, print))); | ||
if (node.else) { | ||
@@ -228,9 +229,6 @@ def.push(path.call(print, 'else')); | ||
indent(path.call(print, 'pending')), | ||
line, | ||
group(concat(['{:then', node.value ? ' ' + node.value : '', '}'])), | ||
indent(path.call(print, 'then')), | ||
line, | ||
group(concat(['{:catch', node.error ? ' ' + node.error : '', '}'])), | ||
indent(path.call(print, 'catch')), | ||
line, | ||
'{/await}', | ||
@@ -248,2 +246,5 @@ ])); | ||
node.name, | ||
node.modifiers && node.modifiers.length | ||
? concat(['|', join('|', node.modifiers)]) | ||
: '', | ||
node.expression | ||
@@ -262,2 +263,22 @@ ? concat(['=', open, printJS(path, print, 'expression'), close]) | ||
]); | ||
case 'Class': | ||
return concat([ | ||
line, | ||
'class:', | ||
node.name, | ||
node.expression.type === 'Identifier' && node.expression.name === node.name | ||
? '' | ||
: concat(['=', '{', printJS(path, print, 'expression'), '}']), | ||
]); | ||
case 'Let': | ||
return concat([ | ||
line, | ||
'let:', | ||
node.name, | ||
// shorthand let directives have `null` expressions | ||
!node.expression || | ||
(node.expression.type === 'Identifier' && node.expression.name === node.name) | ||
? '' | ||
: concat(['=', '{', printJS(path, print, 'expression'), '}']), | ||
]); | ||
case 'DebugTag': | ||
@@ -312,44 +333,90 @@ return concat([ | ||
} | ||
function printChildren(path, print, { skipFirst = false, filter = (node, i) => true } = {}) { | ||
const children = []; | ||
let i = 0; | ||
let isFirst = true; | ||
const childNodes = path.getValue().children; | ||
function isEmptyGroup(group) { | ||
if (group.length === 0) { | ||
return true; | ||
} | ||
if (group.length > 1) { | ||
return false; | ||
} | ||
const lonelyDoc = group[0]; | ||
if (typeof lonelyDoc === 'string' || lonelyDoc.type !== 'line') { | ||
return false; | ||
} | ||
return !lonelyDoc.keepIfLonely; | ||
} | ||
/** | ||
* Due to how `String.prototype.split` works, `TextNode`s with leading whitespace will be printed | ||
* to a `Fill` that has two additional parts at the begnning: an empty string (`''`) and a `line`. | ||
* If such a `Fill` doc is present at the beginning of an inline node group, those additional parts | ||
* need to be removed to prevent additional whitespace at the beginning of the parent's inner | ||
* content or after a sibling block node (i.e. HTML tags). | ||
*/ | ||
function trimLeft(group) { | ||
if (group.length === 0) { | ||
return; | ||
} | ||
const first = group[0]; | ||
if (typeof first === 'string' || first.type !== 'fill') { | ||
return; | ||
} | ||
// find the index of the first part that isn't an empty string or a line | ||
const trimIndex = first.parts.findIndex(part => typeof part === 'string' ? part !== '' : part.type !== 'line'); | ||
first.parts.splice(0, trimIndex); | ||
} | ||
/** | ||
* Due to how `String.prototype.split` works, `TextNode`s with trailing whitespace will be printed | ||
* to a `Fill` that has two additional parts at the end: a `line` and an empty string (`''`). If | ||
* such a `Fill` doc is present at the beginning of an inline node group, those additional parts | ||
* need to be removed to prevent additional whitespace at the end of the parent's inner content or | ||
* before a sibling block node (i.e. HTML tags). | ||
*/ | ||
function trimRight(group) { | ||
if (group.length === 0) { | ||
return; | ||
} | ||
const last = group[group.length - 1]; | ||
if (typeof last === 'string' || last.type !== 'fill') { | ||
return; | ||
} | ||
last.parts.reverse(); | ||
// find the index of the first part that isn't an empty string or a line | ||
const trimIndex = last.parts.findIndex(part => typeof part === 'string' ? part !== '' : part.type !== 'line'); | ||
last.parts.splice(0, trimIndex); | ||
last.parts.reverse(); | ||
} | ||
function printChildren(path, print, surroundingLines = true) { | ||
const childDocs = []; | ||
let currentGroup = []; | ||
/** | ||
* Sequences of inline nodes (currently, `TextNode`s and `MustacheTag`s) are collected into | ||
* groups and printed as a single `Fill` doc so that linebreaks as a result of sibling block | ||
* nodes (currently, all HTML elements) don't cause those inline sequences to break | ||
* prematurely. This is particularly important for whitespace sensitivity, as it is often | ||
* desired to have text directly wrapping a mustache tag without additional whitespace. | ||
*/ | ||
function flush() { | ||
if (!isEmptyGroup(currentGroup)) { | ||
trimLeft(currentGroup); | ||
trimRight(currentGroup); | ||
childDocs.push(fill(currentGroup)); | ||
} | ||
currentGroup = []; | ||
} | ||
path.each(childPath => { | ||
const child = childPath.getValue(); | ||
const index = i; | ||
i++; | ||
if (!filter(child, index)) { | ||
return; | ||
const childNode = childPath.getValue(); | ||
const childDoc = childPath.call(print); | ||
if (isInlineNode(childNode)) { | ||
currentGroup.push(childDoc); | ||
} | ||
if (!(isFirst && skipFirst)) { | ||
if (isInlineNode(child)) { | ||
if (!isEmptyNode(child)) { | ||
let lineType = softline; | ||
if (child.type === 'Text') { | ||
if (/^\s+/.test(child.data)) { | ||
// Remove leading spaces | ||
child.data = trimStart(child.data); | ||
if (!isFirst) { | ||
lineType = line; | ||
} | ||
} | ||
} | ||
children.push(lineType); | ||
} | ||
} | ||
else { | ||
children.push(hardline); | ||
} | ||
else { | ||
flush(); | ||
childDocs.push(fill([breakParent, childDoc])); | ||
} | ||
if (child.type === 'Text') { | ||
if (isLastNode(childNodes, filter, index)) { | ||
// Remove trailing spaces | ||
child.data = trimEnd(child.data); | ||
} | ||
} | ||
children.push(childPath.call(print)); | ||
isFirst = false; | ||
}, 'children'); | ||
return concat(children); | ||
flush(); | ||
return concat([ | ||
surroundingLines ? softline : '', | ||
join(hardline, childDocs), | ||
surroundingLines ? dedent(softline) : '', | ||
]); | ||
} | ||
@@ -370,16 +437,2 @@ function printJS(path, print, name) { | ||
} | ||
function isLastNode(nodes, filter, index) { | ||
for (let i = index + 1; i < nodes.length; i++) { | ||
if (filter(nodes[i], i)) { | ||
return false; | ||
} | ||
} | ||
return true; | ||
} | ||
function trimStart(text) { | ||
return text.replace(/^\s+/, ''); | ||
} | ||
function trimEnd(text) { | ||
return text.replace(/\s+$/, ''); | ||
} | ||
@@ -397,3 +450,3 @@ const { builders: { concat: concat$1, hardline: hardline$1, group: group$1, indent: indent$1 }, utils: { removeLines }, } = prettier.doc; | ||
case 'Script': | ||
return embedTag(path, print, textToDoc, node, 'babel'); | ||
return embedTag(path, print, textToDoc, node, 'typescript'); | ||
case 'Style': | ||
@@ -450,3 +503,3 @@ return embedTag(path, print, textToDoc, node, 'css'); | ||
const encodedContent = contentAttribute.value[0].data; | ||
content = Buffer.from(encodedContent, 'base64').toString('ascii'); | ||
content = Buffer.from(encodedContent, 'base64').toString('utf-8'); | ||
} | ||
@@ -489,3 +542,3 @@ node.attributes = node.attributes.filter(n => n !== contentAttribute); | ||
parsers: ['svelte'], | ||
extensions: ['.svelte', '.html'], | ||
extensions: ['.svelte'], | ||
}, | ||
@@ -492,0 +545,0 @@ ]; |
Sorry, the diff of this file is not supported yet
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
64981
546