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

Refactor code #826

Merged
merged 3 commits into from
Nov 30, 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
173 changes: 90 additions & 83 deletions lib/loader.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,19 @@ 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 {
getOptions,
isUrlRequest,
urlToRequest,
getRemainingRequest,
getCurrentRequest,
stringifyRequest,
} = require('loader-utils');

const { importParser, icssParser, urlParser } = require('./plugins');
const {
getLocalIdent,
getImportPrefix,
placeholderImportItemReplacer,
compileExports,
placholderRegExps,
} = require('./utils');
Expand All @@ -22,7 +28,7 @@ const CssSyntaxError = require('./CssSyntaxError');

module.exports = function loader(content, map) {
const callback = this.async();
const options = loaderUtils.getOptions(this) || {};
const options = getOptions(this) || {};
const sourceMap = options.sourceMap || false;

/* eslint-disable no-param-reassign */
Expand All @@ -43,40 +49,38 @@ module.exports = function loader(content, map) {
}
/* eslint-enable no-param-reassign */

const parserOptions = {};
const resolveImport = options.import !== false;
const resolveUrl = options.url !== false;
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) {
if (resolveUrl) {
// eslint-disable-next-line no-param-reassign
url = url.trim();

if (
!url.replace(/\s/g, '').length ||
!loaderUtils.isUrlRequest(url)
) {
if (!url.replace(/\s/g, '').length || !isUrlRequest(url)) {
return url;
}

if (global) {
return loaderUtils.urlToRequest(url);
return urlToRequest(url);
}
}

return url;
},
}),
extractImports(),
modulesScope({
generateScopedName: function generateScopedName(exportName) {
const localIdentName = options.localIdentName || '[hash:base64]';
const customGetLocalIdent = options.getLocalIdent || getLocalIdent;

return customGetLocalIdent(loaderContext, localIdentName, exportName, {
regExp: options.localIdentRegExp,
hashPrefix: options.hashPrefix || '',
Expand All @@ -86,11 +90,11 @@ module.exports = function loader(content, map) {
}),
];

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

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

Expand All @@ -99,12 +103,10 @@ module.exports = function loader(content, map) {
postcss(plugins)
.process(content, {
// we need a prefix to avoid path rewriting of PostCSS
from: `/css-loader!${loaderUtils
.getRemainingRequest(this)
from: `/css-loader!${getRemainingRequest(this)
.split('!')
.pop()}`,
to: loaderUtils
.getCurrentRequest(this)
to: getCurrentRequest(this)
.split('!')
.pop(),
map: options.sourceMap
Expand All @@ -121,78 +123,83 @@ module.exports = function loader(content, map) {
.warnings()
.forEach((warning) => this.emitWarning(new Warning(warning)));

// for importing CSS
const importUrlPrefix = getImportPrefix(this, options);
const { camelCase, exportOnlyLocals, importLoaders } = options;
const { importItems, urlItems, exports } = parserOptions;
// Run other loader (`postcss-loader`, `sass-loader` and etc) for importing CSS
const importUrlPrefix = getImportPrefix(this, importLoaders);
// Prepare replacer to change from `___CSS_LOADER_IMPORT___INDEX___` to `require('./file.css').locals`
const importItemReplacer = (item) => {
const match = placholderRegExps.importItem.exec(item);
const idx = +match[1];
const importItem = importItems[idx];
const importUrl = importUrlPrefix + importItem.url;

if (exportOnlyLocals) {
return `" + require(${stringifyRequest(
this,
importUrl
)})[${JSON.stringify(importItem.export)}] + "`;
}

let exportJs = compileExports(
parserOptions.exports,
placeholderImportItemReplacer(
return `" + require(${stringifyRequest(
this,
parserOptions.importItems,
importUrlPrefix,
options.exportOnlyLocals
),
options.camelCase
);
importUrl
)}).locals[${JSON.stringify(importItem.export)}] + "`;
};

if (options.exportOnlyLocals) {
if (exportJs) {
exportJs = `module.exports = ${exportJs};`;
}
let exportCode = compileExports(exports, camelCase, (valueAsString) =>
valueAsString.replace(placholderRegExps.importItemG, importItemReplacer)
);

return callback(null, exportJs);
if (exportOnlyLocals) {
return callback(
null,
exportCode ? `module.exports = ${exportCode};` : exportCode
);
}

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

const alreadyImported = {};
const importJs = parserOptions.importItems
const importCode = importItems
.filter((imp) => {
if (!imp.media) {
if (alreadyImported[imp.url]) {
return false;
}

alreadyImported[imp.url] = true;
}

return true;
})
.map((imp) => {
const { url } = imp;
const media = imp.media || '';

if (!loaderUtils.isUrlRequest(url)) {
if (!isUrlRequest(url)) {
return `exports.push([module.id, ${JSON.stringify(
`@import url(${url});`
)}, ${JSON.stringify(media)}]);`;
}

const importUrl = importUrlPrefix + url;

return `exports.i(require(${loaderUtils.stringifyRequest(
return `exports.i(require(${stringifyRequest(
this,
importUrl
)}), ${JSON.stringify(media)});`;
}, this)
.join('\n');

cssAsString = cssAsString.replace(
let cssAsString = JSON.stringify(result.css).replace(
placholderRegExps.importItemG,
placeholderImportItemReplacer(
this,
parserOptions.importItems,
importUrlPrefix
)
importItemReplacer
);

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

if (
options.url !== false &&
parserOptions.urlItems &&
parserOptions.urlItems.length > 0
) {
urlEscapeHelper = `var escape = require(${loaderUtils.stringifyRequest(
let urlEscapeHelperCode = '';

if (resolveUrl && urlItems && urlItems.length > 0) {
urlEscapeHelperCode = `var escape = require(${stringifyRequest(
this,
require.resolve('./runtime/escape.js')
)});\n`;
Expand All @@ -202,7 +209,7 @@ module.exports = function loader(content, map) {
(item) => {
const match = placholderRegExps.urlItem.exec(item);
let idx = +match[1];
const urlItem = parserOptions.urlItems[idx];
const urlItem = urlItems[idx];
const { url } = urlItem;

idx = url.indexOf('?#');
Expand All @@ -217,66 +224,66 @@ module.exports = function loader(content, map) {
// idx === 0 is catched by isUrlRequest
// in cases like url('webfont.eot?#iefix')
urlRequest = url.substr(0, idx);
return `" + escape(require(${loaderUtils.stringifyRequest(
return `" + escape(require(${stringifyRequest(
this,
urlRequest
)}) + "${url.substr(idx)}") + "`;
}

urlRequest = url;

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

if (exportJs) {
exportJs = `exports.locals = ${exportJs};`;
if (exportCode) {
exportCode = `exports.locals = ${exportCode};`;
}

let moduleJs;
if (sourceMap && result.map) {
/* eslint-disable no-param-reassign */
let newMap = result.map;

if (sourceMap && newMap) {
// Add a SourceMap
map = result.map.toJSON();
newMap = newMap.toJSON();

if (map.sources) {
map.sources = map.sources.map(
if (newMap.sources) {
newMap.sources = newMap.sources.map(
(source) =>
source
.split('!')
.pop()
.replace(/\\/g, '/'),
this
);
map.sourceRoot = '';
newMap.sourceRoot = '';
}

map.file = map.file
newMap.file = newMap.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}, ""]);`;
newMap = JSON.stringify(newMap);
}

const runtimeCode = `exports = module.exports = require(${stringifyRequest(
this,
require.resolve('./runtime/api')
)})(${!!sourceMap});`;
const moduleCode = `exports.push([module.id, ${cssAsString}, ""${
newMap ? `,${newMap}` : ''
}]);`;

// Embed runtime
return callback(
null,
`${urlEscapeHelper}exports = module.exports = require(${loaderUtils.stringifyRequest(
this,
require.resolve('./runtime/api.js')
)})(${sourceMap});\n` +
`// imports\n${importJs}\n\n` +
`// module\n${moduleJs}\n\n` +
`// exports\n${exportJs}`
`${urlEscapeHelperCode}${runtimeCode}\n` +
`// imports\n${importCode}\n\n` +
`// module\n${moduleCode}\n\n` +
`// exports\n${exportCode}`
);
})
.catch((error) => {
Expand Down
Loading