'use strict';
Object.defineProperty(exports, '__esModule', { value: true });
var prettier = require('prettier');
// @see http://xahlee.info/js/html5_non-closing_tag.html
const selfClosingTags = [
'area',
'base',
'br',
'col',
'embed',
'hr',
'img',
'input',
'link',
'meta',
'param',
'source',
'track',
'wbr',
];
// https://developer.mozilla.org/en-US/docs/Web/HTML/Block-level_elements#Elements
const blockElements = [
'address',
'article',
'aside',
'blockquote',
'details',
'dialog',
'dd',
'div',
'dl',
'dt',
'fieldset',
'figcaption',
'figure',
'footer',
'form',
'h1',
'h2',
'h3',
'h4',
'h5',
'h6',
'header',
'hgroup',
'hr',
'li',
'main',
'nav',
'ol',
'p',
'pre',
'section',
'table',
'ul',
];
/**
* HTML attributes that we may safely reformat (trim whitespace, add or remove newlines)
*/
const formattableAttributes = [
// None at the moment
// Prettier HTML does not format attributes at all
// and to be consistent we leave this array empty for now
];
function extractAttributes(html) {
const extractAttributesRegex = /<[a-z]+[\s\n]*([\s\S]*?)>/im;
const attributeRegex = /([^\s=]+)(?:=(?:(?:("|')([\s\S]*?)\2)|(?:(\S+?)(?:\s|>|$))))?/gim;
const [, attributesString] = html.match(extractAttributesRegex);
const attrs = [];
let match;
while ((match = attributeRegex.exec(attributesString))) {
const [all, name, quotes, valueQuoted, valueUnquoted] = match;
const value = valueQuoted || valueUnquoted;
const attrStart = match.index;
let valueNode;
if (!value) {
valueNode = true;
}
else {
let valueStart = attrStart + name.length;
if (quotes) {
valueStart += 2;
}
valueNode = [
{
type: 'Text',
data: value,
start: valueStart,
end: valueStart + value.length,
},
];
}
attrs.push({
type: 'Attribute',
name,
value: valueNode,
start: attrStart,
end: attrStart + all.length,
});
}
return attrs;
}
const snippedTagContentAttribute = '✂prettier:content✂';
function snipScriptAndStyleTagContent(source) {
let scriptMatchSpans = getMatchIndexes('script');
let styleMatchSpans = getMatchIndexes('style');
return snipTagContent(snipTagContent(source, 'script', '{}', styleMatchSpans), 'style', '', scriptMatchSpans);
function getMatchIndexes(tagName) {
const regex = getRegexp(tagName);
const indexes = [];
let match = null;
while ((match = regex.exec(source)) != null) {
if (source.slice(match.index, match.index + 4) !== '|<${tagName}([^]*?)>([^]*?)<\/${tagName}>`, 'g');
}
}
function hasSnippedContent(text) {
return text.includes(snippedTagContentAttribute);
}
function unsnipContent(text) {
const regex = /(<\w+.*?)\s*✂prettier:content✂="(.*?)">.*?(?=<\/)/gi;
return text.replace(regex, (_, start, encodedContent) => {
const content = Buffer.from(encodedContent, 'base64').toString('utf8');
return `${start}>${content}`;
});
}
function getText(node, options, unsnip = false) {
const leadingComments = node.leadingComments;
const text = options.originalText.slice(options.locStart(
// if there are comments before the node they are not included
// in the `start` of the node itself
(leadingComments && leadingComments[0]) || node), options.locEnd(node));
if (!unsnip || !hasSnippedContent(text)) {
return text;
}
return unsnipContent(text);
}
function makeChoice(choice) {
return { value: choice, description: choice };
}
const options = {
svelteSortOrder: {
since: '0.6.0',
category: 'Svelte',
type: 'choice',
default: 'options-scripts-markup-styles',
description: 'Sort order for scripts, markup, and styles',
choices: [
makeChoice('options-scripts-markup-styles'),
makeChoice('options-scripts-styles-markup'),
makeChoice('options-markup-styles-scripts'),
makeChoice('options-markup-scripts-styles'),
makeChoice('options-styles-markup-scripts'),
makeChoice('options-styles-scripts-markup'),
makeChoice('scripts-options-markup-styles'),
makeChoice('scripts-options-styles-markup'),
makeChoice('markup-options-styles-scripts'),
makeChoice('markup-options-scripts-styles'),
makeChoice('styles-options-markup-scripts'),
makeChoice('styles-options-scripts-markup'),
makeChoice('scripts-markup-options-styles'),
makeChoice('scripts-styles-options-markup'),
makeChoice('markup-styles-options-scripts'),
makeChoice('markup-scripts-options-styles'),
makeChoice('styles-markup-options-scripts'),
makeChoice('styles-scripts-options-markup'),
makeChoice('scripts-markup-styles-options'),
makeChoice('scripts-styles-markup-options'),
makeChoice('markup-styles-scripts-options'),
makeChoice('markup-scripts-styles-options'),
makeChoice('styles-markup-scripts-options'),
makeChoice('styles-scripts-markup-options'),
makeChoice('none'),
// Deprecated, keep in 2.x for backwards-compatibility. svelte:options will be moved to the top
makeChoice('scripts-markup-styles'),
makeChoice('scripts-styles-markup'),
makeChoice('markup-styles-scripts'),
makeChoice('markup-scripts-styles'),
makeChoice('styles-markup-scripts'),
makeChoice('styles-scripts-markup'),
],
},
svelteStrictMode: {
since: '0.7.0',
category: 'Svelte',
type: 'boolean',
default: false,
description: 'More strict HTML syntax: self-closed tags, quotes in attributes',
},
svelteBracketNewLine: {
since: '0.6.0',
category: 'Svelte',
type: 'boolean',
description: 'Put the `>` of a multiline element on a new line',
deprecated: '2.5.0',
},
svelteAllowShorthand: {
since: '1.0.0',
category: 'Svelte',
type: 'boolean',
default: true,
description: 'Option to enable/disable component attribute shorthand if attribute name and expressions are same',
},
svelteIndentScriptAndStyle: {
since: '1.2.0',
category: 'Svelte',
type: 'boolean',
default: true,
description: 'Whether or not to indent the code inside `}
getText(node, options, true)), embeddedOptions);
if (node.forceSingleLine) {
docs = removeLines(docs);
}
if (node.removeParentheses) {
docs = removeParentheses(docs);
}
return docs;
}
catch (e) {
return getText(node, options, true);
}
}
const embedType = (tag, parser, isTopLevel) => embedTag(tag, options.originalText, path, (content) => formatBodyContent(content, parser, textToDoc, options), print, isTopLevel, options);
const embedScript = (isTopLevel) => embedType('script',
// Use babel-ts as fallback because the absence does not mean the content is not TS,
// the user could have set the default language. babel-ts will format things a little
// bit different though, especially preserving parentheses around dot notation which
// fixes https://github.com/sveltejs/prettier-plugin-svelte/issues/218
isTypeScript(node) ? 'typescript' : 'babel-ts', isTopLevel);
const embedStyle = (isTopLevel) => embedType('style', 'css', isTopLevel);
const embedPug = () => embedType('template', 'pug', false);
switch (node.type) {
case 'Script':
return embedScript(true);
case 'Style':
return embedStyle(true);
case 'Element': {
if (node.name === 'script') {
return embedScript(false);
}
else if (node.name === 'style') {
return embedStyle(false);
}
else if (isPugTemplate(node)) {
return embedPug();
}
}
}
return null;
}
function forceIntoExpression(statement) {
// note the trailing newline: if the statement ends in a // comment,
// we can't add the closing bracket right afterwards
return `(${statement}\n)`;
}
function expressionParser(text, parsers, options) {
const ast = parsers.babel(text, parsers, options);
return Object.assign(Object.assign({}, ast), { program: ast.program.body[0].expression });
}
function preformattedBody(str) {
const firstNewline = /^[\t\f\r ]*\n/;
const lastNewline = /\n[\t\f\r ]*$/;
// If we do not start with a new line prettier might try to break the opening tag
// to keep it together with the string. Use a literal line to skip indentation.
return concat$1([literalline$1, str.replace(firstNewline, '').replace(lastNewline, ''), hardline$1]);
}
function getSnippedContent(node) {
const encodedContent = getAttributeTextValue(snippedTagContentAttribute, node);
if (encodedContent) {
return Buffer.from(encodedContent, 'base64').toString('utf-8');
}
else {
return '';
}
}
function formatBodyContent(content, parser, textToDoc, options) {
try {
const body = textToDoc(content, { parser });
if (parser === 'pug' && typeof body === 'string') {
// Pug returns no docs but a final string.
// Therefore prepend the line offsets
const whitespace = options.useTabs
? '\t'
: ' '.repeat(options.pugTabWidth && options.pugTabWidth > 0
? options.pugTabWidth
: options.tabWidth);
const pugBody = body
.split('\n')
.map((line) => (line ? whitespace + line : line))
.join('\n');
return concat$1([hardline$1, pugBody]);
}
const indentIfDesired = (doc) => options.svelteIndentScriptAndStyle ? indent$1(doc) : doc;
trimRight([body], isLine);
return concat$1([indentIfDesired(concat$1([hardline$1, body])), hardline$1]);
}
catch (error) {
if (process.env.PRETTIER_DEBUG) {
throw error;
}
// We will wind up here if there is a syntax error in the embedded code. If we throw an error,
// prettier will try to print the node with the printer. That will fail with a hard-to-interpret
// error message (e.g. "Unsupported node type", referring to `