diff --git a/README.md b/README.md index 132e674..fa92add 100644 --- a/README.md +++ b/README.md @@ -174,7 +174,7 @@ NODE_ENV=production ./test |---|---|---| |`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()`| |`exclude`| a RegExp that will exclude otherwise included files e.g., to exclude all styles from node_modules `exclude: 'node_modules'`| -|`filetypes`|Configure [postcss syntax loaders](https://github.com/postcss/postcss#syntaxes) like sugerss, LESS and SCSS. || +|`filetypes`|Configure [postcss syntax loaders](https://github.com/postcss/postcss#syntaxes) like sugerss, LESS and SCSS and extra plugins for them. || |`generateScopedName`|Refer to [Generating scoped names](https://github.com/css-modules/postcss-modules#generating-scoped-names)|`[path]___[name]__[local]___[hash:base64:5]`| |`removeImport`|Remove the matching style import. This option is used to enable server-side rendering.|`false`| |`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 ```json "filetypes": { - ".scss": "postcss-scss" + ".scss": { + "syntax": "postcss-scss" + } + } + ``` + + And optionaly specify extra plugins + + ```json + "filetypes": { + ".scss": { + "syntax": "postcss-scss", + "plugins": ["postcss-nested"] + } } ``` diff --git a/package.json b/package.json index 15bbd17..017dac9 100644 --- a/package.json +++ b/package.json @@ -28,12 +28,13 @@ "babel-preset-es2015": "^6.18.0", "eslint": "^3.11.1", "eslint-config-canonical": "^6.0.0", - "flow-bin": "^0.37.4", + "flow-bin": "^0.48.0", "husky": "^0.12.0", "mocha": "^3.2.0", - "semantic-release": "^6.3.5", "postcss-less": "^0.15.0", - "postcss-scss": "^0.4.0" + "postcss-nested": "^1.0.1", + "postcss-scss": "^0.4.0", + "semantic-release": "^6.3.5" }, "engines": { "node": ">4.0.0" diff --git a/src/requireCssModule.js b/src/requireCssModule.js index 28e8703..049225a 100644 --- a/src/requireCssModule.js +++ b/src/requireCssModule.js @@ -18,17 +18,47 @@ import type { StyleModuleMapType } from './types'; -const getTokens = (runner, cssSourceFilePath: string, filetypes): StyleModuleMapType => { +type FileTypeOptions = {| + +syntax: string, + // eslint-disable-next-line no-undef + +plugins?: $ReadOnlyArray +|}; + +const getFiletypeOptions = (cssSourceFilePath: string, filetypes: {[key: string]: FileTypeOptions}): ?FileTypeOptions => { const extension = cssSourceFilePath.substr(cssSourceFilePath.lastIndexOf('.')); - const syntax = filetypes[extension]; + const filetype = filetypes ? filetypes[extension] : null; + return filetype; +}; + +const getSyntax = (filetypeOptions: FileTypeOptions): ?(Function|Object) => { + if (!filetypeOptions) { + return null; + } + + // eslint-disable-next-line import/no-dynamic-require, global-require + return require(filetypeOptions.syntax); +}; + +// eslint-disable-next-line no-undef +const getExtraPlugins = (filetypeOptions: ?FileTypeOptions): $ReadOnlyArray => { + if (!filetypeOptions || !filetypeOptions.plugins) { + return []; + } + + return filetypeOptions.plugins.map((plugin) => { + // eslint-disable-next-line import/no-dynamic-require, global-require + return require(plugin); + }); +}; + +const getTokens = (runner, cssSourceFilePath: string, filetypeOptions: ?FileTypeOptions): StyleModuleMapType => { const options: Object = { from: cssSourceFilePath }; - if (syntax) { - // eslint-disable-next-line import/no-dynamic-require, global-require - options.syntax = require(syntax); + if (filetypeOptions) { + options.syntax = getSyntax(filetypeOptions); } const lazyResult = runner @@ -58,14 +88,19 @@ export default (cssSourceFilePath: string, options: OptionsType): StyleModuleMap context: options.context || process.cwd() }); + const filetypeOptions = getFiletypeOptions(cssSourceFilePath, options.filetypes); + const fetch = (to: string, from: string) => { const fromDirectoryPath = dirname(from); const toPath = resolve(fromDirectoryPath, to); - return getTokens(runner, toPath, options.filetypes); + return getTokens(runner, toPath, filetypeOptions); }; + const extraPlugins = getExtraPlugins(filetypeOptions); + const plugins = [ + ...extraPlugins, Values, LocalByDefault, ExtractImports, @@ -79,5 +114,5 @@ export default (cssSourceFilePath: string, options: OptionsType): StyleModuleMap runner = postcss(plugins); - return getTokens(runner, cssSourceFilePath, options.filetypes); + return getTokens(runner, cssSourceFilePath, filetypeOptions); }; diff --git a/src/schemas/optionsSchema.json b/src/schemas/optionsSchema.json index c97851c..440a1d5 100644 --- a/src/schemas/optionsSchema.json +++ b/src/schemas/optionsSchema.json @@ -11,7 +11,19 @@ "additionalProperties": false, "patternProperties": { "\\..*": { - "type": "string" + "type": "object", + "additionalProperties": false, + "properties": { + "syntax": { + "type": "string" + }, + "plugins": { + "type": "array", + "items": { + "type": "string" + } + } + } } }, "type": "object" @@ -27,4 +39,4 @@ } }, "type": "object" -} +} \ No newline at end of file diff --git a/test/fixtures/react-css-modules/applies extra plugins/actual.js b/test/fixtures/react-css-modules/applies extra plugins/actual.js new file mode 100644 index 0000000..b4ccea8 --- /dev/null +++ b/test/fixtures/react-css-modules/applies extra plugins/actual.js @@ -0,0 +1,3 @@ +import './bar.scss'; + +
; diff --git a/test/fixtures/react-css-modules/applies extra plugins/bar.scss b/test/fixtures/react-css-modules/applies extra plugins/bar.scss new file mode 100644 index 0000000..f69b886 --- /dev/null +++ b/test/fixtures/react-css-modules/applies extra plugins/bar.scss @@ -0,0 +1,7 @@ +.a { + background-color: #ffffff; + + &_modified { + background-color: #000000; + } +} diff --git a/test/fixtures/react-css-modules/applies extra plugins/expected.js b/test/fixtures/react-css-modules/applies extra plugins/expected.js new file mode 100644 index 0000000..417f11d --- /dev/null +++ b/test/fixtures/react-css-modules/applies extra plugins/expected.js @@ -0,0 +1,3 @@ +import './bar.scss'; + +
; diff --git a/test/fixtures/react-css-modules/applies extra plugins/options.json b/test/fixtures/react-css-modules/applies extra plugins/options.json new file mode 100644 index 0000000..0f5bc23 --- /dev/null +++ b/test/fixtures/react-css-modules/applies extra plugins/options.json @@ -0,0 +1,16 @@ +{ + "plugins": [ + [ + "../../../../src", + { + "generateScopedName": "[name]__[local]", + "filetypes": { + ".scss": { + "syntax": "postcss-scss", + "plugins": ["postcss-nested"] + } + } + } + ] + ] +} diff --git a/test/fixtures/react-css-modules/resolves less stylesheets/options.json b/test/fixtures/react-css-modules/resolves less stylesheets/options.json index 903f779..7df8c1a 100644 --- a/test/fixtures/react-css-modules/resolves less stylesheets/options.json +++ b/test/fixtures/react-css-modules/resolves less stylesheets/options.json @@ -5,7 +5,9 @@ { "generateScopedName": "[name]__[local]", "filetypes": { - ".less": "postcss-less" + ".less": { + "syntax": "postcss-less" + } } } ]