Skip to content

Commit 80e9662

Browse files
fix: reduce count of require (#1004)
1 parent e662b61 commit 80e9662

12 files changed

+862
-653
lines changed

src/index.js

+15-15
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,6 @@ import { importParser, icssParser, urlParser } from './plugins';
1313
import {
1414
normalizeSourceMap,
1515
getModulesPlugins,
16-
getImportPrefix,
1716
getFilter,
1817
getApiCode,
1918
getImportCode,
@@ -54,20 +53,19 @@ export default function loader(content, map, meta) {
5453
plugins.push(...getModulesPlugins(options, this));
5554
}
5655

57-
// Run other loader (`postcss-loader`, `sass-loader` and etc) for importing CSS
58-
const importPrefix = getImportPrefix(this, options.importLoaders);
56+
const exportType = options.onlyLocals ? 'locals' : 'full';
5957

6058
plugins.push(icssParser());
6159

62-
if (options.import !== false) {
60+
if (options.import !== false && exportType === 'full') {
6361
plugins.push(
6462
importParser({
6563
filter: getFilter(options.import, this.resourcePath),
6664
})
6765
);
6866
}
6967

70-
if (options.url !== false) {
68+
if (options.url !== false && exportType === 'full') {
7169
plugins.push(
7270
urlParser({
7371
filter: getFilter(options.url, this.resourcePath, (value) =>
@@ -109,22 +107,24 @@ export default function loader(content, map, meta) {
109107
}
110108
}
111109

112-
const isNormalMode = !options.onlyLocals;
113-
114-
const apiCode = isNormalMode ? getApiCode(this, sourceMap) : '';
110+
// Run other loader (`postcss-loader`, `sass-loader` and etc) for importing CSS
111+
const apiCode = exportType === 'full' ? getApiCode(this, sourceMap) : '';
115112
const importCode =
116-
isNormalMode && imports.length > 0
117-
? getImportCode(this, imports, { importPrefix })
113+
imports.length > 0
114+
? getImportCode(this, imports, {
115+
importLoaders: options.importLoaders,
116+
exportType,
117+
})
118+
: '';
119+
const moduleCode =
120+
exportType === 'full'
121+
? getModuleCode(this, result, replacers, sourceMap)
118122
: '';
119-
const moduleCode = isNormalMode
120-
? getModuleCode(this, result, replacers, { sourceMap, importPrefix })
121-
: '';
122123
const exportCode =
123124
exports.length > 0
124125
? getExportCode(this, exports, replacers, {
125-
importPrefix,
126126
localsConvention: options.localsConvention,
127-
onlyLocals: options.onlyLocals,
127+
exportType,
128128
})
129129
: '';
130130

src/plugins/postcss-icss-parser.js

+23-37
Original file line numberDiff line numberDiff line change
@@ -1,63 +1,49 @@
11
import postcss from 'postcss';
22
import { extractICSS, replaceValueSymbols, replaceSymbols } from 'icss-utils';
3-
import loaderUtils from 'loader-utils';
43

54
const pluginName = 'postcss-icss-parser';
65

7-
function hasImportMessage(messages, url) {
8-
return messages.find(
9-
(message) =>
10-
message.pluginName === pluginName &&
11-
message.type === 'import' &&
12-
message.value &&
13-
message.value.url === url &&
14-
message.value.media === ''
15-
);
16-
}
17-
186
export default postcss.plugin(
197
pluginName,
208
() =>
219
function process(css, result) {
2210
const importReplacements = Object.create(null);
2311
const { icssImports, icssExports } = extractICSS(css);
2412

25-
let index = 0;
13+
Object.keys(icssImports).forEach((url, importIndex) => {
14+
const tokens = Object.keys(icssImports[url]);
15+
16+
if (tokens.length === 0) {
17+
return;
18+
}
2619

27-
for (const importUrl of Object.keys(icssImports)) {
28-
const url = loaderUtils.parseString(importUrl);
20+
const importName = `___CSS_LOADER_ICSS_IMPORT_${importIndex}___`;
2921

30-
for (const token of Object.keys(icssImports[importUrl])) {
31-
const name = `___CSS_LOADER_IMPORT___${index}___`;
22+
result.messages.push({
23+
pluginName,
24+
type: 'import',
25+
value: { type: 'icss-import', name: importName, url },
26+
});
27+
28+
tokens.forEach((token, replacementIndex) => {
29+
const name = `___CSS_LOADER_ICSS_IMPORT_${importIndex}_REPLACEMENT_${replacementIndex}___`;
30+
const localName = icssImports[url][token];
3231

33-
index += 1;
3432
importReplacements[token] = name;
3533

3634
result.messages.push({
3735
pluginName,
3836
type: 'replacer',
39-
value: {
40-
type: 'icss-import',
41-
name,
42-
url,
43-
export: icssImports[importUrl][token],
44-
},
37+
value: { type: 'icss-import', name, importName, localName },
4538
});
39+
});
40+
});
4641

47-
if (!hasImportMessage(result.messages, url)) {
48-
result.messages.push({
49-
pluginName,
50-
type: 'import',
51-
value: { type: 'icss-import', url, media: '', name },
52-
});
53-
}
54-
}
42+
if (Object.keys(importReplacements).length > 0) {
43+
replaceSymbols(css, importReplacements);
5544
}
5645

57-
replaceSymbols(css, importReplacements);
58-
59-
for (const exportName of Object.keys(icssExports)) {
60-
const name = exportName;
46+
Object.keys(icssExports).forEach((name) => {
6147
const value = replaceValueSymbols(
6248
icssExports[name],
6349
importReplacements
@@ -68,6 +54,6 @@ export default postcss.plugin(
6854
type: 'export',
6955
value: { name, value },
7056
});
71-
}
57+
});
7258
}
7359
);

src/plugins/postcss-import-parser.js

+28-16
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,6 @@
11
import postcss from 'postcss';
22
import valueParser from 'postcss-value-parser';
33

4-
import { uniqWith } from '../utils';
5-
64
const pluginName = 'postcss-import-parser';
75

86
function getArg(nodes) {
@@ -46,7 +44,7 @@ function parseImport(params) {
4644
}
4745

4846
function walkAtRules(css, result, filter) {
49-
const items = [];
47+
const items = new Map();
5048

5149
css.walkAtRules(/^import$/i, (atRule) => {
5250
// Convert only top-level @import
@@ -79,8 +77,13 @@ function walkAtRules(css, result, filter) {
7977
atRule.remove();
8078

8179
const { url, media } = parsed;
80+
const value = items.get(url);
8281

83-
items.push({ url, media });
82+
if (!value) {
83+
items.set(url, new Set([media]));
84+
} else {
85+
value.add(media);
86+
}
8487
});
8588

8689
return items;
@@ -90,18 +93,27 @@ export default postcss.plugin(
9093
pluginName,
9194
(options) =>
9295
function process(css, result) {
93-
const traversed = walkAtRules(css, result, options.filter);
94-
const paths = uniqWith(
95-
traversed,
96-
(value, other) => value.url === other.url && value.media === other.media
97-
);
98-
99-
paths.forEach((item) => {
100-
result.messages.push({
101-
pluginName,
102-
type: 'import',
103-
value: { type: '@import', url: item.url, media: item.media },
96+
const items = walkAtRules(css, result, options.filter);
97+
98+
[...items]
99+
.reduce((accumulator, currentValue) => {
100+
const [url, medias] = currentValue;
101+
102+
medias.forEach((media) => {
103+
accumulator.push({ url, media });
104+
});
105+
106+
return accumulator;
107+
}, [])
108+
.forEach((item, index) => {
109+
const { url, media } = item;
110+
const name = `___CSS_LOADER_AT_RULE_IMPORT_${index}___`;
111+
112+
result.messages.push({
113+
pluginName,
114+
type: 'import',
115+
value: { type: '@import', name, url, media },
116+
});
104117
});
105-
});
106118
}
107119
);

src/plugins/postcss-url-parser.js

+50-44
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,6 @@
11
import postcss from 'postcss';
22
import valueParser from 'postcss-value-parser';
33

4-
import { uniqWith, flatten } from '../utils';
5-
64
const pluginName = 'postcss-url-parser';
75

86
const isUrlFunc = /url/i;
@@ -51,7 +49,7 @@ function walkUrls(parsed, callback) {
5149
});
5250
}
5351

54-
function getUrlsFromValue(value, result, filter, decl = null) {
52+
function getUrlsFromValue(value, result, filter, decl) {
5553
if (!needParseDecl.test(value)) {
5654
return;
5755
}
@@ -61,14 +59,9 @@ function getUrlsFromValue(value, result, filter, decl = null) {
6159

6260
walkUrls(parsed, (node, url, needQuotes) => {
6361
if (url.trim().replace(/\\[\r\n]/g, '').length === 0) {
64-
result.warn(
65-
`Unable to find uri in '${decl ? decl.toString() : value}'`,
66-
decl
67-
? {
68-
node: decl,
69-
}
70-
: {}
71-
);
62+
result.warn(`Unable to find uri in '${decl ? decl.toString() : value}'`, {
63+
node: decl,
64+
});
7265

7366
return;
7467
}
@@ -77,24 +70,26 @@ function getUrlsFromValue(value, result, filter, decl = null) {
7770
return;
7871
}
7972

80-
urls.push({ url, needQuotes });
73+
const [normalizedUrl, singleQuery, hashValue] = url.split(/(\?)?#/);
74+
const hash =
75+
singleQuery || hashValue
76+
? `${singleQuery ? '?' : ''}${hashValue ? `#${hashValue}` : ''}`
77+
: '';
78+
79+
urls.push({ node, url: normalizedUrl, hash, needQuotes });
8180
});
8281

8382
// eslint-disable-next-line consistent-return
8483
return { parsed, urls };
8584
}
8685

87-
function walkDeclsWithUrl(css, result, filter) {
86+
function walkDecls(css, result, filter) {
8887
const items = [];
8988

9089
css.walkDecls((decl) => {
9190
const item = getUrlsFromValue(decl.value, result, filter, decl);
9291

93-
if (!item) {
94-
return;
95-
}
96-
97-
if (item.urls.length === 0) {
92+
if (!item || item.urls.length === 0) {
9893
return;
9994
}
10095

@@ -104,57 +99,68 @@ function walkDeclsWithUrl(css, result, filter) {
10499
return items;
105100
}
106101

102+
function flatten(array) {
103+
return array.reduce((a, b) => a.concat(b), []);
104+
}
105+
106+
function collectUniqueUrlsWithNodes(array) {
107+
return array.reduce((accumulator, currentValue) => {
108+
const { url, needQuotes, hash, node } = currentValue;
109+
const found = accumulator.find(
110+
(item) =>
111+
url === item.url && needQuotes === item.needQuotes && hash === item.hash
112+
);
113+
114+
if (!found) {
115+
accumulator.push({ url, hash, needQuotes, nodes: [node] });
116+
} else {
117+
found.nodes.push(node);
118+
}
119+
120+
return accumulator;
121+
}, []);
122+
}
123+
107124
export default postcss.plugin(
108125
pluginName,
109126
(options) =>
110127
function process(css, result) {
111-
const traversed = walkDeclsWithUrl(css, result, options.filter);
112-
const paths = uniqWith(
113-
flatten(traversed.map((item) => item.urls)),
114-
(value, other) =>
115-
value.url === other.url && value.needQuotes === other.needQuotes
128+
const traversed = walkDecls(css, result, options.filter);
129+
const paths = collectUniqueUrlsWithNodes(
130+
flatten(traversed.map((item) => item.urls))
116131
);
117-
118-
if (paths.length === 0) {
119-
return;
120-
}
121-
122-
const placeholders = [];
132+
const replacers = new Map();
123133

124134
paths.forEach((path, index) => {
125-
const name = `___CSS_LOADER_URL___${index}___`;
126-
const { url, needQuotes } = path;
127-
128-
placeholders.push({ name, path });
135+
const { url, hash, needQuotes, nodes } = path;
136+
const name = `___CSS_LOADER_URL_IMPORT_${index}___`;
129137

130138
result.messages.push(
131139
{
132140
pluginName,
133141
type: 'import',
134-
value: { type: 'url', url, name, needQuotes },
142+
value: { type: 'url', name, url, needQuotes, hash, index },
135143
},
136144
{
137145
pluginName,
138146
type: 'replacer',
139147
value: { type: 'url', name },
140148
}
141149
);
150+
151+
nodes.forEach((node) => {
152+
replacers.set(node, name);
153+
});
142154
});
143155

144156
traversed.forEach((item) => {
145-
walkUrls(item.parsed, (node, url, needQuotes) => {
146-
const value = placeholders.find(
147-
(placeholder) =>
148-
placeholder.path.url === url &&
149-
placeholder.path.needQuotes === needQuotes
150-
);
151-
152-
if (!value) {
157+
walkUrls(item.parsed, (node) => {
158+
const name = replacers.get(node);
159+
160+
if (!name) {
153161
return;
154162
}
155163

156-
const { name } = value;
157-
158164
// eslint-disable-next-line no-param-reassign
159165
node.type = 'word';
160166
// eslint-disable-next-line no-param-reassign

0 commit comments

Comments
 (0)