Skip to content

Commit

Permalink
refactor: postcss-url-parser (#820)
Browse files Browse the repository at this point in the history
  • Loading branch information
evilebottnawi committed Nov 29, 2018
1 parent 9f66e33 commit eef3c26
Show file tree
Hide file tree
Showing 9 changed files with 295 additions and 68 deletions.
16 changes: 8 additions & 8 deletions lib/plugins/postcss-import-parser.js
Original file line number Diff line number Diff line change
Expand Up @@ -50,31 +50,31 @@ module.exports = postcss.plugin(
function process(css, result) {
const importItems = [];

css.walkAtRules(/^import$/i, (atrule) => {
css.walkAtRules(/^import$/i, (atRule) => {
// Convert only top-level @import
if (atrule.parent.type !== 'root') {
if (atRule.parent.type !== 'root') {
return;
}

if (atrule.nodes) {
if (atRule.nodes) {
result.warn(
"It looks like you didn't end your @import statement correctly. " +
'Child nodes are attached to it.',
{ node: atrule }
{ node: atRule }
);
return;
}

const parsed = parseImport(atrule.params);
const parsed = parseImport(atRule.params);

if (!parsed) {
// eslint-disable-next-line consistent-return
return result.warn(`Unable to find uri in '${atrule.toString()}'`, {
node: atrule,
return result.warn(`Unable to find uri in '${atRule.toString()}'`, {
node: atRule,
});
}

atrule.remove();
atRule.remove();

const { media } = parsed;
let { url } = parsed;
Expand Down
34 changes: 16 additions & 18 deletions lib/plugins/postcss-parser.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,24 @@ module.exports = postcss.plugin(
'postcss-parser',
(options) =>
function process(css) {
const imports = {};
let exports = {};
const importItems = options.importItems || [];
const urlItems = options.urlItems || [];
const icss = icssUtils.extractICSS(css);

const imports = {};
const exports = icss.icssExports;

Object.keys(icss.icssImports).forEach((key) => {
const url = loaderUtils.parseString(key);

Object.keys(icss.icssImports[key]).forEach((prop) => {
imports[`$${prop}`] = importItems.length;
importItems.push({
url,
export: icss.icssImports[key][prop],
});
});
});

function replaceImportsInString(str) {
if (options.import) {
Expand All @@ -36,22 +50,6 @@ module.exports = postcss.plugin(
return str;
}

const icss = icssUtils.extractICSS(css);

exports = icss.icssExports;

Object.keys(icss.icssImports).forEach((key) => {
const url = loaderUtils.parseString(key);

Object.keys(icss.icssImports[key]).forEach((prop) => {
imports[`$${prop}`] = importItems.length;
importItems.push({
url,
export: icss.icssImports[key][prop],
});
});
});

Object.keys(exports).forEach((exportName) => {
exports[exportName] = replaceImportsInString(exports[exportName]);
});
Expand Down
131 changes: 94 additions & 37 deletions lib/plugins/postcss-url-parser.js
Original file line number Diff line number Diff line change
@@ -1,53 +1,110 @@
const postcss = require('postcss');
const Tokenizer = require('css-selector-tokenizer');
const loaderUtils = require('loader-utils');
const valueParser = require('postcss-value-parser');
const { isUrlRequest } = require('loader-utils');

const pluginName = 'postcss-url-parser';

function walkUrls(parsed, callback) {
parsed.walk((node) => {
if (node.type !== 'function' || node.value.toLowerCase() !== 'url') {
return;
}

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

/* eslint-disable */
node.before = '';
node.after = '';
/* eslint-enable */

if (url.trim().replace(/\\[\r\n]/g, '').length !== 0) {
callback(node, url);
}

// Do not traverse inside url
// eslint-disable-next-line consistent-return
return false;
});
}

function filterUrls(parsed, filter) {
const result = [];

walkUrls(parsed, (node, content) => {
if (!filter(content)) {
return;
}

result.push(content);
});

return result;
}

function walkDeclsWithUrl(css, filter) {
const result = [];

css.walkDecls((decl) => {
if (!/url\(/i.test(decl.value)) {
return;
}

const parsed = valueParser(decl.value);
const values = filterUrls(parsed, filter);

if (values.length === 0) {
return;
}

result.push({ decl, parsed, values });
});

return result;
}

function flatten(array) {
return array.reduce((acc, d) => [...acc, ...d], []);
}

function mapUrls(parsed, map) {
walkUrls(parsed, (node, content) => {
const placeholder = map(content);

if (!placeholder) {
return;
}

// eslint-disable-next-line no-param-reassign
node.nodes = [{ type: 'word', value: map(content) }];
});
}

module.exports = postcss.plugin(
pluginName,
(options) =>
function process(css) {
const urlItems = [];
const traversed = walkDeclsWithUrl(css, (value) => isUrlRequest(value));
const paths = flatten(traversed.map((item) => item.values));

function processNode(item) {
switch (item.type) {
case 'value':
item.nodes.forEach(processNode);
break;
case 'nested-item':
item.nodes.forEach(processNode);
break;
case 'url':
if (
item.url.replace(/\s/g, '').length &&
!/^#/.test(item.url) &&
loaderUtils.isUrlRequest(item.url)
) {
// Strip quotes, they will be re-added if the module needs them
/* eslint-disable no-param-reassign */
item.stringType = '';
delete item.innerSpacingBefore;
delete item.innerSpacingAfter;
const { url } = item;
item.url = `___CSS_LOADER_URL___${urlItems.length}___`;
/* eslint-enable no-param-reassign */
urlItems.push({
url,
});
}
break;
// no default
}
if (paths.length === 0) {
return;
}

css.walkDecls((decl) => {
const values = Tokenizer.parseValues(decl.value);
values.nodes.forEach((value) => {
value.nodes.forEach(processNode);
});
const urls = {};

paths.forEach((path, index) => {
urls[path] = `___CSS_LOADER_URL___${index}___`;
urlItems.push({ url: path });
});

traversed.forEach((item) => {
mapUrls(item.parsed, (value) => urls[value]);
// eslint-disable-next-line no-param-reassign
decl.value = Tokenizer.stringifyValues(values);
item.decl.value = item.parsed.toString();
});

// eslint-disable-next-line no-param-reassign
Expand Down

0 comments on commit eef3c26

Please sign in to comment.