Skip to content

Commit fb48e03

Browse files
committed
v2.2.7
1 parent 662697b commit fb48e03

File tree

5 files changed

+150
-78
lines changed

5 files changed

+150
-78
lines changed

changelog.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,7 @@
1+
## V2.2.7
2+
- **[v2]** refine some logs
3+
- **[v2]** make hash of outputs stable
4+
15
## V2.2.6
26
- **[v2]** refine some logs
37
- **[v2]** handle `onResolve` for `.modules.css` files to add `sideEffects: true` & `namespace` to the resolve result

index.d.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,8 @@ declare interface BuildContext {
5959
buildId: string;
6060
buildRoot: string;
6161
packageRoot?: string;
62+
log: (...args: any[]) => void;
63+
relative: (to: string) => `.${string}`;
6264
}
6365

6466
declare function CssModulesPlugin(options?: PluginOptions): Plugin;

lib/plugin.js

Lines changed: 93 additions & 68 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,12 @@ const {
88
pluginName,
99
getRootDir,
1010
pluginNamespace,
11-
builtCssSurfix
11+
buildingCssSuffix,
12+
builtCssSuffix,
13+
modulesCssRegExp,
14+
builtModulesCssRegExp,
15+
getRelativePath,
16+
getBuildId
1217
} = require('./utils.js');
1318
const cssHandler = require('@parcel/css');
1419
const camelCase = require('lodash/camelCase');
@@ -21,6 +26,7 @@ const cache = require('./cache.js');
2126
* @returns {Promise<{resolveDir: string; js: string; css: string; exports: Record<string, string>}>}
2227
*/
2328
const buildCssModulesJs = async ({ fullPath, options, build }) => {
29+
const cssFileName = path.basename(fullPath); // e.g. xxx.module.css?esbuild-css-modules-plugin-building
2430
const { buildId } = build.context;
2531
const resolveDir = path.dirname(fullPath);
2632
const classPrefix = path.basename(fullPath, path.extname(fullPath)).replace(/\./g, '-') + '__';
@@ -41,18 +47,18 @@ const buildCssModulesJs = async ({ fullPath, options, build }) => {
4147
let cssModulesContent = code.toString('utf-8');
4248

4349
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);
5350

54-
const cssFileName = path.basename(fullPath);
55-
const builtCssFileName = cssFileName.replace(/\.modules?\.css$/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);
5662

5763
let cssWithSourceMap = cssModulesContent;
5864
if (map) {
@@ -62,10 +68,14 @@ const buildCssModulesJs = async ({ fullPath, options, build }) => {
6268
}
6369

6470
// 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
6979
? `
7080
export default new Proxy(${classNamesMapString}, {
7181
get: function(source, key) {
@@ -78,7 +88,7 @@ export default new Proxy(${classNamesMapString}, {
7888
`
7989
: `export default ${classNamesMapString};`;
8090

81-
const js = `${injectCode}\n${exportDefault};`;
91+
const js = `${importStatement}\n${exportStatement};`;
8292

8393
return {
8494
js,
@@ -89,68 +99,72 @@ export default new Proxy(${classNamesMapString}, {
8999
};
90100

91101
/**
92-
* onResolve
102+
* onResolveModulesCss
93103
* @description mark module(s).css as sideEffects and add namespace
94104
* @param {import('esbuild').OnResolveArgs} args
95105
* @param {import('..').Build} build
96106
* @returns {Promise<import('esbuild').OnResolveResult>}
97107
*/
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;
103112
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');
105115

106116
/**
107117
* @type {import('esbuild').OnResolveResult}
108118
*/
109119
const result = {
110120
namespace: pluginNamespace,
111-
path: absPath,
121+
suffix: buildingCssSuffix,
122+
path: rpath,
112123
external: false,
113124
pluginData: {
114-
...(pluginData ?? {}),
115-
resolveDir: pluginData?.resolveDir || resolveDir,
116-
path: absPath
125+
...pluginData,
126+
relativePathToBuildRoot: rpath
117127
},
118128
sideEffects: true,
119129
pluginName
120130
};
121131

122132
if (initialOptions.watch) {
123-
log('watching', path.relative(buildRoot, absPath));
133+
log('watching', rpath);
124134
result.watchFiles = [absPath];
125135
}
126136

127137
return result;
128138
};
129139

130140
/**
131-
* onLoad
141+
* onLoadModulesCss
132142
* @param {import('..').Build} build
133143
* @param {import('..').Options} options
134144
* @param {import('esbuild').OnLoadArgs} args
135145
* @return {(import('esbuild').OnLoadResult | null | undefined | Promise<import('esbuild').OnLoadResult | null | undefined>)}
136146
*/
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);
141154

142-
const cached = cache.get(fullPath);
155+
log(`loading ${relative(args.path)}${args.suffix}`);
156+
157+
const cached = cache.get(absPath);
143158
if (cached) {
144-
log('return cache for', path.relative(buildRoot, fullPath));
159+
log('return built cache for', rpath);
145160
return cached;
146161
}
147162

148-
const rpath = path.relative(buildRoot, fullPath);
149163
const hex = createHash('sha256').update(rpath).digest('hex');
150164
const digest = hex.slice(hex.length - 255, hex.length);
151165

152166
const { js, resolveDir, css, exports } = await buildCssModulesJs({
153-
fullPath,
167+
fullPath: absPath,
154168
options,
155169
digest,
156170
build
@@ -160,15 +174,15 @@ const onLoad = async (build, options, args) => {
160174
pluginName,
161175
resolveDir,
162176
pluginData: {
177+
...pluginData,
163178
css,
164179
exports,
165-
digest,
166-
resolveDir
180+
digest
167181
},
168182
contents: js,
169183
loader: 'js'
170184
};
171-
cache.set(fullPath, result);
185+
cache.set(absPath, result);
172186

173187
return result;
174188
};
@@ -200,7 +214,7 @@ const onEnd = async (build, options, result) => {
200214
if (path.extname(f) === '.css') {
201215
const fullpath = path.resolve(buildRoot, f);
202216
const css = readFileSync(fullpath);
203-
cssContents.push(`\n/* ${f} */\n${css}\n`);
217+
cssContents.push(`${css}\n`);
204218
}
205219
});
206220
log('inject css to', path.relative(buildRoot, injectTo));
@@ -221,29 +235,36 @@ const onEnd = async (build, options, result) => {
221235
* @return {Promise<void>}
222236
*/
223237
const prepareBuild = async (build, options) => {
224-
const buildId = v4().replace(/[^0-9a-zA-Z]/g, '');
238+
const buildId = getBuildId(build);
225239
build.initialOptions.metafile = true;
226240
const packageRoot = options.root;
227241
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);
228246

229247
build.context = {
230248
buildId,
231249
buildRoot,
232-
packageRoot
250+
packageRoot,
251+
log,
252+
relative
233253
};
234254
};
235255

236256
/**
237-
* onLoadBuiltCss
257+
* onLoadBuiltModulesCss
238258
* @param {import('esbuild').OnLoadArgs} args
239259
* @param {import('..').Build} build
240260
* @returns {Promise<import('esbuild').OnLoadResult>}
241261
*/
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);
247268

248269
/**
249270
* @type {import('esbuild').OnLoadResult}
@@ -260,12 +281,15 @@ const onLoadBuiltCss = async ({ pluginData }, build) => {
260281
};
261282

262283
/**
263-
* onResolveBuiltCss
284+
* onResolveBuiltModulesCss
264285
* @param {import('esbuild').OnResolveArgs} args
286+
* @param {import('..').Build} build
265287
* @returns {Promise<import('esbuild').OnResolveResult>}
266288
*/
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}`);
269293

270294
/**
271295
* @type {import('esbuild').OnResolveResult}
@@ -274,11 +298,7 @@ const onResolveBuiltCss = async (args) => {
274298
namespace: pluginNamespace,
275299
path: p,
276300
external: false,
277-
pluginData: {
278-
...pluginData,
279-
resolveDir: pluginData.resolveDir || resolveDir,
280-
path: p
281-
},
301+
pluginData,
282302
sideEffects: true,
283303
pluginName
284304
};
@@ -294,27 +314,32 @@ const onResolveBuiltCss = async (args) => {
294314
*/
295315
const setup = async (build, options) => {
296316
await prepareBuild(build, options);
297-
const { buildRoot } = build.context;
298-
const log = getLogger(build);
299317

300-
build.onResolve({ filter: /\.modules?\.css$/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);
302320
});
303321

304-
build.onLoad({ filter: /\.modules?\.css$/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);
307324
});
308325

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+
);
310335

311336
build.onLoad(
312337
{
313-
filter: new RegExp(builtCssSurfix, 'i'),
338+
filter: builtModulesCssRegExp,
314339
namespace: pluginNamespace
315340
},
316341
async (args) => {
317-
return await onLoadBuiltCss(args, build);
342+
return await onLoadBuiltModulesCss(args, build);
318343
}
319344
);
320345

0 commit comments

Comments
 (0)