-
Notifications
You must be signed in to change notification settings - Fork 94
/
Copy pathindex.js
134 lines (112 loc) · 3.86 KB
/
index.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
/**
* Extract-text webpack block.
*
* @see https://github.com/webpack/extract-text-webpack-plugin
*/
const ExtractTextPlugin = require('extract-text-webpack-plugin')
module.exports = extractText
/**
* @param {string} outputFilePattern
* @return {Function}
*/
function extractText(outputFilePattern = 'css/[name].[contenthash:8].css') {
const plugin = new ExtractTextPlugin(outputFilePattern)
const postHook = (context, util) => prevConfig => {
let nextConfig = prevConfig
// Only apply to loaders in the same `match()` group or css loaders if there is no `match()`
const ruleToMatch = context.match || { test: /\.css$/ }
const matchingLoaderRules = getMatchingLoaderRules(ruleToMatch, prevConfig)
if (matchingLoaderRules.length === 0) {
throw new Error(
`extractText(): No loaders found to extract contents from. Looking for loaders matching ${
ruleToMatch.test
}`
)
}
const [fallbackLoaders, nonFallbackLoaders] = splitFallbackRule(matchingLoaderRules)
const newLoaderDef = Object.assign({}, ruleToMatch, {
use: plugin.extract({
fallback: fallbackLoaders,
use: nonFallbackLoaders
})
})
for (const ruleToRemove of matchingLoaderRules) {
nextConfig = removeLoaderRule(ruleToRemove)(nextConfig)
}
nextConfig = util.addPlugin(plugin)(nextConfig)
nextConfig = util.addLoader(newLoaderDef)(nextConfig)
return nextConfig
}
return Object.assign(() => prevConfig => prevConfig, { post: postHook })
}
function getMatchingLoaderRules(ruleToMatch, webpackConfig) {
return webpackConfig.module.rules.filter(
rule =>
isLoaderConditionMatching(rule.test, ruleToMatch.test) &&
isLoaderConditionMatching(rule.exclude, ruleToMatch.exclude) &&
isLoaderConditionMatching(rule.include, ruleToMatch.include)
)
}
function splitFallbackRule(rules) {
const leadingStyleLoaderInAllRules = rules.every(rule => {
return (
rule.use.length > 0 &&
rule.use[0] &&
(rule.use[0] === 'style-loader' || rule.use[0].loader === 'style-loader')
)
})
if (leadingStyleLoaderInAllRules) {
const trimmedRules = rules.map(rule => Object.assign({}, rule, { use: rule.use.slice(1) }))
return [['style-loader'], getUseEntriesFromRules(trimmedRules)]
} else {
return [[], getUseEntriesFromRules(rules)]
}
}
function getUseEntriesFromRules(rules) {
const normalizeUseEntry = use => (typeof use === 'string' ? { loader: use } : use)
return rules.reduce((useEntries, rule) => useEntries.concat(rule.use.map(normalizeUseEntry)), [])
}
/**
* @param {object} rule Remove all loaders that match this loader rule.
* @return {Function}
*/
function removeLoaderRule(rule) {
return prevConfig => {
const newRules = prevConfig.module.rules.filter(
prevRule =>
!(
isLoaderConditionMatching(prevRule.test, rule.test) &&
isLoaderConditionMatching(prevRule.include, rule.include) &&
isLoaderConditionMatching(prevRule.exclude, rule.exclude)
)
)
return Object.assign({}, prevConfig, {
module: Object.assign({}, prevConfig.module, {
rules: newRules
})
})
}
}
function isLoaderConditionMatching(test1, test2) {
if (test1 === test2) {
return true
} else if (typeof test1 !== typeof test2) {
return false
} else if (test1 instanceof RegExp && test2 instanceof RegExp) {
return test1 === test2 || String(test1) === String(test2)
} else if (Array.isArray(test1) && Array.isArray(test2)) {
return areArraysMatching(test1, test2)
}
return false
}
function areArraysMatching(array1, array2) {
if (array1.length !== array2.length) {
return false
}
return array1.every(
item1 =>
array2.indexOf(item1) >= 0 ||
(item1 instanceof RegExp &&
array2.find(item2 => item2 instanceof RegExp && String(item1) === String(item2)))
)
}