Skip to content

Commit db2543f

Browse files
authored
(feat) support singleAttributePerLine (#313)
Closes #305
1 parent 781bdd2 commit db2543f

File tree

13 files changed

+410
-73
lines changed

13 files changed

+410
-73
lines changed

package-lock.json

Lines changed: 12 additions & 9 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@
3434
"@types/node": "^10.12.18",
3535
"@types/prettier": "^2.4.1",
3636
"ava": "3.15.0",
37-
"prettier": "^2.4.1",
37+
"prettier": "^2.7.1",
3838
"rollup": "2.36.0",
3939
"rollup-plugin-typescript": "1.0.1",
4040
"svelte": "^3.47.0",

src/embed.ts

Lines changed: 16 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
11
import { Doc, doc, FastPath, ParserOptions } from 'prettier';
22
import { getText } from './lib/getText';
33
import { snippedTagContentAttribute } from './lib/snipTagContent';
4+
import { isBracketSameLine } from './options';
45
import { PrintFn } from './print';
56
import { isLine, removeParentheses, trimRight } from './print/doc-helpers';
7+
import { groupConcat, printWithPrependedAttributeLine } from './print/helpers';
68
import {
79
getAttributeTextValue,
810
getLeadingComment,
@@ -12,10 +14,10 @@ import {
1214
isTypeScript,
1315
printRaw,
1416
} from './print/node-helpers';
15-
import { ElementNode, Node } from './print/nodes';
17+
import { ElementNode, Node, ScriptNode, StyleNode } from './print/nodes';
1618

1719
const {
18-
builders: { concat, hardline, group, indent, literalline },
20+
builders: { concat, hardline, softline, indent, dedent, literalline },
1921
utils: { removeLines },
2022
} = doc;
2123

@@ -188,7 +190,7 @@ function embedTag(
188190
isTopLevel: boolean,
189191
options: ParserOptions,
190192
) {
191-
const node: Node = path.getNode();
193+
const node: ScriptNode | StyleNode | ElementNode = path.getNode();
192194
const content =
193195
tag === 'template' ? printRaw(node as ElementNode, text) : getSnippedContent(node);
194196
const previousComment = getLeadingComment(path);
@@ -208,19 +210,18 @@ function embedTag(
208210
: hardline
209211
: preformattedBody(content);
210212

211-
const attributes = concat(
212-
path.map(
213-
(childPath) =>
214-
childPath.getNode().name !== snippedTagContentAttribute
215-
? childPath.call(print)
216-
: '',
217-
'attributes',
213+
const openingTag = groupConcat([
214+
'<',
215+
tag,
216+
indent(
217+
groupConcat([
218+
...path.map(printWithPrependedAttributeLine(node, options, print), 'attributes'),
219+
isBracketSameLine(options) ? '' : dedent(softline),
220+
]),
218221
),
219-
);
220-
221-
let result: Doc = group(
222-
concat(['<', tag, indent(group(attributes)), '>', body, '</', tag, '>']),
223-
);
222+
'>',
223+
]);
224+
let result = groupConcat([openingTag, body, '</', tag, '>']);
224225

225226
if (isTopLevel) {
226227
// top level embedded nodes have been moved from their normal position in the

src/lib/extractAttributes.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
import { AttributeNode, TextNode } from '../print/nodes';
22

33
export function extractAttributes(html: string): AttributeNode[] {
4-
const extractAttributesRegex = /<[a-z]+\s*(.*?)>/i;
5-
const attributeRegex = /([^\s=]+)(?:=("|')(.*?)\2)?/gi;
4+
const extractAttributesRegex = /<[a-z]+[\s\n]*([\s\S]*?)>/im;
5+
const attributeRegex = /([^\s=]+)(?:=("|')([\s\S]*?)\2)?/gim;
66

77
const [, attributesString] = html.match(extractAttributesRegex)!;
88

src/print/helpers.ts

Lines changed: 75 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,23 @@
1-
import { ASTNode, Node } from './nodes';
2-
import { Doc, FastPath } from 'prettier';
1+
import {
2+
ASTNode,
3+
AttributeNode,
4+
BodyNode,
5+
ElementNode,
6+
HeadNode,
7+
InlineComponentNode,
8+
Node,
9+
OptionsNode,
10+
ScriptNode,
11+
SlotNode,
12+
SlotTemplateNode,
13+
StyleNode,
14+
TitleNode,
15+
WindowNode,
16+
} from './nodes';
17+
import { Doc, doc, FastPath, ParserOptions } from 'prettier';
318
import { formattableAttributes } from '../lib/elements';
19+
import { PrintFn } from '.';
20+
import { snippedTagContentAttribute } from '../lib/snipTagContent';
421

522
/**
623
* Determines whether or not given node
@@ -48,3 +65,59 @@ export function replaceEndOfLineWith(text: string, replacement: Doc) {
4865
}
4966
return parts;
5067
}
68+
69+
export function groupConcat(contents: doc.builders.Doc[]): doc.builders.Doc {
70+
const { concat, group } = doc.builders;
71+
return group(concat(contents));
72+
}
73+
74+
export function getAttributeLine(
75+
node:
76+
| ElementNode
77+
| InlineComponentNode
78+
| SlotNode
79+
| WindowNode
80+
| HeadNode
81+
| TitleNode
82+
| StyleNode
83+
| ScriptNode
84+
| BodyNode
85+
| OptionsNode
86+
| SlotTemplateNode,
87+
options: ParserOptions,
88+
) {
89+
const { hardline, line } = doc.builders;
90+
const hasThisBinding =
91+
(node.type === 'InlineComponent' && !!node.expression) ||
92+
(node.type === 'Element' && !!node.tag);
93+
94+
const attributes = (node.attributes as Array<AttributeNode>).filter(
95+
(attribute) => attribute.name !== snippedTagContentAttribute,
96+
);
97+
return options.singleAttributePerLine &&
98+
(attributes.length > 1 || (attributes.length && hasThisBinding))
99+
? hardline
100+
: line;
101+
}
102+
103+
export function printWithPrependedAttributeLine(
104+
node:
105+
| ElementNode
106+
| InlineComponentNode
107+
| SlotNode
108+
| WindowNode
109+
| HeadNode
110+
| TitleNode
111+
| StyleNode
112+
| ScriptNode
113+
| BodyNode
114+
| OptionsNode
115+
| SlotTemplateNode,
116+
options: ParserOptions,
117+
print: PrintFn,
118+
): PrintFn {
119+
return (path) =>
120+
path.getNode().name !== snippedTagContentAttribute
121+
? doc.builders.concat([getAttributeLine(node, options), path.call(print)])
122+
: '';
123+
}

0 commit comments

Comments
 (0)