Skip to content

Commit ee52bea

Browse files
committed
use @import instead of @require
@import is valid css and doesn't rely on any strange comment behavior. npm-css is a preprocessor, so handling @import as css and flattening the file is valid behavior. The @require comment file is useless unless run through npm-css anyway so no sense in hiding the fact that you need something like npm-css. see issue #3 for more discussion
1 parent cdb87eb commit ee52bea

File tree

9 files changed

+113
-97
lines changed

9 files changed

+113
-97
lines changed

README.md

Lines changed: 6 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -5,25 +5,23 @@ Require css from node modules.
55
## Syntax
66

77
``` css
8-
/*
9-
@require typeahead
8+
@import "typeahead";
109

11-
@require ./foo.css
12-
@require ./bar/baz.css
13-
*/
10+
@import "./foo.css";
11+
@import "./bar/baz.css";
1412

1513
.foo {
1614
color: red
1715
}
1816
```
1917

20-
npm-css reads comments in the format of `// @require` and inlines the CSS at that path for you. It also understands node modules.
18+
npm-css reads css imports in the format of `@import " ... "` and inlines the CSS at that path for you. It also understands node modules.
2119

22-
If you `@require` a folder it will try to look for a package.json file or read `index.css` by default. See the `package.json` section below.
20+
If you `@import` a folder it will try to look for a package.json file or read `index.css` by default. See the `package.json` section below.
2321

2422
## CLI
2523

26-
To build a single css file from an entry.css with @require statements.
24+
To build a single css file from an entry.css with @import statements.
2725

2826
`npm-css entry.css -o bundle.css`
2927

index.js

Lines changed: 95 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -4,73 +4,111 @@ var path = require('path');
44

55
// vendor
66
var resolve = require('resolve');
7+
var cssp = require('cssp');
8+
var traverse = require('traverse');
79

810
// local
911
var prefix = require('./lib/prefix');
1012

