From: Jakub Pawlowicz Date: Tue, 27 Jan 2015 08:16:38 +0000 (+0000) Subject: Fixes #419 - handling multiple input source maps. X-Git-Url: https://git.ndcode.org/public/gitweb.cgi?a=commitdiff_plain;h=b1636497b66589ab980c98d4e287f5d1f5314046;p=clean-css.git Fixes #419 - handling multiple input source maps. Adds support for multiple input source maps when data is passed in as a hash, e.g.: ``` new CleanCSS({ sourceMap: true }).minify({ 'path/to/source/1': { styles: '...styles...', sourceMap: '...source-map...' }, 'path/to/source/2': { styles: '...styles...', sourceMap: '...source-map...' } }) ``` Effectively it is the same as concatenating source maps, but should be less error-prone. See 'multiple source maps' in `test/source-map-test.js` for examples. --- diff --git a/History.md b/History.md index e5a26352..6fac3ec4 100644 --- a/History.md +++ b/History.md @@ -8,6 +8,7 @@ * Fixed issue [#351](https://github.com/GoalSmashers/clean-css/issues/351) - remote `@import`s after content. * Fixed issue [#357](https://github.com/GoalSmashers/clean-css/issues/357) - non-standard but valid URLs. * Fixed issue [#416](https://github.com/GoalSmashers/clean-css/issues/416) - accepts hash as `minify` argument. +* Fixed issue [#419](https://github.com/GoalSmashers/clean-css/issues/419) - multiple input source maps. * Fixed issue [#435](https://github.com/GoalSmashers/clean-css/issues/435) - `background-clip` in shorthand. * Fixed issue [#439](https://github.com/GoalSmashers/clean-css/issues/439) - `background-origin` in shorthand. * Fixed issue [#442](https://github.com/GoalSmashers/clean-css/issues/442) - space before adjacent `nav`. diff --git a/README.md b/README.md index 3dabed05..3eae040c 100644 --- a/README.md +++ b/README.md @@ -242,6 +242,23 @@ new CleanCSS({ sourceMap: inputSourceMapAsString, target: pathToOutputDirectory }); ``` +Or even multiple input source maps at once (available since version 3.1): + +```javascript +new CleanCSS({ sourceMap: true, target: pathToOutputDirectory }).minify({ + 'path/to/source/1': { + styles: '...styles...', + sourceMap: '...source-map...' + }, + 'path/to/source/2': { + styles: '...styles...', + sourceMap: '...source-map...' + } +}, function (minified) { + // access minified.sourceMap as above +}); +``` + #### Caveats * Shorthand compacting is currently disabled when source maps are enabled, see [#399](https://github.com/GoalSmashers/clean-css/issues/399) diff --git a/lib/clean.js b/lib/clean.js index 751c8bd7..c84beb23 100644 --- a/lib/clean.js +++ b/lib/clean.js @@ -59,6 +59,9 @@ CleanCSS.prototype.minify = function(data, callback) { sourceTracker: new SourceTracker() }; + if (context.options.sourceMap) + context.inputSourceMapTracker = new InputSourceMapTracker(context); + data = new SourceReader(context, data).toString(); if (context.options.processImport || data.indexOf('@shallow') > 0) { @@ -92,7 +95,6 @@ function runMinifier(callback, context) { return function (data) { if (context.options.sourceMap) { - context.inputSourceMapTracker = new InputSourceMapTracker(context); return context.inputSourceMapTracker.track(data, function () { return whenSourceMapReady(data); }); } else { return whenSourceMapReady(data); diff --git a/lib/utils/input-source-map-tracker.js b/lib/utils/input-source-map-tracker.js index 4a83c4b9..31eda870 100644 --- a/lib/utils/input-source-map-tracker.js +++ b/lib/utils/input-source-map-tracker.js @@ -137,6 +137,10 @@ InputSourceMapStore.prototype.track = function (data, whenDone) { fromSource(this, data, whenDone, { files: [], cursor: 0, errors: this.errors }); }; +InputSourceMapStore.prototype.trackLoaded = function (sourceFile, sourceMap) { + this.maps[sourceFile] = new SourceMapConsumer(sourceMap); +}; + InputSourceMapStore.prototype.isTracking = function (sourceInfo) { return !!this.maps[sourceInfo.source]; }; diff --git a/lib/utils/source-reader.js b/lib/utils/source-reader.js index 4bc31f3c..d6b9bf1a 100644 --- a/lib/utils/source-reader.js +++ b/lib/utils/source-reader.js @@ -39,6 +39,8 @@ function fromHash(outerContext, sources) { for (var source in sources) { var styles = sources[source].styles; + var inputSourceMap = sources[source].sourceMap; + var rewriter = new UrlRewriter({ absolute: !!outerContext.options.root, relative: !outerContext.options.root, @@ -47,8 +49,14 @@ function fromHash(outerContext, sources) { fromBase: path.dirname(path.resolve(source)), toBase: toBase }); - styles = rewriter.process(styles); + + if (outerContext.options.sourceMap && inputSourceMap) { + var absoluteSource = path.resolve(source); + styles = outerContext.sourceTracker.store(absoluteSource, styles); + outerContext.inputSourceMapTracker.trackLoaded(absoluteSource, inputSourceMap); + } + data.push(styles); } diff --git a/test/source-map-test.js b/test/source-map-test.js index 4e06678e..0652d134 100644 --- a/test/source-map-test.js +++ b/test/source-map-test.js @@ -783,4 +783,119 @@ vows.describe('source-map') } } }) + .addBatch({ + 'multiple source maps': { + 'relative to local': { + 'topic': new CleanCSS({ sourceMap: true }).minify({ + 'test/fixtures/source-maps/some.css': { + styles: 'div {\n color: red;\n}', + sourceMap: '{"version":3,"sources":["some.less"],"names":[],"mappings":"AAAA;EACE,UAAA","file":"some.css"}' + }, + 'test/fixtures/source-maps/styles.css': { + styles: 'div > a {\n color: blue;\n}', + sourceMap: '{"version":3,"sources":["styles.less"],"names":[],"mappings":"AAAA,GAAI;EACF,WAAA","file":"styles.css"}' + }, + 'test/fixtures/source-maps/nested/once.css': { + styles: 'section > div a {\n color: red;\n}', + sourceMap: '{"version":3,"sources":["once.less"],"names":[],"mappings":"AAAA,OACE,MAAM;EACJ,UAAA","file":"once.css"}' + } + }), + 'has right output': function (errors, minified) { + assert.equal(minified.styles, 'div,section>div a{color:red}div>a{color:#00f}'); + }, + 'should have 5 mappings': function (minified) { + assert.lengthOf(minified.sourceMap._mappings._array, 5); + }, + 'should have "div" mapping': function (minified) { + var mapping = { + generatedLine: 1, + generatedColumn: 0, + originalLine: 1, + originalColumn: 0, + source: 'some.less', + name: null + }; + assert.deepEqual(minified.sourceMap._mappings._array[0], mapping); + }, + 'should have "section > div a" mapping': function (minified) { + var mapping = { + generatedLine: 1, + generatedColumn: 4, + originalLine: 2, + originalColumn: 8, + source: 'once.less', + name: null + }; + assert.deepEqual(minified.sourceMap._mappings._array[1], mapping); + }, + 'should have "color: red" mapping': function (minified) { + var mapping = { + generatedLine: 1, + generatedColumn: 18, + originalLine: 2, + originalColumn: 2, + source: 'some.less', + name: null + }; + assert.deepEqual(minified.sourceMap._mappings._array[2], mapping); + }, + 'should have "div > a" mapping': function (minified) { + var mapping = { + generatedLine: 1, + generatedColumn: 28, + originalLine: 1, + originalColumn: 4, + source: 'styles.less', + name: null + }; + assert.deepEqual(minified.sourceMap._mappings._array[3], mapping); + }, + 'should have "color: blue" mapping': function (minified) { + var mapping = { + generatedLine: 1, + generatedColumn: 34, + originalLine: 2, + originalColumn: 2, + source: 'styles.less', + name: null + }; + assert.deepEqual(minified.sourceMap._mappings._array[4], mapping); + } + } + }, + 'relative to path': { + 'complex but partial input map referenced by path': { + 'topic': new CleanCSS({ sourceMap: true, target: process.cwd() }).minify({ + 'test/fixtures/source-maps/some.css': { + styles: 'div {\n color: red;\n}', + sourceMap: '{"version":3,"sources":["some.less"],"names":[],"mappings":"AAAA;EACE,UAAA","file":"some.css"}' + }, + 'test/fixtures/source-maps/styles.css': { + styles: 'div > a {\n color: blue;\n}', + sourceMap: '{"version":3,"sources":["styles.less"],"names":[],"mappings":"AAAA,GAAI;EACF,WAAA","file":"styles.css"}' + }, + 'test/fixtures/source-maps/nested/once.css': { + styles: 'section > div a {\n color: red;\n}', + sourceMap: '{"version":3,"sources":["once.less"],"names":[],"mappings":"AAAA,OACE,MAAM;EACJ,UAAA","file":"once.css"}' + } + }), + 'should have 5 mappings': function (minified) { + assert.lengthOf(minified.sourceMap._mappings._array, 5); + }, + 'should have right sources': function (minified) { + var sources = []; + minified.sourceMap._mappings._array.forEach(function (m) { + if (sources.indexOf(m.source) === -1) + sources.push(m.source); + }); + + assert.deepEqual(sources, [ + 'test/fixtures/source-maps/some.less', + 'test/fixtures/source-maps/nested/once.less', + 'test/fixtures/source-maps/styles.less' + ]); + } + } + } + }) .export(module);