|
1 | | -var postcss = require('postcss') |
2 | | -var Tokenizer = require('css-selector-tokenizer') |
| 1 | +/* eslint-env node */ |
| 2 | +import postcss from 'postcss' |
| 3 | +import Tokenizer from 'css-selector-tokenizer' |
| 4 | + |
| 5 | +const plugin = 'postcss-modules-local-by-default' |
3 | 6 |
|
4 | 7 | function normalizeNodeArray(nodes) { |
5 | 8 | var array = [] |
@@ -158,222 +161,44 @@ function localizeNode(node, context) { |
158 | 161 | return node |
159 | 162 | } |
160 | 163 |
|
161 | | -function localizeDeclNode(node, context) { |
162 | | - var newNode |
163 | | - switch (node.type) { |
164 | | - case 'item': |
165 | | - if (context.localizeNextItem) { |
166 | | - newNode = Object.create(node) |
167 | | - newNode.name = ':local(' + newNode.name + ')' |
168 | | - context.localizeNextItem = false |
169 | | - return newNode |
170 | | - } |
171 | | - break |
172 | | - |
173 | | - case 'nested-item': |
174 | | - var newNodes = node.nodes.map(function(n) { |
175 | | - return localizeDeclValue(n, context) |
176 | | - }) |
177 | | - node = Object.create(node) |
178 | | - node.nodes = newNodes |
179 | | - break |
180 | | - } |
181 | | - return node |
182 | | -} |
183 | | - |
184 | | -function localizeDeclValue(valueNode, context) { |
185 | | - var newValueNode = Object.create(valueNode) |
186 | | - newValueNode.nodes = valueNode.nodes.map(function(node) { |
187 | | - return localizeDeclNode(node, context) |
188 | | - }) |
189 | | - return newValueNode |
190 | | -} |
191 | | - |
192 | | -function localizeAnimationShorthandDeclValueNodes(nodes, context) { |
193 | | - var validIdent = (validIdent = /^-?[_a-z][_a-z0-9-]*$/i) |
194 | | - |
195 | | - /* |
196 | | - The spec defines some keywords that you can use to describe properties such as the timing |
197 | | - function. These are still valid animation names, so as long as there is a property that accepts |
198 | | - a keyword, it is given priority. Only when all the properties that can take a keyword are |
199 | | - exhausted can the animation name be set to the keyword. I.e. |
200 | | -
|
201 | | - animation: infinite infinite; |
202 | | -
|
203 | | - The animation will repeat an infinite number of times from the first argument, and will have an |
204 | | - animation name of infinite from the second. |
205 | | - */ |
206 | | - var animationKeywords = { |
207 | | - $alternate: 1, |
208 | | - '$alternate-reverse': 1, |
209 | | - $backwards: 1, |
210 | | - $both: 1, |
211 | | - $ease: 1, |
212 | | - '$ease-in': 1, |
213 | | - '$ease-in-out': 1, |
214 | | - '$ease-out': 1, |
215 | | - $forwards: 1, |
216 | | - $infinite: 1, |
217 | | - $linear: 1, |
218 | | - $none: Infinity, // No matter how many times you write none, it will never be an animation name |
219 | | - $normal: 1, |
220 | | - $paused: 1, |
221 | | - $reverse: 1, |
222 | | - $running: 1, |
223 | | - '$step-end': 1, |
224 | | - '$step-start': 1, |
225 | | - $initial: Infinity, |
226 | | - $inherit: Infinity, |
227 | | - $unset: Infinity |
| 164 | +module.exports = postcss.plugin(plugin, (options = {}) => css => { |
| 165 | + if ( |
| 166 | + options.mode && |
| 167 | + options.mode !== 'global' && |
| 168 | + options.mode !== 'local' && |
| 169 | + options.mode !== 'pure' |
| 170 | + ) { |
| 171 | + throw Error( |
| 172 | + 'options.mode must be either "global", "local" or "pure" (default "local")' |
| 173 | + ) |
228 | 174 | } |
229 | | - |
230 | | - var didParseAnimationName = false |
231 | | - var parsedAnimationKeywords = {} |
232 | | - return nodes.map(function(valueNode) { |
233 | | - var value = valueNode.type === 'item' ? valueNode.name.toLowerCase() : null |
234 | | - |
235 | | - var shouldParseAnimationName = false |
236 | | - |
237 | | - if (!didParseAnimationName && value && validIdent.test(value)) { |
238 | | - if ('$' + value in animationKeywords) { |
239 | | - parsedAnimationKeywords['$' + value] = '$' + value in |
240 | | - parsedAnimationKeywords |
241 | | - ? parsedAnimationKeywords['$' + value] + 1 |
242 | | - : 0 |
243 | | - |
244 | | - shouldParseAnimationName = |
245 | | - parsedAnimationKeywords['$' + value] >= animationKeywords['$' + value] |
246 | | - } else { |
247 | | - shouldParseAnimationName = true |
248 | | - } |
| 175 | + var pureMode = options.mode === 'pure' |
| 176 | + var globalMode = options.mode === 'global' |
| 177 | + css.walkRules(function(rule) { |
| 178 | + if (rule.parent.type === 'atrule' && /keyframes$/.test(rule.parent.name)) { |
| 179 | + // ignore keyframe rules |
| 180 | + return |
249 | 181 | } |
250 | | - |
251 | | - var subContext = { |
252 | | - options: context.options, |
253 | | - global: context.global, |
254 | | - localizeNextItem: shouldParseAnimationName && !context.global |
| 182 | + var selector = Tokenizer.parse(rule.selector) |
| 183 | + var context = { |
| 184 | + options: options, |
| 185 | + global: globalMode, |
| 186 | + hasPureGlobals: false |
255 | 187 | } |
256 | | - return localizeDeclNode(valueNode, subContext) |
257 | | - }) |
258 | | -} |
259 | | - |
260 | | -function localizeAnimationShorthandDeclValues(valuesNode, decl, context) { |
261 | | - var newValuesNode = Object.create(valuesNode) |
262 | | - newValuesNode.nodes = valuesNode.nodes.map(function(valueNode, index) { |
263 | | - var newValueNode = Object.create(valueNode) |
264 | | - newValueNode.nodes = localizeAnimationShorthandDeclValueNodes( |
265 | | - valueNode.nodes, |
266 | | - context |
267 | | - ) |
268 | | - return newValueNode |
269 | | - }) |
270 | | - decl.value = Tokenizer.stringifyValues(newValuesNode) |
271 | | -} |
272 | | - |
273 | | -function localizeDeclValues(localize, valuesNode, decl, context) { |
274 | | - var newValuesNode = Object.create(valuesNode) |
275 | | - newValuesNode.nodes = valuesNode.nodes.map(function(valueNode) { |
276 | | - var subContext = { |
277 | | - options: context.options, |
278 | | - global: context.global, |
279 | | - localizeNextItem: localize && !context.global |
| 188 | + var newSelector |
| 189 | + try { |
| 190 | + newSelector = localizeNode(selector, context) |
| 191 | + } catch (e) { |
| 192 | + throw rule.error(e.message) |
280 | 193 | } |
281 | | - return localizeDeclValue(valueNode, subContext) |
282 | | - }) |
283 | | - decl.value = Tokenizer.stringifyValues(newValuesNode) |
284 | | -} |
285 | | - |
286 | | -function localizeDecl(decl, context) { |
287 | | - var valuesNode = Tokenizer.parseValues(decl.value) |
288 | | - |
289 | | - var isAnimation = /animation?$/.test(decl.prop) |
290 | | - if (isAnimation) |
291 | | - return localizeAnimationShorthandDeclValues(valuesNode, decl, context) |
292 | | - |
293 | | - var isAnimationName = /animation(-name)?$/.test(decl.prop) |
294 | | - if (isAnimationName) |
295 | | - return localizeDeclValues(true, valuesNode, decl, context) |
296 | | - |
297 | | - return localizeDeclValues(false, valuesNode, decl, context) |
298 | | -} |
299 | | - |
300 | | -module.exports = postcss.plugin( |
301 | | - 'postcss-modules-local-by-default', |
302 | | - (options = {}) => (css, result) => { |
303 | | - if ( |
304 | | - options.mode && |
305 | | - options.mode !== 'global' && |
306 | | - options.mode !== 'local' && |
307 | | - options.mode !== 'pure' |
308 | | - ) { |
309 | | - throw Error( |
310 | | - 'options.mode must be either "global", "local" or "pure" (default "local")' |
| 194 | + if (pureMode && context.hasPureGlobals) { |
| 195 | + throw rule.error( |
| 196 | + 'Selector "' + |
| 197 | + Tokenizer.stringify(selector) + |
| 198 | + '" is not pure ' + |
| 199 | + '(pure selectors must contain at least one local class or id)' |
311 | 200 | ) |
312 | 201 | } |
313 | | - var pureMode = options.mode === 'pure' |
314 | | - var globalMode = options.mode === 'global' |
315 | | - css.walkAtRules(function(atrule) { |
316 | | - if (/keyframes$/.test(atrule.name)) { |
317 | | - var globalMatch = /^\s*:global\s*\((.+)\)\s*$/.exec(atrule.params) |
318 | | - var localMatch = /^\s*:local\s*\((.+)\)\s*$/.exec(atrule.params) |
319 | | - var globalKeyframes = globalMode |
320 | | - if (globalMatch) { |
321 | | - if (pureMode) { |
322 | | - throw atrule.error( |
323 | | - '@keyframes :global(...) is not allowed in pure mode' |
324 | | - ) |
325 | | - } |
326 | | - atrule.params = globalMatch[1] |
327 | | - globalKeyframes = true |
328 | | - } else if (localMatch) { |
329 | | - atrule.params = localMatch[0] |
330 | | - globalKeyframes = false |
331 | | - } else if (!globalMode) { |
332 | | - atrule.params = ':local(' + atrule.params + ')' |
333 | | - } |
334 | | - atrule.walkDecls(function(decl) { |
335 | | - localizeDecl(decl, { |
336 | | - options: options, |
337 | | - global: globalKeyframes |
338 | | - }) |
339 | | - }) |
340 | | - } |
341 | | - }) |
342 | | - css.walkRules(function(rule) { |
343 | | - if ( |
344 | | - rule.parent.type === 'atrule' && |
345 | | - /keyframes$/.test(rule.parent.name) |
346 | | - ) { |
347 | | - // ignore keyframe rules |
348 | | - return |
349 | | - } |
350 | | - var selector = Tokenizer.parse(rule.selector) |
351 | | - var context = { |
352 | | - options: options, |
353 | | - global: globalMode, |
354 | | - hasPureGlobals: false |
355 | | - } |
356 | | - var newSelector |
357 | | - try { |
358 | | - newSelector = localizeNode(selector, context) |
359 | | - } catch (e) { |
360 | | - throw rule.error(e.message) |
361 | | - } |
362 | | - if (pureMode && context.hasPureGlobals) { |
363 | | - throw rule.error( |
364 | | - 'Selector "' + |
365 | | - Tokenizer.stringify(selector) + |
366 | | - '" is not pure ' + |
367 | | - '(pure selectors must contain at least one local class or id)' |
368 | | - ) |
369 | | - } |
370 | | - // Less-syntax mixins parse as rules with no nodes |
371 | | - if (rule.nodes) { |
372 | | - rule.nodes.forEach(function(decl) { |
373 | | - localizeDecl(decl, context) |
374 | | - }) |
375 | | - } |
376 | | - rule.selector = Tokenizer.stringify(newSelector) |
377 | | - }) |
378 | | - } |
379 | | -) |
| 202 | + rule.selector = Tokenizer.stringify(newSelector) |
| 203 | + }) |
| 204 | +}) |
0 commit comments