Skip to content

Commit b207705

Browse files
Fix class sorting in capture liquid tag (#131)
* Refactor liquid script attribute sorting The Liquid prettier plugin consults the `source` property on various nodes when re-printing the AST as text. Which one gets looked at depends on what type of node is being analyzed and even what ancestor nodes there are. For instance, attributes consult the `source` on the attribute node. But some Liquid Tags end up looking at the source property of the liquid tag instead of the nodes inside it even if they’re modified (for the purposes of attrbute sorting at least). This new setup allows us to push objects with `source` properties that can all be updated simultanously when sorting the value of an attribute. * Fix class sorting in `capture` liquid tag * Update changelog
1 parent 1758ab4 commit b207705

File tree

3 files changed

+41
-37
lines changed

3 files changed

+41
-37
lines changed

CHANGELOG.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
77

88
## [Unreleased]
99

10-
- Nothing yet!
10+
### Fixed
11+
12+
- Fix class sorting in `capture` liquid tag ([#131](https://github.com/tailwindlabs/prettier-plugin-tailwindcss/pull/131))
1113

1214
## [0.2.4] - 2023-03-02
1315

src/index.js

Lines changed: 34 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -353,60 +353,58 @@ function transformGlimmer(ast, { env }) {
353353
function transformLiquid(ast, { env }) {
354354
/** @param {{name: string | {type: string, value: string}[]}} node */
355355
function isClassAttr(node) {
356-
if (Array.isArray(node.name)) {
357-
return node.name.every((n) => n.type === 'TextNode' && n.value === 'class');
358-
}
359-
360-
return node.name === 'class'
356+
return Array.isArray(node.name)
357+
? node.name.every((n) => n.type === 'TextNode' && n.value === 'class')
358+
: node.name === 'class'
361359
}
362360

363-
visit(ast, {
364-
AttrSingleQuoted(node, _parent, _key, _index, meta) {
365-
if (!isClassAttr(node)) {
366-
return;
367-
}
361+
function sortAttribute(attr, path) {
362+
visit(attr.value, {
363+
TextNode(node) {
364+
node.value = sortClasses(node.value, { env });
368365

369-
meta.sortTextNodes = true;
370-
meta.sourceNode = node;
371-
},
366+
let source = node.source.slice(0, node.position.start) + node.value + node.source.slice(node.position.end)
367+
path.forEach(node => (node.source = source))
368+
},
372369

373-
AttrDoubleQuoted(node, _parent, _key, _index, meta) {
374-
if (!isClassAttr(node)) {
375-
return;
376-
}
370+
String(node) {
371+
node.value = sortClasses(node.value, { env });
377372

378-
meta.sortTextNodes = true;
373+
// String position includes the quotes even if the value doesn't
374+
// Hence the +1 and -1 when slicing
375+
let source = node.source.slice(0, node.position.start+1) + node.value + node.source.slice(node.position.end-1)
376+
path.forEach(node => (node.source = source))
377+
},
378+
})
379+
}
380+
381+
visit(ast, {
382+
LiquidTag(node, _parent, _key, _index, meta) {
383+
meta.path = [...meta.path ?? [], node];
384+
},
379385

380-
// With Liquid Script it uses the "source" of certain nodes as the "source of truth"
381-
// We must modify that node's source to get the desired output
382-
// Even if we modify the AST it will be ignored
383-
meta.sourceNode = node;
386+
HtmlElement(node, _parent, _key, _index, meta) {
387+
meta.path = [...meta.path ?? [], node];
384388
},
385389

386-
TextNode(node, _parent, _key, _index, meta) {
387-
if (!meta.sortTextNodes) {
390+
AttrSingleQuoted(node, _parent, _key, _index, meta) {
391+
if (!isClassAttr(node)) {
388392
return;
389393
}
390394

391-
node.value = sortClasses(node.value, { env });
395+
meta.path = [...meta.path ?? [], node];
392396

393-
// This feels hacky but it's necessary
394-
node.source = node.source.slice(0, node.position.start) + node.value + node.source.slice(node.position.end);
395-
meta.sourceNode.source = node.source;
397+
sortAttribute(node, meta.path)
396398
},
397399

398-
String(node, _parent, _key, _index, meta) {
399-
if (!meta.sortTextNodes) {
400+
AttrDoubleQuoted(node, _parent, _key, _index, meta) {
401+
if (!isClassAttr(node)) {
400402
return;
401403
}
402404

403-
node.value = sortClasses(node.value, { env });
405+
meta.path = [...meta.path ?? [], node];
404406

405-
// This feels hacky but it's necessary
406-
// String position includes the quotes even if the value doesn't
407-
// Hence the +1 and -1 when slicing
408-
node.source = node.source.slice(0, node.position.start+1) + node.value + node.source.slice(node.position.end-1);
409-
meta.sourceNode.source = node.source;
407+
sortAttribute(node, meta.path)
410408
},
411409
});
412410
}

tests/plugins.test.js

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -253,6 +253,10 @@ let tests = [
253253
`{% if state == true %}\n <a class="{{ "sm:p-0 p-4" | escape }}" href="https://www.example.com">Example</a>\n{% endif %}`,
254254
`{% if state == true %}\n <a class='{{ "p-4 sm:p-0" | escape }}' href='https://www.example.com'>Example</a>\n{% endif %}`,
255255
],
256+
[
257+
`{%- capture class_ordering -%}<div class="sm:p-0 p-4"></div>{%- endcapture -%}`,
258+
`{%- capture class_ordering -%}<div class="p-4 sm:p-0"></div>{%- endcapture -%}`,
259+
],
256260
],
257261
}
258262
},

0 commit comments

Comments
 (0)