11import { createRule } from '../utils' ;
2- import { getLangValue } from '../utils/ast-utils' ;
2+ import { findAttribute , getLangValue } from '../utils/ast-utils' ;
33import type { SvelteScriptElement , SvelteStyleElement } from 'svelte-eslint-parser/lib/ast' ;
44import { getSourceCode } from '../utils/compat' ;
55
@@ -11,6 +11,7 @@ export default createRule('block-lang', {
1111 category : 'Best Practices' ,
1212 recommended : false
1313 } ,
14+ fixable : 'code' ,
1415 schema : [
1516 {
1617 type : 'object' ,
@@ -88,7 +89,15 @@ export default createRule('block-lang', {
8889 loc : { line : 1 , column : 1 } ,
8990 message : `The <script> block should be present and its lang attribute should be ${ prettyPrintLangs (
9091 allowedScriptLangs
91- ) } .`
92+ ) } .`,
93+ * fix ( fixer ) {
94+ const langAttributeText = getLangAttributeText ( allowedScriptLangs , true ) ;
95+
96+ yield fixer . insertTextAfterRange (
97+ [ 0 , 0 ] ,
98+ `<script${ langAttributeText } >\n</script>\n\n`
99+ ) ;
100+ }
92101 } ) ;
93102 }
94103 for ( const scriptNode of scriptNodes ) {
@@ -97,7 +106,20 @@ export default createRule('block-lang', {
97106 node : scriptNode ,
98107 message : `The lang attribute of the <script> block should be ${ prettyPrintLangs (
99108 allowedScriptLangs
100- ) } .`
109+ ) } .`,
110+ * fix ( fixer ) {
111+ const langAttribute = findAttribute ( scriptNode , 'lang' ) ;
112+ const langAttributeText = getLangAttributeText ( allowedScriptLangs , true ) ;
113+
114+ if ( langAttribute ) {
115+ yield fixer . replaceText ( langAttribute , langAttributeText . trim ( ) ) ;
116+ } else {
117+ yield fixer . insertTextBeforeRange (
118+ [ scriptNode . startTag . range [ 0 ] + 7 , 0 ] ,
119+ langAttributeText
120+ ) ;
121+ }
122+ }
101123 } ) ;
102124 }
103125 }
@@ -106,7 +128,16 @@ export default createRule('block-lang', {
106128 loc : { line : 1 , column : 1 } ,
107129 message : `The <style> block should be present and its lang attribute should be ${ prettyPrintLangs (
108130 allowedStyleLangs
109- ) } .`
131+ ) } .`,
132+ * fix ( fixer ) {
133+ const sourceCode = getSourceCode ( context ) ;
134+ const langAttributeText = getLangAttributeText ( allowedScriptLangs , true ) ;
135+
136+ yield fixer . insertTextAfterRange (
137+ [ sourceCode . text . length , sourceCode . text . length ] ,
138+ `\n\n<style${ langAttributeText } >\n</style>`
139+ ) ;
140+ }
110141 } ) ;
111142 }
112143 for ( const styleNode of styleNodes ) {
@@ -115,7 +146,20 @@ export default createRule('block-lang', {
115146 node : styleNode ,
116147 message : `The lang attribute of the <style> block should be ${ prettyPrintLangs (
117148 allowedStyleLangs
118- ) } .`
149+ ) } .`,
150+ * fix ( fixer ) {
151+ const langAttribute = findAttribute ( styleNode , 'lang' ) ;
152+ const langAttributeText = getLangAttributeText ( allowedStyleLangs , true ) ;
153+
154+ if ( langAttribute ) {
155+ yield fixer . replaceText ( langAttribute , langAttributeText . trim ( ) ) ;
156+ } else {
157+ yield fixer . insertTextBeforeRange (
158+ [ styleNode . startTag . range [ 0 ] + 6 , 0 ] ,
159+ langAttributeText
160+ ) ;
161+ }
162+ }
119163 } ) ;
120164 }
121165 }
@@ -139,3 +183,15 @@ function prettyPrintLangs(langs: (string | null)[]): string {
139183 nonNullLangs . length === 1 ? nonNullLangs [ 0 ] : `one of ${ nonNullLangs . join ( ', ' ) } ` ;
140184 return hasNullText + nonNullText ;
141185}
186+
187+ /**
188+ * Returns the lang attribute text, with special handling of the `null` lang option with respect to the `prependWhitespace` argument.
189+ */
190+ function getLangAttributeText ( langs : ( string | null ) [ ] , prependWhitespace : boolean ) : string {
191+ if ( ! langs . length || langs . includes ( null ) ) return '' ;
192+ const [ firstLang ] = langs ;
193+ if ( langs . length === 1 && firstLang ) {
194+ return `${ prependWhitespace ? ' ' : '' } lang="${ firstLang } "` ;
195+ }
196+ return '' ;
197+ }
0 commit comments