11import type { AST } from "svelte-eslint-parser"
22import type * as ESTree from "estree"
3- import type { Root } from "postcss"
4- import { parse as parseCss } from "postcss"
53import { createRule } from "../utils"
6-
7- /** Parse for CSS */
8- function safeParseCss ( cssCode : string ) {
9- try {
10- return parseCss ( cssCode )
11- } catch {
12- return null
13- }
14- }
4+ import type { SvelteStyleRoot } from "../utils/css-utils"
5+ import { parseStyleAttributeValue , safeParseCss } from "../utils/css-utils"
156
167/** Checks wether the given node is string literal or not */
178function isStringLiteral (
@@ -42,12 +33,11 @@ export default createRule("prefer-style-directive", {
4233 */
4334 function processStyleValue (
4435 node : AST . SvelteAttribute ,
45- root : Root ,
36+ root : SvelteStyleRoot ,
4637 mustacheTags : AST . SvelteMustacheTagText [ ] ,
4738 ) {
48- const valueStartIndex = node . value [ 0 ] . range [ 0 ]
49-
50- root . walkDecls ( ( decl ) => {
39+ root . walk ( ( decl ) => {
40+ if ( decl . type !== "decl" || decl . important ) return
5141 if (
5242 node . parent . attributes . some (
5343 ( attr ) =>
@@ -59,38 +49,28 @@ export default createRule("prefer-style-directive", {
5949 return
6050 }
6151
62- const declRange : AST . Range = [
63- valueStartIndex + decl . source ! . start ! . offset ,
64- valueStartIndex + decl . source ! . end ! . offset + 1 ,
65- ]
6652 if (
6753 mustacheTags . some (
6854 ( tag ) =>
69- ( tag . range [ 0 ] < declRange [ 0 ] && declRange [ 0 ] < tag . range [ 1 ] ) ||
70- ( tag . range [ 0 ] < declRange [ 1 ] && declRange [ 1 ] < tag . range [ 1 ] ) ,
55+ ( tag . range [ 0 ] < decl . range [ 0 ] && decl . range [ 0 ] < tag . range [ 1 ] ) ||
56+ ( tag . range [ 0 ] < decl . range [ 1 ] && decl . range [ 1 ] < tag . range [ 1 ] ) ,
7157 )
7258 ) {
7359 // intersection
7460 return
7561 }
76- const declValueStartIndex =
77- declRange [ 0 ] + decl . prop . length + ( decl . raws . between || "" ) . length
78- const declValueRange : AST . Range = [
79- declValueStartIndex ,
80- declValueStartIndex + ( decl . raws . value ?. value || decl . value ) . length ,
81- ]
8262
8363 context . report ( {
8464 node,
8565 messageId : "unexpected" ,
8666 * fix ( fixer ) {
8767 const styleDirective = `style:${ decl . prop } ="${ sourceCode . text . slice (
88- ...declValueRange ,
68+ ...decl . valueRange ,
8969 ) } "`
9070 if ( root . nodes . length === 1 && root . nodes [ 0 ] === decl ) {
9171 yield fixer . replaceTextRange ( node . range , styleDirective )
9272 } else {
93- yield fixer . removeRange ( declRange )
73+ yield fixer . removeRange ( decl . range )
9474 yield fixer . insertTextAfterRange ( node . range , ` ${ styleDirective } ` )
9575 }
9676 } ,
@@ -104,9 +84,10 @@ export default createRule("prefer-style-directive", {
10484 function processMustacheTags (
10585 mustacheTags : AST . SvelteMustacheTagText [ ] ,
10686 attrNode : AST . SvelteAttribute ,
87+ root : SvelteStyleRoot | null ,
10788 ) {
10889 for ( const mustacheTag of mustacheTags ) {
109- processMustacheTag ( mustacheTag , attrNode )
90+ processMustacheTag ( mustacheTag , attrNode , root )
11091 }
11192 }
11293
@@ -116,6 +97,7 @@ export default createRule("prefer-style-directive", {
11697 function processMustacheTag (
11798 mustacheTag : AST . SvelteMustacheTagText ,
11899 attrNode : AST . SvelteAttribute ,
100+ root : SvelteStyleRoot | null ,
119101 ) {
120102 const node = mustacheTag . expression
121103
@@ -132,14 +114,30 @@ export default createRule("prefer-style-directive", {
132114 // e.g. t ? 'top: 20px' : 'left: 30px'
133115 return
134116 }
117+
118+ if ( root ) {
119+ let foundIntersection = false
120+ root . walk ( ( n ) => {
121+ if (
122+ mustacheTag . range [ 0 ] < n . range [ 1 ] &&
123+ n . range [ 0 ] < mustacheTag . range [ 1 ]
124+ ) {
125+ foundIntersection = true
126+ }
127+ } )
128+ if ( foundIntersection ) {
129+ return
130+ }
131+ }
132+
135133 const positive = node . alternate . value === ""
136- const root = safeParseCss (
134+ const inlineRoot = safeParseCss (
137135 positive ? node . consequent . value : node . alternate . value ,
138136 )
139- if ( ! root || root . nodes . length !== 1 ) {
137+ if ( ! inlineRoot || inlineRoot . nodes . length !== 1 ) {
140138 return
141139 }
142- const decl = root . nodes [ 0 ]
140+ const decl = inlineRoot . nodes [ 0 ]
143141 if ( decl . type !== "decl" ) {
144142 return
145143 }
@@ -221,20 +219,11 @@ export default createRule("prefer-style-directive", {
221219 const mustacheTags = node . value . filter (
222220 ( v ) : v is AST . SvelteMustacheTagText => v . type === "SvelteMustacheTag" ,
223221 )
224- const cssCode = node . value
225- . map ( ( value ) => {
226- if ( value . type === "SvelteMustacheTag" ) {
227- return "_" . repeat ( value . range [ 1 ] - value . range [ 0 ] )
228- }
229- return sourceCode . getText ( value )
230- } )
231- . join ( "" )
232- const root = safeParseCss ( cssCode )
222+ const root = parseStyleAttributeValue ( node , context )
233223 if ( root ) {
234224 processStyleValue ( node , root , mustacheTags )
235- } else {
236- processMustacheTags ( mustacheTags , node )
237225 }
226+ processMustacheTags ( mustacheTags , node , root )
238227 } ,
239228 }
240229 } ,
0 commit comments