diff --git a/CHANGELOG.md b/CHANGELOG.md
index 5c73a0fe..4d1d57b9 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -2,6 +2,9 @@
All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines.
+
+# [0.27.1](https://github.com/webpack/css-loader/compare/v0.27.0...v0.27.1) (2017-03-10)
+
# [0.27.0](https://github.com/webpack/css-loader/compare/v0.26.2...v0.27.0) (2017-03-10)
diff --git a/lib/compile-exports.js b/lib/compile-exports.js
index 04f97b0d..41dcade3 100644
--- a/lib/compile-exports.js
+++ b/lib/compile-exports.js
@@ -17,21 +17,32 @@ module.exports = function compileExports(result, importItemMatcher, camelCaseKey
function addEntry(k) {
res.push("\t" + JSON.stringify(k) + ": " + valueAsString);
}
- if (camelCaseKeys !== 'only' && camelCaseKeys !== 'dashesOnly') {
- addEntry(key);
- }
var targetKey;
- if (camelCaseKeys === true || camelCaseKeys === 'only') {
- targetKey = camelCase(key);
- if (targetKey !== key) {
- addEntry(targetKey);
- }
- } else if (camelCaseKeys === 'dashes' || camelCaseKeys === 'dashesOnly') {
- targetKey = dashesCamelCase(key);
- if (targetKey !== key) {
- addEntry(targetKey);
- }
+ switch(camelCaseKeys) {
+ case true:
+ addEntry(key);
+ targetKey = camelCase(key);
+ if (targetKey !== key) {
+ addEntry(targetKey);
+ }
+ break;
+ case 'dashes':
+ addEntry(key);
+ targetKey = dashesCamelCase(key);
+ if (targetKey !== key) {
+ addEntry(targetKey);
+ }
+ break;
+ case 'only':
+ addEntry(camelCase(key));
+ break;
+ case 'dashesOnly':
+ addEntry(dashesCamelCase(key));
+ break;
+ default:
+ addEntry(key);
+ break;
}
return res;
}, []).join(",\n");
diff --git a/lib/convert-source-map.js b/lib/convert-source-map.js
new file mode 100644
index 00000000..732f6213
--- /dev/null
+++ b/lib/convert-source-map.js
@@ -0,0 +1,143 @@
+/* eslint-disable */
+'use strict';
+// XXXXX: This file should not exist. Working around a core level bug
+// that prevents using fs at loaders.
+//var fs = require('fs'); // XXX
+var path = require('path');
+
+var commentRx = /^\s*\/(?:\/|\*)[@#]\s+sourceMappingURL=data:(?:application|text)\/json;(?:charset[:=]\S+?;)?base64,(?:.*)$/mg;
+var mapFileCommentRx =
+ //Example (Extra space between slashes added to solve Safari bug. Exclude space in production):
+ // / /# sourceMappingURL=foo.js.map /*# sourceMappingURL=foo.js.map */
+ /(?:\/\/[@#][ \t]+sourceMappingURL=([^\s'"]+?)[ \t]*$)|(?:\/\*[@#][ \t]+sourceMappingURL=([^\*]+?)[ \t]*(?:\*\/){1}[ \t]*$)/mg
+
+function decodeBase64(base64) {
+ return new Buffer(base64, 'base64').toString();
+}
+
+function stripComment(sm) {
+ return sm.split(',').pop();
+}
+
+function readFromFileMap(sm, dir) {
+ // NOTE: this will only work on the server since it attempts to read the map file
+
+ mapFileCommentRx.lastIndex = 0;
+ var r = mapFileCommentRx.exec(sm);
+
+ // for some odd reason //# .. captures in 1 and /* .. */ in 2
+ var filename = r[1] || r[2];
+ var filepath = path.resolve(dir, filename);
+
+ try {
+ return fs.readFileSync(filepath, 'utf8');
+ } catch (e) {
+ throw new Error('An error occurred while trying to read the map file at ' + filepath + '\n' + e);
+ }
+}
+
+function Converter (sm, opts) {
+ opts = opts || {};
+
+ if (opts.isFileComment) sm = readFromFileMap(sm, opts.commentFileDir);
+ if (opts.hasComment) sm = stripComment(sm);
+ if (opts.isEncoded) sm = decodeBase64(sm);
+ if (opts.isJSON || opts.isEncoded) sm = JSON.parse(sm);
+
+ this.sourcemap = sm;
+}
+
+Converter.prototype.toJSON = function (space) {
+ return JSON.stringify(this.sourcemap, null, space);
+};
+
+Converter.prototype.toBase64 = function () {
+ var json = this.toJSON();
+ return new Buffer(json).toString('base64');
+};
+
+Converter.prototype.toComment = function (options) {
+ var base64 = this.toBase64();
+ var data = 'sourceMappingURL=data:application/json;charset=utf-8;base64,' + base64;
+ return options && options.multiline ? '/*# ' + data + ' */' : '//# ' + data;
+};
+
+// returns copy instead of original
+Converter.prototype.toObject = function () {
+ return JSON.parse(this.toJSON());
+};
+
+Converter.prototype.addProperty = function (key, value) {
+ if (this.sourcemap.hasOwnProperty(key)) throw new Error('property %s already exists on the sourcemap, use set property instead');
+ return this.setProperty(key, value);
+};
+
+Converter.prototype.setProperty = function (key, value) {
+ this.sourcemap[key] = value;
+ return this;
+};
+
+Converter.prototype.getProperty = function (key) {
+ return this.sourcemap[key];
+};
+
+exports.fromObject = function (obj) {
+ return new Converter(obj);
+};
+
+exports.fromJSON = function (json) {
+ return new Converter(json, { isJSON: true });
+};
+
+exports.fromBase64 = function (base64) {
+ return new Converter(base64, { isEncoded: true });
+};
+
+exports.fromComment = function (comment) {
+ comment = comment
+ .replace(/^\/\*/g, '//')
+ .replace(/\*\/$/g, '');
+
+ return new Converter(comment, { isEncoded: true, hasComment: true });
+};
+
+exports.fromMapFileComment = function (comment, dir) {
+ return new Converter(comment, { commentFileDir: dir, isFileComment: true, isJSON: true });
+};
+
+// Finds last sourcemap comment in file or returns null if none was found
+exports.fromSource = function (content) {
+ var m = content.match(commentRx);
+ return m ? exports.fromComment(m.pop()) : null;
+};
+
+// Finds last sourcemap comment in file or returns null if none was found
+exports.fromMapFileSource = function (content, dir) {
+ var m = content.match(mapFileCommentRx);
+ return m ? exports.fromMapFileComment(m.pop(), dir) : null;
+};
+
+exports.removeComments = function (src) {
+ return src.replace(commentRx, '');
+};
+
+exports.removeMapFileComments = function (src) {
+ return src.replace(mapFileCommentRx, '');
+};
+
+exports.generateMapFileComment = function (file, options) {
+ var data = 'sourceMappingURL=' + file;
+ return options && options.multiline ? '/*# ' + data + ' */' : '//# ' + data;
+};
+
+Object.defineProperty(exports, 'commentRegex', {
+ get: function getCommentRegex () {
+ return commentRx;
+ }
+});
+
+Object.defineProperty(exports, 'mapFileCommentRegex', {
+ get: function getMapFileCommentRegex () {
+ return mapFileCommentRx;
+ }
+});
diff --git a/lib/css-base.js b/lib/css-base.js
index 1e240e59..71b9e7dc 100644
--- a/lib/css-base.js
+++ b/lib/css-base.js
@@ -53,7 +53,7 @@ function cssWithMappingToString(item) {
if (!cssMapping) {
return content;
}
- var convertSourceMap = require('convert-source-map');
+ var convertSourceMap = require('./convert-source-map');
var sourceMapping = convertSourceMap.fromObject(cssMapping).toComment({multiline: true});
var sourceURLs = cssMapping.sources.map(function (source) {
return '/*# sourceURL=' + cssMapping.sourceRoot + source + ' */'
diff --git a/package.json b/package.json
index c1ab06e7..665f3045 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "css-loader",
- "version": "0.27.0",
+ "version": "0.27.1",
"author": "Tobias Koppers @sokra",
"description": "css loader module for webpack",
"engines": {
diff --git a/test/camelCaseTest.js b/test/camelCaseTest.js
index 5c669e84..a9991b76 100644
--- a/test/camelCaseTest.js
+++ b/test/camelCaseTest.js
@@ -5,6 +5,7 @@ var testRaw = require("./helpers").testRaw;
describe("camelCase", function() {
var css = ".btn-info_is-disabled { color: blue; }";
+ var mixedCss = ".btn-info_is-disabled { color: blue; } .simple { color: red; }";
var exports = {
with: [
[1, "._1L-rnCOXCE_7H94L5XT4uB { color: blue; }", ""]
@@ -16,24 +17,24 @@ describe("camelCase", function() {
[1, "._1L-rnCOXCE_7H94L5XT4uB { color: blue; }", ""]
],
withoutOnly: [
- [1, "._1L-rnCOXCE_7H94L5XT4uB { color: blue; }", ""]
+ [1, "._1L-rnCOXCE_7H94L5XT4uB { color: blue; } .KKtodWG-IuEaequFjAsoJ { color: red; }", ""]
],
dashesOnly: [
- [1, "._1L-rnCOXCE_7H94L5XT4uB { color: blue; }", ""]
+ [1, "._1L-rnCOXCE_7H94L5XT4uB { color: blue; } .KKtodWG-IuEaequFjAsoJ { color: red; }", ""]
]
};
exports.with.locals = {'btn-info_is-disabled': '_1L-rnCOXCE_7H94L5XT4uB'};
exports.without.locals = {btnInfoIsDisabled: '_1L-rnCOXCE_7H94L5XT4uB', 'btn-info_is-disabled': '_1L-rnCOXCE_7H94L5XT4uB'};
exports.dashes.locals = {btnInfo_isDisabled: '_1L-rnCOXCE_7H94L5XT4uB', 'btn-info_is-disabled': '_1L-rnCOXCE_7H94L5XT4uB'};
- exports.withoutOnly.locals = {btnInfoIsDisabled: '_1L-rnCOXCE_7H94L5XT4uB'};
- exports.dashesOnly.locals = {btnInfo_isDisabled: '_1L-rnCOXCE_7H94L5XT4uB'};
+ exports.withoutOnly.locals = {btnInfoIsDisabled: '_1L-rnCOXCE_7H94L5XT4uB', simple: 'KKtodWG-IuEaequFjAsoJ'};
+ exports.dashesOnly.locals = {btnInfo_isDisabled: '_1L-rnCOXCE_7H94L5XT4uB', simple: 'KKtodWG-IuEaequFjAsoJ'};
test("with", css, exports.with, "?modules");
test("without", css, exports.without, "?modules&camelCase");
test("dashes", css, exports.dashes, "?modules&camelCase=dashes");
// Remove this option in v1.0.0 and make the removal of the original classname the default behaviour. See #440.
- test("withoutOnly", css, exports.withoutOnly, "?modules&camelCase=only");
+ test("withoutOnly", mixedCss, exports.withoutOnly, "?modules&camelCase=only");
// Remove this option in v1.0.0 and make the removal of the original classname the default behaviour. See #440.
- test("dashesOnly", css, exports.dashesOnly, "?modules&camelCase=dashesOnly");
+ test("dashesOnly", mixedCss, exports.dashesOnly, "?modules&camelCase=dashesOnly");
testRaw("withoutRaw", '.a {}', 'exports.locals = {\n\t"a": "_1buUQJccBRS2-2i27LCoDf"\n};', "?modules&camelCase");
testRaw("dashesRaw", '.a {}', 'exports.locals = {\n\t"a": "_1buUQJccBRS2-2i27LCoDf"\n};', "?modules&camelCase=dashes");