Skip to content

Commit b3f53f5

Browse files
mvsmalgajus
authored andcommitted
feat: add ability to add arbitrary postcss plugins (#100)
* Implemented support for extra postcss plugins * Added test for extra postcss plugins * Fixed eslint issues * Another fix of eslint * Fixed eslint * Removed string type of FiletypeOptions * Updated test to use latest syntax * Updated flow to 0.48.0 to use $ReadOnlyArray BREAKING CHANGE: Filetype is now an object with properties 'syntax' and 'plugins'
1 parent ab2fe0e commit b3f53f5

File tree

9 files changed

+107
-15
lines changed

9 files changed

+107
-15
lines changed

README.md

+15-2
Original file line numberDiff line numberDiff line change
@@ -174,7 +174,7 @@ NODE_ENV=production ./test
174174
|---|---|---|
175175
|`context`|Must match webpack [`context`](https://webpack.github.io/docs/configuration.html#context) configuration. [`css-loader`](https://github.com/webpack/css-loader) inherits `context` values from webpack. Other CSS module implementations might use different context resolution logic.|`process.cwd()`|
176176
|`exclude`| a RegExp that will exclude otherwise included files e.g., to exclude all styles from node_modules `exclude: 'node_modules'`|
177-
|`filetypes`|Configure [postcss syntax loaders](https://github.com/postcss/postcss#syntaxes) like sugerss, LESS and SCSS. ||
177+
|`filetypes`|Configure [postcss syntax loaders](https://github.com/postcss/postcss#syntaxes) like sugerss, LESS and SCSS and extra plugins for them. ||
178178
|`generateScopedName`|Refer to [Generating scoped names](https://github.com/css-modules/postcss-modules#generating-scoped-names)|`[path]___[name]__[local]___[hash:base64:5]`|
179179
|`removeImport`|Remove the matching style import. This option is used to enable server-side rendering.|`false`|
180180
|`webpackHotModuleReloading`|Enables hot reloading of CSS in webpack|`false`|
@@ -198,7 +198,20 @@ To add support for different CSS syntaxes (e.g. SCSS), perform the following two
198198

199199
```json
200200
"filetypes": {
201-
".scss": "postcss-scss"
201+
".scss": {
202+
"syntax": "postcss-scss"
203+
}
204+
}
205+
```
206+
207+
And optionaly specify extra plugins
208+
209+
```json
210+
"filetypes": {
211+
".scss": {
212+
"syntax": "postcss-scss",
213+
"plugins": ["postcss-nested"]
214+
}
202215
}
203216
```
204217

package.json

+4-3
Original file line numberDiff line numberDiff line change
@@ -28,12 +28,13 @@
2828
"babel-preset-es2015": "^6.18.0",
2929
"eslint": "^3.11.1",
3030
"eslint-config-canonical": "^6.0.0",
31-
"flow-bin": "^0.37.4",
31+
"flow-bin": "^0.48.0",
3232
"husky": "^0.12.0",
3333
"mocha": "^3.2.0",
34-
"semantic-release": "^6.3.5",
3534
"postcss-less": "^0.15.0",
36-
"postcss-scss": "^0.4.0"
35+
"postcss-nested": "^1.0.1",
36+
"postcss-scss": "^0.4.0",
37+
"semantic-release": "^6.3.5"
3738
},
3839
"engines": {
3940
"node": ">4.0.0"

src/requireCssModule.js

+42-7
Original file line numberDiff line numberDiff line change
@@ -18,17 +18,47 @@ import type {
1818
StyleModuleMapType
1919
} from './types';
2020

21-
const getTokens = (runner, cssSourceFilePath: string, filetypes): StyleModuleMapType => {
21+
type FileTypeOptions = {|
22+
+syntax: string,
23+
// eslint-disable-next-line no-undef
24+
+plugins?: $ReadOnlyArray<string>
25+
|};
26+
27+
const getFiletypeOptions = (cssSourceFilePath: string, filetypes: {[key: string]: FileTypeOptions}): ?FileTypeOptions => {
2228
const extension = cssSourceFilePath.substr(cssSourceFilePath.lastIndexOf('.'));
23-
const syntax = filetypes[extension];
29+
const filetype = filetypes ? filetypes[extension] : null;
2430

31+
return filetype;
32+
};
33+
34+
const getSyntax = (filetypeOptions: FileTypeOptions): ?(Function|Object) => {
35+
if (!filetypeOptions) {
36+
return null;
37+
}
38+
39+
// eslint-disable-next-line import/no-dynamic-require, global-require
40+
return require(filetypeOptions.syntax);
41+
};
42+
43+
// eslint-disable-next-line no-undef
44+
const getExtraPlugins = (filetypeOptions: ?FileTypeOptions): $ReadOnlyArray<any> => {
45+
if (!filetypeOptions || !filetypeOptions.plugins) {
46+
return [];
47+
}
48+
49+
return filetypeOptions.plugins.map((plugin) => {
50+
// eslint-disable-next-line import/no-dynamic-require, global-require
51+
return require(plugin);
52+
});
53+
};
54+
55+
const getTokens = (runner, cssSourceFilePath: string, filetypeOptions: ?FileTypeOptions): StyleModuleMapType => {
2556
const options: Object = {
2657
from: cssSourceFilePath
2758
};
2859

29-
if (syntax) {
30-
// eslint-disable-next-line import/no-dynamic-require, global-require
31-
options.syntax = require(syntax);
60+
if (filetypeOptions) {
61+
options.syntax = getSyntax(filetypeOptions);
3262
}
3363

3464
const lazyResult = runner
@@ -58,14 +88,19 @@ export default (cssSourceFilePath: string, options: OptionsType): StyleModuleMap
5888
context: options.context || process.cwd()
5989
});
6090

91+
const filetypeOptions = getFiletypeOptions(cssSourceFilePath, options.filetypes);
92+
6193
const fetch = (to: string, from: string) => {
6294
const fromDirectoryPath = dirname(from);
6395
const toPath = resolve(fromDirectoryPath, to);
6496

65-
return getTokens(runner, toPath, options.filetypes);
97+
return getTokens(runner, toPath, filetypeOptions);
6698
};
6799

100+
const extraPlugins = getExtraPlugins(filetypeOptions);
101+
68102
const plugins = [
103+
...extraPlugins,
69104
Values,
70105
LocalByDefault,
71106
ExtractImports,
@@ -79,5 +114,5 @@ export default (cssSourceFilePath: string, options: OptionsType): StyleModuleMap
79114

80115
runner = postcss(plugins);
81116

82-
return getTokens(runner, cssSourceFilePath, options.filetypes);
117+
return getTokens(runner, cssSourceFilePath, filetypeOptions);
83118
};

src/schemas/optionsSchema.json

+14-2
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,19 @@
1111
"additionalProperties": false,
1212
"patternProperties": {
1313
"\\..*": {
14-
"type": "string"
14+
"type": "object",
15+
"additionalProperties": false,
16+
"properties": {
17+
"syntax": {
18+
"type": "string"
19+
},
20+
"plugins": {
21+
"type": "array",
22+
"items": {
23+
"type": "string"
24+
}
25+
}
26+
}
1527
}
1628
},
1729
"type": "object"
@@ -27,4 +39,4 @@
2739
}
2840
},
2941
"type": "object"
30-
}
42+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
import './bar.scss';
2+
3+
<div styleName="a_modified"></div>;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
.a {
2+
background-color: #ffffff;
3+
4+
&_modified {
5+
background-color: #000000;
6+
}
7+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
import './bar.scss';
2+
3+
<div className="bar__a_modified"></div>;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
{
2+
"plugins": [
3+
[
4+
"../../../../src",
5+
{
6+
"generateScopedName": "[name]__[local]",
7+
"filetypes": {
8+
".scss": {
9+
"syntax": "postcss-scss",
10+
"plugins": ["postcss-nested"]
11+
}
12+
}
13+
}
14+
]
15+
]
16+
}

test/fixtures/react-css-modules/resolves less stylesheets/options.json

+3-1
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,9 @@
55
{
66
"generateScopedName": "[name]__[local]",
77
"filetypes": {
8-
".less": "postcss-less"
8+
".less": {
9+
"syntax": "postcss-less"
10+
}
911
}
1012
}
1113
]

0 commit comments

Comments
 (0)