Skip to content

Commit 7f49a0a

Browse files
feat: @value supports importing url() (#1126)
1 parent 791fff3 commit 7f49a0a

10 files changed

+221
-86
lines changed

src/index.js

+30-35
Original file line numberDiff line numberDiff line change
@@ -48,27 +48,10 @@ export default async function loader(content, map, meta) {
4848
return;
4949
}
5050

51-
if (shouldUseModulesPlugins(options)) {
52-
const icssResolver = this.getResolve({
53-
mainFields: ['css', 'style', 'main', '...'],
54-
mainFiles: ['index', '...'],
55-
extensions: [],
56-
conditionNames: ['style'],
57-
});
51+
const needUseModulesPlugins = shouldUseModulesPlugins(options);
5852

59-
plugins.push(
60-
...getModulesPlugins(options, this),
61-
icssParser({
62-
context: this.context,
63-
rootContext: this.rootContext,
64-
resolver: icssResolver,
65-
urlHandler: (url) =>
66-
stringifyRequest(
67-
this,
68-
getPreRequester(this)(options.importLoaders) + url
69-
),
70-
})
71-
);
53+
if (needUseModulesPlugins) {
54+
plugins.push(...getModulesPlugins(options, this));
7255
}
7356

7457
if (shouldUseImportPlugin(options)) {
@@ -113,6 +96,28 @@ export default async function loader(content, map, meta) {
11396
);
11497
}
11598

99+
if (needUseModulesPlugins) {
100+
const icssResolver = this.getResolve({
101+
mainFields: ['css', 'style', 'main', '...'],
102+
mainFiles: ['index', '...'],
103+
extensions: [],
104+
conditionNames: ['style'],
105+
});
106+
107+
plugins.push(
108+
icssParser({
109+
context: this.context,
110+
rootContext: this.rootContext,
111+
resolver: icssResolver,
112+
urlHandler: (url) =>
113+
stringifyRequest(
114+
this,
115+
getPreRequester(this)(options.importLoaders) + url
116+
),
117+
})
118+
);
119+
}
120+
116121
// Reuse CSS AST (PostCSS AST e.g 'postcss-loader') to avoid reparsing
117122
if (meta) {
118123
const { ast } = meta;
@@ -160,8 +165,7 @@ export default async function loader(content, map, meta) {
160165

161166
const imports = [];
162167
const apiImports = [];
163-
const urlReplacements = [];
164-
const icssReplacements = [];
168+
const replacements = [];
165169
const exports = [];
166170

167171
for (const message of result.messages) {
@@ -173,11 +177,8 @@ export default async function loader(content, map, meta) {
173177
case 'api-import':
174178
apiImports.push(message.value);
175179
break;
176-
case 'url-replacement':
177-
urlReplacements.push(message.value);
178-
break;
179-
case 'icss-replacement':
180-
icssReplacements.push(message.value);
180+
case 'replacement':
181+
replacements.push(message.value);
181182
break;
182183
case 'export':
183184
exports.push(message.value);
@@ -189,14 +190,8 @@ export default async function loader(content, map, meta) {
189190
apiImports.sort(sortImports);
190191

191192
const importCode = getImportCode(this, imports, options);
192-
const moduleCode = getModuleCode(
193-
result,
194-
apiImports,
195-
urlReplacements,
196-
icssReplacements,
197-
options
198-
);
199-
const exportCode = getExportCode(exports, icssReplacements, options);
193+
const moduleCode = getModuleCode(result, apiImports, replacements, options);
194+
const exportCode = getExportCode(exports, replacements, options);
200195

201196
callback(null, `${importCode}${moduleCode}${exportCode}`);
202197
}

src/plugins/postcss-icss-parser.js

+2-2
Original file line numberDiff line numberDiff line change
@@ -76,8 +76,8 @@ export default postcss.plugin(
7676
importReplacements[token] = replacementName;
7777

7878
result.messages.push({
79-
type: 'icss-replacement',
80-
value: { replacementName, importName, localName },
79+
type: 'replacement',
80+
value: { type: 'icss', replacementName, importName, localName },
8181
});
8282
}
8383
}

src/plugins/postcss-url-parser.js

+2-9
Original file line numberDiff line numberDiff line change
@@ -233,15 +233,8 @@ export default postcss.plugin(pluginName, (options) => async (css, result) => {
233233

234234
result.messages.push({
235235
pluginName,
236-
type: 'url-replacement',
237-
value: {
238-
replacementName,
239-
importName,
240-
hash,
241-
needQuotes,
242-
index,
243-
order: 4,
244-
},
236+
type: 'replacement',
237+
value: { type: 'url', replacementName, importName, hash, needQuotes },
245238
});
246239
}
247240

src/utils.js

+45-40
Original file line numberDiff line numberDiff line change
@@ -354,13 +354,7 @@ function getImportCode(loaderContext, imports, options) {
354354
return code ? `// Imports\n${code}` : '';
355355
}
356356

357-
function getModuleCode(
358-
result,
359-
apiImports,
360-
urlReplacements,
361-
icssReplacements,
362-
options
363-
) {
357+
function getModuleCode(result, apiImports, replacements, options) {
364358
if (options.modules.exportOnlyLocals === true) {
365359
return 'var ___CSS_LOADER_EXPORT___ = {};\n';
366360
}
@@ -383,33 +377,35 @@ function getModuleCode(
383377
)}${media ? `, ${JSON.stringify(media)}` : ''}]);\n`;
384378
}
385379

386-
for (const item of urlReplacements) {
387-
const { replacementName, importName, hash, needQuotes } = item;
380+
for (const replacement of replacements) {
381+
const { replacementName, importName, type } = replacement;
388382

389-
const getUrlOptions = []
390-
.concat(hash ? [`hash: ${JSON.stringify(hash)}`] : [])
391-
.concat(needQuotes ? 'needQuotes: true' : []);
392-
const preparedOptions =
393-
getUrlOptions.length > 0 ? `, { ${getUrlOptions.join(', ')} }` : '';
383+
if (type === 'url') {
384+
const { hash, needQuotes } = replacement;
394385

395-
beforeCode += `var ${replacementName} = ___CSS_LOADER_GET_URL_IMPORT___(${importName}${preparedOptions});\n`;
396-
397-
code = code.replace(
398-
new RegExp(replacementName, 'g'),
399-
() => `" + ${replacementName} + "`
400-
);
401-
}
386+
const getUrlOptions = []
387+
.concat(hash ? [`hash: ${JSON.stringify(hash)}`] : [])
388+
.concat(needQuotes ? 'needQuotes: true' : []);
389+
const preparedOptions =
390+
getUrlOptions.length > 0 ? `, { ${getUrlOptions.join(', ')} }` : '';
402391

403-
for (const replacement of icssReplacements) {
404-
const { replacementName, importName, localName } = replacement;
392+
beforeCode += `var ${replacementName} = ___CSS_LOADER_GET_URL_IMPORT___(${importName}${preparedOptions});\n`;
405393

406-
code = code.replace(new RegExp(replacementName, 'g'), () =>
407-
options.modules.namedExport
408-
? `" + ${importName}_NAMED___[${JSON.stringify(
409-
camelCase(localName)
410-
)}] + "`
411-
: `" + ${importName}.locals[${JSON.stringify(localName)}] + "`
412-
);
394+
code = code.replace(
395+
new RegExp(replacementName, 'g'),
396+
() => `" + ${replacementName} + "`
397+
);
398+
} else {
399+
const { localName } = replacement;
400+
401+
code = code.replace(new RegExp(replacementName, 'g'), () =>
402+
options.modules.namedExport
403+
? `" + ${importName}_NAMED___[${JSON.stringify(
404+
camelCase(localName)
405+
)}] + "`
406+
: `" + ${importName}.locals[${JSON.stringify(localName)}] + "`
407+
);
408+
}
413409
}
414410

