From f22851cb5a4f21ecf2189a998757724c8e910a93 Mon Sep 17 00:00:00 2001 From: Jakub Pawlowicz Date: Sun, 14 Dec 2014 18:50:39 +0000 Subject: [PATCH] Fixes #400 - CleanCSS#minify accepts an array of filenames. * Both relative and absolute paths are supported - see test/module-test.js. --- History.md | 1 + README.md | 4 +-- bin/cleancss | 26 +++----------------- lib/clean.js | 4 +-- lib/utils/source-reader.js | 35 ++++++++++++++++++++++++++ test/module-test.js | 50 ++++++++++++++++++++++++++++++++++++++ 6 files changed, 94 insertions(+), 26 deletions(-) create mode 100644 lib/utils/source-reader.js diff --git a/History.md b/History.md index 4f6e085d..91578be8 100644 --- a/History.md +++ b/History.md @@ -23,6 +23,7 @@ * Fixed issue [#363](https://github.com/GoalSmashers/clean-css/issues/363) - `rem` units overriding `px`. * Fixed issue [#373](https://github.com/GoalSmashers/clean-css/issues/373) - proper background shorthand merging. * Fixed issue [#395](https://github.com/GoalSmashers/clean-css/issues/395) - unescaped brackets in data URIs. +* Fixed issue [#400](https://github.com/GoalSmashers/clean-css/issues/400) - API to accept an array of filenames. * Fixed issue [#403](https://github.com/GoalSmashers/clean-css/issues/403) - tracking input files in source maps. * Fixed issue [#404](https://github.com/GoalSmashers/clean-css/issues/404) - no state sharing in API. * Refixed issue [#304](https://github.com/GoalSmashers/clean-css/issues/304) - background position merging. diff --git a/README.md b/README.md index 6fc3b4dc..f50a156e 100644 --- a/README.md +++ b/README.md @@ -242,7 +242,7 @@ To generate a source map, use `sourceMap: true` option, e.g.: new CleanCSS({ sourceMap: true, target: pathToOutputDirectory }).minify(source, function (minified) { // access minified.sourceMap for SourceMapGenerator object // see https://github.com/mozilla/source-map/#sourcemapgenerator for more details - // see https://github.com/jakubpawlowicz/clean-css/blob/master/bin/cleancss#L132 on how it's used in clean-css' CLI + // see https://github.com/jakubpawlowicz/clean-css/blob/master/bin/cleancss#L114 on how it's used in clean-css' CLI }); ``` @@ -252,7 +252,7 @@ Using API you can also pass an input source map directly: new CleanCSS({ sourceMap: inputSourceMapAsString, target: pathToOutputDirectory }).minify(source, function (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#L132 on how it's used in clean-css' CLI + // see https://github.com/jakubpawlowicz/clean-css/blob/master/bin/cleancss#L114 on how it's used in clean-css' CLI }); ``` diff --git a/bin/cleancss b/bin/cleancss index d726c539..81003068 100755 --- a/bin/cleancss +++ b/bin/cleancss @@ -73,21 +73,8 @@ var options = { target: commands.output }; -if (commands.args.length > 0) { - var relativeTo = (options.rebase ? options.root : false) || commands.args[0]; - options.relativeTo = path.dirname(path.resolve(relativeTo)); - - options.sources = commands.args.map(function(source) { - var isRemote = /^https?:\/\//.test(source); - - if (options.processImport === false) - source += '@shallow'; - - return isRemote ? - source : - path.relative(options.relativeTo, path.resolve(source)); - }); -} +if (options.root || commands.args.length > 0) + options.relativeTo = path.dirname(path.resolve(options.root || commands.args[0])); if (options.sourceMap && !options.target) { outputFeedback(['Source maps will not be built because you have not specified an output file.'], true); @@ -95,13 +82,8 @@ if (options.sourceMap && !options.target) { } // ... and do the magic! -if (options.sources) { - var data = options.sources - .map(function(source) { - return '@import url(' + source + ');'; - }) - .join(''); - minify(data); +if (commands.args.length > 0) { + minify(commands.args); } else { var stdin = process.openStdin(); stdin.setEncoding('utf-8'); diff --git a/lib/clean.js b/lib/clean.js index 7498c139..116ad8da 100644 --- a/lib/clean.js +++ b/lib/clean.js @@ -19,6 +19,7 @@ var UrlsProcessor = require('./text/urls-processor'); var Compatibility = require('./utils/compatibility'); var InputSourceMapTracker = require('./utils/input-source-map-tracker'); var SourceTracker = require('./utils/source-tracker'); +var SourceReader = require('./utils/source-reader'); var DEFAULT_TIMEOUT = 5000; @@ -58,8 +59,7 @@ CleanCSS.prototype.minify = function(data, callback) { sourceTracker: new SourceTracker() }; - if (Buffer.isBuffer(data)) - data = data.toString(); + data = new SourceReader(context, data).toString(); if (context.options.processImport || data.indexOf('@shallow') > 0) { // inline all imports diff --git a/lib/utils/source-reader.js b/lib/utils/source-reader.js new file mode 100644 index 00000000..c98eb0ba --- /dev/null +++ b/lib/utils/source-reader.js @@ -0,0 +1,35 @@ +var path = require('path'); + +function SourceReader(context, data) { + this.outerContext = context; + this.data = data; +} + +SourceReader.prototype.toString = function () { + if (typeof this.data == 'string') + return this.data; + if (Buffer.isBuffer(this.data)) + return this.data.toString(); + if (Array.isArray(this.data)) + return fromArray(this.outerContext, this.data); + + return this.data; +}; + +function fromArray(outerContext, sources) { + return sources + .map(function (source) { + return outerContext.options.processImport === false ? + source + '@shallow' : + source; + }) + .map(function (source) { + return !outerContext.options.relativeTo || /^https?:\/\//.test(source) ? + source : + path.relative(outerContext.options.relativeTo, source); + }) + .map(function (source) { return '@import url(' + source + ');'; }) + .join(''); +} + +module.exports = SourceReader; diff --git a/test/module-test.js b/test/module-test.js index 64dc0a74..14a5f868 100644 --- a/test/module-test.js +++ b/test/module-test.js @@ -233,5 +233,55 @@ vows.describe('module tests').addBatch({ 'should include source map': function (minified) { assert.instanceOf(minified.sourceMap, SourceMapGenerator); } + }, + 'accepts a list of source files as array': { + 'rebased to the current dir': { + 'relative': { + 'topic': new CleanCSS().minify(['test/data/partials/one.css', 'test/data/partials/three.css']), + 'should give right output': function (minified) { + assert.equal(minified.styles, '.one{color:red}.three{background-image:url(test/data/partials/extra/down.gif)}'); + } + }, + 'absolute': { + 'topic': new CleanCSS({ relativeTo: process.cwd() }).minify([path.resolve('test/data/partials/one.css'), path.resolve('test/data/partials/three.css')]), + 'should give right output': function (minified) { + assert.equal(minified.styles, '.one{color:red}.three{background-image:url(test/data/partials/extra/down.gif)}'); + } + } + }, + 'rebased to a path': { + 'relative': { + 'topic': new CleanCSS({ relativeTo: 'test/data' }).minify(['test/data/partials/one.css', 'test/data/partials/three.css']), + 'should give right output': function (minified) { + assert.equal(minified.styles, '.one{color:red}.three{background-image:url(partials/extra/down.gif)}'); + } + }, + 'absolute': { + 'topic': new CleanCSS({ relativeTo: 'test/data' }).minify([path.resolve('test/data/partials/one.css'), path.resolve('test/data/partials/three.css')]), + 'should give right output': function (minified) { + assert.equal(minified.styles, '.one{color:red}.three{background-image:url(partials/extra/down.gif)}'); + } + } + }, + 'rebased to root': { + 'relative': { + 'topic': new CleanCSS({ root: 'test/data', relativeTo: 'test/data' }).minify(['test/data/partials/one.css', 'test/data/partials/three.css']), + 'should give right output': function (minified) { + assert.equal(minified.styles, '.one{color:red}.three{background-image:url(/partials/extra/down.gif)}'); + } + }, + 'absolute': { + 'topic': new CleanCSS({ root: 'test/data', relativeTo: 'test/data' }).minify([path.resolve('test/data/partials/one.css'), path.resolve('test/data/partials/three.css')]), + 'should give right output': function (minified) { + assert.equal(minified.styles, '.one{color:red}.three{background-image:url(/partials/extra/down.gif)}'); + } + } + }, + 'with imports off': { + 'topic': new CleanCSS({ processImport: false }).minify(['./test/data/partials/two.css']), + 'should give right output': function (minified) { + assert.equal(minified.styles, '@import url(one.css);@import url(extra/three.css);@import url(./extra/four.css);.two{color:#fff}'); + } + } } }).export(module); -- 2.34.1