diff --git a/CHANGELOG.md b/CHANGELOG.md index 17bec530..5d043b6e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,7 @@ * (fix) adjust snip regex to not break commented-out script/style tags ([#212](https://github.com/sveltejs/prettier-plugin-svelte/issues/212)) * (fix) Don't snip style tag that is inside script tag ([#219](https://github.com/sveltejs/prettier-plugin-svelte/issues/219), [#70](https://github.com/sveltejs/prettier-plugin-svelte/issues/70)) * (fix) Better formatting of long attribute values with mustache tags inbetween ([#221](https://github.com/sveltejs/prettier-plugin-svelte/pull/221)) +* (fix) Format properly when using Prettier 2.3.0+ ([#222](https://github.com/sveltejs/prettier-plugin-svelte/pull/222)) ## 2.2.0 diff --git a/package-lock.json b/package-lock.json index 0ee02afa..04b2af33 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1962,9 +1962,9 @@ "dev": true }, "prettier": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.2.1.tgz", - "integrity": "sha512-PqyhM2yCjg/oKkFPtTGUojv7gnZAoG80ttl45O6x2Ug/rMJw4wcc9k6aaf2hibP7BGVCCM33gZoGjyvt9mm16Q==", + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.3.0.tgz", + "integrity": "sha512-kXtO4s0Lz/DW/IJ9QdWhAf7/NmPWQXkFr/r/WkR3vyI+0v8amTDxiaQSLzs8NBlytfLWX/7uQUMIW677yLKl4w==", "dev": true }, "pretty-ms": { diff --git a/package.json b/package.json index 325d2e25..f237bef2 100644 --- a/package.json +++ b/package.json @@ -31,7 +31,7 @@ "@types/node": "^10.12.18", "@types/prettier": "^2.1.6", "ava": "3.15.0", - "prettier": "^2.2.1", + "prettier": "^2.3.0", "rollup": "2.36.0", "@rollup/plugin-commonjs": "14.0.0", "@rollup/plugin-node-resolve": "11.0.1", diff --git a/src/embed.ts b/src/embed.ts index 09608f8c..4cc18d8a 100644 --- a/src/embed.ts +++ b/src/embed.ts @@ -2,6 +2,7 @@ import { Doc, doc, FastPath, ParserOptions } from 'prettier'; import { getText } from './lib/getText'; import { snippedTagContentAttribute } from './lib/snipTagContent'; import { PrintFn } from './print'; +import { isLine, trimRight } from './print/doc-helpers'; import { getAttributeTextValue, getLeadingComment, @@ -80,44 +81,6 @@ function expressionParser(text: string, parsers: any, options: any) { return { ...ast, program: ast.program.body[0].expression }; } -function skipBlank(docs: Doc[]): number { - for (let i = docs.length - 1; i >= 0; i--) { - const doc = docs[i]; - if (typeof doc !== 'string') { - if (doc.type === 'break-parent') { - continue; - } - } - - return i; - } - - return -1; -} - -function nukeLastLine(doc: Doc): Doc { - if (typeof doc === 'string') { - return doc; - } - - switch (doc.type) { - case 'concat': - const end = skipBlank(doc.parts); - if (end > -1) { - return concat([ - ...doc.parts.slice(0, end), - nukeLastLine(doc.parts[end]), - ...doc.parts.slice(end + 1), - ]); - } - break; - case 'line': - return ''; - } - - return doc; -} - function preformattedBody(str: string): Doc { const firstNewline = /^[\t\f\r ]*\n/; const lastNewline = /\n[\t\f\r ]*$/; @@ -148,10 +111,9 @@ function formatBodyContent( try { const indentIfDesired = (doc: Doc) => (indentContent ? indent(doc) : doc); - return concat([ - indentIfDesired(concat([hardline, nukeLastLine(textToDoc(content, { parser }))])), - hardline, - ]); + const body = textToDoc(content, { parser }); + trimRight([body], isLine); + return concat([indentIfDesired(concat([hardline, body])), hardline]); } catch (error) { if (process.env.PRETTIER_DEBUG) { throw error; diff --git a/src/print/doc-helpers.ts b/src/print/doc-helpers.ts index c54cc77a..6b393175 100644 --- a/src/print/doc-helpers.ts +++ b/src/print/doc-helpers.ts @@ -23,6 +23,11 @@ export function isEmptyDoc(doc: Doc): boolean { return !doc.keepIfLonely; } + // Since Prettier 2.3.0, concats are represented as flat arrays + if (Array.isArray(doc)) { + return doc.length === 0; + } + const { contents } = doc as { contents?: Doc }; if (contents) { @@ -54,18 +59,21 @@ export function trim(docs: Doc[], isWhitespace: (doc: Doc) => boolean): Doc[] { } /** - * Trims the leading nodes matching `isWhitespace` independent of nesting level (though all nodes need to be a the same level) - * and returnes the removed nodes. + * Trims the leading nodes matching `isWhitespace` independent of nesting level (though all nodes need to be a the same level). + * If there are empty docs before the first whitespace, they are removed, too. */ -export function trimLeft(group: Doc[], isWhitespace: (doc: Doc) => boolean): Doc[] | undefined { - let firstNonWhitespace = group.findIndex((doc) => !isWhitespace(doc)); +export function trimLeft(group: Doc[], isWhitespace: (doc: Doc) => boolean): void { + let firstNonWhitespace = group.findIndex((doc) => !isEmptyDoc(doc) && !isWhitespace(doc)); if (firstNonWhitespace < 0 && group.length) { firstNonWhitespace = group.length; } if (firstNonWhitespace > 0) { - return group.splice(0, firstNonWhitespace); + const removed = group.splice(0, firstNonWhitespace); + if (removed.every(isEmptyDoc)) { + return trimLeft(group, isWhitespace); + } } else { const parts = getParts(group[0]); @@ -76,14 +84,19 @@ export function trimLeft(group: Doc[], isWhitespace: (doc: Doc) => boolean): Doc } /** - * Trims the trailing nodes matching `isWhitespace` independent of nesting level (though all nodes need to be a the same level) - * and returnes the removed nodes. + * Trims the trailing nodes matching `isWhitespace` independent of nesting level (though all nodes need to be a the same level). + * If there are empty docs after the last whitespace, they are removed, too. */ -export function trimRight(group: Doc[], isWhitespace: (doc: Doc) => boolean): Doc[] | undefined { - let lastNonWhitespace = group.length ? findLastIndex((doc) => !isWhitespace(doc), group) : 0; +export function trimRight(group: Doc[], isWhitespace: (doc: Doc) => boolean): void { + let lastNonWhitespace = group.length + ? findLastIndex((doc) => !isEmptyDoc(doc) && !isWhitespace(doc), group) + : 0; if (lastNonWhitespace < group.length - 1) { - return group.splice(lastNonWhitespace + 1); + const removed = group.splice(lastNonWhitespace + 1); + if (removed.every(isEmptyDoc)) { + return trimRight(group, isWhitespace); + } } else { const parts = getParts(group[group.length - 1]); @@ -95,6 +108,10 @@ export function trimRight(group: Doc[], isWhitespace: (doc: Doc) => boolean): Do function getParts(doc: Doc): Doc[] | undefined { if (typeof doc === 'object') { + // Since Prettier 2.3.0, concats are represented as flat arrays + if (Array.isArray(doc)) { + return doc; + } if (doc.type === 'fill' || doc.type === 'concat') { return doc.parts; }