@@ -30,15 +30,23 @@ function localizeNode(node, context) {
30
30
switch ( node . type ) {
31
31
case "selectors" :
32
32
var resultingGlobal ;
33
+ context . locals = true ;
33
34
newNodes = node . nodes . map ( function ( n ) {
34
- var nContext = { global : context . global , lastWasSpacing : true } ;
35
+ var nContext = {
36
+ global : context . global ,
37
+ lastWasSpacing : true ,
38
+ locals : false
39
+ } ;
35
40
n = localizeNode ( n , nContext ) ;
36
41
if ( typeof resultingGlobal === "undefined" ) {
37
42
resultingGlobal = nContext . global ;
38
43
} else if ( resultingGlobal !== nContext . global ) {
39
44
throw new Error ( "Inconsistent rule global/local result in rule '" +
40
45
Tokenizer . stringify ( node ) + "' (multiple selectors must result in the same mode for the rule)" ) ;
41
46
}
47
+ if ( ! nContext . locals ) {
48
+ context . locals = false ;
49
+ }
42
50
return n ;
43
51
} ) ;
44
52
context . global = resultingGlobal ;
@@ -82,30 +90,47 @@ function localizeNode(node, context) {
82
90
if ( context . inside ) {
83
91
throw new Error ( "A :" + node . name + "(...) is not allowed inside of a :" + context . inside + "(...)" ) ;
84
92
}
85
- subContext = { global : ( node . name === "global" ) , inside : node . name } ;
93
+ subContext = {
94
+ global : ( node . name === "global" ) ,
95
+ inside : node . name ,
96
+ locals : false
97
+ } ;
86
98
node = node . nodes . map ( function ( n ) {
87
99
return localizeNode ( n , subContext ) ;
88
100
} ) ;
89
101
// don't leak spacing
90
102
node [ 0 ] . before = undefined ;
91
103
node [ node . length - 1 ] . after = undefined ;
104
+ if ( subContext . locals ) {
105
+ context . locals = true ;
106
+ }
92
107
} else {
93
- subContext = { global : context . global , inside : context . inside , lastWasSpacing : true } ;
108
+ subContext = {
109
+ global : context . global ,
110
+ inside : context . inside ,
111
+ lastWasSpacing : true ,
112
+ locals : false
113
+ } ;
94
114
newNodes = node . nodes . map ( function ( n ) {
95
115
return localizeNode ( n , subContext ) ;
96
116
} ) ;
97
117
node = Object . create ( node ) ;
98
118
node . nodes = normalizeNodeArray ( newNodes ) ;
119
+ if ( subContext . locals ) {
120
+ context . locals = true ;
121
+ }
99
122
}
100
123
break ;
101
124
125
+ case "id" :
102
126
case "class" :
103
127
if ( ! context . global ) {
104
128
node = {
105
129
type : "nested-pseudo-class" ,
106
130
name : "local" ,
107
131
nodes : [ node ]
108
132
} ;
133
+ context . locals = true ;
109
134
}
110
135
break ;
111
136
}
@@ -125,33 +150,44 @@ function localizeDecl(decl) {
125
150
126
151
module . exports = postcss . plugin ( 'postcss-modules-local-by-default' , function ( options ) {
127
152
if ( options && options . mode ) {
128
- if ( options . mode !== "global" && options . mode !== "local" ) {
129
- throw new Error ( "options.mode must be either 'global' or 'local ' (default 'local')" ) ;
153
+ if ( options . mode !== "global" && options . mode !== "local" && options . mode !== "pure" ) {
154
+ throw new Error ( "options.mode must be either 'global', 'local' or 'pure ' (default 'local')" ) ;
130
155
}
131
156
}
157
+ var pureMode = options && options . mode === "pure" ;
158
+ var globalMode = options && options . mode === "global" ;
132
159
return function ( css ) {
133
- var initialGlobal = options && options . mode === "global" ;
134
160
css . eachAtRule ( function ( atrule ) {
135
161
if ( / k e y f r a m e s $ / . test ( atrule . name ) ) {
136
162
var globalMatch = / ^ \s * : g l o b a l \s * \( ( .+ ) \) \s * $ / . exec ( atrule . params ) ;
137
163
var localMatch = / ^ \s * : l o c a l \s * \( ( .+ ) \) \s * $ / . exec ( atrule . params ) ;
138
164
if ( globalMatch ) {
165
+ if ( pureMode ) {
166
+ throw new Error ( "@keyframes :global(...) is not allowed in pure mode" ) ;
167
+ }
139
168
atrule . params = globalMatch [ 1 ] ;
140
169
} else if ( localMatch ) {
141
170
atrule . params = localMatch [ 0 ] ;
142
- } else if ( ! initialGlobal ) {
171
+ } else if ( ! globalMode ) {
143
172
atrule . params = ":local(" + atrule . params + ")" ;
144
173
}
145
174
}
146
175
} ) ;
147
176
css . eachRule ( function ( rule ) {
148
177
var selector = Tokenizer . parse ( rule . selector ) ;
149
- var context = { global : initialGlobal } ;
150
- selector = localizeNode ( selector , context ) ;
178
+ var context = {
179
+ global : globalMode ,
180
+ locals : false
181
+ } ;
182
+ var newSelector = localizeNode ( selector , context ) ;
183
+ if ( pureMode && ! context . locals ) {
184
+ throw new Error ( "Selector '" + Tokenizer . stringify ( selector ) + "' is not pure " +
185
+ "(pure selectors must contain at least one local class or id)" ) ;
186
+ }
151
187
if ( ! context . global ) {
152
188
rule . nodes . forEach ( localizeDecl ) ;
153
189
}
154
- rule . selector = Tokenizer . stringify ( selector ) ;
190
+ rule . selector = Tokenizer . stringify ( newSelector ) ;
155
191
} ) ;
156
192
} ;
157
193
} ) ;
0 commit comments