Skip to content

Commit

Permalink
Merge pull request #51 from joeybaker/add-streaming
Browse files Browse the repository at this point in the history
fix #17: add a stream output option
  • Loading branch information
joshwnj committed Oct 12, 2015
2 parents 870cc33 + 9b8827a commit 1a77505
Show file tree
Hide file tree
Showing 6 changed files with 115 additions and 16 deletions.
21 changes: 20 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -45,14 +45,33 @@ b.plugin(require('css-modulesify'), {
b.bundle();
```

```js
// or, get the output as a stream
var b = require('browserify')();
var fs = require('fs');

b.add('./main.js');
b.plugin(require('css-modulesify'), {
rootDir: __dirname
});

var bundle = b.bundle()
bundle.on('css stream', function (css) {
css.pipe(fs.createWriteStream('mycss.css'));
});
```

### Options:

- `rootDir`: absolute path to your project's root directory. This is optional but providing it will result in better generated classnames.
- `output`: path to write the generated css.
- `output`: path to write the generated css. If not provided, you'll need to listen to the `'css stream'` event on the bundle to get the output.
- `jsonOutput`: optional path to write a json manifest of classnames.
- `use`: optional array of postcss plugins (by default we use the css-modules core plugins).
- `generateScopedName`: (API only) a function to override the default behaviour of creating locally scoped classnames.

### Events
- `b.bundle().on('css stream', callback)` The callback is called with a readable stream containing the compiled CSS. You can write this to a file.

## Using CSS Modules on the backend

If you want to use CSS Modules in server-generated templates there are a couple of options:
Expand Down
44 changes: 31 additions & 13 deletions index.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ var Core = require('css-modules-loader-core');
var FileSystemLoader = require('css-modules-loader-core/lib/file-system-loader');
var assign = require('object-assign');
var stringHash = require('string-hash');
var ReadableStream = require('stream').Readable;


/*
Expand Down Expand Up @@ -96,10 +97,6 @@ module.exports = function (browserify, options) {
if (!rootDir) { rootDir = process.cwd(); }

var cssOutFilename = options.output || options.o;
if (!cssOutFilename) {
throw new Error('css-modulesify needs the --output / -o option (path to output css file)');
}

var jsonOutFilename = options.json || options.jsonOutput;

// PostCSS plugins passed to FileSystemLoader
Expand Down Expand Up @@ -143,6 +140,10 @@ module.exports = function (browserify, options) {
return plugin;
});

// the compiled CSS stream needs to be avalible to the transform,
// but re-created on each bundle call.
var compiledCssStream;

function transform (filename) {
// only handle .css files
if (!cssExt.test(filename)) {
Expand All @@ -164,6 +165,8 @@ module.exports = function (browserify, options) {
// store this file's source to be written out to disk later
sourceByFile[filename] = loader.finalSource;

compiledCssStream.push(loader.finalSource);

self.queue(output);
self.queue(null);
}, function (err) {
Expand All @@ -177,17 +180,32 @@ module.exports = function (browserify, options) {
});

browserify.on('bundle', function (bundle) {
// on each bundle, create a new stream b/c the old one might have ended
compiledCssStream = new ReadableStream();
compiledCssStream._read = function () {};

bundle.emit('css stream', compiledCssStream);

bundle.on('end', function () {
// Combine the collected sources into a single CSS file
var css = Object.keys(sourceByFile).map(function (file) {
return sourceByFile[file];
}).join('\n');

fs.writeFile(cssOutFilename, css, function (err) {
if (err) {
browserify.emit('error', err);
}
});
var files = Object.keys(sourceByFile);
var css;

// end the output stream
compiledCssStream.push(null);

// write the css file
if (cssOutFilename) {
css = files.map(function (file) {
return sourceByFile[file];
}).join('\n');

fs.writeFile(cssOutFilename, css, function (err) {
if (err) {
browserify.emit('error', err);
}
});
}

// write the classname manifest
if (jsonOutFilename) {
Expand Down
3 changes: 3 additions & 0 deletions tests/cases/multiple-js-files/expected.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
._simple_styles__foo {
color: #F00;
}
1 change: 1 addition & 0 deletions tests/cases/multiple-js-files/main.js
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
module.exports = require('../simple/main.js');
3 changes: 1 addition & 2 deletions tests/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -34,8 +34,7 @@ function runTestCase (dir) {

b.bundle(function (err) {
if (err) {
console.error(err);
return t.fail('Unexpected error');
t.error(err, 'should not error');
}

t.end();
Expand Down
59 changes: 59 additions & 0 deletions tests/stream-output.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
var tape = require('tape');

var browserify = require('browserify');
var proxyquire = require('proxyquire');
var fs = require('fs');
var path = require('path');

var casesDir = path.join(__dirname, 'cases');
var simpleCaseDir = path.join(casesDir, 'simple');
var cssFilesTotal = 1;
var cssOutFilename = 'out.css';

tape('stream output', function (t) {
var fakeFs = {
writeFile: function (filename, content, cb) {
var expected = fs.readFileSync(path.join(simpleCaseDir, 'expected.css'), 'utf8');

t.equal(filename, cssOutFilename, 'correct output filename');
t.equal(content, expected, 'output matches expected');
cb();
}
};

var cssModulesify = proxyquire('../', {
fs: fakeFs
});

t.plan(cssFilesTotal * 2 + 1);

var cssFilesCount = 0;
browserify(path.join(simpleCaseDir, 'main.js'))
.plugin(cssModulesify, {
rootDir: path.join(simpleCaseDir)
})
.on('error', t.error)
.bundle(function noop () {})
.on('css stream', function (stream) {
stream
.on('data', function onData (css) {
var cssString = css.toString();
// just get the first class name, use that as an id
var cssId = cssString.split('\n')[0].split(' ')[0];

t.ok(
++cssFilesCount <= cssFilesTotal
, 'emits data for ' + cssId
);

t.ok(
cssString.indexOf('._styles') === 0
, 'emits compiled css for ' + cssId
);
})
.on('end', function onEnd () {
t.pass('ends the stream');
})
.on('error', t.error);
});
});

0 comments on commit 1a77505

Please sign in to comment.