Skip to content

Commit

Permalink
refactor: merge code from processCss with loader code
Browse files Browse the repository at this point in the history
  • Loading branch information
alexander-akait committed Jul 9, 2018
1 parent b130031 commit d1f80ee
Show file tree
Hide file tree
Showing 3 changed files with 245 additions and 188 deletions.
2 changes: 1 addition & 1 deletion .eslintrc.yml
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ rules:
- error
- never
consistent-return: 'off'
consistent-this: error
consistent-this: 'off'
curly: 'off'
default-case: 'off'
dot-location: 'off'
Expand Down
372 changes: 244 additions & 128 deletions lib/loader.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,136 +3,252 @@
Author Tobias Koppers @sokra
*/
var loaderUtils = require("loader-utils");
var processCss = require("./processCss");
var postcss = require("postcss");
var plugin = require("./plugin");
var getImportPrefix = require("./getImportPrefix");
var compileExports = require("./compile-exports");

var CssLoaderError = require("./CssLoaderError");

module.exports = function(content, map) {
var callback = this.async();
var query = loaderUtils.getOptions(this) || {};
var camelCaseKeys = query.camelCase;
var sourceMap = query.sourceMap || false;

if(sourceMap) {
if (map) {
if (typeof map === "string") {
map = JSON.stringify(map);
}

if (map.sources) {
map.sources = map.sources.map(function (source) {
return source.replace(/\\/g, '/');
});
map.sourceRoot = '';
}
}
} else {
// Some loaders (example `"postcss-loader": "1.x.x"`) always generates source map, we should remove it
map = null;
}

processCss(content, map, {
from: loaderUtils.getRemainingRequest(this).split("!").pop(),
to: loaderUtils.getCurrentRequest(this).split("!").pop(),
query: query,
loaderContext: this,
sourceMap: sourceMap
}, function(err, result) {
if(err) return callback(err);

var cssAsString = JSON.stringify(result.source);

// for importing CSS
var importUrlPrefix = getImportPrefix(this, query);

var alreadyImported = {};
var importJs = result.importItems.filter(function(imp) {
if(!imp.mediaQuery) {
if(alreadyImported[imp.url])
return false;
alreadyImported[imp.url] = true;
}
return true;
}).map(function(imp) {
if(!loaderUtils.isUrlRequest(imp.url)) {
return "exports.push([module.id, " +
JSON.stringify("@import url(" + imp.url + ");") + ", " +
JSON.stringify(imp.mediaQuery) + "]);";
} else {
var importUrl = importUrlPrefix + imp.url;
return "exports.i(require(" + loaderUtils.stringifyRequest(this, importUrl) + "), " + JSON.stringify(imp.mediaQuery) + ");";
}
}, this).join("\n");

function importItemMatcher(item) {
var match = result.importItemRegExp.exec(item);
var idx = +match[1];
var importItem = result.importItems[idx];
var importUrl = importUrlPrefix + importItem.url;
return "\" + require(" + loaderUtils.stringifyRequest(this, importUrl) + ").locals" +
"[" + JSON.stringify(importItem.export) + "] + \"";
}

cssAsString = cssAsString.replace(result.importItemRegExpG, importItemMatcher.bind(this));

// helper for ensuring valid CSS strings from requires
var urlEscapeHelper = "";

if(query.url !== false && result.urlItems.length > 0) {
urlEscapeHelper = "var escape = require(" + loaderUtils.stringifyRequest(this, require.resolve("./url/escape.js")) + ");\n";

cssAsString = cssAsString.replace(result.urlItemRegExpG, function(item) {
var match = result.urlItemRegExp.exec(item);
var idx = +match[1];
var urlItem = result.urlItems[idx];
var url = urlItem.url;
idx = url.indexOf("?#");
if(idx < 0) idx = url.indexOf("#");
var 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) + ")) + \"";
}.bind(this));
}

var exportJs = compileExports(result, importItemMatcher.bind(this), camelCaseKeys);
if (exportJs) {
exportJs = "exports.locals = " + exportJs + ";";
}

var moduleJs;
if(sourceMap && result.map) {
// add a SourceMap
map = result.map;
if(map.sources) {
map.sources = map.sources.map(function(source) {
return source.split("!").pop().replace(/\\/g, '/');
}, this);
map.sourceRoot = "";
}
map.file = map.file.split("!").pop().replace(/\\/g, '/');
map = JSON.stringify(map);
moduleJs = "exports.push([module.id, " + cssAsString + ", \"\", " + map + "]);";
} else {
moduleJs = "exports.push([module.id, " + cssAsString + ", \"\"]);";
}

// embed runtime
callback(null, urlEscapeHelper +
"exports = module.exports = require(" +
loaderUtils.stringifyRequest(this, require.resolve("./runtime.js")) +
")(" + sourceMap + ");\n" +
"// imports\n" +
importJs + "\n\n" +
"// module\n" +
moduleJs + "\n\n" +
"// exports\n" +
exportJs);
}.bind(this));
var callback = this.async();
var query = loaderUtils.getOptions(this) || {};
var camelCaseKeys = query.camelCase;
var sourceMap = query.sourceMap || false;
var loaderContext = this;

