Skip to content

Commit

Permalink
refactor: plugins (#817)
Browse files Browse the repository at this point in the history
  • Loading branch information
evilebottnawi committed Nov 28, 2018
1 parent 21254c8 commit 6cf8a2b
Show file tree
Hide file tree
Showing 8 changed files with 260 additions and 180 deletions.
12 changes: 5 additions & 7 deletions lib/loader.js
Expand Up @@ -50,12 +50,6 @@ module.exports = function loader(content, map) {

const alreadyImported = {};
const importJs = result.importItems
.map((imp) => {
// fixes #781 when importing `url(filename.css )`
// eslint-disable-next-line no-param-reassign
imp.url = imp.url.trim();
return imp;
})
.filter((imp) => {
if (!imp.mediaQuery) {
if (alreadyImported[imp.url]) {
Expand Down Expand Up @@ -98,7 +92,11 @@ module.exports = function loader(content, map) {
// helper for ensuring valid CSS strings from requires
let urlEscapeHelper = '';

if (options.url !== false && result.urlItems.length > 0) {
if (
options.url !== false &&
result.urlItems &&
result.urlItems.length > 0
) {
urlEscapeHelper = `var escape = require(${loaderUtils.stringifyRequest(
this,
require.resolve('./runtime/escape.js')
Expand Down
9 changes: 9 additions & 0 deletions lib/plugins/index.js
@@ -0,0 +1,9 @@
const importParser = require('./postcss-import-parser');
const parser = require('./postcss-parser');
const urlParser = require('./postcss-url-parser');

module.exports = {
importParser,
parser,
urlParser,
};
72 changes: 72 additions & 0 deletions lib/plugins/postcss-import-parser.js
@@ -0,0 +1,72 @@
const postcss = require('postcss');
const loaderUtils = require('loader-utils');
const Tokenizer = require('css-selector-tokenizer');

const pluginName = 'postcss-import-parser';

module.exports = postcss.plugin(
pluginName,
(options) =>
function process(css, result) {
const importItems = [];

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

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

const values = Tokenizer.parseValues(atrule.params);
let [url] = values.nodes[0].nodes;

if (url && url.type === 'url') {
({ url } = url);
} else if (url && url.type === 'string') {
url = url.value;
} else {
result.warn(`Unable to find uri in '${atrule.toString()}'`, {
node: atrule,
});

return;
}

if (!url.replace(/\s/g, '').length) {
result.warn(`Unable to find uri in '${atrule.toString()}'`, {
node: atrule,
});

return;
}

values.nodes[0].nodes.shift();

const mediaQuery = Tokenizer.stringifyValues(values);

url = url.trim();

if (loaderUtils.isUrlRequest(url)) {
url = loaderUtils.urlToRequest(url);
}

importItems.push({
url,
mediaQuery,
});

atrule.remove();
});

// eslint-disable-next-line no-param-reassign
options.importItems = importItems;
}
);
103 changes: 103 additions & 0 deletions lib/plugins/postcss-parser.js
@@ -0,0 +1,103 @@
const postcss = require('postcss');
const valueParser = require('postcss-value-parser');
const icssUtils = require('icss-utils');
const Tokenizer = require('css-selector-tokenizer');
const loaderUtils = require('loader-utils');

module.exports = postcss.plugin(
'postcss-parser',
(options) =>
function process(css) {
const imports = {};
let exports = {};
const importItems = options.importItems || [];
const urlItems = options.urlItems || [];

function replaceImportsInString(str) {
if (options.import) {
const tokens = valueParser(str);

tokens.walk((node) => {
if (node.type !== 'word') {
return;
}

const token = node.value;
const importIndex = imports[`$${token}`];

if (typeof importIndex === 'number') {
// eslint-disable-next-line no-param-reassign
node.value = `___CSS_LOADER_IMPORT___${importIndex}___`;
}
});

return tokens.toString();
}
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]);
});

function processNode(item) {
switch (item.type) {
case 'value':
item.nodes.forEach(processNode);
break;
case 'nested-item':
item.nodes.forEach(processNode);
break;
case 'item': {
const importIndex = imports[`$${item.name}`];
if (typeof importIndex === 'number') {
// eslint-disable-next-line no-param-reassign
item.name = `___CSS_LOADER_IMPORT___${importIndex}___`;
}
break;
}
// no default
}
}

css.walkDecls((decl) => {
const values = Tokenizer.parseValues(decl.value);

values.nodes.forEach((value) => {
value.nodes.forEach(processNode);
});

// eslint-disable-next-line no-param-reassign
decl.value = Tokenizer.stringifyValues(values);
});

css.walkAtRules((atrule) => {
if (typeof atrule.params === 'string') {
// eslint-disable-next-line no-param-reassign
atrule.params = replaceImportsInString(atrule.params);
}
});

/* eslint-disable no-param-reassign */
options.importItems = importItems;
options.urlItems = urlItems;
options.exports = exports;
/* eslint-enable no-param-reassign */
}
);
56 changes: 56 additions & 0 deletions lib/plugins/postcss-url-parser.js
@@ -0,0 +1,56 @@
const postcss = require('postcss');
const Tokenizer = require('css-selector-tokenizer');
const loaderUtils = require('loader-utils');

const pluginName = 'postcss-url-parser';

module.exports = postcss.plugin(
pluginName,
(options) =>
function process(css) {
const urlItems = [];

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
}
}

css.walkDecls((decl) => {
const values = Tokenizer.parseValues(decl.value);
values.nodes.forEach((value) => {
value.nodes.forEach(processNode);
});
// eslint-disable-next-line no-param-reassign
decl.value = Tokenizer.stringifyValues(values);
});

// eslint-disable-next-line no-param-reassign
options.urlItems = urlItems;
}
);

0 comments on commit 6cf8a2b

Please sign in to comment.