From dd46058a6d4a33c62b811dfcc2af0d17a990f739 Mon Sep 17 00:00:00 2001 From: zoubin Date: Fri, 20 May 2016 16:32:01 +0800 Subject: [PATCH 1/4] refactor --- README.md | 323 ++++++------------ example/.gitignore | 2 + example/gulp/multi/gulpfile.js | 61 ---- example/gulp/reduce/gulpfile.js | 58 ---- example/gulp/reduce/src/page/blue/index.css | 6 - example/gulp/reduce/src/page/red/index.css | 11 - .../web_modules/component/button/index.css | 5 - example/package.json | 7 + example/reduce/README.md | 88 +++++ example/reduce/build.js | 80 +++++ example/{without-gulp => reduce}/src/blue.css | 0 .../src}/button/button.png | Bin .../src/button/index.css | 0 .../src/node_modules}/color/index.css | 0 .../src/node_modules/reset/index.css | 0 example/{without-gulp => reduce}/src/red.css | 0 example/without-gulp/build.js | 18 - example/without-gulp/multi.js | 24 -- example/without-gulp/src/button/button.png | Bin 10134 -> 0 bytes .../src/node_modules/color/index.css | 3 - .../src/node_modules/reset/index.css | 5 - example/without-gulp/watch.js | 27 -- example/work-with-gulp/gulpfile.js | 83 +++++ .../src/node_modules/reset/index.css | 0 .../src/page/blue/index.css | 0 .../src/page/red/index.css | 0 .../web_modules/component/button/button.png | Bin .../web_modules/component/button/index.css | 0 .../src/web_modules/helper/color/index.css | 0 index.js | 223 +++++------- lib/sink.js | 49 +++ package.json | 7 +- test/multiple-bundle.js | 37 +- test/single-bundle.js | 33 +- test/source-entries.js | 47 --- test/watch.js | 23 +- 36 files changed, 554 insertions(+), 666 deletions(-) create mode 100644 example/.gitignore delete mode 100644 example/gulp/multi/gulpfile.js delete mode 100644 example/gulp/reduce/gulpfile.js delete mode 100644 example/gulp/reduce/src/page/blue/index.css delete mode 100644 example/gulp/reduce/src/page/red/index.css delete mode 100644 example/gulp/reduce/src/web_modules/component/button/index.css create mode 100644 example/package.json create mode 100644 example/reduce/README.md create mode 100644 example/reduce/build.js rename example/{without-gulp => reduce}/src/blue.css (100%) rename example/{gulp/multi/src/web_modules/component => reduce/src}/button/button.png (100%) rename example/{without-gulp => reduce}/src/button/index.css (100%) rename example/{gulp/multi/src/web_modules/helper => reduce/src/node_modules}/color/index.css (100%) rename example/{gulp/multi => reduce}/src/node_modules/reset/index.css (100%) rename example/{without-gulp => reduce}/src/red.css (100%) delete mode 100644 example/without-gulp/build.js delete mode 100644 example/without-gulp/multi.js delete mode 100644 example/without-gulp/src/button/button.png delete mode 100644 example/without-gulp/src/node_modules/color/index.css delete mode 100644 example/without-gulp/src/node_modules/reset/index.css delete mode 100644 example/without-gulp/watch.js create mode 100644 example/work-with-gulp/gulpfile.js rename example/{gulp/reduce => work-with-gulp}/src/node_modules/reset/index.css (100%) rename example/{gulp/multi => work-with-gulp}/src/page/blue/index.css (100%) rename example/{gulp/multi => work-with-gulp}/src/page/red/index.css (100%) rename example/{gulp/reduce => work-with-gulp}/src/web_modules/component/button/button.png (100%) rename example/{gulp/multi => work-with-gulp}/src/web_modules/component/button/index.css (100%) rename example/{gulp/reduce => work-with-gulp}/src/web_modules/helper/color/index.css (100%) create mode 100644 lib/sink.js delete mode 100644 test/source-entries.js diff --git a/README.md b/README.md index 0494b11..847415f 100644 --- a/README.md +++ b/README.md @@ -18,264 +18,146 @@ Pack CSS into common shared bundles. which make `b.bundle()` output a stream manipulatable by [`gulp`] plugins. ## Example -Suppose we want to pack css in `/path/to/src` (not including those in its subdirectories) into `/path/to/build/bundle.css`. +Check the [example](example/reduce/). -There are already `blue.css` and `red.css` in `/path/to/src`, and they both depend upon `/path/to/src/node_modules/reset/index.js`. - -**Input** - -`blue.css`: -```css -@external "reset"; -@import "color"; -.blue { - color: $blue; +```js +var reduce = require('reduce-css') +var del = require('del') +var path = require('path') +var Transform = require('stream').Transform + +var basedir = path.join(__dirname, 'src') + +bundle(createBundler()) + +function createBundler(watch) { + var basedir = path.join(__dirname, 'src') + var b = reduce.create( + /* glob for entries */ + '*.css', + + /* options for depsify */ + { + basedir, + cache: {}, + packageCache: {}, + }, + + /* options for common-bundle */ + // single bundle + // 'bundle.css', + // multiple bundles + { + groups: '*.css', + common: 'common.css', + }, + + /* options for watchify2 */ + watch && { entryGlob: '*.css' } + ) + return b } -``` - -`red.css`: -```css -@external "reset"; -@external "./button"; -@import "color"; -.red { - color: $red; +function bundle(b) { + var build = path.join(__dirname, 'build') + del.sync(build) + return b.bundle().on('error', log) + .pipe(b.dest(build, { + maxSize: 0, + name: '[name].[hash]', + assetOutFolder: path.join(build, 'assets'), + })) } -``` - -`reset` contains styles to be shared. -We use `@external` to declare that -it should come before `a.css` and `b.css` in the final `bundle.css`. -```css -html, body { - margin: 0; - padding: 0; +function log() { + console.log.apply(console, [].map.call(arguments, function (msg) { + if (typeof msg === 'string') { + return msg + } + return JSON.stringify(msg, null, 2) + })) } -``` - -The `color` module is installed in `node_modules`, -and will be consumed by [`postcss`] when `@import`ed in css. -```css -$red: #FF0000; -$green: #00FF00; -$blue: #0000FF; ``` -`/path/to/src/button` is a button component, -shipped with a background image (`/path/to/src/button/button.png`), -as well as some styles (`/path/to/src/button/index.css`): -```css -@import "color"; -.button { - background-color: $red; - background-image: url(button.png); -} - -``` -The image will be inlined or copied to the build directory -after bundling, and the url in css will also be transformed to -reference to it correctly. - -**Building script** +To watch file changes: ```js -'use strict' - -const reduce = require('reduce-css') - -const build = __dirname + '/build' -const basedir = __dirname + '/src' -const b = reduce.create({ basedir }) -reduce.src('*.css', { cwd: basedir }) - .pipe(reduce.bundle(b, 'bundle.css')) - .pipe(reduce.dest(build, null, { - maxSize: 0, - name: '[name].[hash]', - assetOutFolder: build + '/assets', - })) - -``` - -**Output** - -`/path/to/build/bundle.css`: -```css -html, body { - margin: 0; - padding: 0; -} - -.blue { - color: #0000FF; -} - -.button { - background-color: #FF0000; - background-image: url(assets/button.161fff2.png); -} -.red { - color: #FF0000; -} +var b = createBundler(true) +b.on('update', function update() { + bundle(b) + return update +}()) ``` -The background image has been renamed and copied to `/path/to/build/assets/button.161fff2.png`. - -**Watch** - -To watch file changes: +To work with gulp: ```js -'use strict' - -const reduce = require('reduce-css') - -const build = __dirname + '/build' -const basedir = __dirname + '/src' -const b = reduce.create({ - basedir, - cache: {}, - packageCache: {}, +var gulp = require('gulp') +gulp.task('build', function () { + return bundle(createBundler()) }) -reduce.src('*.css', { cwd: basedir }) - .pipe(reduce.watch(b, 'bundle.css', { entryGlob: '*.css' })) - .on('bundle', function (bundleStream) { - bundleStream.pipe(reduce.dest(build, null, { - maxSize: 0, - name: '[name].[hash]', - assetOutFolder: build + '/assets', - })) - .on('data', file => console.log('bundle:', file.relative)) - .on('end', () => console.log('-'.repeat(40))) - }) - +gulp.task('watch', function (cb) { + var b = createBundler(true) + b.on('update', function update() { + bundle(b) + return update + }()) + b.on('close', cb) +}) ``` -**Common shared bundles** - -Check this [example](example/without-gulp/multi.js). - -## Work with Gulp -Check this [gulpfile](example/gulp/multi/gulpfile.js). - ## API -```javascript -const reduce = require('reduce-css') +```js +var reduce = require('reduce-css') +var b = reduce.create(entries, depsifyOptions, bundleOptions, watchifyOptions) ``` -### reduce.create(opts) +### reduce.create(entries, depsifyOptions, bundleOptions, watchifyOptions) Return a [`depsify`] instance. -`opts` is passed to the [`depsify`] constructor. - -If `opts.postcss` is not `false`, +* `entries`: patterns to locate input files. Check [`globby`] for more details. +* `depsifyOptions`: options for [`depsify`]. +If `depsifyOptions.postcss` is not `false`, the plugin [`reduce-css-postcss`] for [`depsify`] is applied, which use [`postcss`] to preprocess css. +* `bundleOptions`: options for [`common-bundle`]. +* `watchifyOptions`: options for [`watchify2`]. +If specified, file changes are watched. -### reduce.bundle(b, opts) -Return a transform: -* input: [`vinyl-fs#src`] -* output: `b.bundle()` - -**b** - -[`depsify`] instance. - -**opts** - -Options passed to `reduce.bundler`. - -### reduce.watch(b, opts, watchOpts) -Return a transform: -* input: [`vinyl-fs#src`]. -* output: actually no data flows out, - but you can listen to the `bundle` event (triggered on the returned transform) - to process the result of `b.bundle()`. - -`b` and `opts` are the same with `reduce.bundle(b, opts)` - -**watchOpts** - -Options passed to [`watchify2`]. - -To detect new entries, -provide a glob to detect entries as `watchOpts.entryGlob`. - -### reduce.src(patterns, opts) -Same with [`vinyl-fs#src`], except that `opts.read` defaults to `false`. +### b.bundle() +Return a [`vinyl`] stream, +which can be processed by gulp plugins. -### reduce.dest(outFolder, opts, urlOpts) -`outFolder` and `opts` are passed to [`vinyl-fs#dest`] directly. - -[`postcss-custom-url`] is used to transform `url()` expressions in css (paths transformed, assets copied or inlined). - -The actual processor is constructed as: ```js -const url = require('postcss-custom-url') -const postcss = require('postcss') -const urlProcessor = postcss(url([ - [ url.util.inline, urlOpts ], - [ url.util.copy, urlOpts ], -])) +b.bundle().pipe(require('gulp-uglifycss')()).pipe(b.dest('build')) ``` -### reduce.bundler(b, opts) -Plugin for creating common shared bundles. - -**opts** - -Default: `{}` +### b.dest(outFolder, urlTransformOptions) +Works almost the same with [`gulp.dest`], +except that file contents are transformed using [`postcss-custom-url`] +before being written to disk. -* `Function` or `Array`: `b.plugin(opts)` will be executed. - Used to replace the default bundler [`common-bundle`]. -* `String`: all modules are packed into a single bundle, with `opts` the file path. -* otherwise: `opts` is passed to [`common-bundle`] directly. +`urlTransformOptions` is passed to both +the [inline](https://github.com/reducejs/postcss-custom-url#inline) +and [copy](https://github.com/reducejs/postcss-custom-url#copy) +transformers for [`postcss-custom-url`]. +The actual processor: ```js -const reduce = require('reduce-css') -const path = require('path') - -const b = reduce.create({ - entries: ['a.css', 'b.css'], - basedir: '/path/to/src', -}) -b.plugin(reduce.bundler, 'bundle.css') -b.bundle().pipe(reduce.dest('build')) - -``` - -### reduce.watcher(b, opts) -Plugin for watching file changes, addition and deletion. - -`opts` is passed to [`watchify2`] directly. - -A `bundle-stream` event is triggered whenever `b.bundle()` is provoked. - -```js -const reduce = require('reduce-css') -const path = require('path') -const b = reduce.create({ - entries: ['a.css', 'b.css'], - basedir: '/path/to/src', - cache: {}, - packageCache: {}, -}) -b.plugin(reduce.bundler, 'bundle.css') -b.plugin(reduce.watcher, { entryGlob: '*.css' }) -b.on('bundle-stream', function (bundleStream) { - // bundleStream is the result of `b.bundle()` - bundleStream.pipe(reduce.dest('build')) -}) -b.start() +var url = require('postcss-custom-url') +var postcss = require('postcss') +var urlProcessor = postcss(url([ + [ url.util.inline, urlTransformOptions ], + [ url.util.copy, urlTransformOptions ], +])) ``` @@ -293,6 +175,7 @@ b.start() [`gulp`]: https://www.npmjs.com/package/gulp [`watchify2`]: https://github.com/reducejs/watchify2 [`postcss-custom-url`]: https://github.com/reducejs/postcss-custom-url +[`vinyl`]: https://github.com/gulpjs/vinyl [`vinyl-fs#src`]: https://github.com/gulpjs/vinyl-fs#srcglobs-options -[`vinyl-fs#dest`]: https://github.com/gulpjs/vinyl-fs#destfolder-options -[`factor-bundle`]: https://www.npmjs.com/package/factor-bundle +[`gulp.dest`]: https://github.com/gulpjs/vinyl-fs#destfolder-options +[`globby`]: https://github.com/sindresorhus/globby diff --git a/example/.gitignore b/example/.gitignore new file mode 100644 index 0000000..0a80a67 --- /dev/null +++ b/example/.gitignore @@ -0,0 +1,2 @@ +/node_modules/* +!/node_modules/reduce-css diff --git a/example/gulp/multi/gulpfile.js b/example/gulp/multi/gulpfile.js deleted file mode 100644 index 4b6f73f..0000000 --- a/example/gulp/multi/gulpfile.js +++ /dev/null @@ -1,61 +0,0 @@ -'use strict' - -const reduce = require('reduce-css') -const gulp = require('gulp') -const del = require('del') -const path = require('path') -const build = path.join(__dirname, 'build') - -gulp.task('clean', function () { - return del(build) -}) - -gulp.task('build', ['clean'], function () { - let basedir = path.join(__dirname, 'src') - let b = reduce.create({ - basedir, - resolve: { - paths: [path.join(__dirname, 'src', 'web_modules')], - }, - }) - return reduce.src('page/**/index.css', { cwd: basedir }) - .pipe(reduce.bundle(b, { - groups: 'page/**/index.css', - common: 'common.css', - })) - .pipe(reduce.dest(build, null, { - maxSize: 0, - name: '[name].[hash]', - assetOutFolder: path.join(build, 'assets'), - })) -}) - -gulp.task('watch', ['clean'], function () { - let basedir = path.join(__dirname, 'src') - let b = reduce.create({ - basedir, - resolve: { - paths: [path.join(__dirname, 'src', 'web_modules')], - }, - cache: {}, - packageCache: {}, - }) - let count = 1 - return gulp.src('page/**/index.css', { cwd: basedir }) - .pipe(reduce.watch(b, { - groups: 'page/**/index.css', - common: 'common.css', - }, { entryGlob: 'page/**/index.css' })) - .on('bundle', function (bundleStream) { - bundleStream.pipe(reduce.dest(build, null, { - maxSize: 0, - name: '[name].[hash]', - assetOutFolder: path.join(build, 'assets'), - })) - .on('data', file => console.log('bundle:', file.relative)) - .once('end', function () { - console.log('-'.repeat(40), count++ + '') - }) - }) -}) - diff --git a/example/gulp/reduce/gulpfile.js b/example/gulp/reduce/gulpfile.js deleted file mode 100644 index 21f4f70..0000000 --- a/example/gulp/reduce/gulpfile.js +++ /dev/null @@ -1,58 +0,0 @@ -'use strict' - -const reduce = require('reduce-css') -const gulp = require('gulp') -const del = require('del') -const path = require('path') -const build = path.join(__dirname, 'build') - -gulp.task('clean', function () { - return del(build) -}) - -gulp.task('build', ['clean'], function () { - let basedir = path.join(__dirname, 'src') - let b = reduce.create({ - basedir, - resolve: { - paths: [path.join(__dirname, 'src', 'web_modules')], - }, - }) - return reduce.src('page/**/index.css', { cwd: basedir }) - .pipe(reduce.bundle(b, 'bundle.css')) - .pipe(reduce.dest(build, null, { - maxSize: 0, - name: '[name].[hash]', - assetOutFolder: path.join(build, 'assets'), - })) -}) - -gulp.task('watch', ['clean'], function () { - let basedir = path.join(__dirname, 'src') - let b = reduce.create({ - basedir, - resolve: { - paths: [path.join(__dirname, 'src', 'web_modules')], - }, - cache: {}, - packageCache: {}, - }) - let count = 1 - return gulp.src('page/**/index.css', { cwd: basedir }) - .pipe(reduce.watch(b, 'bundle.css', { entryGlob: 'page/**/index.css' })) - .on('bundle', function (bundleStream) { - bundleStream.pipe(reduce.dest(build, null, { - maxSize: 0, - name: '[name].[hash]', - assetOutFolder: path.join(build, 'assets'), - })) - .on('data', file => console.log('bundle:', file.relative)) - .once('end', function () { - console.log('-'.repeat(40), count++ + '') - if (count > 3) { - b.close() - } - }) - }) -}) - diff --git a/example/gulp/reduce/src/page/blue/index.css b/example/gulp/reduce/src/page/blue/index.css deleted file mode 100644 index 38c8213..0000000 --- a/example/gulp/reduce/src/page/blue/index.css +++ /dev/null @@ -1,6 +0,0 @@ -@external "reset"; -@import "helper/color"; -.blue { - color: $blue; -} - diff --git a/example/gulp/reduce/src/page/red/index.css b/example/gulp/reduce/src/page/red/index.css deleted file mode 100644 index ed3ef7b..0000000 --- a/example/gulp/reduce/src/page/red/index.css +++ /dev/null @@ -1,11 +0,0 @@ -@external "reset"; -@external "component/button"; -@import "helper/color"; -.red { - color: $red; -} - -.button { - background-color: $red; -} - diff --git a/example/gulp/reduce/src/web_modules/component/button/index.css b/example/gulp/reduce/src/web_modules/component/button/index.css deleted file mode 100644 index b1e000e..0000000 --- a/example/gulp/reduce/src/web_modules/component/button/index.css +++ /dev/null @@ -1,5 +0,0 @@ -@import "helper/color"; -.button { - background-color: $green; - background-image: url(button.png); -} diff --git a/example/package.json b/example/package.json new file mode 100644 index 0000000..9f43560 --- /dev/null +++ b/example/package.json @@ -0,0 +1,7 @@ +{ + "name": "example", + "dependencies": { + "gulp": "^3.9.0", + "gulp-util": "^3.0.7" + } +} diff --git a/example/reduce/README.md b/example/reduce/README.md new file mode 100644 index 0000000..8c0b2d4 --- /dev/null +++ b/example/reduce/README.md @@ -0,0 +1,88 @@ +# Example + +Suppose we want to pack css in `/path/to/src` (not including those in its subdirectories) into `/path/to/build/bundle.css`. + +There are already `blue.css` and `red.css` in `/path/to/src`, and they both depend upon `/path/to/src/node_modules/reset/index.js`. + +## Input + +`blue.css`: +```css +@external "reset"; +@import "color"; +.blue { + color: $blue; +} + +``` + +`red.css`: +```css +@external "reset"; +@external "./button"; +@import "color"; +.red { + color: $red; +} + +``` + +`reset` contains styles to be shared. +We use `@external` to declare that +it should come before `a.css` and `b.css` in the final `bundle.css`. +```css +html, body { + margin: 0; + padding: 0; +} + +``` + +The `color` module is installed in `node_modules`, +and will be consumed by [`postcss`] when `@import`ed in css. +```css +$red: #FF0000; +$green: #00FF00; +$blue: #0000FF; + +``` + +`/path/to/src/button` is a button component with a background image (`/path/to/src/button/button.png`), +as well as some styles (`/path/to/src/button/index.css`): +```css +@import "color"; +.button { + background-color: $red; + background-image: url(button.png); +} + +``` +The image will be inlined or copied to the build directory +after bundling, and the url in css will also be transformed to +reference to it correctly. + +## Output + +`/path/to/build/bundle.css`: +```css +html, body { + margin: 0; + padding: 0; +} + +.blue { + color: #0000FF; +} + +.button { + background-color: #FF0000; + background-image: url(assets/button.161fff2.png); +} +.red { + color: #FF0000; +} + +``` + +The background image has been renamed and copied to `/path/to/build/assets/button.161fff2.png`. + diff --git a/example/reduce/build.js b/example/reduce/build.js new file mode 100644 index 0000000..2b7a4a4 --- /dev/null +++ b/example/reduce/build.js @@ -0,0 +1,80 @@ +var reduce = require('reduce-css') +var del = require('del') +var path = require('path') +var Transform = require('stream').Transform + +var basedir = path.join(__dirname, 'src') + +var i = process.argv.indexOf('-w') +if (i === -1) { + i = process.argv.indexOf('--watch') +} +var needWatch = i > -1 +if (needWatch) { + var b = createBundler(true) + b.on('update', function update() { + bundle(b) + return update + }()) +} else { + bundle(createBundler()) +} + +function createBundler(watch) { + var basedir = path.join(__dirname, 'src') + var b = reduce.create( + /* glob for entries */ + '*.css', + + /* options for depsify */ + { + basedir, + cache: {}, + packageCache: {}, + }, + + /* options for common-bundle */ + // single bundle + // 'bundle.css', + // multiple bundles + { + groups: '*.css', + common: 'common.css', + }, + + /* options for watchify2 */ + watch && { entryGlob: '*.css' } + ) + return b +} + +function bundle(b) { + var startTime = Date.now() + log('Start bundling') + var build = path.join(__dirname, 'build') + del.sync(build) + return b.bundle().on('error', log) + .pipe(Transform({ + objectMode: true, + transform: function (file, enc, next) { + log('-', file.relative, file.contents.length, 'bytes') + next(null, file) + } + })) + .pipe(b.dest(build, { + maxSize: 0, + name: '[name].[hash]', + assetOutFolder: path.join(build, 'assets'), + })) + .on('end', () => log('End bundling in', Date.now() - startTime, 'ms')) +} + +function log() { + console.log.apply(console, [].map.call(arguments, function (msg) { + if (typeof msg === 'string') { + return msg + } + return JSON.stringify(msg, null, 2) + })) +} + diff --git a/example/without-gulp/src/blue.css b/example/reduce/src/blue.css similarity index 100% rename from example/without-gulp/src/blue.css rename to example/reduce/src/blue.css diff --git a/example/gulp/multi/src/web_modules/component/button/button.png b/example/reduce/src/button/button.png similarity index 100% rename from example/gulp/multi/src/web_modules/component/button/button.png rename to example/reduce/src/button/button.png diff --git a/example/without-gulp/src/button/index.css b/example/reduce/src/button/index.css similarity index 100% rename from example/without-gulp/src/button/index.css rename to example/reduce/src/button/index.css diff --git a/example/gulp/multi/src/web_modules/helper/color/index.css b/example/reduce/src/node_modules/color/index.css similarity index 100% rename from example/gulp/multi/src/web_modules/helper/color/index.css rename to example/reduce/src/node_modules/color/index.css diff --git a/example/gulp/multi/src/node_modules/reset/index.css b/example/reduce/src/node_modules/reset/index.css similarity index 100% rename from example/gulp/multi/src/node_modules/reset/index.css rename to example/reduce/src/node_modules/reset/index.css diff --git a/example/without-gulp/src/red.css b/example/reduce/src/red.css similarity index 100% rename from example/without-gulp/src/red.css rename to example/reduce/src/red.css diff --git a/example/without-gulp/build.js b/example/without-gulp/build.js deleted file mode 100644 index 53918c3..0000000 --- a/example/without-gulp/build.js +++ /dev/null @@ -1,18 +0,0 @@ -'use strict' - -const del = require('del') -const reduce = require('reduce-css') - -const build = __dirname + '/build' -const basedir = __dirname + '/src' -const b = reduce.create({ basedir }) -del(build).then(function () { - reduce.src('*.css', { cwd: basedir }) - .pipe(reduce.bundle(b, 'bundle.css')) - .pipe(reduce.dest(build, null, { - maxSize: 0, - name: '[name].[hash]', - assetOutFolder: build + '/assets', - })) -}) - diff --git a/example/without-gulp/multi.js b/example/without-gulp/multi.js deleted file mode 100644 index fe082f4..0000000 --- a/example/without-gulp/multi.js +++ /dev/null @@ -1,24 +0,0 @@ -'use strict' - -const del = require('del') -const reduce = require('reduce-css') - -const build = __dirname + '/build' -const basedir = __dirname + '/src' -const b = reduce.create({ basedir }) -b.on('common.map', function (map) { - console.log('bundles:', Object.keys(map).join(', ')) -}) -del(build).then(function () { - reduce.src('*.css', { cwd: basedir }) - .pipe(reduce.bundle(b, { - groups: '*.css', - common: 'common.css', - })) - .pipe(reduce.dest(build, null, { - maxSize: 0, - name: '[name].[hash]', - assetOutFolder: build + '/assets', - })) -}) - diff --git a/example/without-gulp/src/button/button.png b/example/without-gulp/src/button/button.png deleted file mode 100644 index 872000b80c82aeab745f066d2463bb7cfd907f3d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 10134 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 Eg0!rs0RR91 diff --git a/example/without-gulp/src/node_modules/color/index.css b/example/without-gulp/src/node_modules/color/index.css deleted file mode 100644 index 6049190..0000000 --- a/example/without-gulp/src/node_modules/color/index.css +++ /dev/null @@ -1,3 +0,0 @@ -$red: #FF0000; -$green: #00FF00; -$blue: #0000FF; diff --git a/example/without-gulp/src/node_modules/reset/index.css b/example/without-gulp/src/node_modules/reset/index.css deleted file mode 100644 index 20e182a..0000000 --- a/example/without-gulp/src/node_modules/reset/index.css +++ /dev/null @@ -1,5 +0,0 @@ -html, body { - margin: 0; - padding: 0; -} - diff --git a/example/without-gulp/watch.js b/example/without-gulp/watch.js deleted file mode 100644 index 1aef676..0000000 --- a/example/without-gulp/watch.js +++ /dev/null @@ -1,27 +0,0 @@ -'use strict' - -const del = require('del') -const reduce = require('reduce-css') - -const build = __dirname + '/build' -const basedir = __dirname + '/src' -const b = reduce.create({ - basedir, - cache: {}, - packageCache: {}, -}) - -del(build).then(function () { - reduce.src('*.css', { cwd: basedir }) - .pipe(reduce.watch(b, 'bundle.css', { entryGlob: '*.css' })) - .on('bundle', function (bundleStream) { - bundleStream.pipe(reduce.dest(build, null, { - maxSize: 0, - name: '[name].[hash]', - assetOutFolder: build + '/assets', - })) - .on('data', file => console.log('bundle:', file.relative)) - .on('end', () => console.log('-'.repeat(40))) - }) -}) - diff --git a/example/work-with-gulp/gulpfile.js b/example/work-with-gulp/gulpfile.js new file mode 100644 index 0000000..a6f4122 --- /dev/null +++ b/example/work-with-gulp/gulpfile.js @@ -0,0 +1,83 @@ +'use strict' + +const reduce = require('reduce-css') +const gulp = require('gulp') +const del = require('del') +const path = require('path') +const gutil = require('gulp-util') +const Transform = require('stream').Transform + +gulp.task('build', function () { + return bundle(createBundler()) +}) + +gulp.task('watch', function (cb) { + var b = createBundler(true) + b.on('update', function update() { + bundle(b) + return update + }()) + b.on('close', cb) +}) + +function createBundler(watch) { + var basedir = path.join(__dirname, 'src') + var b = reduce.create( + /* glob for entries */ + 'page/**/index.css', + + /* options for depsify */ + { + basedir, + resolve: { + paths: [path.join(__dirname, 'src', 'web_modules')], + }, + cache: {}, + packageCache: {}, + }, + + /* options for common-bundle */ + // single bundle + // 'bundle.css', + // multiple bundles + { + groups: 'page/**/index.css', + common: 'common.css', + }, + + /* options for watchify2 */ + watch && { entryGlob: 'page/**/index.css' } + ) + return b +} + +function bundle(b) { + var startTime = Date.now() + log('Start bundling') + var build = path.join(__dirname, 'build') + del.sync(build) + return b.bundle().on('error', log) + .pipe(Transform({ + objectMode: true, + transform: function (file, enc, next) { + log('-', file.relative, file.contents.length, 'bytes') + next(null, file) + } + })) + .pipe(b.dest(build, { + maxSize: 0, + name: '[name].[hash]', + assetOutFolder: path.join(build, 'assets'), + })) + .on('end', () => log('End bundling in', Date.now() - startTime, 'ms')) +} + +function log() { + gutil.log.apply(gutil, [].map.call(arguments, function (msg) { + if (typeof msg === 'string') { + return msg + } + return JSON.stringify(msg, null, 2) + })) +} + diff --git a/example/gulp/reduce/src/node_modules/reset/index.css b/example/work-with-gulp/src/node_modules/reset/index.css similarity index 100% rename from example/gulp/reduce/src/node_modules/reset/index.css rename to example/work-with-gulp/src/node_modules/reset/index.css diff --git a/example/gulp/multi/src/page/blue/index.css b/example/work-with-gulp/src/page/blue/index.css similarity index 100% rename from example/gulp/multi/src/page/blue/index.css rename to example/work-with-gulp/src/page/blue/index.css diff --git a/example/gulp/multi/src/page/red/index.css b/example/work-with-gulp/src/page/red/index.css similarity index 100% rename from example/gulp/multi/src/page/red/index.css rename to example/work-with-gulp/src/page/red/index.css diff --git a/example/gulp/reduce/src/web_modules/component/button/button.png b/example/work-with-gulp/src/web_modules/component/button/button.png similarity index 100% rename from example/gulp/reduce/src/web_modules/component/button/button.png rename to example/work-with-gulp/src/web_modules/component/button/button.png diff --git a/example/gulp/multi/src/web_modules/component/button/index.css b/example/work-with-gulp/src/web_modules/component/button/index.css similarity index 100% rename from example/gulp/multi/src/web_modules/component/button/index.css rename to example/work-with-gulp/src/web_modules/component/button/index.css diff --git a/example/gulp/reduce/src/web_modules/helper/color/index.css b/example/work-with-gulp/src/web_modules/helper/color/index.css similarity index 100% rename from example/gulp/reduce/src/web_modules/helper/color/index.css rename to example/work-with-gulp/src/web_modules/helper/color/index.css diff --git a/index.js b/index.js index e320e05..936fba0 100644 --- a/index.js +++ b/index.js @@ -1,171 +1,132 @@ 'use strict' -const stream = require('stream') -const vfs = require('vinyl-fs') -const postcss = require('postcss') -const url = require('postcss-custom-url') -const File = require('vinyl') -const combine = require('stream-combiner2') -const buffer = require('vinyl-buffer') -const Depsify = require('depsify') +var Stream = require('stream') +var vfs = require('vinyl-fs') +var PostCSS = require('postcss') +var url = require('postcss-custom-url') +var combine = require('stream-combiner2') +var buffer = require('vinyl-buffer') +var Depsify = require('depsify') +var path = require('path') +var glob = require('globby') +var sink = require('./lib/sink') -function bundler(b, opts) { - if (typeof opts === 'function' || Array.isArray(opts)) { - return b.plugin(opts) - } - - opts = opts || {} - if (typeof opts === 'string') { - opts = { groups: { output: opts } } - } +function through(write, end) { + return Stream.Transform({ + objectMode: true, + transform: write || function (o, enc, next) { next(null, o) }, + flush: end, + }) +} - let urlProcessor = postcss(url) - opts.pack = function (options) { - let pipeline = b.pack() - pipeline.pop() - pipeline.push( +function bundler(b, opts) { + var urlProcessor = PostCSS(url) + b.on('common.pipeline', function (bundleFile, pipeline) { + var pack = b.pack() + pack.pop() + pack.push( through(function (row, _, next) { urlProcessor.process(row.source, { from: row.file, - to: options.to, + to: bundleFile, }).then(function (result) { next(null, result.css) }) }) ) - return pipeline - } + var packPipeline = pipeline.get('pack') + packPipeline.splice.apply(packPipeline, [0, 1].concat(pack)) + }) b.plugin(require('common-bundle'), opts) -} - -function through(write, end) { - return stream.Transform({ - objectMode: true, - transform: write, - flush: end, + b.on('reset', function reset() { + b.pipeline.push(buffer()) + return reset + }()) + b.on('bundle', output => { + output.on('error', err => delete err.stream) }) } -function watcher(b, wopts) { - b.plugin(require('watchify2'), wopts) - let close = b.close +function watchify(b, opts) { + b.plugin(require('watchify2'), opts) + var close = b.close b.close = function () { close() b.emit('close') } - b.start = function () { - b.emit('bundle-stream', b.bundle()) - } - b.on('update', b.start) } -function bundle(b, opts) { - b.plugin(bundler, opts) +function urlify(outFolder, urlOpts) { + var urlProcessor = PostCSS(url([ + [ url.util.inline, urlOpts ], + [ url.util.copy, urlOpts ], + ])) - return through( - function (file, enc, next) { - b.add(file.path) - next() - }, - function (next) { - b.bundle() - .on('data', data => this.push(data)) - .on('end', next) - } - ) + return through(function (file, _, next) { + urlProcessor.process(file.contents.toString('utf8'), { + from: file.path, + to: path.resolve(outFolder, file.relative), + }).then(function (result) { + file.contents = Buffer(result.css) + next(null, file) + }, err => this.emit('error', err)) + }) } -function watch(b, opts, wopts) { - b.plugin(bundler, opts) - b.plugin(watcher, wopts) +function postcss(b, opts) { + b.plugin(require('reduce-css-postcss'), { + processorFilter: function (pipeline) { + pipeline.get('postcss-simple-import').push({ + resolve: b._options.resolve, + }) - return through( - function (file, enc, next) { - b.add(file.path) - next() - }, - function (next) { - b.on('bundle-stream', s => this.emit('bundle', s)) - b.once('close', next) - b.start() - } - ) -} + if (typeof opts === 'function') { + return opts(pipeline) + } -function src(pattern, opts) { - opts = opts || {} - opts.read = false - return vfs.src(pattern, opts) -} - -function dest(outFolder, outOpts, urlOpts) { - let files = [] - let emptyFiles = [] - let urlProcessor = postcss(url([ - [ url.util.inline, urlOpts ], - [ url.util.copy, urlOpts ], - ])) - return combine.obj( - buffer(), - through(function (file, _, next) { - if (file.isNull()) return next() - files.push(file) - let f = new File({ - cwd: file.cwd, - base: file.base, - path: file.path, - contents: null, - }) - emptyFiles.push(f) - next(null, f) - }), - vfs.dest(outFolder, outOpts), - through(function (file, _, next) { - let i = emptyFiles.indexOf(file) - let writePath = file.path - file = files[i] - urlProcessor.process( - file.contents.toString('utf8'), - { from: file.path, to: writePath } - ).then(function (result) { - file.contents = Buffer(result.css) - next(null, file) - }, err => this.emit('error', err)) - }), - vfs.dest(outFolder, outOpts) - ) + pipeline.push.apply( + pipeline, [].concat(opts).filter(Boolean) + ) + }, + }) } -function create(opts) { +function create(entries, opts, bundleOptions, watchOpts) { + if (typeof entries !== 'string' && !Array.isArray(entries)) { + bundleOptions = opts + opts = entries + entries = null + } opts = opts || {} - let b = new Depsify(Object.assign({ atRuleName: 'external' }, opts)) + var b = new Depsify(Object.assign({ atRuleName: 'external' }, opts)) if (opts.postcss !== false) { - b.plugin(require('reduce-css-postcss'), { - processorFilter: function (pipeline) { - pipeline.get('postcss-simple-import').push({ - resolve: b._options.resolve, - }) - - if (typeof opts.postcss === 'function') { - return opts.postcss(pipeline) - } - - pipeline.push.apply( - pipeline, [].concat(opts.postcss).filter(Boolean) - ) - }, - }) + b.plugin(postcss, opts.postcss) + } + if (entries) { + glob.sync(entries, { cwd: b._options.basedir }) + .forEach(function (file) { + b.add(file) + }) + } + b.plugin(bundler, bundleOptions) + if (watchOpts) { + b.plugin(watchify, typeof watchOpts === 'object' ? watchOpts : {}) + } + b.dest = function (outFolder, urlOpts) { + var output = combine.obj( + urlify(outFolder, urlOpts), + vfs.dest(outFolder) + ) + process.nextTick(sink(output)) + return output } return b } module.exports = { bundler, - watcher, - bundle, - watch, - dest, - src, + watchify, + urlify, create, } diff --git a/lib/sink.js b/lib/sink.js new file mode 100644 index 0000000..70eb50e --- /dev/null +++ b/lib/sink.js @@ -0,0 +1,49 @@ +'use strict' + +var Writable = require('stream').Writable + +function listenerCount(stream, evt) { + return stream.listeners(evt).length +} + +function hasListeners(stream) { + return !!(listenerCount(stream, 'readable') || listenerCount(stream, 'data')) +} + +function sink(stream) { + var sinkAdded = false + var sinkStream = new Writable({ objectMode: true }) + sinkStream._write = function (file, enc, cb) { cb() } + + function addSink() { + if (sinkAdded) { + return + } + + if (hasListeners(stream)) { + return + } + + sinkAdded = true + stream.pipe(sinkStream) + } + + function removeSink(evt) { + if (evt !== 'readable' && evt !== 'data') { + return + } + + if (hasListeners(stream)) { + sinkAdded = false + stream.unpipe(sinkStream) + } + } + + stream.on('newListener', removeSink) + stream.on('removeListener', removeSink) + stream.on('removeListener', addSink) + + return addSink +} + +module.exports = sink diff --git a/package.json b/package.json index 293e7c3..96276b6 100644 --- a/package.json +++ b/package.json @@ -30,13 +30,13 @@ }, "homepage": "https://github.com/reducejs/reduce-css#readme", "dependencies": { - "common-bundle": "^0.4.0", + "common-bundle": "^0.5.0", "depsify": "^4.0.0", + "globby": "^4.1.0", "postcss": "^5.0.5", "postcss-custom-url": "^4.0.0", "reduce-css-postcss": "^3.0.0", "stream-combiner2": "^1.1.1", - "vinyl": "^1.1.0", "vinyl-buffer": "^1.0.0", "vinyl-fs": "^2.2.1", "watchify2": "^0.1.0" @@ -45,10 +45,7 @@ "compare-directory": "^1.0.1", "del": "^2.0.2", "eslint": "^2.1.0", - "gulp": "^3.9.0", "mkdirp": "^0.5.1", - "postcss-advanced-variables": "^1.2.2", - "postcss-simple-import": "^3.0.0", "tap": "^5.0.0" } } diff --git a/test/multiple-bundle.js b/test/multiple-bundle.js index 44f6612..fcee41d 100644 --- a/test/multiple-bundle.js +++ b/test/multiple-bundle.js @@ -7,28 +7,29 @@ const del = require('del') const compare = require('compare-directory') const fixtures = path.resolve.bind(path, __dirname, 'fixtures') -const dest = fixtures.bind(null, 'build', 'multiple-bundles') -const expect = fixtures.bind(null, 'expected', 'multiple-bundles') +const build = fixtures('build', 'multiple-bundles') +const expect = fixtures('expected', 'multiple-bundles') test('multiple bundles', function(t) { - let basedir = fixtures('src') - let b = reduce.create({ basedir }) - del(dest()).then(function () { - reduce.src('*.css', { cwd: basedir }) - .pipe(reduce.bundle(b, { + del(build).then(function () { + var basedir = fixtures('src') + var b = reduce.create( + '*.css', + { basedir }, + { groups: '+(a|b).css', common: 'common.css', - })) - .pipe(reduce.dest(dest(), null, { - maxSize: 0, - useHash: true, - assetOutFolder: fixtures('build', 'multiple-bundles', 'images'), - })) - .on('data', () => {}) - .on('end', function () { - compare(t, ['**/*.css', '**/*.png'], dest(), expect()) - t.end() - }) + } + ) + b.bundle().pipe(b.dest(build, { + maxSize: 0, + useHash: true, + assetOutFolder: fixtures('build', 'multiple-bundles', 'images'), + })) + .on('end', function () { + compare(t, ['**/*.css', '**/*.png'], build, expect) + t.end() + }) }) }) diff --git a/test/single-bundle.js b/test/single-bundle.js index 42c9ebe..56929f0 100644 --- a/test/single-bundle.js +++ b/test/single-bundle.js @@ -7,24 +7,25 @@ const del = require('del') const compare = require('compare-directory') const fixtures = path.resolve.bind(path, __dirname, 'fixtures') -const dest = fixtures.bind(null, 'build', 'single-bundle') -const expect = fixtures.bind(null, 'expected', 'single-bundle') +const build = fixtures('build', 'single-bundle') +const expect = fixtures('expected', 'single-bundle') test('single bundle', function(t) { - let basedir = fixtures('src') - let b = reduce.create({ basedir }) - del(dest()).then(function () { - reduce.src('*.css', { cwd: basedir }) - .pipe(reduce.bundle(b, 'common.css')) - .pipe(reduce.dest(dest(), null, { - maxSize: 0, - assetOutFolder: fixtures('build', 'single-bundle', 'images'), - })) - .on('data', () => {}) - .on('end', function () { - compare(t, ['**/*.css', '**/*.png'], dest(), expect()) - t.end() - }) + del(build).then(function () { + var basedir = fixtures('src') + var b = reduce.create( + '*.css', + { basedir }, + 'common.css' + ) + b.bundle().pipe(b.dest(build, { + maxSize: 0, + assetOutFolder: fixtures('build', 'single-bundle', 'images'), + })) + .on('end', function () { + compare(t, ['**/*.css', '**/*.png'], build, expect) + t.end() + }) }) }) diff --git a/test/source-entries.js b/test/source-entries.js deleted file mode 100644 index bf27862..0000000 --- a/test/source-entries.js +++ /dev/null @@ -1,47 +0,0 @@ -'use strict' - -const test = require('tap').test -const reduce = require('..') -const path = require('path') -const fixtures = path.resolve.bind(path, __dirname, 'fixtures') -const fs = require('fs') -const del = require('del') -const DEST = fixtures('build', 'common.css') - -test('source entries', function(t) { - let b = reduce.create({ - entries: [ - { - file: '/a', - source: '', - }, - '/b', - ], - fileCache: { - '/b': 'b{}', - '/c': 'c{}', - '/d': 'd{}', - }, - resolve: function (file, parent) { - return path.resolve(parent.basedir, file) - }, - dependenciesFilter: function (deps, file) { - var base = path.basename(file) - return base === 'a' ? ['/c'] : ['/d'] - }, - }) - del(DEST).then(function () { - b.plugin(reduce.bundler, 'common.css') - b.bundle() - .pipe(reduce.dest(fixtures('build'))) - .on('data', () => {}) - .on('end', function () { - t.equal( - fs.readFileSync(DEST, 'utf8'), - 'd{}c{}b{}' - ) - t.end() - }) - }) -}) - diff --git a/test/watch.js b/test/watch.js index 6059540..fae1b84 100644 --- a/test/watch.js +++ b/test/watch.js @@ -48,19 +48,20 @@ entries.forEach(write) test('watch', function(t) { let count = 3 - let b = reduce.create({ basedir: src() }) - - reduce.src(['a.css', 'b.css'], { cwd: src() }) - .pipe(reduce.watch(b, { + let b = reduce.create( + ['a.css', 'b.css'], + { basedir: src() }, + { common: 'c.css', groups: '+(a|b).css', - })) - .on('bundle', function (bundleStream) { - bundleStream.pipe(reduce.dest(dest())) - .on('data', () => {}) - .once('finish', () => setTimeout(next, 50)) - }) - + }, + true + ) + b.on('update', function update() { + b.bundle().pipe(b.dest(dest())) + .once('end', () => setTimeout(next, 50)) + return update + }()) function next() { t.equal( From 3a0dbbd377c3aafc39870746d01e1f06877c9aa4 Mon Sep 17 00:00:00 2001 From: zoubin Date: Fri, 20 May 2016 16:36:11 +0800 Subject: [PATCH 2/4] travis: add 5 --- .travis.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.travis.yml b/.travis.yml index ae99d9c..72cb113 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,4 +1,5 @@ language: node_js node_js: - "stable" + - "5" - "4" From 72ea8136aebd7582074f6ca18f87c0f4ffb0144f Mon Sep 17 00:00:00 2001 From: zoubin Date: Fri, 20 May 2016 16:41:38 +0800 Subject: [PATCH 3/4] readme: remove unused lines --- README.md | 3 --- 1 file changed, 3 deletions(-) diff --git a/README.md b/README.md index 847415f..7dc15d8 100644 --- a/README.md +++ b/README.md @@ -24,9 +24,6 @@ Check the [example](example/reduce/). var reduce = require('reduce-css') var del = require('del') var path = require('path') -var Transform = require('stream').Transform - -var basedir = path.join(__dirname, 'src') bundle(createBundler()) From 319e1ece03d28984237d8099015920601ff8f7b8 Mon Sep 17 00:00:00 2001 From: zoubin Date: Fri, 20 May 2016 16:45:20 +0800 Subject: [PATCH 4/4] readme: typo --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 7dc15d8..59d56b7 100644 --- a/README.md +++ b/README.md @@ -140,7 +140,7 @@ b.bundle().pipe(require('gulp-uglifycss')()).pipe(b.dest('build')) ### b.dest(outFolder, urlTransformOptions) Works almost the same with [`gulp.dest`], except that file contents are transformed using [`postcss-custom-url`] -before being written to disk. +before written to disk. `urlTransformOptions` is passed to both the [inline](https://github.com/reducejs/postcss-custom-url#inline)