11'use strict' ;
22
33const postcss = require ( 'postcss' ) ;
4- const Tokenizer = require ( 'css -selector-tokenizer ' ) ;
4+ const selectorParser = require ( 'postcss -selector-parser ' ) ;
55
66const hasOwnProperty = Object . prototype . hasOwnProperty ;
77
8- function getSingleLocalNamesForComposes ( selectors ) {
9- return selectors . nodes . map ( node => {
8+ function getSingleLocalNamesForComposes ( root ) {
9+ return root . nodes . map ( node => {
1010 if ( node . type !== 'selector' || node . nodes . length !== 1 ) {
1111 throw new Error (
12- 'composition is only allowed when selector is single :local class name not in "' +
13- Tokenizer . stringify ( selectors ) +
14- '"'
12+ `composition is only allowed when selector is single :local class name not in "${ root } "`
1513 ) ;
1614 }
1715 node = node . nodes [ 0 ] ;
1816 if (
19- node . type !== 'nested- pseudo-class ' ||
20- node . name !== 'local' ||
17+ node . type !== 'pseudo' ||
18+ node . value !== ': local' ||
2119 node . nodes . length !== 1
2220 ) {
2321 throw new Error (
2422 'composition is only allowed when selector is single :local class name not in "' +
25- Tokenizer . stringify ( selectors ) +
23+ root +
2624 '", "' +
27- Tokenizer . stringify ( node ) +
25+ node +
2826 '" is weird'
2927 ) ;
3028 }
31- node = node . nodes [ 0 ] ;
32- if ( node . type !== 'selector' || node . nodes . length !== 1 ) {
29+ node = node . first ;
30+ if ( node . type !== 'selector' || node . length !== 1 ) {
3331 throw new Error (
3432 'composition is only allowed when selector is single :local class name not in "' +
35- Tokenizer . stringify ( selectors ) +
33+ root +
3634 '", "' +
37- Tokenizer . stringify ( node ) +
35+ node +
3836 '" is weird'
3937 ) ;
4038 }
41- node = node . nodes [ 0 ] ;
39+ node = node . first ;
4240 if ( node . type !== 'class' ) {
4341 // 'id' is not possible, because you can't compose ids
4442 throw new Error (
4543 'composition is only allowed when selector is single :local class name not in "' +
46- Tokenizer . stringify ( selectors ) +
44+ root +
4745 '", "' +
48- Tokenizer . stringify ( node ) +
46+ node +
4947 '" is weird'
5048 ) ;
5149 }
52- return node . name ;
50+ return node . value ;
5351 } ) ;
5452}
5553
@@ -74,40 +72,45 @@ const processor = postcss.plugin('postcss-modules-scope', function(options) {
7472 }
7573
7674 function localizeNode ( node ) {
77- const newNode = Object . create ( node ) ;
7875 switch ( node . type ) {
7976 case 'selector' :
80- newNode . nodes = node . nodes . map ( localizeNode ) ;
81- return newNode ;
77+ node . nodes = node . map ( localizeNode ) ;
78+ return node ;
8279 case 'class' :
80+ return selectorParser . className ( {
81+ value : exportScopedName ( node . value ) ,
82+ } ) ;
8383 case 'id' : {
84- newNode . name = exportScopedName ( node . name ) ;
85- return newNode ;
84+ return selectorParser . id ( {
85+ value : exportScopedName ( node . value ) ,
86+ } ) ;
8687 }
8788 }
8889 throw new Error (
89- node . type +
90- ' ("' +
91- Tokenizer . stringify ( node ) +
92- '") is not allowed in a :local block'
90+ `${ node . type } ("${ node } ") is not allowed in a :local block`
9391 ) ;
9492 }
9593
9694 function traverseNode ( node ) {
9795 switch ( node . type ) {
98- case 'nested- pseudo-class ' :
99- if ( node . name === 'local' ) {
96+ case 'pseudo' :
97+ if ( node . value === ': local' ) {
10098 if ( node . nodes . length !== 1 ) {
10199 throw new Error ( 'Unexpected comma (",") in :local block' ) ;
102100 }
103- return localizeNode ( node . nodes [ 0 ] ) ;
101+ const selector = localizeNode ( node . first , node . spaces ) ;
102+ // move the spaces that were around the psuedo selector to the first
103+ // non-container node
104+ selector . first . spaces = node . spaces ;
105+
106+ node . replaceWith ( selector ) ;
107+ return ;
104108 }
105109 /* falls through */
106- case 'selectors ' :
110+ case 'root ' :
107111 case 'selector' : {
108- const newNode = Object . create ( node ) ;
109- newNode . nodes = node . nodes . map ( traverseNode ) ;
110- return newNode ;
112+ node . each ( traverseNode ) ;
113+ break ;
111114 }
112115 }
113116 return node ;
@@ -125,14 +128,16 @@ const processor = postcss.plugin('postcss-modules-scope', function(options) {
125128
126129 // Find any :local classes
127130 css . walkRules ( rule => {
128- const selector = Tokenizer . parse ( rule . selector ) ;
129- const newSelector = traverseNode ( selector ) ;
130- rule . selector = Tokenizer . stringify ( newSelector ) ;
131+ let parsedSelector = selectorParser ( ) . astSync ( rule ) ;
132+
133+ rule . selector = traverseNode ( parsedSelector . clone ( ) ) . toString ( ) ;
134+ // console.log(rule.selector);
131135 rule . walkDecls ( / c o m p o s e s | c o m p o s e - w i t h / , decl => {
132- const localNames = getSingleLocalNamesForComposes ( selector ) ;
136+ const localNames = getSingleLocalNamesForComposes ( parsedSelector ) ;
133137 const classes = decl . value . split ( / \s + / ) ;
134138 classes . forEach ( className => {
135139 const global = / ^ g l o b a l \( ( [ ^ \) ] + ) \) $ / . exec ( className ) ;
140+
136141 if ( global ) {
137142 localNames . forEach ( exportedName => {
138143 exports [ exportedName ] . push ( global [ 1 ] ) ;
@@ -196,7 +201,7 @@ const processor = postcss.plugin('postcss-modules-scope', function(options) {
196201 exportRule . append ( {
197202 prop : exportedName ,
198203 value : exports [ exportedName ] . join ( ' ' ) ,
199- raws : { before : '\n ' }
204+ raws : { before : '\n ' } ,
200205 } )
201206 ) ;
202207 css . append ( exportRule ) ;
@@ -209,7 +214,7 @@ processor.generateScopedName = function(exportedName, path) {
209214 . replace ( / \. [ ^ \. \/ \\ ] + $ / , '' )
210215 . replace ( / [ \W _ ] + / g, '_' )
211216 . replace ( / ^ _ | _ $ / g, '' ) ;
212- return `_${ sanitisedPath } __${ exportedName } ` ;
217+ return `_${ sanitisedPath } __${ exportedName } ` . trim ( ) ;
213218} ;
214219
215220module . exports = processor ;
0 commit comments