Skip to content
This repository was archived by the owner on Jun 24, 2024. It is now read-only.

Commit 5819800

Browse files
committed
feat: Add bundle method
Adds a new `bundle` method to match the API of the JS writer. This allows you to instantiate a writer a single time, but consume from it multiple times BREAKING CHANGE: The user must now call `.bundle` before the writer can be consumed
1 parent fe4e513 commit 5819800

File tree

5 files changed

+147
-45
lines changed

5 files changed

+147
-45
lines changed

.eslintrc

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,6 @@
99
"finn-prettier"
1010
],
1111
"env": {
12-
"node": true,
1312
"jest": true
1413
}
15-
}
14+
}

README.md

Lines changed: 21 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,7 @@ npm install asset-pipe-css-writer
6060
### Require the writer
6161

6262
```js
63-
const CssWriter = require('asset-pipe-css-writer');
63+
const CssWriter = require('@asset-pipe/css-writer');
6464
```
6565

6666
### Instantiating the writer
@@ -82,12 +82,12 @@ const writer = new CssWriter([
8282

8383
### Consuming content from the writer
8484

85-
The writer is a readable stream in object mode so in order to access the data
86-
you may register a data handler and listen for objects to be passed to the
87-
handler:
85+
The writer is an event emitter, which has a method called `bundle`, which
86+
returns a readable stream in object mode so in order to access the data you may
87+
register a data handler and listen for objects to be passed to the handler:
8888

8989
```js
90-
writer.on('data', data => {
90+
writer.bundle().on('data', data => {
9191
// { id, name, version, file, content }
9292
});
9393
```
@@ -96,7 +96,7 @@ You might also pipe the writer into a writeable or transform stream (with input
9696
in object mode):
9797

9898
```js
99-
const { Writable } = require('stream');
99+
const { Writeable } = require('stream');
100100
const consumer = new Writeable({
101101
objectMode: true,
102102
write(chunk, encoding, callback) {
@@ -106,5 +106,19 @@ const consumer = new Writeable({
106106
},
107107
});
108108

109-
writer.pipe(consumer);
109+
writer.bundle().pipe(consumer);
110+
```
111+
112+
If you want to create a single file output, send `true` as the second argument
113+
when creating the `Writer`.
114+
115+
```js
116+
const writer = new CssWriter(
117+
['/path/to/css/file1.css', '/path/to/css/file2.css'],
118+
true,
119+
);
120+
121+
writer.bundle().on('data', data => {
122+
// the two files bundled together as a single CSS
123+
});
110124
```

lib/writer.js

Lines changed: 48 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,48 @@
11
'use strict';
22

3-
const { Readable } = require('stream');
3+
const EventEmitter = require('events');
4+
const { Readable, Transform } = require('stream');
45
const { identifyCssModule, bundleCssModule } = require('./util');
56
const { existsSync } = require('fs');
67
const { isAbsolute } = require('path');
78
const assert = require('assert');
89
const { hasher } = require('asset-pipe-common');
910

10-
module.exports = class Writer extends Readable {
11-
constructor(files = []) {
11+
class Stream extends Readable {
12+
constructor(files) {
1213
super({ objectMode: true });
1314

15+
this.files = files;
16+
}
17+
18+
async _read() {
19+
const file = this.files.shift();
20+
if (!file) {
21+
this.push(null);
22+
return;
23+
}
24+
25+
try {
26+
const [css, meta] = await Promise.all([
27+
bundleCssModule(file),
28+
identifyCssModule(file),
29+
]);
30+
meta.id = hasher(
31+
`${meta.name}|${meta.version}|${meta.file}|${css}`
32+
);
33+
meta.content = css;
34+
this.push(meta);
35+
} catch (err) {
36+
this.emit('error', err);
37+
}
38+
}
39+
}
40+
41+
module.exports = class Writer extends EventEmitter {
42+
constructor(files = [], bundle = false) {
43+
super({ objectMode: true });
44+
this.shouldBundle = bundle;
45+
1446
assert(
1547
Array.isArray(files) || typeof files === 'string',
1648
`Expected 'files' to be of type 'Array' or a 'string', instead got '${typeof files}'`
@@ -38,25 +70,20 @@ module.exports = class Writer extends Readable {
3870
}
3971
}
4072

41-
async _read() {
42-
const file = this.files.shift();
43-
if (!file) {
44-
this.push(null);
45-
return;
46-
}
73+
bundle() {
74+
const cssStream = new Stream(this.files.slice(0));
4775

48-
try {
49-
const [css, meta] = await Promise.all([
50-
bundleCssModule(file),
51-
identifyCssModule(file),
52-
]);
53-
meta.id = hasher(
54-
`${meta.name}|${meta.version}|${meta.file}|${css}`
55-
);
56-
meta.content = css;
57-
this.push(meta);
58-
} catch (err) {
59-
this.emit('error', err);
76+
if (!this.shouldBundle) {
77+
return cssStream;
6078
}
79+
80+
return cssStream.pipe(
81+
new Transform({
82+
objectMode: true,
83+
transform(chunk, enc, next) {
84+
next(null, chunk.content);
85+
},
86+
})
87+
);
6188
}
6289
};

package-lock.json

Lines changed: 23 additions & 7 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

test/writer.test.js

Lines changed: 54 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
'use strict';
22

3-
/* global test, expect, beforeEach, jest */
4-
3+
const fs = require('fs');
54
const path = require('path');
65
const { hasher } = require('asset-pipe-common');
76
const { identifyCssModule, bundleCssModule } = require('../lib/util.js');
@@ -64,7 +63,7 @@ test('new Writer(filePath)', done => {
6463
const filePath = path.join(__dirname, 'test-assets/my-module-1/main.css');
6564
const fileRef = 'my-module-1/main.css';
6665

67-
const writer = new Writer(filePath);
66+
const writer = new Writer(filePath).bundle();
6867
const items = [];
6968

7069
writer.on('data', item => {
@@ -82,6 +81,31 @@ test('new Writer(filePath)', done => {
8281
});
8382
});
8483

84+
test('new Writer(filePath) can be reused', done => {
85+
expect.assertions(2);
86+
const filePath = path.join(__dirname, 'test-assets/my-module-1/main.css');
87+
const dataMock = jest.fn();
88+
89+
const writer = new Writer(filePath);
90+
const bundle1 = writer.bundle();
91+
92+
bundle1.on('data', dataMock);
93+
94+
bundle1.on('end', () => {
95+
expect(dataMock).toHaveBeenCalledTimes(1);
96+
97+
const bundle2 = writer.bundle();
98+
99+
bundle2.on('data', dataMock);
100+
101+
bundle2.on('end', () => {
102+
expect(dataMock).toHaveBeenCalledTimes(2);
103+
104+
done();
105+
});
106+
});
107+
});
108+
85109
test('new Writer(filePath) relative paths throw error', () => {
86110
expect.assertions(1);
87111
const filePath = './test-assets/my-module-1/main.css';
@@ -96,7 +120,7 @@ test('new Writer([filePath])', done => {
96120
const filePath = path.join(__dirname, 'test-assets/my-module-1/main.css');
97121
const fileRef = 'my-module-1/main.css';
98122

99-
const writer = new Writer([filePath]);
123+
const writer = new Writer([filePath]).bundle();
100124
const items = [];
101125

102126
writer.on('data', item => {
@@ -122,7 +146,7 @@ test('Writer processes @import statements', done => {
122146
);
123147
const fileRef = 'my-module-3/css/main.css';
124148

125-
const writer = new Writer([filePath]);
149+
const writer = new Writer([filePath]).bundle();
126150
const items = [];
127151

128152
writer.on('data', item => {
@@ -154,7 +178,7 @@ test('new Writer([filePath1, filePath2]) ensures correct order', done => {
154178
);
155179
const fileRef2 = 'my-module-2/css/main.css';
156180

157-
const writer = new Writer([filePath1, filePath2]);
181+
const writer = new Writer([filePath1, filePath2]).bundle();
158182
const items = [];
159183

160184
writer.on('data', item => {
@@ -244,7 +268,7 @@ test('writer emits error', done => {
244268
const CssWriter = require('..');
245269
const filePath = path.join(__dirname, 'test-assets/my-module-1/main.css');
246270

247-
const writer = new CssWriter(filePath);
271+
const writer = new CssWriter(filePath).bundle();
248272

249273
writer.on('error', error => {
250274
expect(error).toBeInstanceOf(Error);
@@ -254,7 +278,7 @@ test('writer emits error', done => {
254278
});
255279

256280
test('new Writer() emits nothing but does not break', done => {
257-
const writer = new Writer();
281+
const writer = new Writer().bundle();
258282
const items = [];
259283

260284
writer.on('data', item => {
@@ -266,3 +290,25 @@ test('new Writer() emits nothing but does not break', done => {
266290
done();
267291
});
268292
});
293+
294+
test('new Writer(filepath, bundle: true) emits a bundle', done => {
295+
expect.assertions(1);
296+
const filePath = path.join(__dirname, 'test-assets/my-module-1/main.css');
297+
298+
const writer = new Writer([filePath], true).bundle();
299+
300+
writer.on('error', e => {
301+
done.fail(e);
302+
});
303+
const items = [];
304+
305+
writer.on('data', item => {
306+
items.push(item);
307+
});
308+
309+
writer.on('end', () => {
310+
expect(items).toEqual([fs.readFileSync(filePath, 'utf8')]);
311+
312+
done();
313+
});
314+
});

0 commit comments

Comments
 (0)