Skip to content

Refactor loader #825

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

Merged
merged 3 commits into from
Nov 29, 2018
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.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
184 changes: 139 additions & 45 deletions lib/loader.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,23 @@
MIT License http://www.opensource.org/licenses/mit-license.php
Author Tobias Koppers @sokra
*/
const postcss = require('postcss');
const localByDefault = require('postcss-modules-local-by-default');
const extractImports = require('postcss-modules-extract-imports');
const modulesScope = require('postcss-modules-scope');
const modulesValues = require('postcss-modules-values');
const loaderUtils = require('loader-utils');

const processCss = require('./processCss');
const { importParser, icssParser, urlParser } = require('./plugins');
const {
getLocalIdent,
getImportPrefix,
placeholderImportItemReplacer,
compileExports,
placholderRegExps,
} = require('./utils');
const Warning = require('./Warning');
const CssSyntaxError = require('./CssSyntaxError');

module.exports = function loader(content, map) {
const callback = this.async();
Expand All @@ -34,27 +43,92 @@ module.exports = function loader(content, map) {
}
/* eslint-enable no-param-reassign */

processCss(
content,
map,
{
loaderContext: this,
loaderOptions: options,
sourceMap,
},
(err, result) => {
if (err) {
return callback(err);
}
const loaderContext = this;
const localIdentName = options.localIdentName || '[hash:base64]';
const customGetLocalIdent = options.getLocalIdent || getLocalIdent;

const parserOptions = {
url: options.url !== false,
import: options.import !== false,
};

const plugins = [
modulesValues,
localByDefault({
mode: options.modules ? 'local' : 'global',
rewriteUrl(global, url) {
if (parserOptions.url) {
// eslint-disable-next-line no-param-reassign
url = url.trim();

if (
!url.replace(/\s/g, '').length ||
!loaderUtils.isUrlRequest(url)
) {
return url;
}
if (global) {
return loaderUtils.urlToRequest(url);
}
}
return url;
},
}),
extractImports(),
modulesScope({
generateScopedName: function generateScopedName(exportName) {
return customGetLocalIdent(loaderContext, localIdentName, exportName, {
regExp: options.localIdentRegExp,
hashPrefix: options.hashPrefix || '',
context: options.context,
});
},
}),
];

if (options.import !== false) {
plugins.push(importParser(parserOptions));
}

if (options.url !== false) {
plugins.push(urlParser(parserOptions));
}

plugins.push(icssParser(parserOptions));

postcss(plugins)
.process(content, {
// we need a prefix to avoid path rewriting of PostCSS
from: `/css-loader!${loaderUtils
.getRemainingRequest(this)
.split('!')
.pop()}`,
to: loaderUtils
.getCurrentRequest(this)
.split('!')
.pop(),
map: options.sourceMap
? {
prev: map,
sourcesContent: true,
inline: false,
annotation: false,
}
: null,
})
.then((result) => {
result
.warnings()
.forEach((warning) => this.emitWarning(new Warning(warning)));

// for importing CSS
const importUrlPrefix = getImportPrefix(this, options);

let exportJs = compileExports(
result,
parserOptions.exports,
placeholderImportItemReplacer(
this,
result,
parserOptions.importItems,
importUrlPrefix,
options.exportOnlyLocals
),
Expand All @@ -69,10 +143,10 @@ module.exports = function loader(content, map) {
return callback(null, exportJs);
}

let cssAsString = JSON.stringify(result.source);
let cssAsString = JSON.stringify(result.css);

const alreadyImported = {};
const importJs = result.importItems
const importJs = parserOptions.importItems
.filter((imp) => {
if (!imp.media) {
if (alreadyImported[imp.url]) {
Expand Down Expand Up @@ -102,48 +176,61 @@ module.exports = function loader(content, map) {
.join('\n');

cssAsString = cssAsString.replace(
result.importItemRegExpG,
placeholderImportItemReplacer(this, result, importUrlPrefix)
placholderRegExps.importItemG,
placeholderImportItemReplacer(
this,
parserOptions.importItems,
importUrlPrefix
)
);

// helper for ensuring valid CSS strings from requires
let urlEscapeHelper = '';

if (
options.url !== false &&
result.urlItems &&
result.urlItems.length > 0
parserOptions.urlItems &&
parserOptions.urlItems.length > 0
) {
urlEscapeHelper = `var escape = require(${loaderUtils.stringifyRequest(
this,
require.resolve('./runtime/escape.js')
)});\n`;

cssAsString = cssAsString.replace(result.urlItemRegExpG, (item) => {
const match = result.urlItemRegExp.exec(item);
let idx = +match[1];
const urlItem = result.urlItems[idx];
const { url } = urlItem;
idx = url.indexOf('?#');
if (idx < 0) {
idx = url.indexOf('#');
}
let urlRequest;
if (idx > 0) {
// idx === 0 is catched by isUrlRequest
// in cases like url('webfont.eot?#iefix')
urlRequest = url.substr(0, idx);
cssAsString = cssAsString.replace(
placholderRegExps.urlItemG,
(item) => {
const match = placholderRegExps.urlItem.exec(item);
let idx = +match[1];
const urlItem = parserOptions.urlItems[idx];
const { url } = urlItem;

idx = url.indexOf('?#');

if (idx < 0) {
idx = url.indexOf('#');
}

let urlRequest;

if (idx > 0) {
// idx === 0 is catched by isUrlRequest
// in cases like url('webfont.eot?#iefix')
urlRequest = url.substr(0, idx);
return `" + escape(require(${loaderUtils.stringifyRequest(
this,
urlRequest
)}) + "${url.substr(idx)}") + "`;
}

urlRequest = url;

return `" + escape(require(${loaderUtils.stringifyRequest(
this,
urlRequest
)}) + "${url.substr(idx)}") + "`;
)})) + "`;
}
urlRequest = url;
return `" + escape(require(${loaderUtils.stringifyRequest(
this,
urlRequest
)})) + "`;
});
);
}

if (exportJs) {
Expand All @@ -154,7 +241,8 @@ module.exports = function loader(content, map) {
if (sourceMap && result.map) {
/* eslint-disable no-param-reassign */
// Add a SourceMap
({ map } = result);
map = result.map.toJSON();

if (map.sources) {
map.sources = map.sources.map(
(source) =>
Expand All @@ -166,12 +254,14 @@ module.exports = function loader(content, map) {
);
map.sourceRoot = '';
}

map.file = map.file
.split('!')
.pop()
.replace(/\\/g, '/');
map = JSON.stringify(map);
/* eslint-enable no-param-reassign */

moduleJs = `exports.push([module.id, ${cssAsString}, "", ${map}]);`;
} else {
moduleJs = `exports.push([module.id, ${cssAsString}, ""]);`;
Expand All @@ -188,6 +278,10 @@ module.exports = function loader(content, map) {
`// module\n${moduleJs}\n\n` +
`// exports\n${exportJs}`
);
}
);
})
.catch((error) => {
callback(
error.name === 'CssSyntaxError' ? new CssSyntaxError(error) : error
);
});
};
120 changes: 0 additions & 120 deletions lib/processCss.js

This file was deleted.

Loading