UNPKG

4.23 kBJavaScriptView Raw
1// TODO refactor this smelly code!
2const loaderDefaults = require('../config').loader;
3const getAllModules = require('./get-all-modules');
4const isModuleShouldBeExtracted = require('./is-module-should-be-extracted');
5const getModuleChunk = require('./get-module-chunk');
6const interpolate = require('./interpolate');
7const getMatchedRule = require('./get-matched-rule');
8
9class MappedListItem {
10 /**
11 * @param {SpriteSymbol} symbol
12 * @param {NormalModule} module
13 * @param {string} spriteFilename
14 */
15 constructor(symbol, module, spriteFilename) {
16 this.symbol = symbol;
17 this.module = module;
18 this.resource = symbol.request.file;
19 this.spriteFilename = spriteFilename;
20 }
21
22 get url() {
23 return `${this.spriteFilename}#${this.symbol.id}`;
24 }
25
26 get useUrl() {
27 return `${this.spriteFilename}#${this.symbol.useId}`;
28 }
29}
30
31class MappedList {
32 /**
33 * @param {SpriteSymbol[]} symbols
34 * @param {Compilation} compilation
35 */
36 constructor(symbols, compilation, shouldLog = false) {
37 const { compiler } = compilation;
38
39 this.symbols = symbols;
40 this.rule = getMatchedRule(compiler);
41 this.allModules = getAllModules(compilation);
42 this.spriteModules = this.allModules.filter(isModuleShouldBeExtracted);
43 this.shouldLog = shouldLog;
44 this.items = this.create();
45 }
46
47 /**
48 * @param {MappedListItem[]} data
49 * @return {Object<string, MappedListItem>}
50 */
51 static groupItemsBySpriteFilename(data) {
52 return data
53 .map(item => item.spriteFilename)
54 .filter((value, index, self) => self.indexOf(value) === index)
55 .reduce((acc, spriteFilename) => {
56 acc[spriteFilename] = data.filter(item => item.spriteFilename === spriteFilename);
57 return acc;
58 }, {});
59 }
60
61 /**
62 * @param {MappedListItem[]} data
63 * @param {Function} [mapper] Custom grouper function
64 * @return {Object<string, MappedListItem>}
65 */
66 static groupItemsBySymbolFile(data, mapper) {
67 return data.reduce((acc, item) => {
68 if (mapper) {
69 mapper(acc, item);
70 } else {
71 acc[item.resource] = item;
72 }
73 return acc;
74 }, {});
75 }
76
77 /**
78 * @return {MappedListItem[]}
79 */
80 create() {
81 const { symbols, spriteModules, allModules, rule } = this;
82
83 const data = symbols.reduce((acc, symbol) => {
84 const resource = symbol.request.file;
85 const module = spriteModules.find((m) => {
86 return 'resource' in m ? m.resource.split('?')[0] === resource : false;
87 });
88
89 let spriteFilename = rule.spriteFilename || loaderDefaults.spriteFilename;
90
91 const chunk = module ? getModuleChunk(module, allModules) : null;
92
93 if (typeof spriteFilename !== 'function' && chunk && chunk.name) {
94 spriteFilename = spriteFilename.replace('[chunkname]', chunk.name);
95 } else if (typeof spriteFilename === 'function') {
96 spriteFilename = spriteFilename(resource);
97 }
98
99 if (rule && module) {
100 acc.push(new MappedListItem(symbol, module, spriteFilename));
101 }
102
103 return acc;
104 }, []);
105
106 // Additional pass to interpolate [hash] in spriteFilename
107 const itemsBySpriteFilename = MappedList.groupItemsBySpriteFilename(data);
108 const filenames = Object.keys(itemsBySpriteFilename);
109
110 filenames.forEach((filename) => {
111 if (!filename.includes('hash')) {
112 return;
113 }
114
115 const items = itemsBySpriteFilename[filename];
116 const spriteSymbols = items.map(item => item.symbol);
117 const content = spriteSymbols.map(s => s.render()).join('');
118 const interpolatedName = interpolate(filename, {
119 resourcePath: filename,
120 content
121 });
122
123 items
124 .filter(item => item.spriteFilename !== interpolatedName)
125 .forEach(item => item.spriteFilename = interpolatedName);
126 });
127
128 return data;
129 }
130
131 /**
132 * @return {Object<string, MappedListItem>}
133 */
134 groupItemsBySpriteFilename() {
135 return MappedList.groupItemsBySpriteFilename(this.items);
136 }
137
138 /**
139 * @param {Function} [mapper] Custom grouper function
140 * @return {Object<string, MappedListItem>}
141 */
142 groupItemsBySymbolFile(mapper) {
143 return MappedList.groupItemsBySymbolFile(this.items, mapper);
144 }
145}
146
147module.exports = MappedList;