Fixes #857 - normalizes CleanCSS API interface.
authorJakub Pawlowicz <contact@jakubpawlowicz.com>
Sun, 15 Jan 2017 10:28:13 +0000 (11:28 +0100)
committerJakub Pawlowicz <contact@jakubpawlowicz.com>
Sun, 15 Jan 2017 10:33:06 +0000 (11:33 +0100)
Why:

* `sourceMap` option has to be a boolean now;
* input source map can be passed as a 2nd argument to `minify`
  method, making callback a 3rd;
* all other options are stateless so should be the `sourceMap`.

History.md
README.md
lib/clean.js
test/source-map-test.js

index 678bc53..90bb2db 100644 (file)
@@ -43,6 +43,7 @@
 * Fixed issue [#845](https://github.com/jakubpawlowicz/clean-css/issues/845) - web compatibility of 4.0 branch.
 * Fixed issue [#847](https://github.com/jakubpawlowicz/clean-css/issues/847) - regression in handling invalid selectors.
 * Fixed issue [#849](https://github.com/jakubpawlowicz/clean-css/issues/849) - disables inlining protocol-less resources.
+* Fixed issue [#857](https://github.com/jakubpawlowicz/clean-css/issues/857) - normalizes CleanCSS API interface.
 
 [3.4.23 / 2016-12-17](https://github.com/jakubpawlowicz/clean-css/compare/v3.4.22...v3.4.23)
 ==================
index b90d89e..47b11e9 100644 (file)
--- a/README.md
+++ b/README.md
@@ -41,6 +41,7 @@ There will be some breaking changes:
 * moves `mediaMerging`, `restructuring`, `semanticMerging`, and `shorthandCompacting` to level 2 optimizations options, see examples below;
 * level 1 optimizations are the new default, up to 3.x it was level 2;
 * `--keep-line-breaks` / `keepBreaks` option is replaced with `--format keep-breaks` / `{ format: 'keep-breaks' }` to ease transition.
+* `sourceMap` option is API has to be a boolean from now on. If you want to specify an input source map pass it a 2nd argument to `minify` method or via a hash instead;
 
 Please note this list is not final. You are more than welcome to comment these changes in [4.0 release discussion](https://github.com/jakubpawlowicz/clean-css/issues/842) thread.
 
@@ -207,8 +208,7 @@ CleanCSS constructor accepts a hash as a parameter, i.e.,
 * `level` - an integer denoting optimization level applied or a hash with a fine-grained configuration; see examples below; defaults to `1`
 * `rebase` - set to false to skip URL rebasing
 * `rebaseTo` - a directory to which all URLs are rebased (most likely the directory under which the output file will live), defaults to the current directory
-* `sourceMap` - exposes source map under `sourceMap` property, e.g. `new CleanCSS().minify(source).sourceMap` (default is false)
-  If input styles are a product of CSS preprocessor (Less, Sass) an input source map can be passed as a string.
+* `sourceMap` - set to true to build output source map; defaults to `false`
 * `sourceMapInlineSources` - set to true to inline sources inside a source map's `sourcesContent` field (defaults to false)
   It is also required to process inlined sources from input source maps.
 
@@ -440,11 +440,11 @@ new CleanCSS({ sourceMap: true, rebaseTo: pathToOutputDirectory })
 });
 ```
 
-Using API you can also pass an input source map directly:
+Using API you can also pass an input source map directly as a 2nd argument to `minify` method:
 
 ```js
-new CleanCSS({ sourceMap: inputSourceMapAsString, rebaseTo: pathToOutputDirectory })
-  .minify(source, function (error, minified) {
+new CleanCSS({ sourceMap: true, rebaseTo: pathToOutputDirectory })
+  .minify(source, inputSourceMap, function (error, minified) {
     // access minified.sourceMap to access SourceMapGenerator object
     // see https://github.com/mozilla/source-map/#sourcemapgenerator for more details
     // see https://github.com/jakubpawlowicz/clean-css/blob/master/bin/cleancss#L114 on how it's used in clean-css' CLI
index d9a1006..6db9f59 100644 (file)
@@ -40,7 +40,7 @@ var CleanCSS = module.exports = function CleanCSS(options) {
     level: optimizationLevelFrom(options.level),
     rebase: undefined === options.rebase ? true : !!options.rebase,
     rebaseTo: ('rebaseTo' in options) ? path.resolve(options.rebaseTo) : process.cwd(),
-    sourceMap: options.sourceMap,
+    sourceMap: !!options.sourceMap,
     sourceMapInlineSources: !!options.sourceMapInlineSources
   };
 
@@ -70,7 +70,13 @@ function proxyOptionsFrom(httpProxy) {
     {};
 }
 
-CleanCSS.prototype.minify = function (input, callback) {
+CleanCSS.prototype.minify = function (input, maybeSourceMap, maybeCallback) {
+  var sourceMap = typeof maybeSourceMap != 'function' ?
+    maybeSourceMap :
+    null;
+  var callback = sourceMap ?
+    maybeCallback :
+    maybeSourceMap;
   var context = {
     stats: {
       efficiency: 0,
@@ -93,8 +99,8 @@ CleanCSS.prototype.minify = function (input, callback) {
     warnings: []
   };
 
-  if (typeof this.options.sourceMap == 'string') {
-    context.inputSourceMapTracker.track(undefined, this.options.sourceMap);
+  if (sourceMap) {
+    context.inputSourceMapTracker.track(undefined, sourceMap);
   }
 
   return runner(context.localOnly)(function () {
index 7c01edd..f28f896 100644 (file)
@@ -445,7 +445,7 @@ vows.describe('source-map')
   .addBatch({
     'input map as string': {
       'topic': function () {
-        return new CleanCSS({ sourceMap: inputMap }).minify('div > a {\n  color: red;\n}');
+        return new CleanCSS({ sourceMap: true }).minify('div > a {\n  color: red;\n}', inputMap);
       },
       'has 3 mappings': function (minified) {
         assert.lengthOf(minified.sourceMap._mappings._array, 3);
@@ -486,7 +486,7 @@ vows.describe('source-map')
     },
     'input map as source map object': {
       'topic': function () {
-        return new CleanCSS({ sourceMap: JSON.parse(inputMap) }).minify('div > a {\n  color: red;\n}');
+        return new CleanCSS({ sourceMap: true }).minify('div > a {\n  color: red;\n}', JSON.parse(inputMap));
       },
       'has 3 mappings': function (minified) {
         assert.lengthOf(minified.sourceMap._mappings._array, 3);
@@ -525,6 +525,47 @@ vows.describe('source-map')
         assert.deepEqual(minified.sourceMap._mappings._array[2], mapping);
       }
     },
+    'input map and a callback': {
+      'topic': function () {
+        new CleanCSS({ sourceMap: true }).minify('div > a {\n  color: red;\n}', JSON.parse(inputMap), this.callback);
+      },
+      'has 3 mappings': function (error, minified) {
+        assert.lengthOf(minified.sourceMap._mappings._array, 3);
+      },
+      'has `div > a` mapping': function (error, minified) {
+        var mapping = {
+          generatedLine: 1,
+          generatedColumn: 0,
+          originalLine: 1,
+          originalColumn: 4,
+          source: 'styles.less',
+          name: null
+        };
+        assert.deepEqual(minified.sourceMap._mappings._array[0], mapping);
+      },
+      'has `color` mapping': function (error, minified) {
+        var mapping = {
+          generatedLine: 1,
+          generatedColumn: 6,
+          originalLine: 2,
+          originalColumn: 2,
+          source: 'styles.less',
+          name: null
+        };
+        assert.deepEqual(minified.sourceMap._mappings._array[1], mapping);
+      },
+      'has `red` mapping': function (error, minified) {
+        var mapping = {
+          generatedLine: 1,
+          generatedColumn: 12,
+          originalLine: 2,
+          originalColumn: 2,
+          source: 'styles.less',
+          name: null
+        };
+        assert.deepEqual(minified.sourceMap._mappings._array[2], mapping);
+      }
+    },
     'input map from source': {
       'topic': function () {
         return new CleanCSS({ level: 2, sourceMap: true }).minify('div > a {\n  color: red;\n}/*# sourceMappingURL=' + inputMapPath + ' */');
@@ -806,7 +847,11 @@ vows.describe('source-map')
     },
     'input source map with missing mutliselector input': {
       'topic': function () {
-        return new CleanCSS({ sourceMap: '{"version":3,"sources":["source.css"],"names":[],"mappings":"AAAA;;;;IAII,YAAW;EACd"}' }).minify('a,\na:hover,\na:visited\n{\n    color: red;\n}');
+        return new CleanCSS({ sourceMap: true })
+          .minify(
+            'a,\na:hover,\na:visited\n{\n    color: red;\n}',
+            '{"version":3,"sources":["source.css"],"names":[],"mappings":"AAAA;;;;IAII,YAAW;EACd"}'
+          );
       },
       'has 5 mappings': function (minified) {
         assert.lengthOf(minified.sourceMap._mappings._array, 5);
@@ -869,7 +914,11 @@ vows.describe('source-map')
     },
     'input source map with missing mutliselector sortable input': {
       'topic': function () {
-        return new CleanCSS({ sourceMap: '{"version":3,"sources":["source.css"],"names":[],"mappings":"AAAA;;;;IAII,YAAW;EACd"}' }).minify('a.button:link,\na.button:visited,\na.button:hover\n{\n    color: red;\n}');
+        return new CleanCSS({ sourceMap: true })
+          .minify(
+            'a.button:link,\na.button:visited,\na.button:hover\n{\n    color: red;\n}',
+            '{"version":3,"sources":["source.css"],"names":[],"mappings":"AAAA;;;;IAII,YAAW;EACd"}'
+          );
       },
       'has 5 mappings': function (minified) {
         assert.lengthOf(minified.sourceMap._mappings._array, 5);
@@ -1458,9 +1507,12 @@ vows.describe('source-map')
       'single': {
         'topic': function () {
           return new CleanCSS({
-            sourceMap: '{"version":3,"sources":["styles.less"],"names":[],"mappings":"AAAA,GAAI;EACF,WAAA","file":"styles.css","sourcesContent":["div > a {\\n  color: blue;\\n}\\n"]}',
+            sourceMap: true,
             sourceMapInlineSources: true
-          }).minify('div > a {\n  color: red;\n}');
+          }).minify(
+            'div > a {\n  color: red;\n}',
+            '{"version":3,"sources":["styles.less"],"names":[],"mappings":"AAAA,GAAI;EACF,WAAA","file":"styles.css","sourcesContent":["div > a {\\n  color: blue;\\n}\\n"]}'
+          );
         },
         'has 3 mappings': function (minified) {
           assert.lengthOf(minified.sourceMap._mappings._array, 3);