8
8
pluginName,
9
9
getRootDir,
10
10
pluginNamespace,
11
- builtCssSurfix
11
+ buildingCssSuffix,
12
+ builtCssSuffix,
13
+ modulesCssRegExp,
14
+ builtModulesCssRegExp,
15
+ getRelativePath,
16
+ getBuildId
12
17
} = require ( './utils.js' ) ;
13
18
const cssHandler = require ( '@parcel/css' ) ;
14
19
const camelCase = require ( 'lodash/camelCase' ) ;
@@ -21,6 +26,7 @@ const cache = require('./cache.js');
21
26
* @returns {Promise<{resolveDir: string; js: string; css: string; exports: Record<string, string>}> }
22
27
*/
23
28
const buildCssModulesJs = async ( { fullPath, options, build } ) => {
29
+ const cssFileName = path . basename ( fullPath ) ; // e.g. xxx.module.css?esbuild-css-modules-plugin-building
24
30
const { buildId } = build . context ;
25
31
const resolveDir = path . dirname ( fullPath ) ;
26
32
const classPrefix = path . basename ( fullPath , path . extname ( fullPath ) ) . replace ( / \. / g, '-' ) + '__' ;
@@ -41,18 +47,18 @@ const buildCssModulesJs = async ({ fullPath, options, build }) => {
41
47
let cssModulesContent = code . toString ( 'utf-8' ) ;
42
48
43
49
const cssModulesJSON = { } ;
44
- Object . keys ( exports ) . forEach ( ( originClass ) => {
45
- const patchedClass = exports [ originClass ] . name ;
46
- cssModulesJSON [ camelCase ( originClass ) ] = classPrefix + patchedClass ;
47
- cssModulesContent = cssModulesContent . replace (
48
- new RegExp ( `\\.${ patchedClass } ` , 'g' ) ,
49
- '.' + classPrefix + patchedClass
50
- ) ;
51
- } ) ;
52
- const classNamesMapString = JSON . stringify ( cssModulesJSON ) ;
53
50
54
- const cssFileName = path . basename ( fullPath ) ;
55
- const builtCssFileName = cssFileName . replace ( / \. m o d u l e s ? \. c s s $ / i, builtCssSurfix ) ;
51
+ Object . keys ( exports )
52
+ . sort ( ) // to keep order consistent in different builds
53
+ . forEach ( ( originClass ) => {
54
+ const patchedClass = exports [ originClass ] . name ;
55
+ cssModulesJSON [ camelCase ( originClass ) ] = classPrefix + patchedClass ;
56
+ cssModulesContent = cssModulesContent . replace (
57
+ new RegExp ( `\\.${ patchedClass } ` , 'g' ) ,
58
+ '.' + classPrefix + patchedClass
59
+ ) ;
60
+ } ) ;
61
+ const classNamesMapString = JSON . stringify ( cssModulesJSON ) ;
56
62
57
63
let cssWithSourceMap = cssModulesContent ;
58
64
if ( map ) {
@@ -62,10 +68,14 @@ const buildCssModulesJs = async ({ fullPath, options, build }) => {
62
68
}
63
69
64
70
// fix path issue on Windows: https://github.com/indooorsman/esbuild-css-modules-plugin/issues/12
65
- const cssImportPath = './' + builtCssFileName . split ( path . sep ) . join ( path . posix . sep ) . trim ( ) ;
66
- const injectCode = `import "${ cssImportPath } ";` ;
67
-
68
- const exportDefault = options . inject
71
+ const cssImportPath =
72
+ './' +
73
+ cssFileName . split ( path . sep ) . join ( path . posix . sep ) . trim ( ) . replace ( buildingCssSuffix , '' ) +
74
+ builtCssSuffix ;
75
+ // => ./xxx.module.css?esbuild-css-modules-plugin-built
76
+ const importStatement = `import "${ cssImportPath } ";` ;
77
+
78
+ const exportStatement = options . inject
69
79
? `
70
80
export default new Proxy(${ classNamesMapString } , {
71
81
get: function(source, key) {
@@ -78,7 +88,7 @@ export default new Proxy(${classNamesMapString}, {
78
88
`
79
89
: `export default ${ classNamesMapString } ;` ;
80
90
81
- const js = `${ injectCode } \n${ exportDefault } ;` ;
91
+ const js = `${ importStatement } \n${ exportStatement } ;` ;
82
92
83
93
return {
84
94
js,
@@ -89,68 +99,72 @@ export default new Proxy(${classNamesMapString}, {
89
99
} ;
90
100
91
101
/**
92
- * onResolve
102
+ * onResolveModulesCss
93
103
* @description mark module(s).css as sideEffects and add namespace
94
104
* @param {import('esbuild').OnResolveArgs } args
95
105
* @param {import('..').Build } build
96
106
* @returns {Promise<import('esbuild').OnResolveResult> }
97
107
*/
98
- const onResolve = async ( args , build ) => {
99
- const { resolve, initialOptions } = build ;
100
- const log = getLogger ( build ) ;
101
- const { resolveDir, path : p , pluginData } = args ;
102
- const { buildRoot } = build . context ;
108
+ const onResolveModulesCss = async ( args , build ) => {
109
+ const { resolve, initialOptions, context } = build ;
110
+ const { resolveDir, path : p , pluginData = { } } = args ;
111
+ const { log, relative } = context ;
103
112
const { path : absPath } = await resolve ( p , { resolveDir } ) ;
104
- log ( 'resolve' , p , 'to' , path . relative ( buildRoot , absPath ) ) ;
113
+ const rpath = relative ( absPath ) ;
114
+ log ( 'resolve' , p , 'to' , rpath , 'from build root' ) ;
105
115
106
116
/**
107
117
* @type {import('esbuild').OnResolveResult }
108
118
*/
109
119
const result = {
110
120
namespace : pluginNamespace ,
111
- path : absPath ,
121
+ suffix : buildingCssSuffix ,
122
+ path : rpath ,
112
123
external : false ,
113
124
pluginData : {
114
- ...( pluginData ?? { } ) ,
115
- resolveDir : pluginData ?. resolveDir || resolveDir ,
116
- path : absPath
125
+ ...pluginData ,
126
+ relativePathToBuildRoot : rpath
117
127
} ,
118
128
sideEffects : true ,
119
129
pluginName
120
130
} ;
121
131
122
132
if ( initialOptions . watch ) {
123
- log ( 'watching' , path . relative ( buildRoot , absPath ) ) ;
133
+ log ( 'watching' , rpath ) ;
124
134
result . watchFiles = [ absPath ] ;
125
135
}
126
136
127
137
return result ;
128
138
} ;
129
139
130
140
/**
131
- * onLoad
141
+ * onLoadModulesCss
132
142
* @param {import('..').Build } build
133
143
* @param {import('..').Options } options
134
144
* @param {import('esbuild').OnLoadArgs } args
135
145
* @return {(import('esbuild').OnLoadResult | null | undefined | Promise<import('esbuild').OnLoadResult | null | undefined>) }
136
146
*/
137
- const onLoad = async ( build , options , args ) => {
138
- const log = getLogger ( build ) ;
139
- const { path : fullPath } = args ;
140
- const { buildRoot } = build . context ;
147
+ const onLoadModulesCss = async ( build , options , args ) => {
148
+ const { path : maybeFullPath , pluginData = { } } = args ;
149
+ const { buildRoot, log, relative } = build . context ;
150
+ const absPath = path . isAbsolute ( maybeFullPath )
151
+ ? maybeFullPath
152
+ : path . resolve ( buildRoot , maybeFullPath ) ;
153
+ const rpath = relative ( absPath ) ;
141
154
142
- const cached = cache . get ( fullPath ) ;
155
+ log ( `loading ${ relative ( args . path ) } ${ args . suffix } ` ) ;
156
+
157
+ const cached = cache . get ( absPath ) ;
143
158
if ( cached ) {
144
- log ( 'return cache for' , path . relative ( buildRoot , fullPath ) ) ;
159
+ log ( 'return built cache for' , rpath ) ;
145
160
return cached ;
146
161
}
147
162
148
- const rpath = path . relative ( buildRoot , fullPath ) ;
149
163
const hex = createHash ( 'sha256' ) . update ( rpath ) . digest ( 'hex' ) ;
150
164
const digest = hex . slice ( hex . length - 255 , hex . length ) ;
151
165
152
166
const { js, resolveDir, css, exports } = await buildCssModulesJs ( {
153
- fullPath,
167
+ fullPath : absPath ,
154
168
options,
155
169
digest,
156
170
build
@@ -160,15 +174,15 @@ const onLoad = async (build, options, args) => {
160
174
pluginName,
161
175
resolveDir,
162
176
pluginData : {
177
+ ...pluginData ,
163
178
css,
164
179
exports,
165
- digest,
166
- resolveDir
180
+ digest
167
181
} ,
168
182
contents : js ,
169
183
loader : 'js'
170
184
} ;
171
- cache . set ( fullPath , result ) ;
185
+ cache . set ( absPath , result ) ;
172
186
173
187
return result ;
174
188
} ;
@@ -200,7 +214,7 @@ const onEnd = async (build, options, result) => {
200
214
if ( path . extname ( f ) === '.css' ) {
201
215
const fullpath = path . resolve ( buildRoot , f ) ;
202
216
const css = readFileSync ( fullpath ) ;
203
- cssContents . push ( `\n/* ${ f } */\n ${ css } \n` ) ;
217
+ cssContents . push ( `${ css } \n` ) ;
204
218
}
205
219
} ) ;
206
220
log ( 'inject css to' , path . relative ( buildRoot , injectTo ) ) ;
@@ -221,29 +235,36 @@ const onEnd = async (build, options, result) => {
221
235
* @return {Promise<void> }
222
236
*/
223
237
const prepareBuild = async ( build , options ) => {
224
- const buildId = v4 ( ) . replace ( / [ ^ 0 - 9 a - z A - Z ] / g , '' ) ;
238
+ const buildId = getBuildId ( build ) ;
225
239
build . initialOptions . metafile = true ;
226
240
const packageRoot = options . root ;
227
241
const buildRoot = getRootDir ( build ) ;
242
+ const log = getLogger ( build ) ;
243
+ const relative = ( to ) => getRelativePath ( build , to ) ;
244
+
245
+ log ( `root of this build(#${ buildId } ):` , buildRoot ) ;
228
246
229
247
build . context = {
230
248
buildId,
231
249
buildRoot,
232
- packageRoot
250
+ packageRoot,
251
+ log,
252
+ relative
233
253
} ;
234
254
} ;
235
255
236
256
/**
237
- * onLoadBuiltCss
257
+ * onLoadBuiltModulesCss
238
258
* @param {import('esbuild').OnLoadArgs } args
239
259
* @param {import('..').Build } build
240
260
* @returns {Promise<import('esbuild').OnLoadResult> }
241
261
*/
242
- const onLoadBuiltCss = async ( { pluginData } , build ) => {
243
- const { buildRoot } = build . context ;
244
- const { css, resolveDir, path : p } = pluginData ;
245
- const log = getLogger ( build ) ;
246
- log ( 'loading built css' , p , 'in' , path . relative ( buildRoot , resolveDir ) ) ;
262
+ const onLoadBuiltModulesCss = async ( { pluginData } , build ) => {
263
+ const { log, buildRoot } = build . context ;
264
+ const { css, relativePathToBuildRoot } = pluginData ;
265
+ const absPath = path . resolve ( buildRoot , relativePathToBuildRoot ) ;
266
+ const resolveDir = path . dirname ( absPath ) ;
267
+ log ( 'loading built css for' , relativePathToBuildRoot ) ;
247
268
248
269
/**
249
270
* @type {import('esbuild').OnLoadResult }
@@ -260,12 +281,15 @@ const onLoadBuiltCss = async ({ pluginData }, build) => {
260
281
} ;
261
282
262
283
/**
263
- * onResolveBuiltCss
284
+ * onResolveBuiltModulesCss
264
285
* @param {import('esbuild').OnResolveArgs } args
286
+ * @param {import('..').Build } build
265
287
* @returns {Promise<import('esbuild').OnResolveResult> }
266
288
*/
267
- const onResolveBuiltCss = async ( args ) => {
268
- const { resolveDir, path : p , pluginData = { } } = args ;
289
+ const onResolveBuiltModulesCss = async ( args , build ) => {
290
+ const { path : p , pluginData = { } } = args ;
291
+
292
+ build . context ?. log ( `resolve built css: ${ args . path } ` ) ;
269
293
270
294
/**
271
295
* @type {import('esbuild').OnResolveResult }
@@ -274,11 +298,7 @@ const onResolveBuiltCss = async (args) => {
274
298
namespace : pluginNamespace ,
275
299
path : p ,
276
300
external : false ,
277
- pluginData : {
278
- ...pluginData ,
279
- resolveDir : pluginData . resolveDir || resolveDir ,
280
- path : p
281
- } ,
301
+ pluginData,
282
302
sideEffects : true ,
283
303
pluginName
284
304
} ;
@@ -294,27 +314,32 @@ const onResolveBuiltCss = async (args) => {
294
314
*/
295
315
const setup = async ( build , options ) => {
296
316
await prepareBuild ( build , options ) ;
297
- const { buildRoot } = build . context ;
298
- const log = getLogger ( build ) ;
299
317
300
- build . onResolve ( { filter : / \. m o d u l e s ? \. c s s $ / i , namespace : 'file' } , async ( args ) => {
301
- return await onResolve ( args , build ) ;
318
+ build . onResolve ( { filter : modulesCssRegExp , namespace : 'file' } , async ( args ) => {
319
+ return await onResolveModulesCss ( args , build ) ;
302
320
} ) ;
303
321
304
- build . onLoad ( { filter : / \. m o d u l e s ? \. c s s $ / i, namespace : pluginNamespace } , async ( args ) => {
305
- log ( 'loading' , path . relative ( buildRoot , args . path ) ) ;
306
- return await onLoad ( build , options , args ) ;
322
+ build . onLoad ( { filter : modulesCssRegExp , namespace : pluginNamespace } , async ( args ) => {
323
+ return await onLoadModulesCss ( build , options , args ) ;
307
324
} ) ;
308
325
309
- build . onResolve ( { filter : new RegExp ( builtCssSurfix , 'i' ) } , onResolveBuiltCss ) ;
326
+ build . onResolve (
327
+ {
328
+ filter : builtModulesCssRegExp ,
329
+ namespace : pluginNamespace
330
+ } ,
331
+ async ( args ) => {
332
+ return await onResolveBuiltModulesCss ( args , build ) ;
333
+ }
334
+ ) ;
310
335
311
336
build . onLoad (
312
337
{
313
- filter : new RegExp ( builtCssSurfix , 'i' ) ,
338
+ filter : builtModulesCssRegExp ,
314
339
namespace : pluginNamespace
315
340
} ,
316
341
async ( args ) => {
317
- return await onLoadBuiltCss ( args , build ) ;
342
+ return await onLoadBuiltModulesCss ( args , build ) ;
318
343
}
319
344
) ;
320
345
0 commit comments