Skip to content

Commit

Permalink
fix: allow consumers to access CssModule and CssDependency (#703)
Browse files Browse the repository at this point in the history
  • Loading branch information
barak007 committed Feb 25, 2021
1 parent 1be21d2 commit 6484345
Show file tree
Hide file tree
Showing 4 changed files with 110 additions and 72 deletions.
72 changes: 53 additions & 19 deletions src/index.js
Expand Up @@ -3,9 +3,14 @@
import { validate } from 'schema-utils';

import schema from './plugin-options.json';
import { shared, MODULE_TYPE, compareModulesByIdentifier } from './utils';
import {
MODULE_TYPE,
compareModulesByIdentifier,
provideLoaderContext,
} from './utils';

const pluginName = 'mini-css-extract-plugin';
export const pluginName = 'mini-css-extract-plugin';
export const pluginSymbol = Symbol(pluginName);

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

/**
* @type WeakMap<webpack, CssModule>
*/
const cssModuleCache = new WeakMap();
/**
* @type WeakMap<webpack, CssDependency>
*/
const cssDependencyCache = new WeakMap();

class MiniCssExtractPlugin {
static getCssModule(webpack) {
/**
* Prevent creation of multiple CssModule classes to allow other integrations to get the current CssModule.
*/
if (cssModuleCache.has(webpack)) {
return cssModuleCache.get(webpack);
}
class CssModule extends webpack.Module {
constructor({
context,
Expand Down Expand Up @@ -134,6 +154,8 @@ class MiniCssExtractPlugin {
}
}

cssModuleCache.set(webpack, CssModule);

if (
webpack.util &&
webpack.util.serialization &&
Expand Down Expand Up @@ -181,6 +203,12 @@ class MiniCssExtractPlugin {
}

static getCssDependency(webpack) {
/**
* Prevent creation of multiple CssDependency classes to allow other integrations to get the current CssDependency.
*/
if (cssDependencyCache.has(webpack)) {
return cssDependencyCache.get(webpack);
}
// eslint-disable-next-line no-shadow
class CssDependency extends webpack.Dependency {
constructor(
Expand Down Expand Up @@ -231,6 +259,8 @@ class MiniCssExtractPlugin {
}
}

cssDependencyCache.set(webpack, CssDependency);

if (
webpack.util &&
webpack.util.serialization &&
Expand Down Expand Up @@ -356,16 +386,18 @@ class MiniCssExtractPlugin {
}
}

// initializeCssDependency
// eslint-disable-next-line no-shadow
const { CssModule, CssDependency } = shared(webpack, (webpack) => {
// eslint-disable-next-line no-shadow
const CssModule = MiniCssExtractPlugin.getCssModule(webpack);
// eslint-disable-next-line no-shadow
const CssDependency = MiniCssExtractPlugin.getCssDependency(webpack);

return { CssModule, CssDependency };
});
const CssModule = MiniCssExtractPlugin.getCssModule(webpack);
const CssDependency = MiniCssExtractPlugin.getCssDependency(webpack);

provideLoaderContext(
compiler,
`${pluginName} loader context`,
(loaderContext) => {
// eslint-disable-next-line no-param-reassign
loaderContext[pluginSymbol] = true;
},
false
);

compiler.hooks.thisCompilation.tap(pluginName, (compilation) => {
class CssModuleFactory {
Expand Down Expand Up @@ -1093,7 +1125,7 @@ class MiniCssExtractPlugin {
return usedModules;
}

modules = [...modules];
const modulesList = [...modules];

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

if (typeof chunkGroup[moduleIndexFunctionName] === 'function') {
// Store dependencies for modules
const moduleDependencies = new Map(modules.map((m) => [m, new Set()]));
const moduleDependencies = new Map(
modulesList.map((m) => [m, new Set()])
);
const moduleDependenciesReasons = new Map(
modules.map((m) => [m, new Map()])
modulesList.map((m) => [m, new Map()])
);

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

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

while (usedModules.size < modules.length) {
while (usedModules.size < modulesList.length) {
let success = false;
let bestMatch;
let bestMatchDeps;
Expand Down Expand Up @@ -1227,8 +1261,8 @@ class MiniCssExtractPlugin {
// (to avoid a breaking change)
// TODO remove this in next major version
// and increase minimum webpack version to 4.12.0
modules.sort((a, b) => a.index2 - b.index2);
usedModules = modules;
modulesList.sort((a, b) => a.index2 - b.index2);
usedModules = modulesList;
}

this._sortedModulesCache.set(chunk, usedModules);
Expand Down
55 changes: 19 additions & 36 deletions src/loader.js
Expand Up @@ -3,10 +3,10 @@ import path from 'path';
import loaderUtils from 'loader-utils';
import { validate } from 'schema-utils';

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

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

function hotLoader(content, context) {
const accept = context.locals
Expand Down Expand Up @@ -37,6 +37,12 @@ export function pitch(request) {
baseDataPath: 'options',
});

if (!this[pluginSymbol]) {
throw new Error(
"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"
);
}

const loaders = this.loaders.slice(this.loaderIndex + 1);

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

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

const NormalModule = webpack.NormalModule
? webpack.NormalModule
: // eslint-disable-next-line global-require
require('webpack/lib/NormalModule');

childCompiler.hooks.thisCompilation.tap(
`${pluginName} loader`,
(compilation) => {
const normalModuleHook =
typeof NormalModule.getCompilationHooks !== 'undefined'
? NormalModule.getCompilationHooks(compilation).loader
: compilation.hooks.normalModuleLoader;

normalModuleHook.tap(`${pluginName} loader`, (loaderContext, module) => {
if (module.request === request) {
// eslint-disable-next-line no-param-reassign
module.loaders = loaders.map((loader) => {
return {
loader: loader.path,
options: loader.options,
ident: loader.ident,
};
});
}
provideLoaderContext(childCompiler, `${pluginName} loader`, (_, module) => {
if (module.request === request) {
// eslint-disable-next-line no-param-reassign
module.loaders = loaders.map((loader) => {
return {
loader: loader.path,
options: loader.options,
ident: loader.ident,
};
});
}
);
});

let source;

Expand Down Expand Up @@ -205,15 +196,7 @@ export function pitch(request) {
}

const count = identifierCountMap.get(dependency.identifier) || 0;
const { CssDependency } = shared(webpack, () => {
return {};
});

if (!CssDependency) {
throw new Error(
"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"
);
}
const CssDependency = MiniCssExtractPlugin.getCssDependency(webpack);

this._module.addDependency(
(lastDep = new CssDependency(dependency, dependency.context, count))
Expand Down
38 changes: 21 additions & 17 deletions src/utils.js
Expand Up @@ -49,26 +49,30 @@ function compareModulesByIdentifier(a, b) {
return compareIds(a.identifier(), b.identifier());
}

const initializeCache = new WeakMap();

function shared(webpack, initializer) {
const cacheEntry = initializeCache.get(webpack);

// eslint-disable-next-line no-undefined
if (cacheEntry !== undefined) {
return cacheEntry;
}

const constructors = initializer(webpack);
const result = { ...constructors };

initializeCache.set(webpack, result);

return result;
function provideLoaderContext(compiler, name, handler, thisCompilation = true) {
const NormalModule =
compiler.webpack && compiler.webpack.NormalModule
? compiler.webpack.NormalModule
: // eslint-disable-next-line global-require
require('webpack/lib/NormalModule');

compiler.hooks[thisCompilation ? 'thisCompilation' : 'compilation'].tap(
name,
(compilation) => {
const normalModuleHook =
typeof NormalModule.getCompilationHooks !== 'undefined'
? NormalModule.getCompilationHooks(compilation).loader
: compilation.hooks.normalModuleLoader;

normalModuleHook.tap(name, (loaderContext, module) =>
handler(loaderContext, module, compilation)
);
}
);
}

export {
shared,
provideLoaderContext,
MODULE_TYPE,
findModuleById,
evalModuleCode,
Expand Down
17 changes: 17 additions & 0 deletions test/api.test.js
@@ -0,0 +1,17 @@
import webpack from 'webpack';

import MiniCssExtractPlugin from '../src';

describe('API', () => {
it('should return the same CssModule when same webpack instance provided', () => {
expect(MiniCssExtractPlugin.getCssModule(webpack)).toEqual(
MiniCssExtractPlugin.getCssModule(webpack)
);
});

it('should return the same CssDependency when same webpack instance provided', () => {
expect(MiniCssExtractPlugin.getCssDependency(webpack)).toEqual(
MiniCssExtractPlugin.getCssDependency(webpack)
);
});
});

0 comments on commit 6484345

Please sign in to comment.