1
- const stylelint = require ( 'stylelint' ) ;
1
+ import { AtRule , Declaration , Node , Result , Root } from 'postcss' ;
2
+ import { createPlugin , Plugin , utils } from 'stylelint' ;
2
3
3
4
/** Name of this stylelint rule. */
4
5
const ruleName = 'material/theme-mixin-api' ;
@@ -19,10 +20,11 @@ const themeMixinRegex =
19
20
* 3. Checks if the `-theme` mixins have the duplicate style check set up. We want to
20
21
* consistently check for duplicative theme styles so that we can warn consumers.
21
22
*/
22
- const plugin = stylelint . createPlugin ( ruleName , ( isEnabled , options , context ) => {
23
- return ( root , result ) => {
24
- if ( ! isEnabled )
23
+ const plugin = ( isEnabled : boolean , options : never , context : { fix : boolean } ) => {
24
+ return ( root : Root , result : Result ) => {
25
+ if ( ! isEnabled ) {
25
26
return ;
27
+ }
26
28
27
29
root . walkAtRules ( 'mixin' , node => {
28
30
const matches = node . params . match ( themeMixinRegex ) ;
@@ -38,21 +40,21 @@ const plugin = stylelint.createPlugin(ruleName, (isEnabled, options, context) =>
38
40
// a comma. This is not always correct because Sass maps can be constructed in parameters.
39
41
// These would contain commas that throw of the argument retrieval. It's acceptable that
40
42
// this rule will fail in such edge-cases. There is no AST for `postcss.AtRule` params.
41
- const arguments = matches [ 5 ] . split ( ',' ) ;
43
+ const args = matches [ 5 ] . split ( ',' ) ;
42
44
43
45
if ( type === 'theme' ) {
44
- validateThemeMixin ( node , componentName , arguments ) ;
46
+ validateThemeMixin ( node , componentName , args ) ;
45
47
} else {
46
- validateIndividualSystemMixins ( node , type , arguments ) ;
48
+ validateIndividualSystemMixins ( node , type , args ) ;
47
49
}
48
50
} ) ;
49
51
50
- function validateThemeMixin ( node , componentName , arguments ) {
51
- if ( arguments . length !== 1 ) {
52
+ function validateThemeMixin ( node : AtRule , componentName : string , args : string [ ] ) {
53
+ if ( args . length !== 1 ) {
52
54
reportError ( node , 'Expected theme mixin to only declare a single argument.' ) ;
53
- } else if ( arguments [ 0 ] !== '$theme-or-color-config' ) {
55
+ } else if ( args [ 0 ] !== '$theme-or-color-config' ) {
54
56
if ( context . fix ) {
55
- node . params = node . params . replace ( arguments [ 0 ] , '$theme-or-color-config' ) ;
57
+ node . params = node . params . replace ( args [ 0 ] , '$theme-or-color-config' ) ;
56
58
} else {
57
59
reportError ( node , 'Expected first mixin argument to be called `$theme-or-color-config`.' ) ;
58
60
}
@@ -62,11 +64,13 @@ const plugin = stylelint.createPlugin(ruleName, (isEnabled, options, context) =>
62
64
const legacyColorExtractExpr = `_mat-legacy-get-theme($theme-or-color-config)` ;
63
65
const duplicateStylesCheckExpr =
64
66
`_mat-check-duplicate-theme-styles(${ themePropName } , '${ componentName } ')` ;
65
- const legacyConfigDecl =
66
- node . nodes . find ( n => n . type === 'decl' && n . value === legacyColorExtractExpr ) ;
67
- const hasDuplicateStylesCheck = node . nodes . find (
68
- n =>
69
- n . type === 'atrule' && n . name === 'include' && n . params === duplicateStylesCheckExpr ) ;
67
+ const legacyConfigDecl = ! ! node . nodes &&
68
+ node . nodes . find (
69
+ ( n ) : n is Declaration => n . type === 'decl' && n . value === legacyColorExtractExpr ) ;
70
+ const hasDuplicateStylesCheck = ! ! node . nodes &&
71
+ node . nodes . find (
72
+ n => n . type === 'atrule' && n . name === 'include' &&
73
+ n . params === duplicateStylesCheckExpr ) ;
70
74
71
75
if ( ! legacyConfigDecl ) {
72
76
if ( context . fix ) {
@@ -98,21 +102,21 @@ const plugin = stylelint.createPlugin(ruleName, (isEnabled, options, context) =>
98
102
}
99
103
}
100
104
101
- function validateIndividualSystemMixins ( node , type , arguments ) {
102
- if ( arguments . length !== 1 ) {
105
+ function validateIndividualSystemMixins ( node : AtRule , type : string , args : string [ ] ) {
106
+ if ( args . length !== 1 ) {
103
107
reportError ( node , 'Expected mixin to only declare a single argument.' ) ;
104
- } else if ( arguments [ 0 ] !== '$config-or-theme' ) {
108
+ } else if ( args [ 0 ] !== '$config-or-theme' ) {
105
109
if ( context . fix ) {
106
- node . params = node . params . replace ( arguments [ 0 ] , '$config-or-theme' ) ;
110
+ node . params = node . params . replace ( args [ 0 ] , '$config-or-theme' ) ;
107
111
} else {
108
112
reportError ( node , 'Expected first mixin argument to be called `$config-or-theme`.' ) ;
109
113
}
110
114
}
111
115
112
116
const expectedProperty = type === 'density' ? '$density-scale' : '$config' ;
113
117
const expectedValue = `mat-get-${ type } -config($config-or-theme)` ;
114
- const configExtractionNode =
115
- node . nodes . find ( n => n . type === 'decl' && n . value === expectedValue ) ;
118
+ const configExtractionNode = ! ! node . nodes &&
119
+ node . nodes . find ( ( n ) : n is Declaration => n . type === 'decl' && n . value === expectedValue ) ;
116
120
117
121
if ( ! configExtractionNode ) {
118
122
if ( context . fix ) {
@@ -131,11 +135,12 @@ const plugin = stylelint.createPlugin(ruleName, (isEnabled, options, context) =>
131
135
}
132
136
}
133
137
134
- function reportError ( node , message ) {
135
- stylelint . utils . report ( { result, ruleName, node, message} ) ;
138
+ function reportError ( node : Node , message : string ) {
139
+ utils . report ( { result, ruleName, node, message} ) ;
136
140
}
137
141
} ;
138
- } ) ;
142
+ } ;
139
143
140
- plugin . ruleName = ruleName ;
141
- module . exports = plugin ;
144
+ // Note: We need to cast the value explicitly to `Plugin` because the stylelint types
145
+ // do not type the context parameter. https://stylelint.io/developer-guide/rules#add-autofix
146
+ module . exports = createPlugin ( ruleName , plugin as Plugin ) ;
0 commit comments