'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 `