Skip to content

Commit 3338656

Browse files
fix: reduce number of require for url (#854)
1 parent 533abbe commit 3338656

15 files changed

+430
-253
lines changed

src/index.js

+93-47
Original file line numberDiff line numberDiff line change
@@ -17,14 +17,15 @@ import {
1717
getCurrentRequest,
1818
stringifyRequest,
1919
} from 'loader-utils';
20+
import camelCase from 'lodash/camelCase';
2021

2122
import schema from './options.json';
2223
import { importParser, icssParser, urlParser } from './plugins';
2324
import {
2425
getLocalIdent,
2526
getImportPrefix,
26-
compileExports,
2727
placholderRegExps,
28+
dashesCamelCase,
2829
} from './utils';
2930
import Warning from './Warning';
3031
import CssSyntaxError from './CssSyntaxError';
@@ -137,10 +138,9 @@ export default function loader(content, map, meta) {
137138
.forEach((warning) => this.emitWarning(new Warning(warning)));
138139

139140
const messages = result.messages || [];
140-
const { camelCase, exportOnlyLocals, importLoaders } = options;
141141

142142
// Run other loader (`postcss-loader`, `sass-loader` and etc) for importing CSS
143-
const importUrlPrefix = getImportPrefix(this, importLoaders);
143+
const importUrlPrefix = getImportPrefix(this, options.importLoaders);
144144

145145
// Prepare replacer to change from `___CSS_LOADER_IMPORT___INDEX___` to `require('./file.css').locals`
146146
const importItemReplacer = (placeholder) => {
@@ -162,7 +162,7 @@ export default function loader(content, map, meta) {
162162
const { item } = message;
163163
const importUrl = importUrlPrefix + urlToRequest(item.url);
164164

165-
if (exportOnlyLocals) {
165+
if (options.exportOnlyLocals) {
166166
return `" + require(${stringifyRequest(
167167
this,
168168
importUrl
@@ -175,18 +175,65 @@ export default function loader(content, map, meta) {
175175
)}).locals[${JSON.stringify(item.export)}] + "`;
176176
};
177177

178-
let exportCode = compileExports(messages, camelCase, (valueAsString) =>
179-
valueAsString.replace(placholderRegExps.importItemG, importItemReplacer)
180-
);
178+
const exports = messages
179+
.filter((message) => message.type === 'export')
180+
.reduce((accumulator, message) => {
181+
const { key, value } = message.item;
182+
183+
let valueAsString = JSON.stringify(value);
184+
185+
valueAsString = valueAsString.replace(
186+
placholderRegExps.importItemG,
187+
importItemReplacer
188+
);
189+
190+
function addEntry(k) {
191+
accumulator.push(`\t${JSON.stringify(k)}: ${valueAsString}`);
192+
}
193+
194+
let targetKey;
195+
196+
switch (options.camelCase) {
197+
case true:
198+
addEntry(key);
199+
targetKey = camelCase(key);
200+
201+
if (targetKey !== key) {
202+
addEntry(targetKey);
203+
}
204+
break;
205+
case 'dashes':
206+
addEntry(key);
207+
targetKey = dashesCamelCase(key);
208+
209+
if (targetKey !== key) {
210+
addEntry(targetKey);
211+
}
212+
break;
213+
case 'only':
214+
addEntry(camelCase(key));
215+
break;
216+
case 'dashesOnly':
217+
addEntry(dashesCamelCase(key));
218+
break;
219+
default:
220+
addEntry(key);
221+
break;
222+
}
223+
224+
return accumulator;
225+
}, []);
181226

182-
if (exportOnlyLocals) {
227+
if (options.exportOnlyLocals) {
183228
return callback(
184229
null,
185-
exportCode ? `module.exports = ${exportCode};` : exportCode
230+
exports.length > 0
231+
? `module.exports = {\n${exports.join(',\n')}\n};`
232+
: ''
186233
);
187234
}
188235

189-
const importCode = messages
236+
const imports = messages
190237
.filter((message) => message.type === 'import')
191238
.map((message) => {
192239
const { url } = message.item;
@@ -204,56 +251,52 @@ export default function loader(content, map, meta) {
204251
this,
205252
importUrl
206253
)}), ${JSON.stringify(media)});`;
207-
}, this)
208-
.join('\n');
254+
}, this);
209255

210256
let cssAsString = JSON.stringify(result.css).replace(
211257
placholderRegExps.importItemG,
212258
importItemReplacer
213259
);
214260

215-
// helper for ensuring valid CSS strings from requires
216-
let urlEscapeHelperCode = '';
261+
// Helper for ensuring valid CSS strings from requires
262+
let hasUrlEscapeHelper = false;
217263

218264
messages
219265
.filter((message) => message.type === 'url')
220266
.forEach((message) => {
221-
if (!urlEscapeHelperCode) {
222-
urlEscapeHelperCode = `var escape = require(${stringifyRequest(
223-
this,
224-
require.resolve('./runtime/escape.js')
225-
)});\n`;
267+
if (!hasUrlEscapeHelper) {
268+
imports.push(
269+
`var urlEscape = require(${stringifyRequest(
270+
this,
271+
require.resolve('./runtime/url-escape.js')
272+
)});`
273+
);
274+
275+
hasUrlEscapeHelper = true;
226276
}
227277

228278
const { item } = message;
229279
const { url, placeholder } = item;
280+
// Remove `#hash` and `?#hash` from `require`
281+
const [normalizedUrl, singleQuery, hashValue] = url.split(/(\?)?#/);
282+
const hash =
283+
singleQuery || hashValue
284+
? `"${singleQuery ? '?' : ''}${hashValue ? `#${hashValue}` : ''}"`
285+
: '';
286+
287+
imports.push(
288+
`var ${placeholder} = urlEscape(require(${stringifyRequest(
289+
this,
290+
urlToRequest(normalizedUrl)
291+
)})${hash ? ` + ${hash}` : ''});`
292+
);
230293

231294
cssAsString = cssAsString.replace(
232295
new RegExp(placeholder, 'g'),
233-
() => {
234-
// Remove `#hash` and `?#hash` from `require`
235-
const [normalizedUrl, singleQuery, hashValue] = url.split(
236-
/(\?)?#/
237-
);
238-
const hash =
239-
singleQuery || hashValue
240-
? `"${singleQuery ? '?' : ''}${
241-
hashValue ? `#${hashValue}` : ''
242-
}"`
243-
: '';
244-
245-
return `" + escape(require(${stringifyRequest(
246-
this,
247-
urlToRequest(normalizedUrl)
248-
)})${hash ? ` + ${hash}` : ''}) + "`;
249-
}
296+
() => `" + ${placeholder} + "`
250297
);
251298
});
252299

253-
if (exportCode) {
254-
exportCode = `exports.locals = ${exportCode};`;
255-
}
256-
257300
let newMap = result.map;
258301

259302
if (sourceMap && newMap) {
@@ -282,18 +325,21 @@ export default function loader(content, map, meta) {
282325
const runtimeCode = `exports = module.exports = require(${stringifyRequest(
283326
this,
284327
require.resolve('./runtime/api')
285-
)})(${!!sourceMap});`;
286-
const moduleCode = `exports.push([module.id, ${cssAsString}, ""${
328+
)})(${!!sourceMap});\n`;
329+
const importCode =
330+
imports.length > 0 ? `// Imports\n${imports.join('\n')}\n\n` : '';
331+
const moduleCode = `// Module\nexports.push([module.id, ${cssAsString}, ""${
287332
newMap ? `,${newMap}` : ''
288-
}]);`;
333+
}]);\n\n`;
334+
const exportsCode =
335+
exports.length > 0
336+
? `// Exports\nexports.locals = {\n${exports.join(',\n')}\n};`
337+
: '';
289338

290339
// Embed runtime
291340
return callback(
292341
null,
293-
`${urlEscapeHelperCode}${runtimeCode}\n` +
294-
`// imports\n${importCode}\n\n` +
295-
`// module\n${moduleCode}\n\n` +
296-
`// exports\n${exportCode}`
342+
runtimeCode + importCode + moduleCode + exportsCode
297343
);
298344
})
299345
.catch((error) => {
File renamed without changes.

src/utils.js

+1-52
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@
44
*/
55
import path from 'path';
66

7-
import camelCase from 'lodash/camelCase';
87
import loaderUtils from 'loader-utils';
98

109
const placholderRegExps = {
@@ -35,56 +34,6 @@ function dashesCamelCase(str) {
3534
);
3635
}
3736

38-
function compileExports(messages, camelCaseKeys, valueHandler) {
39-
const exports = messages
40-
.filter((message) => message.type === 'export')
41-
.reduce((accumulator, message) => {
42-
const { key, value } = message.item;
43-
44-
let valueAsString = JSON.stringify(value);
45-
46-
valueAsString = valueHandler(valueAsString);
47-
48-
function addEntry(k) {
49-
accumulator.push(`\t${JSON.stringify(k)}: ${valueAsString}`);
50-
}
51-
52-
let targetKey;
53-
54-
switch (camelCaseKeys) {
55-
case true:
56-
addEntry(key);
57-
targetKey = camelCase(key);
58-
59-
if (targetKey !== key) {
60-
addEntry(targetKey);
61-
}
62-
break;
63-
case 'dashes':
64-
addEntry(key);
65-
targetKey = dashesCamelCase(key);
66-
67-
if (targetKey !== key) {
68-
addEntry(targetKey);
69-
}
70-
break;
71-
case 'only':
72-
addEntry(camelCase(key));
73-
break;
74-
case 'dashesOnly':
75-
addEntry(dashesCamelCase(key));
76-
break;
77-
default:
78-
addEntry(key);
79-
break;
80-
}
81-
82-
return accumulator;
83-
}, []);
84-
85-
return exports.length > 0 ? `{\n${exports.join(',\n')}\n}` : '';
86-
}
87-
8837
function getLocalIdent(loaderContext, localIdentName, localName, options) {
8938
if (!options.context) {
9039
// eslint-disable-next-line no-param-reassign
@@ -112,4 +61,4 @@ function getLocalIdent(loaderContext, localIdentName, localName, options) {
11261
.replace(/^((-?[0-9])|--)/, '_$1');
11362
}
11463

115-
export { compileExports, getImportPrefix, getLocalIdent, placholderRegExps };
64+
export { getImportPrefix, getLocalIdent, placholderRegExps, dashesCamelCase };

0 commit comments

Comments
 (0)