@@ -6,15 +6,16 @@ const pluginOptions = {
6
6
includePaths : [ ] ,
7
7
localIdentName : '[local]-[hash:base64:6]' ,
8
8
getLocalIdent : getLocalIdent ,
9
+ strict : false ,
9
10
} ;
10
11
11
12
const regex = {
12
13
module : / \$ ( s t y l e ) ? \. ( : ? [ \w \d - ] * ) / gm,
13
14
style : / < s t y l e ( \s [ ^ ] * ?) ? > ( [ ^ ] * ?) < \/ s t y l e > / gi,
14
15
pathUnallowed : / [ < > : " / \\ | ? * ] / g,
15
16
class : ( className ) => {
16
- return new RegExp ( `\\.(${ className } )\\b(?![-_])` , 'gm' )
17
- }
17
+ return new RegExp ( `\\.(${ className } )\\b(?![-_])` , 'gm' ) ;
18
+ } ,
18
19
} ;
19
20
20
21
let moduleClasses = { } ;
@@ -35,7 +36,7 @@ function generateName(resourcePath, styles, className) {
35
36
interpolateName ( { resourcePath } , localName , { content } )
36
37
. replace ( / \. / g, '-' )
37
38
) ;
38
-
39
+
39
40
// replace unwanted characters from [path]
40
41
if ( regex . pathUnallowed . test ( interpolatedName ) ) {
41
42
interpolatedName = interpolatedName . replace ( regex . pathUnallowed , '_' ) ;
@@ -72,20 +73,38 @@ const markup = async ({ content, filename }) => {
72
73
const styles = content . match ( regex . style ) ;
73
74
moduleClasses [ filename ] = { } ;
74
75
75
- return { code : content . replace ( regex . module , ( match , key , className ) => {
76
- let replacement = '' ;
77
- if ( styles . length ) {
78
- if ( regex . class ( className ) . test ( styles [ 0 ] ) ) {
79
- const interpolatedName = generateName (
80
- filename ,
81
- styles [ 0 ] ,
82
- className
76
+ return {
77
+ code : content . replace ( regex . module , ( match , key , className ) => {
78
+ let replacement = '' ;
79
+ if ( ! className . length ) {
80
+ throw new Error (
81
+ `Invalid class name in file ${ filename } .\n` +
82
+ 'This usually happens when using dynamic classes with svelte-preprocess-cssmodules.'
83
83
) ;
84
+ }
85
+
86
+ if ( ! regex . class ( className ) . test ( `.${ className } ` ) ) {
87
+ throw new Error ( `Classname "${ className } " in file ${ filename } is not valid` ) ;
88
+ }
89
+
90
+ if ( styles . length ) {
91
+ if ( ! regex . class ( className ) . test ( styles [ 0 ] ) ) {
92
+ if ( pluginOptions . strict ) {
93
+ throw new Error (
94
+ `Classname "${ className } " was not found in declared ${ filename } <style>`
95
+ ) ;
96
+ } else {
97
+ // In non-strict mode, we just remove $style classes that don't have a definition
98
+ return '' ;
99
+ }
100
+ }
101
+
102
+ const interpolatedName = generateName ( filename , styles [ 0 ] , className ) ;
84
103
85
104
const customInterpolatedName = pluginOptions . getLocalIdent (
86
105
{
87
106
context : path . dirname ( filename ) ,
88
- resourcePath : filename ,
107
+ resourcePath : filename ,
89
108
} ,
90
109
{
91
110
interpolatedName,
@@ -101,18 +120,19 @@ const markup = async ({ content, filename }) => {
101
120
moduleClasses [ filename ] [ className ] = customInterpolatedName ;
102
121
replacement = customInterpolatedName ;
103
122
}
104
- }
105
- return replacement ;
106
- } ) } ;
123
+ return replacement ;
124
+ } ) ,
125
+ } ;
107
126
} ;
108
127
128
+ const GLOBALIZE_PLACEHOLDER = '__to_globalize__' ;
109
129
const style = async ( { content, filename } ) => {
110
130
let code = content ;
111
131
112
132
if ( ! moduleClasses . hasOwnProperty ( filename ) ) {
113
133
return { code } ;
114
134
}
115
-
135
+
116
136
const classes = moduleClasses [ filename ] ;
117
137
118
138
if ( Object . keys ( classes ) . length === 0 ) {
@@ -122,11 +142,35 @@ const style = async ({ content, filename }) => {
122
142
for ( const className in classes ) {
123
143
code = code . replace (
124
144
regex . class ( className ) ,
125
- ( ) => `:global( .${ classes [ className ] } ) `
145
+ ( ) => `.${ GLOBALIZE_PLACEHOLDER } ${ classes [ className ] } `
126
146
) ;
127
147
}
128
148
129
- return { code } ;
149
+ let codeOutput = '' ;
150
+ let cssRules = code . split ( '}' ) ;
151
+
152
+ // Remove last element of the split. It should be the empty string that comes after the last ' }'.
153
+ const lastChunk = cssRules . pop ( ) ;
154
+
155
+ // We wrap all css selector containing a scoped CSS class in :global() svelte css statement
156
+ for ( const cssRule of cssRules ) {
157
+ let [ selector , rule ] = cssRule . split ( '{' ) ;
158
+ if ( selector . includes ( GLOBALIZE_PLACEHOLDER ) ) {
159
+ const selectorTrimmed = selector . trim ( ) ;
160
+ selector = selector . replace (
161
+ selectorTrimmed ,
162
+ `:global(${ selectorTrimmed . replace (
163
+ new RegExp ( GLOBALIZE_PLACEHOLDER , 'g' ) ,
164
+ ''
165
+ ) } )`
166
+ ) ;
167
+ }
168
+ codeOutput += `${ selector } {${ rule } }` ;
169
+ }
170
+
171
+ codeOutput += lastChunk ;
172
+
173
+ return { code : codeOutput } ;
130
174
} ;
131
175
132
176
module . exports = ( options ) => {
@@ -136,5 +180,5 @@ module.exports = (options) => {
136
180
return {
137
181
markup,
138
182
style,
139
- }
140
- } ;
183
+ } ;
184
+ } ;
0 commit comments