Skip to content

Commit 47b836f

Browse files
authored
Merge a6d9608 into 1be21d2
2 parents 1be21d2 + a6d9608 commit 47b836f

File tree

4 files changed

+110
-72
lines changed

4 files changed

+110
-72
lines changed

src/index.js

+53-19
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,14 @@
33
import { validate } from 'schema-utils';
44

55
import schema from './plugin-options.json';
6-
import { shared, MODULE_TYPE, compareModulesByIdentifier } from './utils';
6+
import {
7+
MODULE_TYPE,
8+
compareModulesByIdentifier,
9+
provideLoaderContext,
10+
} from './utils';
711

8-
const pluginName = 'mini-css-extract-plugin';
12+
export const pluginName = 'mini-css-extract-plugin';
13+
export const pluginSymbol = Symbol(pluginName);
914

1015
const REGEXP_CHUNKHASH = /\[chunkhash(?::(\d+))?\]/i;
1116
const REGEXP_CONTENTHASH = /\[contenthash(?::(\d+))?\]/i;
@@ -18,8 +23,23 @@ const CODE_GENERATION_RESULT = {
1823
runtimeRequirements: new Set(),
1924
};
2025

26+
/**
27+
* @type WeakMap<webpack, CssModule>
28+
*/
29+
const cssModuleCache = new WeakMap();
30+
/**
31+
* @type WeakMap<webpack, CssDependency>
32+
*/
33+
const cssDependencyCache = new WeakMap();
34+
2135
class MiniCssExtractPlugin {
2236
static getCssModule(webpack) {
37+
/**
38+
* Prevent creation of multiple CssModule classes to allow other integrations to get the current CssModule.
39+
*/
40+
if (cssModuleCache.has(webpack)) {
41+
return cssModuleCache.get(webpack);
42+
}
2343
class CssModule extends webpack.Module {
2444
constructor({
2545
context,
@@ -134,6 +154,8 @@ class MiniCssExtractPlugin {
134154
}
135155
}
136156

157+
cssModuleCache.set(webpack, CssModule);
158+
137159
if (
138160
webpack.util &&
139161
webpack.util.serialization &&
@@ -181,6 +203,12 @@ class MiniCssExtractPlugin {
181203
}
182204

183205
static getCssDependency(webpack) {
206+
/**
207+
* Prevent creation of multiple CssDependency classes to allow other integrations to get the current CssDependency.
208+
*/
209+
if (cssDependencyCache.has(webpack)) {
210+
return cssDependencyCache.get(webpack);
211+
}
184212
// eslint-disable-next-line no-shadow
185213
class CssDependency extends webpack.Dependency {
186214
constructor(
@@ -231,6 +259,8 @@ class MiniCssExtractPlugin {
231259
}
232260
}
233261

262+
cssDependencyCache.set(webpack, CssDependency);
263+
234264
if (
235265
webpack.util &&
236266
webpack.util.serialization &&
@@ -356,16 +386,18 @@ class MiniCssExtractPlugin {
356386
}
357387
}
358388

359-
// initializeCssDependency
360-
// eslint-disable-next-line no-shadow
361-
const { CssModule, CssDependency } = shared(webpack, (webpack) => {
362-
// eslint-disable-next-line no-shadow
363-
const CssModule = MiniCssExtractPlugin.getCssModule(webpack);
364-
// eslint-disable-next-line no-shadow
365-
const CssDependency = MiniCssExtractPlugin.getCssDependency(webpack);
366-
367-
return { CssModule, CssDependency };
368-
});
389+
const CssModule = MiniCssExtractPlugin.getCssModule(webpack);
390+
const CssDependency = MiniCssExtractPlugin.getCssDependency(webpack);
391+
392+
provideLoaderContext(
393+
compiler,
394+
`${pluginName} loader context`,
395+
(loaderContext) => {
396+
// eslint-disable-next-line no-param-reassign
397+
loaderContext[pluginSymbol] = true;
398+
},
399+
false
400+
);
369401

370402
compiler.hooks.thisCompilation.tap(pluginName, (compilation) => {
371403
class CssModuleFactory {
@@ -1093,7 +1125,7 @@ class MiniCssExtractPlugin {
10931125
return usedModules;
10941126
}
10951127

1096-
modules = [...modules];
1128+
const modulesList = [...modules];
10971129

10981130
const [chunkGroup] = chunk.groupsIterable;
10991131
const moduleIndexFunctionName =
@@ -1103,16 +1135,18 @@ class MiniCssExtractPlugin {
11031135

11041136
if (typeof chunkGroup[moduleIndexFunctionName] === 'function') {
11051137
// Store dependencies for modules
1106-
const moduleDependencies = new Map(modules.map((m) => [m, new Set()]));
1138+
const moduleDependencies = new Map(
1139+
modulesList.map((m) => [m, new Set()])
1140+
);
11071141
const moduleDependenciesReasons = new Map(
1108-
modules.map((m) => [m, new Map()])
1142+
modulesList.map((m) => [m, new Map()])
11091143
);
11101144

11111145
// Get ordered list of modules per chunk group
11121146
// This loop also gathers dependencies from the ordered lists
11131147
// Lists are in reverse order to allow to use Array.pop()
11141148
const modulesByChunkGroup = Array.from(chunk.groupsIterable, (cg) => {
1115-
const sortedModules = modules
1149+
const sortedModules = modulesList
11161150
.map((m) => {
11171151
return {
11181152
module: m,
@@ -1145,7 +1179,7 @@ class MiniCssExtractPlugin {
11451179

11461180
const unusedModulesFilter = (m) => !usedModules.has(m);
11471181

1148-
while (usedModules.size < modules.length) {
1182+
while (usedModules.size < modulesList.length) {
11491183
let success = false;
11501184
let bestMatch;
11511185
let bestMatchDeps;
@@ -1227,8 +1261,8 @@ class MiniCssExtractPlugin {
12271261
// (to avoid a breaking change)
12281262
// TODO remove this in next major version
12291263
// and increase minimum webpack version to 4.12.0
1230-
modules.sort((a, b) => a.index2 - b.index2);
1231-
usedModules = modules;
1264+
modulesList.sort((a, b) => a.index2 - b.index2);
1265+
usedModules = modulesList;
12321266
}
12331267

12341268
this._sortedModulesCache.set(chunk, usedModules);

src/loader.js

+19-36
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,10 @@ import path from 'path';
33
import loaderUtils from 'loader-utils';
44
import { validate } from 'schema-utils';
55

6-
import { shared, findModuleById, evalModuleCode } from './utils';
6+
import { findModuleById, evalModuleCode, provideLoaderContext } from './utils';
77
import schema from './loader-options.json';
88

9-
const pluginName = 'mini-css-extract-plugin';
9+
import MiniCssExtractPlugin, { pluginName, pluginSymbol } from './index';
1010

1111
function hotLoader(content, context) {
1212
const accept = context.locals
@@ -37,6 +37,12 @@ export function pitch(request) {
3737
baseDataPath: 'options',
3838
});
3939

40+
if (!this[pluginSymbol]) {
41+
throw new Error(
42+
"You forgot to add 'mini-css-extract-plugin' plugin (i.e. `{ plugins: [new MiniCssExtractPlugin()] }`), please read https://github.com/webpack-contrib/mini-css-extract-plugin#getting-started"
43+
);
44+
}
45+
4046
const loaders = this.loaders.slice(this.loaderIndex + 1);
4147

4248
this.addDependency(this.resourcePath);
@@ -108,33 +114,18 @@ export function pitch(request) {
108114

109115
new LimitChunkCountPlugin({ maxChunks: 1 }).apply(childCompiler);
110116

111-
const NormalModule = webpack.NormalModule
112-
? webpack.NormalModule
113-
: // eslint-disable-next-line global-require
114-
require('webpack/lib/NormalModule');
115-
116-
childCompiler.hooks.thisCompilation.tap(
117-
`${pluginName} loader`,
118-
(compilation) => {
119-
const normalModuleHook =
120-
typeof NormalModule.getCompilationHooks !== 'undefined'
121-
? NormalModule.getCompilationHooks(compilation).loader
122-
: compilation.hooks.normalModuleLoader;
123-
124-
normalModuleHook.tap(`${pluginName} loader`, (loaderContext, module) => {
125-
if (module.request === request) {
126-
// eslint-disable-next-line no-param-reassign
127-
module.loaders = loaders.map((loader) => {
128-
return {
129-
loader: loader.path,
130-
options: loader.options,
131-
ident: loader.ident,
132-
};
133-
});
134-
}
117+
provideLoaderContext(childCompiler, `${pluginName} loader`, (_, module) => {
118+
if (module.request === request) {
119+
// eslint-disable-next-line no-param-reassign
120+
module.loaders = loaders.map((loader) => {
121+
return {
122+
loader: loader.path,
123+
options: loader.options,
124+
ident: loader.ident,
125+
};
135126
});
136127
}
137-
);
128+
});
138129

139130
let source;
140131

@@ -205,15 +196,7 @@ export function pitch(request) {
205196
}
206197

207198
const count = identifierCountMap.get(dependency.identifier) || 0;
208-
const { CssDependency } = shared(webpack, () => {
209-
return {};
210-
});
211-
212-
if (!CssDependency) {
213-
throw new Error(
214-
"You forgot to add 'mini-css-extract-plugin' plugin (i.e. `{ plugins: [new MiniCssExtractPlugin()] }`), please read https://github.com/webpack-contrib/mini-css-extract-plugin#getting-started"
215-
);
216-
}
199+
const CssDependency = MiniCssExtractPlugin.getCssDependency(webpack);
217200

218201
this._module.addDependency(
219202
(lastDep = new CssDependency(dependency, dependency.context, count))

src/utils.js

+21-17
Original file line numberDiff line numberDiff line change
@@ -49,26 +49,30 @@ function compareModulesByIdentifier(a, b) {
4949
return compareIds(a.identifier(), b.identifier());
5050
}
5151

52-
const initializeCache = new WeakMap();
53-
54-
function shared(webpack, initializer) {
55-
const cacheEntry = initializeCache.get(webpack);
56-
57-
// eslint-disable-next-line no-undefined
58-
if (cacheEntry !== undefined) {
59-
return cacheEntry;
60-
}
61-
62-
const constructors = initializer(webpack);
63-
const result = { ...constructors };
64-
65-
initializeCache.set(webpack, result);
66-
67-
return result;
52+
function provideLoaderContext(compiler, name, handler, thisCompilation = true) {
53+
const NormalModule =
54+
compiler.webpack && compiler.webpack.NormalModule
55+
? compiler.webpack.NormalModule
56+
: // eslint-disable-next-line global-require
57+
require('webpack/lib/NormalModule');
58+
59+
compiler.hooks[thisCompilation ? 'thisCompilation' : 'compilation'].tap(
60+
name,
61+
(compilation) => {
62+
const normalModuleHook =
63+
typeof NormalModule.getCompilationHooks !== 'undefined'
64+
? NormalModule.getCompilationHooks(compilation).loader
65+
: compilation.hooks.normalModuleLoader;
66+
67+
normalModuleHook.tap(name, (loaderContext, module) =>
68+
handler(loaderContext, module, compilation)
69+
);
70+
}
71+
);
6872
}
6973

7074
export {
71-
shared,
75+
provideLoaderContext,
7276
MODULE_TYPE,
7377
findModuleById,
7478
evalModuleCode,

test/api.test.js

+17
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
import webpack from 'webpack';
2+
3+
import MiniCssExtractPlugin from '../src';
4+
5+
describe('API', () => {
6+
it('should return the same CssModule when same webpack instance provided', () => {
7+
expect(MiniCssExtractPlugin.getCssModule(webpack)).toEqual(
8+
MiniCssExtractPlugin.getCssModule(webpack)
9+
);
10+
});
11+
12+
it('should return the same CssDependency when same webpack instance provided', () => {
13+
expect(MiniCssExtractPlugin.getCssDependency(webpack)).toEqual(
14+
MiniCssExtractPlugin.getCssDependency(webpack)
15+
);
16+
});
17+
});

0 commit comments

Comments
 (0)