Skip to content

Commit 91ac55d

Browse files
Add support for Glimmer / Handlebars (#83)
* Add Glimmer / Handlebars support * Update Glimmer tests * Add test case * Update glimmer parsing * Remove nested transform fn * Rewrite using visitors * Only make on pass over the Glimmer AST * Cleanup code a bit Co-authored-by: Brad Cornes <[email protected]>
1 parent bc08a56 commit 91ac55d

File tree

2 files changed

+67
-5
lines changed

2 files changed

+67
-5
lines changed

src/index.js

Lines changed: 44 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import prettierParserHTML from 'prettier/parser-html'
33
import prettierParserPostCSS from 'prettier/parser-postcss'
44
import prettierParserBabel from 'prettier/parser-babel'
55
import prettierParserEspree from 'prettier/parser-espree'
6+
import prettierParserGlimmer from 'prettier/parser-glimmer'
67
import prettierParserMeriyah from 'prettier/parser-meriyah'
78
import prettierParserFlow from 'prettier/parser-flow'
89
import prettierParserTypescript from 'prettier/parser-typescript'
@@ -234,6 +235,43 @@ function transformHtml(attributes, computedAttributes = []) {
234235
return transform
235236
}
236237

238+
function transformGlimmer(ast, { env }) {
239+
visit(ast, {
240+
AttrNode(attr, parent, key, index, meta) {
241+
let attributes = ['class']
242+
243+
if (attributes.includes(attr.name) && attr.value) {
244+
meta.sortTextNodes = true
245+
}
246+
},
247+
248+
TextNode(node, parent, key, index, meta) {
249+
if (!meta.sortTextNodes) {
250+
return
251+
}
252+
253+
let siblings = parent?.type === 'ConcatStatement' ? {
254+
prev: parent.parts[index - 1],
255+
next: parent.parts[index + 1],
256+
} : null
257+
258+
node.chars = sortClasses(node.chars, {
259+
env,
260+
ignoreFirst: siblings?.prev && !/^\s/.test(node.chars),
261+
ignoreLast: siblings?.next && !/\s$/.test(node.chars),
262+
})
263+
},
264+
265+
StringLiteral(node, parent, key, index, meta) {
266+
if (!meta.sortTextNodes) {
267+
return
268+
}
269+
270+
node.value = sortClasses(node.value, { env })
271+
},
272+
})
273+
}
274+
237275
function sortStringLiteral(node, { env }) {
238276
let result = sortClasses(node.value, { env })
239277
let didChange = result !== node.value
@@ -364,6 +402,7 @@ export const printers = {
364402

365403
export const parsers = {
366404
html: createParser(prettierParserHTML.parsers.html, transformHtml(['class'])),
405+
glimmer: createParser(prettierParserGlimmer.parsers.glimmer, transformGlimmer),
367406
lwc: createParser(prettierParserHTML.parsers.lwc, transformHtml(['class'])),
368407
angular: createParser(
369408
prettierParserHTML.parsers.angular,
@@ -442,13 +481,13 @@ function transformSvelte(ast, { env, changes }) {
442481

443482
// https://lihautan.com/manipulating-ast-with-javascript/
444483
function visit(ast, callbackMap) {
445-
function _visit(node, parent, key, index) {
484+
function _visit(node, parent, key, index, meta = {}) {
446485
if (typeof callbackMap === 'function') {
447-
if (callbackMap(node, parent, key, index) === false) {
486+
if (callbackMap(node, parent, key, index, meta) === false) {
448487
return
449488
}
450489
} else if (node.type in callbackMap) {
451-
if (callbackMap[node.type](node, parent, key, index) === false) {
490+
if (callbackMap[node.type](node, parent, key, index, meta) === false) {
452491
return
453492
}
454493
}
@@ -459,11 +498,11 @@ function visit(ast, callbackMap) {
459498
if (Array.isArray(child)) {
460499
for (let j = 0; j < child.length; j++) {
461500
if (child[j] !== null) {
462-
_visit(child[j], node, keys[i], j)
501+
_visit(child[j], node, keys[i], j, {...meta})
463502
}
464503
}
465504
} else if (typeof child?.type === 'string') {
466-
_visit(child, node, keys[i], i)
505+
_visit(child, node, keys[i], i, {...meta})
467506
}
468507
}
469508
}

tests/test.js

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -130,8 +130,31 @@ let vue = [
130130
],
131131
]
132132

133+
let glimmer = [
134+
t`<div class='${yes}'></div>`,
135+
t`<!-- <div class='${no}'></div> -->`,
136+
t`<div class='${yes} {{"${yes}"}}'></div>`,
137+
t`<div class='${yes} {{"${yes}"}} ${yes}'></div>`,
138+
t`<div class='${yes} {{"${yes}"}} {{if someVar "${yes}" "${yes}"}}'></div>`,
139+
t`<div class='${yes} {{"${yes}"}} {{if someVar "${yes}" "${yes}"}}' {{if someVar "attr='${no}'" "attr='${no}'"}}></div>`,
140+
[
141+
`<div class='md:inline flex sm:block{{someVar}}'></div>`,
142+
`<div class='flex md:inline sm:block{{someVar}}'></div>`,
143+
],
144+
[
145+
`<div class='sm:p-0 p-0 {{someVar}}sm:block md:inline flex'></div>`,
146+
`<div class='p-0 sm:p-0 {{someVar}}sm:block flex md:inline'></div>`,
147+
],
148+
t`<div not-class='${no}'></div>`,
149+
["<div class=' sm:p-0 p-0 '></div>", "<div class='p-0 sm:p-0'></div>"],
150+
t`<div class></div>`,
151+
t`<div class=''></div>`,
152+
t`{{link 'Some page' href=person.url class='${no}'}}`,
153+
]
154+
133155
let tests = {
134156
html,
157+
glimmer,
135158
lwc: html,
136159
vue: [...vue, t`<div :class="\`${yes} \${someVar} ${yes} \${'${yes}'}\`"></div>`],
137160
angular: [

0 commit comments

Comments
 (0)