415411
return `${beforeCode}// Module\n___CSS_LOADER_EXPORT___.push([module.id, ${code}, ""${sourceMapValue}]);\n`;
@@ -421,7 +417,7 @@ function dashesCamelCase(str) {
421417
);
422418
}
423419

424-
function getExportCode(exports, icssReplacements, options) {
420+
function getExportCode(exports, replacements, options) {
425421
let code = '';
426422
let localsCode = '';
427423

@@ -476,16 +472,25 @@ function getExportCode(exports, icssReplacements, options) {
476472
}
477473
}
478474

479-
for (const replacement of icssReplacements) {
480-
const { replacementName, importName, localName } = replacement;
475+
for (const replacement of replacements) {
476+
const { replacementName, type } = replacement;
481477

482-
localsCode = localsCode.replace(new RegExp(replacementName, 'g'), () =>
483-
options.modules.namedExport
484-
? `" + ${importName}_NAMED___[${JSON.stringify(
485-
camelCase(localName)
486-
)}] + "`
487-
: `" + ${importName}.locals[${JSON.stringify(localName)}] + "`
488-
);
478+
if (type === 'url') {
479+
localsCode = localsCode.replace(
480+
new RegExp(replacementName, 'g'),
481+
() => `" + ${replacementName} + "`
482+
);
483+
} else {
484+
const { importName, localName } = replacement;
485+
486+
localsCode = localsCode.replace(new RegExp(replacementName, 'g'), () =>
487+
options.modules.namedExport
488+
? `" + ${importName}_NAMED___[${JSON.stringify(
489+
camelCase(localName)
490+
)}] + "`
491+
: `" + ${importName}.locals[${JSON.stringify(localName)}] + "`
492+
);
493+
}
489494
}
490495

491496
if (localsCode) {

test/__snapshots__/modules-option.test.js.snap

+92
Original file line numberDiff line numberDiff line change
@@ -3676,6 +3676,98 @@ Object {
36763676
36773677
exports[`"modules" option should work js template with "namedExport" option: warnings 1`] = `Array []`;
36783678
3679+
exports[`"modules" option should work with "url" and "namedExport": errors 1`] = `Array []`;
3680+
3681+
exports[`"modules" option should work with "url" and "namedExport": module 1`] = `
3682+
"// Imports
3683+
import ___CSS_LOADER_API_IMPORT___ from \\"../../../../src/runtime/api.js\\";
3684+
import ___CSS_LOADER_ICSS_IMPORT_0___, * as ___CSS_LOADER_ICSS_IMPORT_0____NAMED___ from \\"-!../../../../src/index.js??[ident]!./shared.css\\";
3685+
import ___CSS_LOADER_GET_URL_IMPORT___ from \\"../../../../src/runtime/getUrl.js\\";
3686+
import ___CSS_LOADER_URL_IMPORT_0___ from \\"./img.png\\";
3687+
var ___CSS_LOADER_EXPORT___ = ___CSS_LOADER_API_IMPORT___(false);
3688+
___CSS_LOADER_EXPORT___.i(___CSS_LOADER_ICSS_IMPORT_0___, \\"\\", true);
3689+
var ___CSS_LOADER_URL_REPLACEMENT_0___ = ___CSS_LOADER_GET_URL_IMPORT___(___CSS_LOADER_URL_IMPORT_0___);
3690+
// Module
3691+
___CSS_LOADER_EXPORT___.push([module.id, \\"a {\\\\n background: url(\\" + ___CSS_LOADER_URL_REPLACEMENT_0___ + \\");\\\\n}\\\\n\\\\nbody {\\\\n background: \\" + ___CSS_LOADER_ICSS_IMPORT_0____NAMED___[\\"vUrlOther\\"] + \\";\\\\n}\\\\n\\", \\"\\"]);
3692+
// Exports
3693+
export const vUrl = \\"url(\\" + ___CSS_LOADER_URL_REPLACEMENT_0___ + \\")\\";
3694+
export const vUrlOther = \\"\\" + ___CSS_LOADER_ICSS_IMPORT_0____NAMED___[\\"vUrlOther\\"] + \\"\\";
3695+
export default ___CSS_LOADER_EXPORT___;
3696+
"
3697+
`;
3698+
3699+
exports[`"modules" option should work with "url" and "namedExport": result 1`] = `
3700+
Array [
3701+
Array [
3702+
"../../src/index.js?[ident]!./modules/url/shared.css",
3703+
"
3704+
",
3705+
"",
3706+
],
3707+
Array [
3708+
"./modules/url/source.css",
3709+
"a {
3710+
background: url(/webpack/public/path/img.png);
3711+
}
3712+
3713+
body {
3714+
background: url(/webpack/public/path/img.png);
3715+
}
3716+
",
3717+
"",
3718+
],
3719+
]
3720+
`;
3721+
3722+
exports[`"modules" option should work with "url" and "namedExport": warnings 1`] = `Array []`;
3723+
3724+
exports[`"modules" option should work with "url": errors 1`] = `Array []`;
3725+
3726+
exports[`"modules" option should work with "url": module 1`] = `
3727+
"// Imports
3728+
import ___CSS_LOADER_API_IMPORT___ from \\"../../../../src/runtime/api.js\\";
3729+
import ___CSS_LOADER_ICSS_IMPORT_0___ from \\"-!../../../../src/index.js??[ident]!./shared.css\\";
3730+
import ___CSS_LOADER_GET_URL_IMPORT___ from \\"../../../../src/runtime/getUrl.js\\";
3731+
import ___CSS_LOADER_URL_IMPORT_0___ from \\"./img.png\\";
3732+
var ___CSS_LOADER_EXPORT___ = ___CSS_LOADER_API_IMPORT___(false);
3733+
___CSS_LOADER_EXPORT___.i(___CSS_LOADER_ICSS_IMPORT_0___, \\"\\", true);
3734+
var ___CSS_LOADER_URL_REPLACEMENT_0___ = ___CSS_LOADER_GET_URL_IMPORT___(___CSS_LOADER_URL_IMPORT_0___);
3735+
// Module
3736+
___CSS_LOADER_EXPORT___.push([module.id, \\"a {\\\\n background: url(\\" + ___CSS_LOADER_URL_REPLACEMENT_0___ + \\");\\\\n}\\\\n\\\\nbody {\\\\n background: \\" + ___CSS_LOADER_ICSS_IMPORT_0___.locals[\\"v-url-other\\"] + \\";\\\\n}\\\\n\\", \\"\\"]);
3737+
// Exports
3738+
___CSS_LOADER_EXPORT___.locals = {
3739+
\\"v-url\\": \\"url(\\" + ___CSS_LOADER_URL_REPLACEMENT_0___ + \\")\\",
3740+
\\"v-url-other\\": \\"\\" + ___CSS_LOADER_ICSS_IMPORT_0___.locals[\\"v-url-other\\"] + \\"\\"
3741+
};
3742+
export default ___CSS_LOADER_EXPORT___;
3743+
"
3744+
`;
3745+
3746+
exports[`"modules" option should work with "url": result 1`] = `
3747+
Array [
3748+
Array [
3749+
"../../src/index.js?[ident]!./modules/url/shared.css",
3750+
"
3751+
",
3752+
"",
3753+
],
3754+
Array [
3755+
"./modules/url/source.css",
3756+
"a {
3757+
background: url(/webpack/public/path/img.png);
3758+
}
3759+
3760+
body {
3761+
background: url(/webpack/public/path/img.png);
3762+
}
3763+
",
3764+
"",
3765+
],
3766+
]
3767+
`;
3768+
3769+
exports[`"modules" option should work with "url": warnings 1`] = `Array []`;
3770+
36793771
exports[`"modules" option should work with a modules.auto Function that returns "false": errors 1`] = `Array []`;
36803772
36813773
exports[`"modules" option should work with a modules.auto Function that returns "false": module 1`] = `

test/fixtures/modules/url/img.png

76.3 KB
Loading

test/fixtures/modules/url/shared.css

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
@value v-url-other: url('./img.png');

test/fixtures/modules/url/source.css

+10
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
@value v-url: url('./img.png');
2+
@value v-url-other from './shared.css';
3+
4+
a {
5+
background: v-url;
6+
}
7+
8+
body {
9+
background: v-url-other;
10+
}

test/fixtures/modules/url/source.js

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
import css from './source.css';
2+
3+
__export__ = css;
4+
5+
export default css;

0 commit comments

Comments
 (0)