11-
module.exports = npmcss;
13+
// replace nodes with tree
14+
// nodes is an array representing a node
15+
function replace(nodes, tree) {
1216

13-
// process given file for // @require statements
14-
// file should be /full/path/to/file.css
15-
function npmcss(file) {
16-
// load source file
17+
// truncate previous nodes
18+
nodes.splice(0, nodes.length);
19+
20+
// insert tree as new nodes
21+
tree.forEach(function(node) {
22+
nodes.push(node);
23+
});
24+
}
25+
26+
// flatten @imports given a basepath, and a tree
27+
// inline all of the import statements into the tree
28+
// @return a tree with all statements inlined
29+
function flatten(file) {
1730
var src = fs.readFileSync(file, 'utf8');
1831

19-
// replace all instances of @require foo with proper content
20-
src = src.replace(/@require (.*)\n/g, function(str, name) {
21-
22-
// base path for the file we want to load
23-
var base = path.dirname(file);
24-
25-
// relative path, just read and load the file
26-
if (name[0] === '.') {
27-
// if path is a dir, see if package.json is available
28-
// if avail, read style field
29-
// if not avail or no style field, use index.css
30-
var filepath = path.join(base, name);
31-
32-
// path is a file
33-
if (fs.statSync(filepath).isFile()) {
34-
return '*/\n' + npmcss(filepath) + '\n/*';
35-
}
36-
37-
// path is a dir
38-
var pkginfo = path.join(filepath, 'package.json');
39-
40-
// package.json exists for path
41-
// use style info and prefix
42-
if (fs.existsSync(pkginfo)) {
43-
var info = JSON.parse(fs.readFileSync(pkginfo));
44-
filepath = path.join(base, name, info.style || 'index.css')
45-
return '*/\n' + prefix(info.name, npmcss(filepath)) + '\n/*';
46-
}
47-
48-
// no package.json, try index.css in the dir
49-
filepath = path.join(filepath, 'index.css');
50-
51-
// do not prefix if just loading index.css from a dir
52-
// allows for easier organization of multi css files without prefixing
53-
if (fs.existsSync(filepath)) {
54-
return '*/\n' + npmcss(filepath) + '\n/*';
55-
}
56-
57-
// if all else fails, return original source
58-
return src;
32+
var base = path.dirname(file);
33+
var tree = cssp.parse(src);
34+
35+
traverse(tree).forEach(function (node) {
36+
var self = this;
37+
38+
if (node !== 'atrules') {
39+
return;
40+
}
41+
42+
node = this.parent.node;
43+
44+
// ignore non import
45+
if (node[1][0] !== 'atkeyword' && nodes[1][1][0] !== 'ident' &&
46+
node[1][1][1] !== 'import') {
47+
return;
48+
}
49+
50+
// ignore non string imports
51+
if (node[3][0] !== 'string') {
52+
return;
5953
}
6054

61-
var res = resolve.sync(name, {
62-
basedir: base,
63-
extensions: ['.css'],
64-
packageFilter: function (pkg) {
65-
pkg.main = pkg.style;
66-
return pkg;
67-
}
68-
});
69-
70-
// run resolution on the required css file
71-
return '*/\n' + prefix(name, npmcss(res)) + '\n/*';
55+
// remove quotes from imported name
56+
var name = node[3][1].replace(/["']/g, '');
57+
58+
// @import "module"
59+
if (name[0] !== '.') {
60+
61+
// lookup the entry css file
62+
var filepath = resolve.sync(name, {
63+
basedir: base,
64+
extensions: ['.css'],
65+
packageFilter: function (pkg) {
66+
pkg.main = pkg.style;
67+
return pkg;
68+
}
69+
});
70+
71+
// run css file through tree -> flatten -> prefix
72+
// get required module as a css tree
73+
// replace @import node with new tree
74+
return replace(self.parent.node, prefix(name, flatten(filepath)));
75+
}
76+
77+
var filepath = path.join(base, name);
78+
79+
// path is a file
80+
if (fs.statSync(filepath).isFile()) {
81+
return replace(self.parent.node, flatten(filepath));
82+
}
83+
84+
// path is a dir
85+
var pkginfo = path.join(filepath, 'package.json');
86+
87+
// package.json exists for path
88+
// use style info and prefix
89+
if (fs.existsSync(pkginfo)) {
90+
var info = JSON.parse(fs.readFileSync(pkginfo));
91+
filepath = path.join(base, name, info.style || 'index.css')
92+
return replace(self.parent.node, prefix(info.name, flatten(filepath)));
93+
}
94+
95+
// no package.json, try index.css in the dir
96+
filepath = path.join(filepath, 'index.css');
97+
98+
// do not prefix if just loading index.css from a dir
99+
// allows for easier organization of multi css files without prefixing
100+
if (fs.existsSync(filepath)) {
101+
return replace(self.parent.node, flatten(filepath));
102+
}
72103
});
73104

74-
return src;
105+
return tree;
75106
}
76107

108+
// process given file for // @require statements
109+
// file should be /full/path/to/file.css
110+
module.exports = function(file) {
111+
var base = path.dirname(file);
112+
return cssp.translate(flatten(file));
113+
};
114+

lib/prefix.js

Lines changed: 6 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,11 @@
11
// vendor
2-
var cssp = require('cssp');
32
var traverse = require('traverse');
43

5-
module.exports = function (name, src) {
6-
if (typeof opts === 'string') {
7-
opts = { prefix : opts };
8-
}
9-
10-
var tree = cssp.parse(src);
11-
4+
// prefix selectors of a css tree with a given name
5+
// if the selector has already been properly prefixed, it is not changed
6+
// modifies the tree in place
7+
// @return tree
8+
module.exports = function prefix(name, tree) {
129
traverse(tree).forEach(function (node) {
1310
if (node !== 'simpleselector') {
1411
return;
@@ -41,6 +38,5 @@ module.exports = function (name, src) {
4138
);
4239
});
4340

44-
return cssp.translate(tree);
41+
return tree;
4542
};
46-

test/expected/index.css

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,3 @@
1-
/*
2-
*/
31
/* .foo {} */
42
.foo {}
53

6-
/* */

test/expected/input.css

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,7 @@
1-
/*
2-
*/
31
.foo {
42
color: red;
53
}
64

7-
/* */
85
/* foobar stuff */
96

107
/* .foobar */
@@ -37,5 +34,4 @@ p.foobar {}
3734
/* .foobar .baz .foobar */
3835
.foobar .baz .foobar {}
3936

40-
/**/
4137

test/expected/relative.css

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,6 @@
11
/* test require dir */
22

3-
/* should load localwidget based on localwidget/package.json
4-
/*
5-
*/
3+
/* should load localwidget based on localwidget/package.json */
64
/* .localwidget .foo */
75
.localwidget .foo {}
86

9-
/**/

test/fixtures/index.css

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1 @@
1-
/*
2-
@require ./foo
3-
*/
1+
@import "./foo";

test/fixtures/input.css

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,3 @@
1-
/*
2-
@require ./required.css
3-
@require foobar
4-
*/
1+
@import "./required.css";
2+
@import "foobar";
53

test/fixtures/relative.css

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,4 @@
11
/* test require dir */
22

3-
/* should load localwidget based on localwidget/package.json
4-
/*
5-
@require ./localwidget
6-
*/
3+
/* should load localwidget based on localwidget/package.json */
4+
@import "./localwidget";

0 commit comments

Comments
 (0)