Skip to content

Commit f0820ce

Browse files
committed
Support es6 for mini-css-extract-plugin
1 parent a6c503d commit f0820ce

File tree

3 files changed

+477
-0
lines changed

3 files changed

+477
-0
lines changed

src/mini-css-extract-plugin/cjs.js

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
'use strict';
2+
3+
module.exports = require('./index').default;

src/mini-css-extract-plugin/index.js

Lines changed: 314 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,314 @@
1+
'use strict';
2+
3+
Object.defineProperty(exports, "__esModule", {
4+
value: true
5+
});
6+
7+
var _fs = require('fs');
8+
9+
var _fs2 = _interopRequireDefault(_fs);
10+
11+
var _path = require('path');
12+
13+
var _path2 = _interopRequireDefault(_path);
14+
15+
var _webpack = require('webpack');
16+
17+
var _webpack2 = _interopRequireDefault(_webpack);
18+
19+
var _webpackSources = require('webpack-sources');
20+
21+
var _webpackSources2 = _interopRequireDefault(_webpackSources);
22+
23+
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
24+
25+
const { ConcatSource, SourceMapSource, OriginalSource } = _webpackSources2.default;
26+
const { Template, util: { createHash } } = _webpack2.default;
27+
28+
const NS = _path2.default.dirname(_fs2.default.realpathSync(__filename));
29+
30+
const pluginName = 'mini-css-extract-plugin';
31+
32+
const REGEXP_CHUNKHASH = /\[chunkhash(?::(\d+))?\]/i;
33+
const REGEXP_CONTENTHASH = /\[contenthash(?::(\d+))?\]/i;
34+
const REGEXP_NAME = /\[name\]/i;
35+
36+
class CssDependency extends _webpack2.default.Dependency {
37+
constructor({ identifier, content, media, sourceMap }, context, identifierIndex) {
38+
super();
39+
this.identifier = identifier;
40+
this.identifierIndex = identifierIndex;
41+
this.content = content;
42+
this.media = media;
43+
this.sourceMap = sourceMap;
44+
this.context = context;
45+
}
46+
47+
getResourceIdentifier() {
48+
return `css-module-${this.identifier}-${this.identifierIndex}`;
49+
}
50+
}
51+
52+
class CssDependencyTemplate {
53+
apply() {}
54+
}
55+
56+
class CssModule extends _webpack2.default.Module {
57+
constructor(dependency) {
58+
super(NS, dependency.context);
59+
this._identifier = dependency.identifier;
60+
this._identifierIndex = dependency.identifierIndex;
61+
this.content = dependency.content;
62+
this.media = dependency.media;
63+
this.sourceMap = dependency.sourceMap;
64+
}
65+
66+
// no source() so webpack doesn't do add stuff to the bundle
67+
68+
size() {
69+
return this.content.length;
70+
}
71+
72+
identifier() {
73+
return `css ${this._identifier} ${this._identifierIndex}`;
74+
}
75+
76+
readableIdentifier(requestShortener) {
77+
return `css ${requestShortener.shorten(this._identifier)}${this._identifierIndex ? ` (${this._identifierIndex})` : ''}`;
78+
}
79+
80+
nameForCondition() {
81+
const resource = this._identifier.split('!').pop();
82+
const idx = resource.indexOf('?');
83+
if (idx >= 0) return resource.substring(0, idx);
84+
return resource;
85+
}
86+
87+
updateCacheModule(module) {
88+
this.content = module.content;
89+
this.media = module.media;
90+
this.sourceMap = module.sourceMap;
91+
}
92+
93+
needRebuild() {
94+
return true;
95+
}
96+
97+
build(options, compilation, resolver, fileSystem, callback) {
98+
this.buildInfo = {};
99+
this.buildMeta = {};
100+
callback();
101+
}
102+
103+
updateHash(hash) {
104+
super.updateHash(hash);
105+
hash.update(this.content);
106+
hash.update(this.media || '');
107+
hash.update(JSON.stringify(this.sourceMap || ''));
108+
}
109+
}
110+
111+
class CssModuleFactory {
112+
create({ dependencies: [dependency] }, callback) {
113+
callback(null, new CssModule(dependency));
114+
}
115+
}
116+
117+
class MiniCssExtractPlugin {
118+
constructor(options) {
119+
this.options = Object.assign({
120+
filename: '[name].css'
121+
}, options);
122+
if (!this.options.chunkFilename) {
123+
const { filename } = this.options;
124+
const hasName = filename.includes('[name]');
125+
const hasId = filename.includes('[id]');
126+
const hasChunkHash = filename.includes('[chunkhash]');
127+
// Anything changing depending on chunk is fine
128+
if (hasChunkHash || hasName || hasId) {
129+
this.options.chunkFilename = filename;
130+
} else {
131+
// Elsewise prefix '[id].' in front of the basename to make it changing
132+
this.options.chunkFilename = filename.replace(/(^|\/)([^/]*(?:\?|$))/, '$1[id].$2');
133+
}
134+
}
135+
}
136+
137+
apply(compiler) {
138+
compiler.hooks.thisCompilation.tap(pluginName, compilation => {
139+
compilation.hooks.normalModuleLoader.tap(pluginName, (lc, m) => {
140+
const loaderContext = lc;
141+
const module = m;
142+
loaderContext[NS] = content => {
143+
if (!Array.isArray(content) && content != null) {
144+
throw new Error(`Exported value was not extracted as an array: ${JSON.stringify(content)}`);
145+
}
146+
const identifierCountMap = new Map();
147+
for (const line of content) {
148+
const count = identifierCountMap.get(line.identifier) || 0;
149+
module.addDependency(new CssDependency(line, m.context, count));
150+
identifierCountMap.set(line.identifier, count + 1);
151+
}
152+
};
153+
});
154+
compilation.dependencyFactories.set(CssDependency, new CssModuleFactory());
155+
compilation.dependencyTemplates.set(CssDependency, new CssDependencyTemplate());
156+
compilation.mainTemplate.hooks.renderManifest.tap(pluginName, (result, { chunk }) => {
157+
const renderedModules = Array.from(chunk.modulesIterable).filter(module => module.type === NS);
158+
if (renderedModules.length > 0) {
159+
result.push({
160+
render: () => this.renderContentAsset(renderedModules, compilation.runtimeTemplate.requestShortener),
161+
filenameTemplate: this.options.filename,
162+
pathOptions: {
163+
chunk,
164+
contentHashType: NS
165+
},
166+
identifier: `mini-css-extract-plugin.${chunk.id}`,
167+
hash: chunk.contentHash[NS]
168+
});
169+
}
170+
});
171+
compilation.chunkTemplate.hooks.renderManifest.tap(pluginName, (result, { chunk }) => {
172+
const renderedModules = Array.from(chunk.modulesIterable).filter(module => module.type === NS);
173+
if (renderedModules.length > 0) {
174+
result.push({
175+
render: () => this.renderContentAsset(renderedModules, compilation.runtimeTemplate.requestShortener),
176+
filenameTemplate: this.options.chunkFilename,
177+
pathOptions: {
178+
chunk,
179+
contentHashType: NS
180+
},
181+
identifier: `mini-css-extract-plugin.${chunk.id}`,
182+
hash: chunk.contentHash[NS]
183+
});
184+
}
185+
});
186+
compilation.mainTemplate.hooks.hashForChunk.tap(pluginName, (hash, chunk) => {
187+
const { chunkFilename } = this.options;
188+
if (REGEXP_CHUNKHASH.test(chunkFilename)) {
189+
hash.update(JSON.stringify(chunk.getChunkMaps(true).hash));
190+
}
191+
if (REGEXP_CONTENTHASH.test(chunkFilename)) {
192+
hash.update(JSON.stringify(chunk.getChunkMaps(true).contentHash[NS] || {}));
193+
}
194+
if (REGEXP_NAME.test(chunkFilename)) {
195+
hash.update(JSON.stringify(chunk.getChunkMaps(true).name));
196+
}
197+
});
198+
compilation.hooks.contentHash.tap(pluginName, chunk => {
199+
const { outputOptions } = compilation;
200+
const { hashFunction, hashDigest, hashDigestLength } = outputOptions;
201+
const hash = createHash(hashFunction);
202+
for (const m of chunk.modulesIterable) {
203+
if (m.type === NS) {
204+
m.updateHash(hash);
205+
}
206+
}
207+
const { contentHash } = chunk;
208+
contentHash[NS] = hash.digest(hashDigest).substring(0, hashDigestLength);
209+
});
210+
const { mainTemplate } = compilation;
211+
mainTemplate.hooks.localVars.tap(pluginName, (source, chunk) => {
212+
const chunkMap = this.getCssChunkObject(chunk);
213+
if (Object.keys(chunkMap).length > 0) {
214+
return Template.asString([source, '', '// object to store loaded CSS chunks', 'var installedCssChunks = {', Template.indent(chunk.ids.map(id => `${JSON.stringify(id)}: 0`).join(',\n')), '}']);
215+
}
216+
return source;
217+
});
218+
mainTemplate.hooks.requireEnsure.tap(pluginName, (source, chunk, hash) => {
219+
const chunkMap = this.getCssChunkObject(chunk);
220+
if (Object.keys(chunkMap).length > 0) {
221+
const chunkMaps = chunk.getChunkMaps();
222+
const linkHrefPath = mainTemplate.getAssetPath(JSON.stringify(this.options.chunkFilename), {
223+
hash: `" + ${mainTemplate.renderCurrentHashCode(hash)} + "`,
224+
hashWithLength: length => `" + ${mainTemplate.renderCurrentHashCode(hash, length)} + "`,
225+
chunk: {
226+
id: '" + chunkId + "',
227+
hash: `" + ${JSON.stringify(chunkMaps.hash)}[chunkId] + "`,
228+
hashWithLength(length) {
229+
const shortChunkHashMap = Object.create(null);
230+
for (const chunkId of Object.keys(chunkMaps.hash)) {
231+
if (typeof chunkMaps.hash[chunkId] === 'string') {
232+
shortChunkHashMap[chunkId] = chunkMaps.hash[chunkId].substring(0, length);
233+
}
234+
}
235+
return `" + ${JSON.stringify(shortChunkHashMap)}[chunkId] + "`;
236+
},
237+
contentHash: {
238+
[NS]: `" + ${JSON.stringify(chunkMaps.contentHash[NS])}[chunkId] + "`
239+
},
240+
contentHashWithLength: {
241+
[NS]: length => {
242+
const shortContentHashMap = {};
243+
const contentHash = chunkMaps.contentHash[NS];
244+
for (const chunkId of Object.keys(contentHash)) {
245+
if (typeof contentHash[chunkId] === 'string') {
246+
shortContentHashMap[chunkId] = contentHash[chunkId].substring(0, length);
247+
}
248+
}
249+
return `" + ${JSON.stringify(shortContentHashMap)}[chunkId] + "`;
250+
}
251+
},
252+
name: `" + (${JSON.stringify(chunkMaps.name)}[chunkId]||chunkId) + "`
253+
},
254+
contentHashType: NS
255+
});
256+
return Template.asString([source, '', '// mini-css-extract-plugin CSS loading', `var cssChunks = ${JSON.stringify(chunkMap)};`, 'if(installedCssChunks[chunkId]) promises.push(installedCssChunks[chunkId]);', 'else if(installedCssChunks[chunkId] !== 0 && cssChunks[chunkId]) {', Template.indent(['promises.push(installedCssChunks[chunkId] = new Promise(function(resolve, reject) {', Template.indent([`var href = ${linkHrefPath};`, `var fullhref = ${mainTemplate.requireFn}.p + href;`, 'var existingLinkTags = document.getElementsByTagName("link");', 'for(var i = 0; i < existingLinkTags.length; i++) {', Template.indent(['var tag = existingLinkTags[i];', 'var dataHref = tag.getAttribute("data-href") || tag.getAttribute("href");', 'if(tag.rel === "stylesheet" && (dataHref === href || dataHref === fullhref)) return resolve();']), '}', 'var existingStyleTags = document.getElementsByTagName("style");', 'for(var i = 0; i < existingStyleTags.length; i++) {', Template.indent(['var tag = existingStyleTags[i];', 'var dataHref = tag.getAttribute("data-href");', 'if(dataHref === href || dataHref === fullhref) return resolve();']), '}', 'var linkTag = document.createElement("link");', 'linkTag.rel = "stylesheet";', 'linkTag.type = "text/css";', 'linkTag.onload = resolve;', 'linkTag.onerror = function(event) {', Template.indent(['var request = event && event.target && event.target.src || fullhref;', 'var err = new Error("Loading CSS chunk " + chunkId + " failed.\\n(" + request + ")");', 'err.request = request;', 'reject(err);']), '};', 'linkTag.href = fullhref;', 'var head = document.getElementsByTagName("head")[0];', 'head.appendChild(linkTag);']), '}).then(function() {', Template.indent(['installedCssChunks[chunkId] = 0;']), '}));']), '}']);
257+
}
258+
return source;
259+
});
260+
});
261+
}
262+
263+
getCssChunkObject(mainChunk) {
264+
const obj = {};
265+
for (const chunk of mainChunk.getAllAsyncChunks()) {
266+
for (const module of chunk.modulesIterable) {
267+
if (module.type === NS) {
268+
obj[chunk.id] = 1;
269+
break;
270+
}
271+
}
272+
}
273+
return obj;
274+
}
275+
276+
renderContentAsset(modules, requestShortener) {
277+
modules.sort((a, b) => a.index2 - b.index2);
278+
const source = new ConcatSource();
279+
const externalsSource = new ConcatSource();
280+
for (const m of modules) {
281+
if (/^@import url/.test(m.content)) {
282+
// HACK for IE
283+
// http://stackoverflow.com/a/14676665/1458162
284+
let { content } = m;
285+
if (m.media) {
286+
// insert media into the @import
287+
// this is rar
288+
// TODO improve this and parse the CSS to support multiple medias
289+
content = content.replace(/;|\s*$/, m.media);
290+
}
291+
externalsSource.add(content);
292+
externalsSource.add('\n');
293+
} else {
294+
if (m.media) {
295+
source.add(`@media ${m.media} {\n`);
296+
}
297+
if (m.sourceMap) {
298+
source.add(new SourceMapSource(m.content, m.readableIdentifier(requestShortener), m.sourceMap));
299+
} else {
300+
source.add(new OriginalSource(m.content, m.readableIdentifier(requestShortener)));
301+
}
302+
source.add('\n');
303+
if (m.media) {
304+
source.add('}\n');
305+
}
306+
}
307+
}
308+
return new ConcatSource(externalsSource, source);
309+
}
310+
}
311+
312+
MiniCssExtractPlugin.loader = require.resolve('./loader');
313+
314+
exports.default = MiniCssExtractPlugin;

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy