Skip to content

Commit

Permalink
feat: @value supports importing url() (#1126)
Browse files Browse the repository at this point in the history
  • Loading branch information
evilebottnawi committed Jul 23, 2020
1 parent 791fff3 commit 7f49a0a
Show file tree
Hide file tree
Showing 10 changed files with 221 additions and 86 deletions.
65 changes: 30 additions & 35 deletions src/index.js
Expand Up @@ -48,27 +48,10 @@ export default async function loader(content, map, meta) {
return;
}

if (shouldUseModulesPlugins(options)) {
const icssResolver = this.getResolve({
mainFields: ['css', 'style', 'main', '...'],
mainFiles: ['index', '...'],
extensions: [],
conditionNames: ['style'],
});
const needUseModulesPlugins = shouldUseModulesPlugins(options);

plugins.push(
...getModulesPlugins(options, this),
icssParser({
context: this.context,
rootContext: this.rootContext,
resolver: icssResolver,
urlHandler: (url) =>
stringifyRequest(
this,
getPreRequester(this)(options.importLoaders) + url
),
})
);
if (needUseModulesPlugins) {
plugins.push(...getModulesPlugins(options, this));
}

if (shouldUseImportPlugin(options)) {
Expand Down Expand Up @@ -113,6 +96,28 @@ export default async function loader(content, map, meta) {
);
}

if (needUseModulesPlugins) {
const icssResolver = this.getResolve({
mainFields: ['css', 'style', 'main', '...'],
mainFiles: ['index', '...'],
extensions: [],
conditionNames: ['style'],
});

plugins.push(
icssParser({
context: this.context,
rootContext: this.rootContext,
resolver: icssResolver,
urlHandler: (url) =>
stringifyRequest(
this,
getPreRequester(this)(options.importLoaders) + url
),
})
);
}

// Reuse CSS AST (PostCSS AST e.g 'postcss-loader') to avoid reparsing
if (meta) {
const { ast } = meta;
Expand Down Expand Up @@ -160,8 +165,7 @@ export default async function loader(content, map, meta) {

const imports = [];
const apiImports = [];
const urlReplacements = [];
const icssReplacements = [];
const replacements = [];
const exports = [];

for (const message of result.messages) {
Expand All @@ -173,11 +177,8 @@ export default async function loader(content, map, meta) {
case 'api-import':
apiImports.push(message.value);
break;
case 'url-replacement':
urlReplacements.push(message.value);
break;
case 'icss-replacement':
icssReplacements.push(message.value);
case 'replacement':
replacements.push(message.value);
break;
case 'export':
exports.push(message.value);
Expand All @@ -189,14 +190,8 @@ export default async function loader(content, map, meta) {
apiImports.sort(sortImports);

const importCode = getImportCode(this, imports, options);
const moduleCode = getModuleCode(
result,
apiImports,
urlReplacements,
icssReplacements,
options
);
const exportCode = getExportCode(exports, icssReplacements, options);
const moduleCode = getModuleCode(result, apiImports, replacements, options);
const exportCode = getExportCode(exports, replacements, options);

callback(null, `${importCode}${moduleCode}${exportCode}`);
}
4 changes: 2 additions & 2 deletions src/plugins/postcss-icss-parser.js
Expand Up @@ -76,8 +76,8 @@ export default postcss.plugin(
importReplacements[token] = replacementName;

result.messages.push({
type: 'icss-replacement',
value: { replacementName, importName, localName },
type: 'replacement',
value: { type: 'icss', replacementName, importName, localName },
});
}
}
Expand Down
11 changes: 2 additions & 9 deletions src/plugins/postcss-url-parser.js
Expand Up @@ -233,15 +233,8 @@ export default postcss.plugin(pluginName, (options) => async (css, result) => {

result.messages.push({
pluginName,
type: 'url-replacement',
value: {
replacementName,
importName,
hash,
needQuotes,
index,
order: 4,
},
type: 'replacement',
value: { type: 'url', replacementName, importName, hash, needQuotes },
});
}

Expand Down
85 changes: 45 additions & 40 deletions src/utils.js
Expand Up @@ -354,13 +354,7 @@ function getImportCode(loaderContext, imports, options) {
return code ? `// Imports\n${code}` : '';
}

function getModuleCode(
result,
apiImports,
urlReplacements,
icssReplacements,
options
) {
function getModuleCode(result, apiImports, replacements, options) {
if (options.modules.exportOnlyLocals === true) {
return 'var ___CSS_LOADER_EXPORT___ = {};\n';
}
Expand All @@ -383,33 +377,35 @@ function getModuleCode(
)}${media ? `, ${JSON.stringify(media)}` : ''}]);\n`;
}

for (const item of urlReplacements) {
const { replacementName, importName, hash, needQuotes } = item;
for (const replacement of replacements) {
const { replacementName, importName, type } = replacement;

const getUrlOptions = []
.concat(hash ? [`hash: ${JSON.stringify(hash)}`] : [])
.concat(needQuotes ? 'needQuotes: true' : []);
const preparedOptions =
getUrlOptions.length > 0 ? `, { ${getUrlOptions.join(', ')} }` : '';
if (type === 'url') {
const { hash, needQuotes } = replacement;

beforeCode += `var ${replacementName} = ___CSS_LOADER_GET_URL_IMPORT___(${importName}${preparedOptions});\n`;

code = code.replace(
new RegExp(replacementName, 'g'),
() => `" + ${replacementName} + "`
);
}
const getUrlOptions = []
.concat(hash ? [`hash: ${JSON.stringify(hash)}`] : [])
.concat(needQuotes ? 'needQuotes: true' : []);
const preparedOptions =
getUrlOptions.length > 0 ? `, { ${getUrlOptions.join(', ')} }` : '';

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

code = code.replace(new RegExp(replacementName, 'g'), () =>
options.modules.namedExport
? `" + ${importName}_NAMED___[${JSON.stringify(
camelCase(localName)
)}] + "`
: `" + ${importName}.locals[${JSON.stringify(localName)}] + "`
);
code = code.replace(
new RegExp(replacementName, 'g'),
() => `" + ${replacementName} + "`
);
} else {
const { localName } = replacement;

code = code.replace(new RegExp(replacementName, 'g'), () =>
options.modules.namedExport
? `" + ${importName}_NAMED___[${JSON.stringify(
camelCase(localName)
)}] + "`
: `" + ${importName}.locals[${JSON.stringify(localName)}] + "`
);
}
}

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

