Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: handling urls in @import #1016

Merged
merged 1 commit into from Dec 11, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
36 changes: 23 additions & 13 deletions src/plugins/postcss-import-parser.js
@@ -1,22 +1,24 @@
import postcss from 'postcss';
import valueParser from 'postcss-value-parser';
import { isUrlRequest, urlToRequest } from 'loader-utils';
import { isUrlRequest } from 'loader-utils';

const pluginName = 'postcss-import-parser';
import { normalizeUrl } from '../utils';

function getArg(nodes) {
return nodes.length !== 0 && nodes[0].type === 'string'
? nodes[0].value
: valueParser.stringify(nodes);
}
const pluginName = 'postcss-import-parser';

function getUrl(node) {
function getParsedValue(node) {
if (node.type === 'function' && node.value.toLowerCase() === 'url') {
return getArg(node.nodes);
const { nodes } = node;
const isStringValue = nodes.length !== 0 && nodes[0].type === 'string';
const url = isStringValue ? nodes[0].value : valueParser.stringify(nodes);

return { url, isStringValue };
}

if (node.type === 'string') {
return node.value;
const url = node.value;

return { url, isStringValue: true };
}

return null;
Expand All @@ -29,14 +31,22 @@ function parseImport(params) {
return null;
}

let url = getUrl(nodes[0]);
const value = getParsedValue(nodes[0]);

if (!url || url.trim().length === 0) {
if (!value) {
return null;
}

let { url } = value;

if (url.trim().length === 0) {
return null;
}

if (isUrlRequest(url)) {
url = urlToRequest(url);
const { isStringValue } = value;

url = normalizeUrl(url, isStringValue);
}

return {
Expand Down
49 changes: 22 additions & 27 deletions src/plugins/postcss-url-parser.js
@@ -1,8 +1,7 @@
import postcss from 'postcss';
import valueParser from 'postcss-value-parser';
import { urlToRequest } from 'loader-utils';

import { unescape } from '../utils';
import { normalizeUrl } from '../utils';

const pluginName = 'postcss-url-parser';

Expand All @@ -21,13 +20,11 @@ function walkUrls(parsed, callback) {
}

if (isUrlFunc.test(node.value)) {
const isStringNode =
node.nodes.length !== 0 && node.nodes[0].type === 'string';
const url = isStringNode
? node.nodes[0].value
: valueParser.stringify(node.nodes);
const { nodes } = node;
const isStringValue = nodes.length !== 0 && nodes[0].type === 'string';
const url = isStringValue ? nodes[0].value : valueParser.stringify(nodes);

callback(getNodeFromUrlFunc(node), url, false, isStringNode);
callback(getNodeFromUrlFunc(node), url, false, isStringValue);

// Do not traverse inside `url`
// eslint-disable-next-line consistent-return
Expand All @@ -36,18 +33,22 @@ function walkUrls(parsed, callback) {

if (isImageSetFunc.test(node.value)) {
node.nodes.forEach((nNode) => {
if (nNode.type === 'function' && isUrlFunc.test(nNode.value)) {
const isStringNode =
nNode.nodes.length !== 0 && nNode.nodes[0].type === 'string';
const url = isStringNode
? nNode.nodes[0].value
: valueParser.stringify(nNode.nodes);

callback(getNodeFromUrlFunc(nNode), url, false, isStringNode);
const { type, value } = nNode;

if (type === 'function' && isUrlFunc.test(value)) {
const { nodes } = nNode;

const isStringValue =
nodes.length !== 0 && nodes[0].type === 'string';
const url = isStringValue
? nodes[0].value
: valueParser.stringify(nodes);

callback(getNodeFromUrlFunc(nNode), url, false, isStringValue);
}

if (nNode.type === 'string') {
callback(nNode, nNode.value, true, true);
if (type === 'string') {
callback(nNode, value, true, true);
}
});

Expand All @@ -66,7 +67,7 @@ function getUrlsFromValue(value, result, filter, decl) {
const parsed = valueParser(value);
const urls = [];

walkUrls(parsed, (node, url, needQuotes, isStringNode) => {
walkUrls(parsed, (node, url, needQuotes, isStringValue) => {
if (url.trim().replace(/\\[\r\n]/g, '').length === 0) {
result.warn(`Unable to find uri in '${decl ? decl.toString() : value}'`, {
node: decl,
Expand All @@ -80,19 +81,13 @@ function getUrlsFromValue(value, result, filter, decl) {
}

const splittedUrl = url.split(/(\?)?#/);
let [normalizedUrl] = splittedUrl;
const [, singleQuery, hashValue] = splittedUrl;
const [urlWithoutHash, singleQuery, hashValue] = splittedUrl;
const hash =
singleQuery || hashValue
? `${singleQuery ? '?' : ''}${hashValue ? `#${hashValue}` : ''}`
: '';

// See https://drafts.csswg.org/css-values-4/#strings
if (isStringNode && /\\[\n]/.test(normalizedUrl)) {
normalizedUrl = normalizedUrl.replace(/\\[\n]/g, '');
}

normalizedUrl = urlToRequest(decodeURIComponent(unescape(normalizedUrl)));
const normalizedUrl = normalizeUrl(urlWithoutHash, isStringValue);

urls.push({ node, url: normalizedUrl, hash, needQuotes });
});
Expand Down
52 changes: 33 additions & 19 deletions src/utils.js
Expand Up @@ -4,7 +4,11 @@
*/
import path from 'path';

import loaderUtils, { isUrlRequest, stringifyRequest } from 'loader-utils';
import loaderUtils, {
isUrlRequest,
stringifyRequest,
urlToRequest,
} from 'loader-utils';
import normalizePath from 'normalize-path';
import cssesc from 'cssesc';
import modulesValues from 'postcss-modules-values';
Expand All @@ -13,23 +17,6 @@ import extractImports from 'postcss-modules-extract-imports';
import modulesScope from 'postcss-modules-scope';
import camelCase from 'camelcase';

function getImportPrefix(loaderContext, importLoaders) {
if (importLoaders === false) {
return '';
}

const numberImportedLoaders = parseInt(importLoaders, 10) || 0;
const loadersRequest = loaderContext.loaders
.slice(
loaderContext.loaderIndex,
loaderContext.loaderIndex + 1 + numberImportedLoaders
)
.map((x) => x.request)
.join('!');

return `-!${loadersRequest}!`;
}

const whitespace = '[\\x20\\t\\r\\n\\f]';
const unescapeRegExp = new RegExp(
`\\\\([\\da-f]{1,6}${whitespace}?|(${whitespace})|.)`,
Expand Down Expand Up @@ -90,6 +77,16 @@ function getLocalIdent(loaderContext, localIdentName, localName, options) {
).replace(/\\\[local\\\]/gi, localName);
}

function normalizeUrl(url, isStringValue) {
let normalizedUrl = url;

if (isStringValue && /\\[\n]/.test(normalizedUrl)) {
normalizedUrl = normalizedUrl.replace(/\\[\n]/g, '');
}

return urlToRequest(decodeURIComponent(unescape(normalizedUrl)));
}

function getFilter(filter, resourcePath, defaultFilter = null) {
return (item) => {
if (defaultFilter && !defaultFilter(item)) {
Expand Down Expand Up @@ -186,6 +183,23 @@ function normalizeSourceMap(map) {
return newMap;
}

function getImportPrefix(loaderContext, importLoaders) {
if (importLoaders === false) {
return '';
}

const numberImportedLoaders = parseInt(importLoaders, 10) || 0;
const loadersRequest = loaderContext.loaders
.slice(
loaderContext.loaderIndex,
loaderContext.loaderIndex + 1 + numberImportedLoaders
)
.map((x) => x.request)
.join('!');

return `-!${loadersRequest}!`;
}

function getImportCode(
loaderContext,
imports,
Expand Down Expand Up @@ -395,7 +409,7 @@ function getExportCode(
}

export {
unescape,
normalizeUrl,
getFilter,
getModulesPlugins,
normalizeSourceMap,
Expand Down