Skip to content

Commit 904b127

Browse files
author
Neal Granger
committed
Support for named exports and arrays.
Any named es6 export on the loaded file is interpreted as class definition and appended to the definitions from the default export. This allows styles to be defined with exports that map 1:1 with css modules. Any block, selector or property can now be defined as an array of values. This allows defining multiple properties of the same name within a class.
1 parent 6978526 commit 904b127

File tree

1 file changed

+94
-22
lines changed

1 file changed

+94
-22
lines changed

index.js

Lines changed: 94 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,9 @@
11
var defaults = require('lodash/defaults');
2+
var mapKeys = require('lodash/mapKeys');
3+
var isArray = require('lodash/isArray');
4+
var omit = require('lodash/omit');
5+
var isString = require('lodash/isString');
6+
var isNumber = require('lodash/isNumber');
27
var loaderUtils = require('loader-utils');
38
var dangerousStyleValue = require('react/lib/dangerousStyleValue');
49
var hyphenateStyleName = require('fbjs/lib/hyphenateStyleName');
@@ -15,38 +20,69 @@ function line(pretty) {
1520
return pretty ? '\n' : '';
1621
}
1722

18-
function parse(config, styles, level) {
19-
level = level === undefined ? 0 : level;
23+
function isProp(value) {
24+
return isArray(value) ?
25+
isProp(value[0], true) :
26+
isString(value) || isNumber(value);
27+
}
28+
29+
function format(config, value, name, level, inProp) {
30+
level = level !== undefined ? level : 0;
2031

2132
var pretty = config.pretty;
2233
var css = '';
34+
var indentLevel = level;
2335

24-
for (var styleName in styles) {
25-
if (!styles.hasOwnProperty(styleName)) {
26-
continue;
36+
if (isArray(value)) {
37+
for (var i = 0, len = value.length; i < len; i ++) {
38+
css += format(config, value[i], name, level, inProp);
2739
}
40+
return css;
41+
}
2842

29-
// Extract the style definition or nested block.
30-
var styleValue = styles[styleName];
31-
32-
if (styleValue === null) {
33-
continue;
43+
if (inProp) {
44+
// The `name` and `value` args currently represent a css property and value.
45+
// Use React's css style processing funcs to generate css markup.
46+
47+
css += indent(pretty, indentLevel) + hyphenateStyleName(name) + ':' + space(pretty);
48+
css += dangerousStyleValue(name, value) + ';' + line(pretty);
49+
} else {
50+
// The `name` and `value` args currently represent a block containing css
51+
// properties or further nested blocks. Iterate through and parse
52+
// the nested values.
53+
54+
if (name) {
55+
// Unless we are in the global css scope (`name` is undefined), add a new
56+
// block to the markup.
57+
css += indent(pretty, indentLevel) + name + space(pretty) + '{' + line(pretty);
58+
indentLevel += 1;
3459
}
3560

36-
// Remove whitespace from selector/block.
37-
styleName = styleName.trim();
61+
for (var key in value) {
62+
if (!value.hasOwnProperty(key)) {
63+
continue;
64+
}
3865

39-
var block = Object.prototype.toString.call(styleValue) === '[object Object]';
66+
// Extract the style definition or nested block.
67+
var innerValue = value[key];
4068

41-
if (block) {
42-
css += indent(pretty, level) + styleName + space(pretty) + '{' + line(pretty);
43-
css += parse(config, styleValue, level + 1);
44-
css += indent(pretty, level) + '}' + line(pretty) + line(pretty);
45-
continue;
69+
if (innerValue === null) {
70+
continue;
71+
}
72+
73+
// Determine if the inner value is a block or a property.
74+
var innerIsProp = isProp(innerValue);
75+
76+
// Remove whitespace from selector/block/property.
77+
var innerName = key.trim();
78+
79+
css += format(config, innerValue, innerName, level + 1, innerIsProp);
4680
}
4781

48-
css += indent(pretty, level + 1) + hyphenateStyleName(styleName) + ':' + space(pretty);
49-
css += dangerousStyleValue(styleName, styleValue) + ';' + line(pretty);
82+
if (name) {
83+
// Close the open block.
84+
css += indent(pretty, level) + '}' + line(pretty) + line(pretty);
85+
}
5086
}
5187

5288
return css;
@@ -55,9 +91,45 @@ function parse(config, styles, level) {
5591
module.exports = function(content) {
5692
this.cacheable();
5793

58-
config = defaults(loaderUtils.getLoaderConfig(this, 'jsCssLoader'), {pretty: process.env.NODE_ENV !== 'production'});
94+
config = defaults(
95+
loaderUtils.getLoaderConfig(this, 'jsCssLoader'),
96+
{pretty: process.env.NODE_ENV !== 'production'}
97+
);
5998

6099
var styles = this.exec(content, this.resourcePath);
61100

62-
return parse(config, styles.__esModule ? styles.default : styles);
101+
var css = '';
102+
103+
if (styles.__esModule) {
104+
// When using Babel, css classes can be defined as named es6 exports.
105+
//
106+
// e.x.
107+
//
108+
// ```
109+
// export default {
110+
// '.base': {
111+
// color: 'black'
112+
// }
113+
// };
114+
// ```
115+
//
116+
// is the same as
117+
//
118+
// ```
119+
// export const base = {
120+
// color: 'black'
121+
// };
122+
// ```
123+
return format(config, [
124+
styles.default,
125+
mapKeys(
126+
omit(styles, 'default'),
127+
function (value, key) {
128+
return '.' + key;
129+
}
130+
),
131+
]);
132+
}
133+
134+
return format(config, styles);
63135
};

0 commit comments

Comments
 (0)