function getExportCode(exports, icssReplacements, options) {
function getExportCode(exports, replacements, options) {
let code = '';
let localsCode = '';

Expand Down Expand Up @@ -476,16 +472,25 @@ function getExportCode(exports, icssReplacements, options) {
}
}

for (const replacement of icssReplacements) {
const { replacementName, importName, localName } = replacement;
for (const replacement of replacements) {
const { replacementName, type } = replacement;

localsCode = localsCode.replace(new RegExp(replacementName, 'g'), () =>
options.modules.namedExport
? `" + ${importName}_NAMED___[${JSON.stringify(
camelCase(localName)
)}] + "`
: `" + ${importName}.locals[${JSON.stringify(localName)}] + "`
);
if (type === 'url') {
localsCode = localsCode.replace(
new RegExp(replacementName, 'g'),
() => `" + ${replacementName} + "`
);
} else {
const { importName, localName } = replacement;

localsCode = localsCode.replace(new RegExp(replacementName, 'g'), () =>
options.modules.namedExport
? `" + ${importName}_NAMED___[${JSON.stringify(
camelCase(localName)
)}] + "`
: `" + ${importName}.locals[${JSON.stringify(localName)}] + "`
);
}
}

if (localsCode) {
Expand Down
92 changes: 92 additions & 0 deletions test/__snapshots__/modules-option.test.js.snap
Expand Up @@ -3676,6 +3676,98 @@ Object {
exports[`"modules" option should work js template with "namedExport" option: warnings 1`] = `Array []`;
exports[`"modules" option should work with "url" and "namedExport": errors 1`] = `Array []`;
exports[`"modules" option should work with "url" and "namedExport": module 1`] = `
"// Imports
import ___CSS_LOADER_API_IMPORT___ from \\"../../../../src/runtime/api.js\\";
import ___CSS_LOADER_ICSS_IMPORT_0___, * as ___CSS_LOADER_ICSS_IMPORT_0____NAMED___ from \\"-!../../../../src/index.js??[ident]!./shared.css\\";
import ___CSS_LOADER_GET_URL_IMPORT___ from \\"../../../../src/runtime/getUrl.js\\";
import ___CSS_LOADER_URL_IMPORT_0___ from \\"./img.png\\";
var ___CSS_LOADER_EXPORT___ = ___CSS_LOADER_API_IMPORT___(false);
___CSS_LOADER_EXPORT___.i(___CSS_LOADER_ICSS_IMPORT_0___, \\"\\", true);
var ___CSS_LOADER_URL_REPLACEMENT_0___ = ___CSS_LOADER_GET_URL_IMPORT___(___CSS_LOADER_URL_IMPORT_0___);
// Module
___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\\", \\"\\"]);
// Exports
export const vUrl = \\"url(\\" + ___CSS_LOADER_URL_REPLACEMENT_0___ + \\")\\";
export const vUrlOther = \\"\\" + ___CSS_LOADER_ICSS_IMPORT_0____NAMED___[\\"vUrlOther\\"] + \\"\\";
export default ___CSS_LOADER_EXPORT___;
"
`;
exports[`"modules" option should work with "url" and "namedExport": result 1`] = `
Array [
Array [
"../../src/index.js?[ident]!./modules/url/shared.css",
"
",
"",
],
Array [
"./modules/url/source.css",
"a {
background: url(/webpack/public/path/img.png);
}
body {
background: url(/webpack/public/path/img.png);
}
",
"",
],
]
`;
exports[`"modules" option should work with "url" and "namedExport": warnings 1`] = `Array []`;
exports[`"modules" option should work with "url": errors 1`] = `Array []`;
exports[`"modules" option should work with "url": module 1`] = `
"// Imports
import ___CSS_LOADER_API_IMPORT___ from \\"../../../../src/runtime/api.js\\";
import ___CSS_LOADER_ICSS_IMPORT_0___ from \\"-!../../../../src/index.js??[ident]!./shared.css\\";
import ___CSS_LOADER_GET_URL_IMPORT___ from \\"../../../../src/runtime/getUrl.js\\";
import ___CSS_LOADER_URL_IMPORT_0___ from \\"./img.png\\";
var ___CSS_LOADER_EXPORT___ = ___CSS_LOADER_API_IMPORT___(false);
___CSS_LOADER_EXPORT___.i(___CSS_LOADER_ICSS_IMPORT_0___, \\"\\", true);
var ___CSS_LOADER_URL_REPLACEMENT_0___ = ___CSS_LOADER_GET_URL_IMPORT___(___CSS_LOADER_URL_IMPORT_0___);
// Module
___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\\", \\"\\"]);
// Exports
___CSS_LOADER_EXPORT___.locals = {
\\"v-url\\": \\"url(\\" + ___CSS_LOADER_URL_REPLACEMENT_0___ + \\")\\",
\\"v-url-other\\": \\"\\" + ___CSS_LOADER_ICSS_IMPORT_0___.locals[\\"v-url-other\\"] + \\"\\"
};
export default ___CSS_LOADER_EXPORT___;
"
`;
exports[`"modules" option should work with "url": result 1`] = `
Array [
Array [
"../../src/index.js?[ident]!./modules/url/shared.css",
"
",
"",
],
Array [
"./modules/url/source.css",
"a {
background: url(/webpack/public/path/img.png);
}
body {
background: url(/webpack/public/path/img.png);
}
",
"",
],
]
`;
exports[`"modules" option should work with "url": warnings 1`] = `Array []`;
exports[`"modules" option should work with a modules.auto Function that returns "false": errors 1`] = `Array []`;
exports[`"modules" option should work with a modules.auto Function that returns "false": module 1`] = `
Expand Down
Binary file added test/fixtures/modules/url/img.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 1 addition & 0 deletions test/fixtures/modules/url/shared.css
@@ -0,0 +1 @@
@value v-url-other: url('./img.png');
10 changes: 10 additions & 0 deletions test/fixtures/modules/url/source.css
@@ -0,0 +1,10 @@
@value v-url: url('./img.png');
@value v-url-other from './shared.css';

a {
background: v-url;
}

body {
background: v-url-other;
}
5 changes: 5 additions & 0 deletions test/fixtures/modules/url/source.js
@@ -0,0 +1,5 @@
import css from './source.css';

__export__ = css;

export default css;

0 comments on commit 7f49a0a

Please sign in to comment.