@@ -28,6 +28,8 @@ function createParser(
28
28
) {
29
29
let customizationDefaults : Customizations = {
30
30
staticAttrs : new Set ( meta . staticAttrs ?? [ ] ) ,
31
+ prefixAttrs : new Set ( meta . prefixAttrs ?? [ ] ) ,
32
+ suffixAttrs : new Set ( meta . suffixAttrs ?? [ ] ) ,
31
33
dynamicAttrs : new Set ( meta . dynamicAttrs ?? [ ] ) ,
32
34
functions : new Set ( meta . functions ?? [ ] ) ,
33
35
}
@@ -273,11 +275,11 @@ function transformDynamicJsAttribute(attr: any, env: TransformerEnv) {
273
275
}
274
276
275
277
function transformHtml ( ast : any , { env, changes } : TransformerContext ) {
276
- let { staticAttrs , dynamicAttrs } = env . customizations
278
+ let { dynamicAttrs } = env . customizations
277
279
let { parser } = env . options
278
280
279
281
for ( let attr of ast . attrs ?? [ ] ) {
280
- if ( staticAttrs . has ( attr . name ) ) {
282
+ if ( isSortableAttribute ( env . customizations , attr . name ) ) {
281
283
attr . value = sortClasses ( attr . value , { env } )
282
284
} else if ( dynamicAttrs . has ( attr . name ) ) {
283
285
if ( ! / [ ` ' " ] / . test ( attr . value ) ) {
@@ -298,11 +300,9 @@ function transformHtml(ast: any, { env, changes }: TransformerContext) {
298
300
}
299
301
300
302
function transformGlimmer ( ast : any , { env } : TransformerContext ) {
301
- let { staticAttrs } = env . customizations
302
-
303
303
visit ( ast , {
304
304
AttrNode ( attr , _path , meta ) {
305
- if ( staticAttrs . has ( attr . name ) && attr . value ) {
305
+ if ( isSortableAttribute ( env . customizations , attr . name ) && attr . value ) {
306
306
meta . sortTextNodes = true
307
307
}
308
308
} ,
@@ -354,12 +354,10 @@ function transformGlimmer(ast: any, { env }: TransformerContext) {
354
354
}
355
355
356
356
function transformLiquid ( ast : any , { env } : TransformerContext ) {
357
- let { staticAttrs } = env . customizations
358
-
359
357
function isClassAttr ( node : { name : string | { type : string ; value : string } [ ] } ) {
360
358
return Array . isArray ( node . name )
361
- ? node . name . every ( ( n ) => n . type === 'TextNode' && staticAttrs . has ( n . value ) )
362
- : staticAttrs . has ( node . name )
359
+ ? node . name . every ( ( n ) => n . type === 'TextNode' && isSortableAttribute ( env . customizations , n . value ) )
360
+ : isSortableAttribute ( env . customizations , node . name )
363
361
}
364
362
365
363
function hasSurroundingQuotes ( str : string ) {
@@ -566,6 +564,22 @@ function sortTemplateLiteral(
566
564
return didChange
567
565
}
568
566
567
+ function isSortableAttribute ( customizations : Customizations , name : string ) {
568
+ const { staticAttrs, suffixAttrs, prefixAttrs } = customizations
569
+
570
+ if ( staticAttrs . has ( name ) ) return true
571
+
572
+ for ( const prefix of prefixAttrs ) {
573
+ if ( name . startsWith ( prefix ) ) return true
574
+ }
575
+
576
+ for ( const suffix of suffixAttrs ) {
577
+ if ( name . endsWith ( suffix ) ) return true
578
+ }
579
+
580
+ return false
581
+ }
582
+
569
583
function isSortableTemplateExpression (
570
584
node : import ( '@babel/types' ) . TaggedTemplateExpression | import ( 'ast-types' ) . namedTypes . TaggedTemplateExpression ,
571
585
functions : Set < string > ,
@@ -653,7 +667,7 @@ function canCollapseWhitespaceIn(path: Path<import('@babel/types').Node, any>) {
653
667
// We cross several parsers that share roughly the same shape so things are
654
668
// good enough. The actual AST we should be using is probably estree + ts.
655
669
function transformJavaScript ( ast : import ( '@babel/types' ) . Node , { env } : TransformerContext ) {
656
- let { staticAttrs , functions } = env . customizations
670
+ let { functions } = env . customizations
657
671
658
672
function sortInside ( ast : import ( '@babel/types' ) . Node ) {
659
673
visit ( ast , ( node , path ) => {
@@ -685,7 +699,7 @@ function transformJavaScript(ast: import('@babel/types').Node, { env }: Transfor
685
699
return
686
700
}
687
701
688
- if ( ! staticAttrs . has ( node . name . name ) ) {
702
+ if ( ! isSortableAttribute ( env . customizations , node . name . name ) ) {
689
703
return
690
704
}
691
705
@@ -787,11 +801,11 @@ function transformCss(ast: any, { env }: TransformerContext) {
787
801
}
788
802
789
803
function transformAstro ( ast : any , { env, changes } : TransformerContext ) {
790
- let { staticAttrs , dynamicAttrs } = env . customizations
804
+ let { dynamicAttrs } = env . customizations
791
805
792
806
if ( ast . type === 'element' || ast . type === 'custom-element' || ast . type === 'component' ) {
793
807
for ( let attr of ast . attributes ?? [ ] ) {
794
- if ( staticAttrs . has ( attr . name ) && attr . type === 'attribute' && attr . kind === 'quoted' ) {
808
+ if ( isSortableAttribute ( env . customizations , attr . name ) && attr . type === 'attribute' && attr . kind === 'quoted' ) {
795
809
attr . value = sortClasses ( attr . value , {
796
810
env,
797
811
} )
@@ -812,8 +826,6 @@ function transformAstro(ast: any, { env, changes }: TransformerContext) {
812
826
}
813
827
814
828
function transformMarko ( ast : any , { env } : TransformerContext ) {
815
- let { staticAttrs } = env . customizations
816
-
817
829
const nodesToVisit = [ ast ]
818
830
while ( nodesToVisit . length > 0 ) {
819
831
const currentNode = nodesToVisit . pop ( )
@@ -832,7 +844,7 @@ function transformMarko(ast: any, { env }: TransformerContext) {
832
844
nodesToVisit . push ( ...currentNode . body )
833
845
break
834
846
case 'MarkoAttribute' :
835
- if ( ! staticAttrs . has ( currentNode . name ) ) break
847
+ if ( ! isSortableAttribute ( env . customizations , currentNode . name ) ) break
836
848
switch ( currentNode . value . type ) {
837
849
case 'ArrayExpression' :
838
850
const classList = currentNode . value . elements
@@ -862,7 +874,7 @@ function transformTwig(ast: any, { env, changes }: TransformerContext) {
862
874
863
875
visit ( ast , {
864
876
Attribute ( node , _path , meta ) {
865
- if ( ! staticAttrs . has ( node . name . name ) ) return
877
+ if ( ! isSortableAttribute ( env . customizations , node . name . name ) ) return
866
878
867
879
meta . sortTextNodes = true
868
880
} ,
@@ -893,16 +905,14 @@ function transformTwig(ast: any, { env, changes }: TransformerContext) {
893
905
}
894
906
895
907
function transformPug ( ast : any , { env } : TransformerContext ) {
896
- let { staticAttrs } = env . customizations
897
-
898
908
// This isn't optimal
899
909
// We should merge the classes together across class attributes and class tokens
900
910
// And then we sort them
901
911
// But this is good enough for now
902
912
903
913
// First sort the classes in attributes
904
914
for ( const token of ast . tokens ) {
905
- if ( token . type === 'attribute' && staticAttrs . has ( token . name ) ) {
915
+ if ( token . type === 'attribute' && isSortableAttribute ( env . customizations , token . name ) ) {
906
916
token . val = [ token . val . slice ( 0 , 1 ) , sortClasses ( token . val . slice ( 1 , - 1 ) , { env } ) , token . val . slice ( - 1 ) ] . join ( '' )
907
917
}
908
918
}
@@ -947,10 +957,8 @@ function transformPug(ast: any, { env }: TransformerContext) {
947
957
}
948
958
949
959
function transformSvelte ( ast : any , { env, changes } : TransformerContext ) {
950
- let { staticAttrs } = env . customizations
951
-
952
960
for ( let attr of ast . attributes ?? [ ] ) {
953
- if ( ! staticAttrs . has ( attr . name ) || attr . type !== 'Attribute' ) {
961
+ if ( ! isSortableAttribute ( env . customizations , attr . name ) || attr . type !== 'Attribute' ) {
954
962
continue
955
963
}
956
964
@@ -1238,6 +1246,16 @@ export interface PluginOptions {
1238
1246
*/
1239
1247
tailwindAttributes ?: string [ ]
1240
1248
1249
+ /**
1250
+ * List of prefixes to match attributes that contain classes.
1251
+ */
1252
+ tailwindAttributesStartsWith ?: string [ ]
1253
+
1254
+ /**
1255
+ * List of suffixes to match attributes that contain classes.
1256
+ */
1257
+ tailwindAttributesEndsWith ?: string [ ]
1258
+
1241
1259
/**
1242
1260
* Preserve whitespace around Tailwind classes when sorting.
1243
1261
*/
0 commit comments