if (sourceMap) {
if (map) {
if (typeof map === "string") {
map = JSON.stringify(map);
}

if (map.sources) {
map.sources = map.sources.map(function(source) {
return source.replace(/\\/g, "/");
});
map.sourceRoot = "";
}
}
} else {
// Some loaders (example `"postcss-loader": "1.x.x"`) always generates source map, we should remove it
map = null;
}

var parserOptions = {
url: query.url !== false,
import: query.import !== false,
resolve: loaderContext.resolve
};

postcss([plugin(parserOptions)])
.process(content, {
// we need a prefix to avoid path rewriting of PostCSS
from:
"/css-loader!" +
loaderUtils
.getRemainingRequest(loaderContext)
.split("!")
.pop(),
to: loaderUtils
.getCurrentRequest(loaderContext)
.split("!")
.pop(),
map: sourceMap
? {
prev: map,
sourcesContent: true,
inline: false,
annotation: false
}
: null
})
.then(function(result) {
var cssAsString = JSON.stringify(result.css);
// for importing CSS
var importUrlPrefix = getImportPrefix(loaderContext, query);
var alreadyImported = {};
var importItems = parserOptions.importItems;
var importJs = importItems
.filter(function(imp) {
if (!imp.mediaQuery) {
if (alreadyImported[imp.url]) {
return false;
}

alreadyImported[imp.url] = true;
}

return true;
})
.map(function(imp) {
if (!loaderUtils.isUrlRequest(imp.url)) {
return (
"exports.push([module.id, " +
JSON.stringify("@import url(" + imp.url + ");") +
", " +
JSON.stringify(imp.mediaQuery) +
"]);"
);
}

var importUrl = importUrlPrefix + imp.url;

return (
"exports.i(require(" +
loaderUtils.stringifyRequest(this, importUrl) +
"), " +
JSON.stringify(imp.mediaQuery) +
");"
);
})
.join("\n");

function importItemMatcher(item) {
var match = /___CSS_LOADER_IMPORT___([0-9]+)___/.exec(item);
var idx = +match[1];
var importItem = parserOptions.importItems[idx];
var importUrl = importUrlPrefix + importItem.url;

return (
'" + require(' +
loaderUtils.stringifyRequest(this, importUrl) +
").locals" +
"[" +
JSON.stringify(importItem.export) +
'] + "'
);
}

cssAsString = cssAsString.replace(
/___CSS_LOADER_IMPORT___([0-9]+)___/g,
importItemMatcher
);

// helper for ensuring valid CSS strings from requires
var urlEscapeHelper = "";
var urlItems = parserOptions.urlItems;

if (query.url !== false && urlItems.length > 0) {
urlEscapeHelper =
"var escape = require(" +
loaderUtils.stringifyRequest(
loaderContext,
require.resolve("./url/escape.js")
) +
");\n";

cssAsString = cssAsString.replace(
/___CSS_LOADER_URL___([0-9]+)___/g,
function(item) {
var match = /___CSS_LOADER_URL___([0-9]+)___/.exec(item);
var idx = +match[1];
var urlItem = urlItems[idx];
var url = urlItem.url;

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

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

var 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(loaderContext, urlRequest) +
')) + "' +
url.substr(idx)
);
}

urlRequest = url;

return (
'" + escape(require(' +
loaderUtils.stringifyRequest(loaderContext, urlRequest) +
')) + "'
);
}
);
}

var exportJs = compileExports(
parserOptions,
importItemMatcher,
camelCaseKeys
);

if (exportJs) {
exportJs = "exports.locals = " + exportJs + ";";
}

var moduleJs;

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

if (map.sources) {
map.sources = map.sources.map(function(source) {
return source
.split("!")
.pop()
.replace(/\\/g, "/");
}, loaderContext);
map.sourceRoot = "";
}

map.file = map.file
.split("!")
.pop()
.replace(/\\/g, "/");
map = JSON.stringify(map);

moduleJs =
"exports.push([module.id, " + cssAsString + ', "", ' + map + "]);";
} else {
moduleJs = "exports.push([module.id, " + cssAsString + ', ""]);';
}

// embed runtime
callback(
null,
urlEscapeHelper +
"exports = module.exports = require(" +
loaderUtils.stringifyRequest(
loaderContext,
require.resolve("./runtime.js")
) +
")(" +
sourceMap +
");\n" +
"// imports\n" +
importJs +
"\n\n" +
"// module\n" +
moduleJs +
"\n\n" +
"// exports\n" +
exportJs
);
})
.catch(function(error) {
callback(
error.name === "CssSyntaxError"
? new CssLoaderError(
"Syntax Error",
error.reason,
error.line != null && error.column != null
? { line: error.line, column: error.column }
: null,
error.input.source
)
: error
);
});
};
Loading

0 comments on commit d1f80ee

Please sign in to comment.