Skip to content

Commit

Permalink
refactor: loader (#956)
Browse files Browse the repository at this point in the history
  • Loading branch information
evilebottnawi authored Jun 10, 2019
1 parent 0672e78 commit 49f3ff0
Show file tree
Hide file tree
Showing 15 changed files with 300 additions and 354 deletions.
80 changes: 44 additions & 36 deletions src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,12 +19,12 @@ import {
normalizeSourceMap,
getModulesPlugins,
getImportPrefix,
getIcssItemReplacer,
getFilter,
getRuntimeCode,
getApiCode,
getImportCode,
getModuleCode,
getExportCode,
prepareCode,
} from './utils';
import Warning from './Warning';
import CssSyntaxError from './CssSyntaxError';
Expand All @@ -37,14 +37,9 @@ export default function loader(content, map, meta) {
const callback = this.async();
const sourceMap = options.sourceMap || false;

if (sourceMap && map) {
// eslint-disable-next-line no-param-reassign
map = normalizeSourceMap(map);
} else {
// Some loaders (example `"postcss-loader": "1.x.x"`) always generates source map, we should remove it
// eslint-disable-next-line no-param-reassign
map = null;
}
// Some loaders (example `"postcss-loader": "1.x.x"`) always generates source map, we should remove it
// eslint-disable-next-line no-param-reassign
map = sourceMap && map ? normalizeSourceMap(map) : null;

// Reuse CSS AST (PostCSS AST e.g 'postcss-loader') to avoid reparsing
if (meta) {
Expand All @@ -62,11 +57,22 @@ export default function loader(content, map, meta) {
plugins.push(...getModulesPlugins(options, this));
}

plugins.push(icssParser());
// Run other loader (`postcss-loader`, `sass-loader` and etc) for importing CSS
const importPrefix = getImportPrefix(this, options.importLoaders);

plugins.push(
icssParser({
loaderContext: this,
importPrefix,
exportLocalsStyle: options.exportLocalsStyle,
})
);

if (options.import !== false) {
plugins.push(
importParser({
loaderContext: this,
importPrefix,
filter: getFilter(options.import, this.resourcePath),
})
);
Expand All @@ -75,6 +81,7 @@ export default function loader(content, map, meta) {
if (options.url !== false) {
plugins.push(
urlParser({
loaderContext: this,
filter: getFilter(options.url, this.resourcePath, (value) =>
isUrlRequest(value)
),
Expand Down Expand Up @@ -108,36 +115,37 @@ export default function loader(content, map, meta) {
result.messages = [];
}

const {
exportOnlyLocals: onlyLocals,
exportLocalsStyle: localsStyle,
} = options;
// Run other loader (`postcss-loader`, `sass-loader` and etc) for importing CSS
const importPrefix = getImportPrefix(this, options.importLoaders);
// Prepare replacer to change from `___CSS_LOADER_IMPORT___INDEX___` to `require('./file.css').locals`
const replacer = getIcssItemReplacer(
result,
this,
importPrefix,
onlyLocals
);
const { exportOnlyLocals: onlyLocals } = options;

// eslint-disable-next-line no-param-reassign
result.cssLoaderBuildInfo = {
onlyLocals,
localsStyle,
importPrefix,
replacer,
};
const importItems = result.messages
.filter((message) => (message.type === 'import' ? message : false))
.reduce((accumulator, currentValue) => {
accumulator.push(currentValue.import);

return accumulator;
}, []);
const exportItems = result.messages
.filter((message) => (message.type === 'export' ? message : false))
.reduce((accumulator, currentValue) => {
accumulator.push(currentValue.export);

return accumulator;
}, []);

const runtimeCode = getRuntimeCode(result, this, sourceMap);
const importCode = getImportCode(result, this);
const moduleCode = getModuleCode(result);
const exportsCode = getExportCode(result);
const importCode = getImportCode(importItems, onlyLocals);
const moduleCode = getModuleCode(result, sourceMap, onlyLocals);
const exportCode = getExportCode(exportItems, onlyLocals);
const apiCode = getApiCode(this, sourceMap, onlyLocals);

return callback(
null,
runtimeCode + importCode + moduleCode + exportsCode
prepareCode(
{ apiCode, importCode, moduleCode, exportCode },
result.messages,
this,
importPrefix,
onlyLocals
)
);
})
.catch((error) => {
Expand Down
29 changes: 20 additions & 9 deletions src/plugins/postcss-icss-parser.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ import postcss from 'postcss';
import { extractICSS, replaceValueSymbols, replaceSymbols } from 'icss-utils';
import loaderUtils from 'loader-utils';

import { getExportItemCode, getImportItemCode } from '../utils';

const pluginName = 'postcss-icss-parser';

function hasImportMessage(messages, url) {
Expand All @@ -16,7 +18,7 @@ function hasImportMessage(messages, url) {

export default postcss.plugin(
pluginName,
() =>
(options = {}) =>
function process(css, result) {
const importReplacements = Object.create(null);
const { icssImports, icssExports } = extractICSS(css);
Expand All @@ -37,10 +39,18 @@ export default postcss.plugin(
});

if (!hasImportMessage(result.messages, url)) {
const media = '';
const { loaderContext, importPrefix } = options;

result.messages.push({
pluginName,
type: 'import',
item: { url, media: '' },
import: getImportItemCode(
{ url, media },
loaderContext,
importPrefix
),
item: { url, media },
});
}
}
Expand All @@ -49,16 +59,17 @@ export default postcss.plugin(
replaceSymbols(css, importReplacements);

for (const exportName of Object.keys(icssExports)) {
const name = exportName;
const value = replaceValueSymbols(
icssExports[name],
importReplacements
);

result.messages.push({
pluginName,
export: getExportItemCode(name, value, options.exportLocalsStyle),
type: 'export',
item: {
key: exportName,
value: replaceValueSymbols(
icssExports[exportName],
importReplacements
),
},
item: { name, value },
});
}
}
Expand Down
27 changes: 15 additions & 12 deletions src/plugins/postcss-import-parser.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import postcss from 'postcss';
import valueParser from 'postcss-value-parser';

import { uniqWith, getImportItemCode } from '../utils';

const pluginName = 'postcss-import-parser';

function getArg(nodes) {
Expand Down Expand Up @@ -84,25 +86,26 @@ function walkAtRules(css, result, filter) {
return items;
}

function uniq(array) {
return array.reduce(
(acc, d) =>
!acc.find((el) => el.url === d.url && el.media === d.media)
? [...acc, d]
: acc,
[]
);
}

export default postcss.plugin(
pluginName,
(options = {}) =>
function process(css, result) {
const traversed = walkAtRules(css, result, options.filter);
const paths = uniq(traversed);
const paths = uniqWith(
traversed,
(value, other) => value.url === other.url && value.media === other.media
);

paths.forEach((item) => {
result.messages.push({ pluginName, type: 'import', item });
result.messages.push({
pluginName,
type: 'import',
import: getImportItemCode(
item,
options.loaderContext,
options.importPrefix
),
});
});
}
);
43 changes: 25 additions & 18 deletions src/plugins/postcss-url-parser.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import postcss from 'postcss';
import valueParser from 'postcss-value-parser';

import { uniqWith, flatten, getUrlHelperCode, getUrlItemCode } from '../utils';

const pluginName = 'postcss-url-parser';

const isUrlFunc = /url/i;
Expand Down Expand Up @@ -102,29 +104,15 @@ function walkDeclsWithUrl(css, result, filter) {
return items;
}

function uniqWith(array, comparator) {
return array.reduce(
(acc, d) => (!acc.some((item) => comparator(d, item)) ? [...acc, d] : acc),
[]
);
}

function flatten(array) {
return array.reduce((a, b) => a.concat(b), []);
}

function isEqual(value, other) {
return value.url === other.url && value.needQuotes === other.needQuotes;
}

export default postcss.plugin(
pluginName,
(options = {}) =>
function process(css, result) {
const traversed = walkDeclsWithUrl(css, result, options.filter);
const paths = uniqWith(
flatten(traversed.map((item) => item.urls)),
isEqual
(value, other) =>
value.url === other.url && value.needQuotes === other.needQuotes
);

if (paths.length === 0) {
Expand All @@ -133,16 +121,35 @@ export default postcss.plugin(

const placeholders = [];

let hasUrlHelper = false;

paths.forEach((path, index) => {
const { loaderContext } = options;
const placeholder = `___CSS_LOADER_URL___${index}___`;
const { url, needQuotes } = path;

placeholders.push({ placeholder, path });

if (!hasUrlHelper) {
result.messages.push({
pluginName,
type: 'import',
import: getUrlHelperCode(loaderContext),
});

// eslint-disable-next-line no-param-reassign
hasUrlHelper = true;
}

result.messages.push({
pluginName,
type: 'url',
item: { url, placeholder, needQuotes },
type: 'import',
import: getUrlItemCode(
{ url, placeholder, needQuotes },
loaderContext
),
importType: 'url',
placeholder,
});
});

Expand Down
2 changes: 1 addition & 1 deletion src/runtime/get-url.js → src/runtime/getUrl.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
module.exports = function escape(url, needQuotes) {
module.exports = (url, needQuotes) => {
if (typeof url !== 'string') {
return url;
}
Expand Down
Loading

0 comments on commit 49f3ff0

Please sign in to comment.