From 8b42dd56f94674d5fd4af12cac451a84588ce1db Mon Sep 17 00:00:00 2001 From: Mikhail Date: Mon, 12 Jun 2017 11:04:26 +0200 Subject: [PATCH 1/9] Implemented support for extra postcss plugins --- package.json | 5 +-- src/requireCssModule.js | 58 ++++++++++++++++++++++++++++------ src/schemas/optionsSchema.json | 22 +++++++++++-- 3 files changed, 72 insertions(+), 13 deletions(-) diff --git a/package.json b/package.json index 15bbd17..89bc6c8 100644 --- a/package.json +++ b/package.json @@ -31,9 +31,10 @@ "flow-bin": "^0.37.4", "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..cd82096 100644 --- a/src/requireCssModule.js +++ b/src/requireCssModule.js @@ -18,19 +18,54 @@ import type { StyleModuleMapType } from './types'; -const getTokens = (runner, cssSourceFilePath: string, filetypes): StyleModuleMapType => { +type FileTypeOptions = {| + syntax: string, + plugins: Array +|}; + +const getFiletypeOptions = (cssSourceFilePath: string, filetypes: Object): ?(string|FileTypeOptions) => { const extension = cssSourceFilePath.substr(cssSourceFilePath.lastIndexOf('.')); - const syntax = filetypes[extension]; + const filetype = filetypes ? filetypes[extension] : null; - const options: Object = { - from: cssSourceFilePath - }; + return filetype; +} + +const getSyntax = (filetypeOptions: ?(string|FileTypeOptions)) => { + if (!filetypeOptions) { + return null; + } + + if (typeof filetypeOptions === 'string') { + // eslint-disable-next-line import/no-dynamic-require, global-require + return require(filetypeOptions); + } + + if (typeof filetypeOptions === 'object') { + // eslint-disable-next-line import/no-dynamic-require, global-require + return require(filetypeOptions.syntax); + } +} - if (syntax) { +const getExtraPlugins = (filetypeOptions: ?(string|FileTypeOptions)): Array => { + console.log(filetypeOptions); + if (!filetypeOptions) { + return []; + } + + if (typeof filetypeOptions === 'object') { // eslint-disable-next-line import/no-dynamic-require, global-require - options.syntax = require(syntax); + return filetypeOptions.plugins.map(plugin => require(plugin)); } + return []; +} + +const getTokens = (runner, cssSourceFilePath: string, filetypeOptions: ?(string|FileTypeOptions)): StyleModuleMapType => { + const options: Object = { + from: cssSourceFilePath, + syntax: getSyntax(filetypeOptions) + }; + const lazyResult = runner .process(readFileSync(cssSourceFilePath, 'utf-8'), options); @@ -58,14 +93,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 +119,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..28b4e69 100644 --- a/src/schemas/optionsSchema.json +++ b/src/schemas/optionsSchema.json @@ -11,7 +11,25 @@ "additionalProperties": false, "patternProperties": { "\\..*": { - "type": "string" + "oneOf": [ + { + "type": "string" + }, + { + "type": "object", + "additionalProperties": false, + "properties": { + "syntax": { + "type": "string" + }, + "plugins": { + "type": "array", + "items": { "type": "string" }, + "minItems": 1 + } + } + } + ] } }, "type": "object" @@ -27,4 +45,4 @@ } }, "type": "object" -} +} \ No newline at end of file From e7a3fbbaebf6ecaffdc512f6304daa28171dd8c1 Mon Sep 17 00:00:00 2001 From: Mikhail Date: Mon, 12 Jun 2017 11:05:09 +0200 Subject: [PATCH 2/9] Added test for extra postcss plugins --- .../applies extra plugins/actual.js | 3 +++ .../applies extra plugins/bar.scss | 7 +++++++ .../applies extra plugins/expected.js | 3 +++ .../applies extra plugins/options.json | 16 ++++++++++++++++ 4 files changed, 29 insertions(+) create mode 100644 test/fixtures/react-css-modules/applies extra plugins/actual.js create mode 100644 test/fixtures/react-css-modules/applies extra plugins/bar.scss create mode 100644 test/fixtures/react-css-modules/applies extra plugins/expected.js create mode 100644 test/fixtures/react-css-modules/applies extra plugins/options.json 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"] + } + } + } + ] + ] +} From 819323ae39d82722cc0845f0bc829233794b5006 Mon Sep 17 00:00:00 2001 From: Mikhail Date: Mon, 12 Jun 2017 11:49:25 +0200 Subject: [PATCH 3/9] Fixed eslint issues --- src/requireCssModule.js | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/src/requireCssModule.js b/src/requireCssModule.js index cd82096..1abb44b 100644 --- a/src/requireCssModule.js +++ b/src/requireCssModule.js @@ -28,7 +28,7 @@ const getFiletypeOptions = (cssSourceFilePath: string, filetypes: Object): ?(str const filetype = filetypes ? filetypes[extension] : null; return filetype; -} +}; const getSyntax = (filetypeOptions: ?(string|FileTypeOptions)) => { if (!filetypeOptions) { @@ -44,21 +44,22 @@ const getSyntax = (filetypeOptions: ?(string|FileTypeOptions)) => { // eslint-disable-next-line import/no-dynamic-require, global-require return require(filetypeOptions.syntax); } -} + + return null; +}; const getExtraPlugins = (filetypeOptions: ?(string|FileTypeOptions)): Array => { - console.log(filetypeOptions); if (!filetypeOptions) { return []; } if (typeof filetypeOptions === 'object') { // eslint-disable-next-line import/no-dynamic-require, global-require - return filetypeOptions.plugins.map(plugin => require(plugin)); + return filetypeOptions.plugins.map((plugin) => { return require(plugin); }); } return []; -} +}; const getTokens = (runner, cssSourceFilePath: string, filetypeOptions: ?(string|FileTypeOptions)): StyleModuleMapType => { const options: Object = { From 9e1caa44ced8c0a3e08ad349aed6ce594431387b Mon Sep 17 00:00:00 2001 From: Mikhail Date: Mon, 12 Jun 2017 11:57:13 +0200 Subject: [PATCH 4/9] Another fix of eslint --- src/requireCssModule.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/requireCssModule.js b/src/requireCssModule.js index 1abb44b..ab46805 100644 --- a/src/requireCssModule.js +++ b/src/requireCssModule.js @@ -55,7 +55,9 @@ const getExtraPlugins = (filetypeOptions: ?(string|FileTypeOptions)): Array if (typeof filetypeOptions === 'object') { // eslint-disable-next-line import/no-dynamic-require, global-require - return filetypeOptions.plugins.map((plugin) => { return require(plugin); }); + return filetypeOptions.plugins.map((plugin) => { + return require(plugin); + }); } return []; From fd2142365319170523a776267b694620ec996ad5 Mon Sep 17 00:00:00 2001 From: Mikhail Date: Mon, 12 Jun 2017 12:21:42 +0200 Subject: [PATCH 5/9] Fixed eslint --- src/requireCssModule.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/requireCssModule.js b/src/requireCssModule.js index ab46805..c9dc279 100644 --- a/src/requireCssModule.js +++ b/src/requireCssModule.js @@ -54,8 +54,8 @@ const getExtraPlugins = (filetypeOptions: ?(string|FileTypeOptions)): Array } if (typeof filetypeOptions === 'object') { - // eslint-disable-next-line import/no-dynamic-require, global-require return filetypeOptions.plugins.map((plugin) => { + // eslint-disable-next-line import/no-dynamic-require, global-require return require(plugin); }); } From 95526346dbcad465b53cb339b817e2e145547510 Mon Sep 17 00:00:00 2001 From: Mikhail Date: Thu, 15 Jun 2017 09:18:18 +0200 Subject: [PATCH 6/9] Removed string type of FiletypeOptions --- src/requireCssModule.js | 48 ++++++++++++++-------------------- src/schemas/optionsSchema.json | 24 +++++++---------- 2 files changed, 29 insertions(+), 43 deletions(-) diff --git a/src/requireCssModule.js b/src/requireCssModule.js index c9dc279..049225a 100644 --- a/src/requireCssModule.js +++ b/src/requireCssModule.js @@ -19,56 +19,48 @@ import type { } from './types'; type FileTypeOptions = {| - syntax: string, - plugins: Array + +syntax: string, + // eslint-disable-next-line no-undef + +plugins?: $ReadOnlyArray |}; -const getFiletypeOptions = (cssSourceFilePath: string, filetypes: Object): ?(string|FileTypeOptions) => { +const getFiletypeOptions = (cssSourceFilePath: string, filetypes: {[key: string]: FileTypeOptions}): ?FileTypeOptions => { const extension = cssSourceFilePath.substr(cssSourceFilePath.lastIndexOf('.')); const filetype = filetypes ? filetypes[extension] : null; return filetype; }; -const getSyntax = (filetypeOptions: ?(string|FileTypeOptions)) => { +const getSyntax = (filetypeOptions: FileTypeOptions): ?(Function|Object) => { if (!filetypeOptions) { return null; } - if (typeof filetypeOptions === 'string') { - // eslint-disable-next-line import/no-dynamic-require, global-require - return require(filetypeOptions); - } - - if (typeof filetypeOptions === 'object') { - // eslint-disable-next-line import/no-dynamic-require, global-require - return require(filetypeOptions.syntax); - } - - return null; + // eslint-disable-next-line import/no-dynamic-require, global-require + return require(filetypeOptions.syntax); }; -const getExtraPlugins = (filetypeOptions: ?(string|FileTypeOptions)): Array => { - if (!filetypeOptions) { +// eslint-disable-next-line no-undef +const getExtraPlugins = (filetypeOptions: ?FileTypeOptions): $ReadOnlyArray => { + if (!filetypeOptions || !filetypeOptions.plugins) { return []; } - if (typeof filetypeOptions === 'object') { - return filetypeOptions.plugins.map((plugin) => { - // eslint-disable-next-line import/no-dynamic-require, global-require - return require(plugin); - }); - } - - 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: ?(string|FileTypeOptions)): StyleModuleMapType => { +const getTokens = (runner, cssSourceFilePath: string, filetypeOptions: ?FileTypeOptions): StyleModuleMapType => { const options: Object = { - from: cssSourceFilePath, - syntax: getSyntax(filetypeOptions) + from: cssSourceFilePath }; + if (filetypeOptions) { + options.syntax = getSyntax(filetypeOptions); + } + const lazyResult = runner .process(readFileSync(cssSourceFilePath, 'utf-8'), options); diff --git a/src/schemas/optionsSchema.json b/src/schemas/optionsSchema.json index 28b4e69..440a1d5 100644 --- a/src/schemas/optionsSchema.json +++ b/src/schemas/optionsSchema.json @@ -11,25 +11,19 @@ "additionalProperties": false, "patternProperties": { "\\..*": { - "oneOf": [ - { + "type": "object", + "additionalProperties": false, + "properties": { + "syntax": { "type": "string" }, - { - "type": "object", - "additionalProperties": false, - "properties": { - "syntax": { - "type": "string" - }, - "plugins": { - "type": "array", - "items": { "type": "string" }, - "minItems": 1 - } + "plugins": { + "type": "array", + "items": { + "type": "string" } } - ] + } } }, "type": "object" From a77f1a401293a56c3e888ea2ec6595f740252e7f Mon Sep 17 00:00:00 2001 From: Mikhail Date: Thu, 15 Jun 2017 09:18:43 +0200 Subject: [PATCH 7/9] Updated test to use latest syntax --- .../react-css-modules/resolves less stylesheets/options.json | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) 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" + } } } ] From d93adf454da59012476a301d14da0c825a519aa1 Mon Sep 17 00:00:00 2001 From: Mikhail Date: Thu, 15 Jun 2017 09:19:38 +0200 Subject: [PATCH 8/9] BREAKING CHANGE: Filetype is now an object with properties 'syntax' and 'plugins' --- README.md | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) 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"] + } } ``` From 97323c1fa2ec0e108fd492a9a8e6ceb9e1f4e9f0 Mon Sep 17 00:00:00 2001 From: Mikhail Date: Thu, 15 Jun 2017 09:48:27 +0200 Subject: [PATCH 9/9] Updated flow to 0.48.0 to use $ReadOnlyArray --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 89bc6c8..017dac9 100644 --- a/package.json +++ b/package.json @@ -28,7 +28,7 @@ "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", "postcss-less": "^0.15.0",