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

Commit e377007

Browse files
authored
Merge pull request #1 from asset-pipe/develop
Develop
2 parents 379916c + 1af1f9b commit e377007

File tree

20 files changed

+5107
-4758
lines changed

20 files changed

+5107
-4758
lines changed

.eslintignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
coverage

.eslintrc

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,11 @@
11
{
2-
"root": true,
3-
"extends": [
4-
"finn",
5-
"finn/node"
6-
]
7-
}
2+
"root": true,
3+
"parserOptions": {
4+
"ecmaVersion": 2017
5+
},
6+
"extends": ["finn", "finn/node"],
7+
"env": {
8+
"node": true,
9+
"jest": true
10+
}
11+
}

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,3 +6,4 @@ tmp/**/*
66
.idea/**/*
77
*.iml
88
*.log
9+
coverage

.travis.yml

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@ language: node_js
22

33
node_js:
44
- "8"
5-
- "6"
65

76
script:
87
- npm test

README.md

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,80 @@
11
# asset-pipe-css-writer
22

3+
A module that takes any number of css file entry points and packages them together with meta data before providing them as a readable stream.
4+
5+
## Overview
6+
7+
Given any number of css file paths, for each file path, this module will:
8+
1. fetch the file at the path
9+
2. fetch a name and version from the nearest package.json to the file
10+
3. bundle the css found in the file (resolving any @import statements and inlining them)
11+
4. put all this together in an object (See Output data format below)
12+
13+
The module provides a readable stream of the resulting objects.
14+
15+
### Output data format
16+
17+
```js
18+
{
19+
// Unique id for entry. Created by hashing together name, version and file
20+
id: '4f32a8e1c6cf6e5885241f3ea5fee583560b2dfde38b21ec3f9781c91d58f42e',
21+
// 'name' from nearest package.json file found by working up from the css file's directory
22+
name: 'my-module-1',
23+
// 'version' from nearest package.json file found by working up from the css file's directory
24+
version: '1.0.1',
25+
// path to file on disk relative to nearest package.json file found by working up from the css file's directory
26+
file: 'my-module-1/main.css',
27+
// bundled css content with any @import statements inlined
28+
content: '/* ... */'
29+
}
30+
```
31+
32+
## Installation
33+
34+
```bash
35+
npm install asset-pipe-css-writer
36+
```
37+
38+
## Usage
39+
40+
### Require the writer
41+
```js
42+
const CssWriter = require('asset-pipe-css-writer')
43+
```
44+
45+
### Instantiating the writer
46+
47+
Either pass a path to a single css file:
48+
```js
49+
const writer = new CssWriter('/path/to/css/file.css')
50+
```
51+
52+
Or pass an array of paths to css files:
53+
```js
54+
const writer = new CssWriter(['/path/to/css/file1.css', '/path/to/css/file2.css'])
55+
```
56+
57+
### Consuming content from the writer
58+
59+
The writer is a readable stream in object mode so in order to access the data you may register a data handler
60+
and listen for objects to be passed to the handler:
61+
```js
62+
writer.on('data', data => {
63+
// { id, name, version, file, content }
64+
})
65+
```
66+
67+
You might also pipe the writer into a writeable or transform stream (with input in object mode):
68+
```js
69+
const { Writable } = require('stream')
70+
const consumer = new Writeable({
71+
objectMode: true,
72+
write(chunk, encoding, callback) {
73+
// chunk will be an object of the shape: { id, name, version, file, content }
74+
console.log(chunk)
75+
callback()
76+
}
77+
})
78+
79+
writer.pipe(consumer)
80+
```

lib/util.js

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
'use strict';
2+
3+
const path = require('path');
4+
const { promisify } = require('util');
5+
const fs = require('fs');
6+
const readFile = promisify(fs.readFile);
7+
const readPkgUp = require('read-pkg-up');
8+
const postcss = require('postcss');
9+
const atImport = require('postcss-import');
10+
11+
module.exports.identifyCssModule = async function identifyCssModule (filePath) {
12+
const { pkg: { name, version }, path: packagePath } = await readPkgUp({
13+
normalize: false,
14+
cwd: path.dirname(filePath),
15+
});
16+
const file = filePath.replace(path.dirname(packagePath), name);
17+
18+
return { name, version, file };
19+
};
20+
21+
module.exports.bundleCssModule = async function bundleCssModule (filePath) {
22+
const fileContents = await readFile(filePath, 'utf8');
23+
const { css } = await postcss()
24+
.use(atImport())
25+
.process(fileContents, { from: filePath });
26+
return css;
27+
};

lib/writer.js

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
'use strict';
2+
3+
const { Readable } = require('stream');
4+
const { identifyCssModule, bundleCssModule } = require('./util');
5+
const { existsSync } = require('fs');
6+
const { isAbsolute } = require('path');
7+
const assert = require('assert');
8+
const { hasher } = require('asset-pipe-common');
9+
10+
module.exports = class Writer extends Readable {
11+
constructor (files = []) {
12+
super({ objectMode: true });
13+
14+
assert(
15+
Array.isArray(files) || typeof files === 'string',
16+
`Expected 'files' to be of type 'Array' or a 'string', instead got '${typeof files}'`
17+
);
18+
this.files = Array.isArray(files) ? files : [files];
19+
for (const file of this.files) {
20+
assert(typeof file === 'string', `Expected 'file' (${file}) to be of type 'string', instead got '${typeof file}'`);
21+
assert(isAbsolute(file), `Expected 'file' (${file}) to be an absolute path to a file but it was not`);
22+
assert(existsSync(file), `Expected 'file' (${file}) to exist on file system but it did not`);
23+
}
24+
}
25+
26+
async _read () {
27+
const file = this.files.shift();
28+
if (!file) {
29+
this.push(null);
30+
return;
31+
}
32+
33+
try {
34+
const [css, meta] = await Promise.all([bundleCssModule(file), identifyCssModule(file)]);
35+
meta.id = hasher(`${meta.name}|${meta.version}|${meta.file}|${css}`);
36+
meta.content = css;
37+
this.push(meta);
38+
} catch (err) {
39+
this.emit('error', err);
40+
}
41+
}
42+
};

0 commit comments

Comments
 (0)