From 70bf2da7087650790c788707c230af5c333d5796 Mon Sep 17 00:00:00 2001 From: Ian VanSchooten Date: Wed, 19 Jul 2017 10:59:36 -0400 Subject: [PATCH 01/10] Add .eslintignore for test fixtures The double quotes that the plugin adds go against the linting config of the project, and editors that auto-fix linting rules will try to convert quotes in `expected.js` files to single quotes, causing tests to fail. It seems safest to ignore all fixture files from linting. --- .eslintignore | 1 + .gitignore | 1 + 2 files changed, 2 insertions(+) create mode 100644 .eslintignore diff --git a/.eslintignore b/.eslintignore new file mode 100644 index 0000000..66e9ef6 --- /dev/null +++ b/.eslintignore @@ -0,0 +1 @@ +/test/fixtures diff --git a/.gitignore b/.gitignore index 214d437..e944420 100755 --- a/.gitignore +++ b/.gitignore @@ -6,6 +6,7 @@ node_modules !.babelrc !.editorconfig !.eslintrc +!.eslintignore !.flowconfig !.gitignore !.npmignore From dad14782864b27df754f6b9ba1932308933d5d20 Mon Sep 17 00:00:00 2001 From: Ian VanSchooten Date: Wed, 19 Jul 2017 11:49:54 -0400 Subject: [PATCH 02/10] Allow createObjectExpression to take a boolean --- src/createObjectExpression.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/createObjectExpression.js b/src/createObjectExpression.js index d5e8c07..247b51f 100644 --- a/src/createObjectExpression.js +++ b/src/createObjectExpression.js @@ -5,7 +5,7 @@ import BabelTypes, { } from 'babel-types'; type InputObjectType = { - [key: string]: string | InputObjectType + [key: string]: string | boolean | InputObjectType }; /** @@ -26,6 +26,8 @@ const createObjectExpression = (t: BabelTypes, object: InputObjectType): ObjectE newValue = t.stringLiteral(value); } else if (typeof value === 'object') { newValue = createObjectExpression(t, value); + } else if (typeof value === 'boolean') { + newValue = t.booleanLiteral(value); } else { throw new Error('Unexpected type.'); } From 48ccc245f724932a312f74ea49447951c508f370 Mon Sep 17 00:00:00 2001 From: Ian VanSchooten Date: Wed, 19 Jul 2017 11:55:58 -0400 Subject: [PATCH 03/10] Allow getClassName to take an options argument The only option is to silence styleName errors, for example in production. If `silenceStyleNameErrors` is true, the styleNames which cannot be found will simply be removed from the resulting classNames, matching the behavior of `react-css-modules`. This commit alone will not result in a change in behavior, the next steps will be to pass in the option from the babel-plugin. --- src/getClassName.js | 35 ++++++++++++++++++++++++++++------- 1 file changed, 28 insertions(+), 7 deletions(-) diff --git a/src/getClassName.js b/src/getClassName.js index 2b5b53f..5758462 100644 --- a/src/getClassName.js +++ b/src/getClassName.js @@ -9,7 +9,7 @@ const isNamespacedStyleName = (styleName: string): boolean => { return styleName.indexOf('.') !== -1; }; -const getClassNameForNamespacedStyleName = (styleName: string, styleModuleImportMap: StyleModuleImportMapType): string => { +const getClassNameForNamespacedStyleName = (styleName: string, styleModuleImportMap: StyleModuleImportMapType, silenceErrors: boolean): ?string => { // Note: // Do not use the desctructing syntax with Babel. // Desctructing adds _slicedToArray helper. @@ -18,22 +18,39 @@ const getClassNameForNamespacedStyleName = (styleName: string, styleModuleImport const moduleName = styleNameParts[1]; if (!moduleName) { - throw new Error('Invalid style name.'); + if (silenceErrors) { + return null; + } else { + throw new Error('Invalid style name.'); + } } if (!styleModuleImportMap[importName]) { - throw new Error('CSS module import does not exist.'); + if (silenceErrors) { + return null; + } else { + throw new Error('CSS module import does not exist.'); + } } if (!styleModuleImportMap[importName][moduleName]) { - throw new Error('CSS module does not exist.'); + if (silenceErrors) { + return null; + } else { + throw new Error('CSS module does not exist.'); + } } return styleModuleImportMap[importName][moduleName]; }; -export default (styleNameValue: string, styleModuleImportMap: StyleModuleImportMapType): string => { +type OptionsType = {| + silenceStyleNameErrors: boolean +|}; + +export default (styleNameValue: string, styleModuleImportMap: StyleModuleImportMapType, options?: OptionsType): string => { const styleModuleImportMapKeys = Object.keys(styleModuleImportMap); + const silenceStyleNameErrors = Boolean(options && options.silenceStyleNameErrors); return styleNameValue .split(' ') @@ -42,7 +59,7 @@ export default (styleNameValue: string, styleModuleImportMap: StyleModuleImportM }) .map((styleName) => { if (isNamespacedStyleName(styleName)) { - return getClassNameForNamespacedStyleName(styleName, styleModuleImportMap); + return getClassNameForNamespacedStyleName(styleName, styleModuleImportMap, silenceStyleNameErrors); } if (styleModuleImportMapKeys.length === 0) { @@ -55,11 +72,15 @@ export default (styleNameValue: string, styleModuleImportMap: StyleModuleImportM const styleModuleMap: StyleModuleMapType = styleModuleImportMap[styleModuleImportMapKeys[0]]; - if (!styleModuleMap[styleName]) { + if (!styleModuleMap[styleName] && !silenceStyleNameErrors) { throw new Error('Could not resolve the styleName \'' + styleName + '\'.'); } return styleModuleMap[styleName]; }) + .filter((className) => { + // Remove any styles which could not be found (if silenceStyleNameErrors) + return className; + }) .join(' '); }; From 657aa8a02f862ecb62bcc0ece22a340800262bd9 Mon Sep 17 00:00:00 2001 From: Ian VanSchooten Date: Wed, 19 Jul 2017 11:56:20 -0400 Subject: [PATCH 04/10] Add `silenceStyleNameErrors` to options schema --- src/schemas/optionsSchema.json | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/schemas/optionsSchema.json b/src/schemas/optionsSchema.json index ad6189f..92cbe11 100644 --- a/src/schemas/optionsSchema.json +++ b/src/schemas/optionsSchema.json @@ -43,6 +43,9 @@ }, "webpackHotModuleReloading": { "type": "boolean" + }, + "silenceStyleNameErrors": { + "type": "boolean" } }, "type": "object" From c59bba52573c0404ecdb6d26a84006cbf70309c7 Mon Sep 17 00:00:00 2001 From: Ian VanSchooten Date: Wed, 19 Jul 2017 12:00:59 -0400 Subject: [PATCH 05/10] Pass silenceStyleNameErrors to getClassName This hooks up the options set by the user into getClassName, so that it has an effect. However, getClassName can also be used at runtime, which this commit does not address. --- src/index.js | 3 ++- src/resolveStringLiteral.js | 8 ++++++-- .../actual.js | 3 +++ .../expected.js | 3 +++ .../foo.css | 1 + .../options.json | 11 +++++++++++ 6 files changed, 26 insertions(+), 3 deletions(-) create mode 100644 test/fixtures/react-css-modules/does not throw error for stylename not found when silenceStyleNameErrors is true/actual.js create mode 100644 test/fixtures/react-css-modules/does not throw error for stylename not found when silenceStyleNameErrors is true/expected.js create mode 100644 test/fixtures/react-css-modules/does not throw error for stylename not found when silenceStyleNameErrors is true/foo.css create mode 100644 test/fixtures/react-css-modules/does not throw error for stylename not found when silenceStyleNameErrors is true/options.json diff --git a/src/index.js b/src/index.js index 5a50c76..8135bfa 100644 --- a/src/index.js +++ b/src/index.js @@ -187,7 +187,8 @@ export default ({ resolveStringLiteral( path, filenameMap[filename].styleModuleImportMap, - styleNameAttribute + styleNameAttribute, + {silenceStyleNameErrors: Boolean(stats.opts.silenceStyleNameErrors)} ); return; diff --git a/src/resolveStringLiteral.js b/src/resolveStringLiteral.js index fd60fe4..c2add9e 100644 --- a/src/resolveStringLiteral.js +++ b/src/resolveStringLiteral.js @@ -12,16 +12,20 @@ import type { StyleModuleImportMapType } from './types'; +type OptionsType = {| + silenceStyleNameErrors: boolean +|}; + /** * Updates the className value of a JSX element using a provided styleName attribute. */ -export default (path: *, styleModuleImportMap: StyleModuleImportMapType, styleNameAttribute: JSXAttribute): void => { +export default (path: *, styleModuleImportMap: StyleModuleImportMapType, styleNameAttribute: JSXAttribute, options: OptionsType): void => { const classNameAttribute = path.node.openingElement.attributes .find((attribute) => { return typeof attribute.name !== 'undefined' && attribute.name.name === 'className'; }); - const resolvedStyleName = getClassName(styleNameAttribute.value.value, styleModuleImportMap); + const resolvedStyleName = getClassName(styleNameAttribute.value.value, styleModuleImportMap, options); if (classNameAttribute) { if (isStringLiteral(classNameAttribute.value)) { diff --git a/test/fixtures/react-css-modules/does not throw error for stylename not found when silenceStyleNameErrors is true/actual.js b/test/fixtures/react-css-modules/does not throw error for stylename not found when silenceStyleNameErrors is true/actual.js new file mode 100644 index 0000000..a5749ad --- /dev/null +++ b/test/fixtures/react-css-modules/does not throw error for stylename not found when silenceStyleNameErrors is true/actual.js @@ -0,0 +1,3 @@ +import "./foo.css"; + +
; diff --git a/test/fixtures/react-css-modules/does not throw error for stylename not found when silenceStyleNameErrors is true/expected.js b/test/fixtures/react-css-modules/does not throw error for stylename not found when silenceStyleNameErrors is true/expected.js new file mode 100644 index 0000000..a25d27a --- /dev/null +++ b/test/fixtures/react-css-modules/does not throw error for stylename not found when silenceStyleNameErrors is true/expected.js @@ -0,0 +1,3 @@ +import "./foo.css"; + +
; diff --git a/test/fixtures/react-css-modules/does not throw error for stylename not found when silenceStyleNameErrors is true/foo.css b/test/fixtures/react-css-modules/does not throw error for stylename not found when silenceStyleNameErrors is true/foo.css new file mode 100644 index 0000000..5512dae --- /dev/null +++ b/test/fixtures/react-css-modules/does not throw error for stylename not found when silenceStyleNameErrors is true/foo.css @@ -0,0 +1 @@ +.a {} diff --git a/test/fixtures/react-css-modules/does not throw error for stylename not found when silenceStyleNameErrors is true/options.json b/test/fixtures/react-css-modules/does not throw error for stylename not found when silenceStyleNameErrors is true/options.json new file mode 100644 index 0000000..70362a1 --- /dev/null +++ b/test/fixtures/react-css-modules/does not throw error for stylename not found when silenceStyleNameErrors is true/options.json @@ -0,0 +1,11 @@ +{ + "plugins": [ + [ + "../../../../src", + { + "generateScopedName": "[name]__[local]", + "silenceStyleNameErrors": true + } + ] + ] +} From 3c5f4ddf4124fc8fd012726920819107870384e2 Mon Sep 17 00:00:00 2001 From: Ian VanSchooten Date: Wed, 19 Jul 2017 12:08:07 -0400 Subject: [PATCH 06/10] Provide silenceStyleNameErrors at runtime This commit adds the options object for getClassName iif silenceStyleNameErrors is true. By not always including the options object, it helps to save a few characters in the generated code. --- src/index.js | 3 ++- src/replaceJsxExpressionContainer.js | 22 ++++++++++++++----- .../actual.js | 5 +++++ .../expected.js | 13 +++++++++++ .../foo.css | 1 + .../options.json | 11 ++++++++++ 6 files changed, 49 insertions(+), 6 deletions(-) create mode 100644 test/fixtures/react-css-modules/provides silenceStyleNameErrors to getClassName at runtime/actual.js create mode 100644 test/fixtures/react-css-modules/provides silenceStyleNameErrors to getClassName at runtime/expected.js create mode 100644 test/fixtures/react-css-modules/provides silenceStyleNameErrors to getClassName at runtime/foo.css create mode 100644 test/fixtures/react-css-modules/provides silenceStyleNameErrors to getClassName at runtime/options.json diff --git a/src/index.js b/src/index.js index 8135bfa..233a2f2 100644 --- a/src/index.js +++ b/src/index.js @@ -203,7 +203,8 @@ export default ({ path, styleNameAttribute, filenameMap[filename].importedHelperIndentifier, - filenameMap[filename].styleModuleImportMapIdentifier + filenameMap[filename].styleModuleImportMapIdentifier, + {silenceStyleNameErrors: Boolean(stats.opts.silenceStyleNameErrors)} ); } }, diff --git a/src/replaceJsxExpressionContainer.js b/src/replaceJsxExpressionContainer.js index 4b70fac..fd9dc7e 100644 --- a/src/replaceJsxExpressionContainer.js +++ b/src/replaceJsxExpressionContainer.js @@ -10,6 +10,11 @@ import BabelTypes, { jSXIdentifier } from 'babel-types'; import conditionalClassMerge from './conditionalClassMerge'; +import createObjectExpression from './createObjectExpression'; + +type OptionsType = {| + silenceStyleNameErrors: boolean +|}; export default ( t: BabelTypes, @@ -17,7 +22,8 @@ export default ( path: Object, styleNameAttribute: JSXAttribute, importedHelperIndentifier: Identifier, - styleModuleImportMapIdentifier: Identifier + styleModuleImportMapIdentifier: Identifier, + options: OptionsType ): void => { const expressionContainerValue = styleNameAttribute.value; const classNameAttribute = path.node.openingElement.attributes @@ -31,12 +37,18 @@ export default ( path.node.openingElement.attributes.splice(path.node.openingElement.attributes.indexOf(styleNameAttribute), 1); + const args = [ + expressionContainerValue.expression, + styleModuleImportMapIdentifier + ]; + + if (options.silenceStyleNameErrors) { + args.push(createObjectExpression(t, options)); + } + const styleNameExpression = t.callExpression( importedHelperIndentifier, - [ - expressionContainerValue.expression, - styleModuleImportMapIdentifier - ] + args ); if (classNameAttribute) { diff --git a/test/fixtures/react-css-modules/provides silenceStyleNameErrors to getClassName at runtime/actual.js b/test/fixtures/react-css-modules/provides silenceStyleNameErrors to getClassName at runtime/actual.js new file mode 100644 index 0000000..e0bf2e7 --- /dev/null +++ b/test/fixtures/react-css-modules/provides silenceStyleNameErrors to getClassName at runtime/actual.js @@ -0,0 +1,5 @@ +import './foo.css'; + +const styleNameFoo = 'a-c'; + +
; diff --git a/test/fixtures/react-css-modules/provides silenceStyleNameErrors to getClassName at runtime/expected.js b/test/fixtures/react-css-modules/provides silenceStyleNameErrors to getClassName at runtime/expected.js new file mode 100644 index 0000000..a127586 --- /dev/null +++ b/test/fixtures/react-css-modules/provides silenceStyleNameErrors to getClassName at runtime/expected.js @@ -0,0 +1,13 @@ +import _getClassName from 'babel-plugin-react-css-modules/dist/browser/getClassName'; +import './foo.css'; + +const _styleModuleImportMap = { + './foo.css': { + 'a-b': 'foo__a-b' + } +}; +const styleNameFoo = 'a-c'; + +
; diff --git a/test/fixtures/react-css-modules/provides silenceStyleNameErrors to getClassName at runtime/foo.css b/test/fixtures/react-css-modules/provides silenceStyleNameErrors to getClassName at runtime/foo.css new file mode 100644 index 0000000..794d0af --- /dev/null +++ b/test/fixtures/react-css-modules/provides silenceStyleNameErrors to getClassName at runtime/foo.css @@ -0,0 +1 @@ +.a-b {} diff --git a/test/fixtures/react-css-modules/provides silenceStyleNameErrors to getClassName at runtime/options.json b/test/fixtures/react-css-modules/provides silenceStyleNameErrors to getClassName at runtime/options.json new file mode 100644 index 0000000..70362a1 --- /dev/null +++ b/test/fixtures/react-css-modules/provides silenceStyleNameErrors to getClassName at runtime/options.json @@ -0,0 +1,11 @@ +{ + "plugins": [ + [ + "../../../../src", + { + "generateScopedName": "[name]__[local]", + "silenceStyleNameErrors": true + } + ] + ] +} From 449eac16a58b7ab40bf0ff549dac2a0d4dea6835 Mon Sep 17 00:00:00 2001 From: Ian VanSchooten Date: Wed, 19 Jul 2017 12:16:53 -0400 Subject: [PATCH 07/10] Restructure Configuration docs Seeing the flow types first thing in the Configuration section was confusing to new users, so this adds a brief example of how to set babel plugin options, and moves the flow types underneath the table of options. --- README.md | 43 ++++++++++++++++++++++++++++++------------- 1 file changed, 30 insertions(+), 13 deletions(-) diff --git a/README.md b/README.md index 9d8fee0..0717ade 100644 --- a/README.md +++ b/README.md @@ -170,22 +170,21 @@ NODE_ENV=production ./test ## Configuration -```js -type FiletypeOptionsType = {| - +syntax: string, - +plugins?: $ReadOnlyArray -|}; - -type FiletypesConfigurationType = { - [key: string]: FiletypeOptionsType -}; - -type GenerateScopedNameType = (localName: string, resourcePath: string) => string; - -type GenerateScopedNameConfigurationType = GenerateScopedNameType | string; +Configure the options for the plugin within your `.babelrc` as follows: + +```json +{ + "plugins": [ + ["react-css-modules", { + "option": "value" + }] + ] +} ``` +### Options + |Name|Type|Description|Default| |---|---|---|---| |`context`|`string`|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()`| @@ -200,6 +199,24 @@ Missing a configuration? [Raise an issue](https://github.com/gajus/babel-plugin- > Note: > The default configuration should work out of the box with the [css-loader](https://github.com/webpack/css-loader). +#### Option types (flow) + +```js +type FiletypeOptionsType = {| + +syntax: string, + +plugins?: $ReadOnlyArray +|}; + +type FiletypesConfigurationType = { + [key: string]: FiletypeOptionsType +}; + +type GenerateScopedNameType = (localName: string, resourcePath: string) => string; + +type GenerateScopedNameConfigurationType = GenerateScopedNameType | string; + +``` + ### Configurate syntax loaders To add support for different CSS syntaxes (e.g. SCSS), perform the following two steps: From 55334784fbb9b0ecb2c1e64c240ad168bd63a55c Mon Sep 17 00:00:00 2001 From: Ian VanSchooten Date: Wed, 19 Jul 2017 12:20:48 -0400 Subject: [PATCH 08/10] Add silenceStyleNameErrors to README --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 0717ade..0c1525d 100644 --- a/README.md +++ b/README.md @@ -193,6 +193,7 @@ Configure the options for the plugin within your `.babelrc` as follows: |`generateScopedName`|`?GenerateScopedNameConfigurationType`|Refer to [Generating scoped names](https://github.com/css-modules/postcss-modules#generating-scoped-names)|`[path]___[name]__[local]___[hash:base64:5]`| |`removeImport`|`boolean`|Remove the matching style import. This option is used to enable server-side rendering.|`false`| |`webpackHotModuleReloading`|`boolean`|Enables hot reloading of CSS in webpack|`false`| +|`silenceStyleNameErrors`|`boolean`|Prevents errors being thrown for undefined CSS modules (using a `styleName` for which there is no CSS module defined). Setting this option to `true` is equivalent to setting `errorWhenNotFound: false` in [react-css-modules](https://github.com/gajus/react-css-modules#errorwhennotfound). |`false`| Missing a configuration? [Raise an issue](https://github.com/gajus/babel-plugin-react-css-modules/issues/new?title=New%20configuration:). From a194433018e074e0c0869d6ec96ee095259e4d2c Mon Sep 17 00:00:00 2001 From: Ian VanSchooten Date: Wed, 19 Jul 2017 13:09:19 -0400 Subject: [PATCH 09/10] Loosen InputObjectType MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Flow was already not happy with using `InputObjectType` within its own definition, and was converting it to `{[key: string]: string | any}` when I inspected it in Nuclide. I’m honestly not sure how to specify a union type on an indexer as was being attempted, because flow complains as soon as you try to provide an object that has a property with a typed value. --- src/createObjectExpression.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/createObjectExpression.js b/src/createObjectExpression.js index 247b51f..1654911 100644 --- a/src/createObjectExpression.js +++ b/src/createObjectExpression.js @@ -5,7 +5,7 @@ import BabelTypes, { } from 'babel-types'; type InputObjectType = { - [key: string]: string | boolean | InputObjectType + [key: string]: * }; /** From 185e1ce71a39f7db8dce14c68832fb66c26d3270 Mon Sep 17 00:00:00 2001 From: Ian VanSchooten Date: Sat, 19 Aug 2017 22:43:28 -0400 Subject: [PATCH 10/10] Allow more options to handle missing styleNames MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This changes the option from a boolean “silenceStyleNameErrors” to an enum “handleMissingStyleName” option, with possible values of “throw” (default), “warn”, and “ignore”. --- README.md | 2 +- src/getClassName.js | 57 +++++++++++++------ src/index.js | 5 +- src/replaceJsxExpressionContainer.js | 10 +++- src/resolveStringLiteral.js | 5 +- src/schemas/optionsDefaults.js | 5 ++ src/schemas/optionsSchema.json | 4 +- src/types.js | 2 + .../actual.js | 0 .../expected.js | 0 .../foo.css | 0 .../options.json | 2 +- .../actual.js | 0 .../expected.js | 2 +- .../foo.css | 0 .../options.json | 2 +- 16 files changed, 66 insertions(+), 30 deletions(-) create mode 100644 src/schemas/optionsDefaults.js rename test/fixtures/react-css-modules/{does not throw error for stylename not found when silenceStyleNameErrors is true => does not throw error for stylename not found when handleMissingStyleName is warn}/actual.js (100%) rename test/fixtures/react-css-modules/{does not throw error for stylename not found when silenceStyleNameErrors is true => does not throw error for stylename not found when handleMissingStyleName is warn}/expected.js (100%) rename test/fixtures/react-css-modules/{does not throw error for stylename not found when silenceStyleNameErrors is true => does not throw error for stylename not found when handleMissingStyleName is warn}/foo.css (100%) rename test/fixtures/react-css-modules/{does not throw error for stylename not found when silenceStyleNameErrors is true => does not throw error for stylename not found when handleMissingStyleName is warn}/options.json (74%) rename test/fixtures/react-css-modules/{provides silenceStyleNameErrors to getClassName at runtime => provides handleMissingStyleName to getClassName at runtime}/actual.js (100%) rename test/fixtures/react-css-modules/{provides silenceStyleNameErrors to getClassName at runtime => provides handleMissingStyleName to getClassName at runtime}/expected.js (88%) rename test/fixtures/react-css-modules/{provides silenceStyleNameErrors to getClassName at runtime => provides handleMissingStyleName to getClassName at runtime}/foo.css (100%) rename test/fixtures/react-css-modules/{provides silenceStyleNameErrors to getClassName at runtime => provides handleMissingStyleName to getClassName at runtime}/options.json (74%) diff --git a/README.md b/README.md index 0c1525d..6dbe7ec 100644 --- a/README.md +++ b/README.md @@ -193,7 +193,7 @@ Configure the options for the plugin within your `.babelrc` as follows: |`generateScopedName`|`?GenerateScopedNameConfigurationType`|Refer to [Generating scoped names](https://github.com/css-modules/postcss-modules#generating-scoped-names)|`[path]___[name]__[local]___[hash:base64:5]`| |`removeImport`|`boolean`|Remove the matching style import. This option is used to enable server-side rendering.|`false`| |`webpackHotModuleReloading`|`boolean`|Enables hot reloading of CSS in webpack|`false`| -|`silenceStyleNameErrors`|`boolean`|Prevents errors being thrown for undefined CSS modules (using a `styleName` for which there is no CSS module defined). Setting this option to `true` is equivalent to setting `errorWhenNotFound: false` in [react-css-modules](https://github.com/gajus/react-css-modules#errorwhennotfound). |`false`| +|`handleMissingStyleName`|`"throw"`, `"warn"`, `"ignore"`|Determines what should be done for undefined CSS modules (using a `styleName` for which there is no CSS module defined). Setting this option to `"ignore"` is equivalent to setting `errorWhenNotFound: false` in [react-css-modules](https://github.com/gajus/react-css-modules#errorwhennotfound). |`"throw"`| Missing a configuration? [Raise an issue](https://github.com/gajus/babel-plugin-react-css-modules/issues/new?title=New%20configuration:). diff --git a/src/getClassName.js b/src/getClassName.js index 5758462..fbc3032 100644 --- a/src/getClassName.js +++ b/src/getClassName.js @@ -2,42 +2,58 @@ import type { StyleModuleMapType, - StyleModuleImportMapType + StyleModuleImportMapType, + HandleMissingStyleNameOptionType } from './types'; +import optionsDefaults from './schemas/optionsDefaults'; const isNamespacedStyleName = (styleName: string): boolean => { return styleName.indexOf('.') !== -1; }; -const getClassNameForNamespacedStyleName = (styleName: string, styleModuleImportMap: StyleModuleImportMapType, silenceErrors: boolean): ?string => { +const getClassNameForNamespacedStyleName = ( + styleName: string, + styleModuleImportMap: StyleModuleImportMapType, + handleMissingStyleNameOption?: HandleMissingStyleNameOptionType +): ?string => { // Note: // Do not use the desctructing syntax with Babel. // Desctructing adds _slicedToArray helper. const styleNameParts = styleName.split('.'); const importName = styleNameParts[0]; const moduleName = styleNameParts[1]; + const handleMissingStyleName = handleMissingStyleNameOption || optionsDefaults.handleMissingStyleName; if (!moduleName) { - if (silenceErrors) { - return null; - } else { + if (handleMissingStyleName === 'throw') { throw new Error('Invalid style name.'); + } else if (handleMissingStyleName === 'warn') { + // eslint-disable-next-line no-console + console.warn('Invalid style name.'); + } else { + return null; } } if (!styleModuleImportMap[importName]) { - if (silenceErrors) { - return null; - } else { + if (handleMissingStyleName === 'throw') { throw new Error('CSS module import does not exist.'); + } else if (handleMissingStyleName === 'warn') { + // eslint-disable-next-line no-console + console.warn('CSS module import does not exist.'); + } else { + return null; } } if (!styleModuleImportMap[importName][moduleName]) { - if (silenceErrors) { - return null; - } else { + if (handleMissingStyleName === 'throw') { throw new Error('CSS module does not exist.'); + } else if (handleMissingStyleName === 'warn') { + // eslint-disable-next-line no-console + console.warn('CSS module does not exist.'); + } else { + return null; } } @@ -45,12 +61,11 @@ const getClassNameForNamespacedStyleName = (styleName: string, styleModuleImport }; type OptionsType = {| - silenceStyleNameErrors: boolean + handleMissingStyleName: HandleMissingStyleNameOptionType |}; -export default (styleNameValue: string, styleModuleImportMap: StyleModuleImportMapType, options?: OptionsType): string => { +export default (styleNameValue: string, styleModuleImportMap: StyleModuleImportMapType, options: OptionsType): string => { const styleModuleImportMapKeys = Object.keys(styleModuleImportMap); - const silenceStyleNameErrors = Boolean(options && options.silenceStyleNameErrors); return styleNameValue .split(' ') @@ -59,7 +74,7 @@ export default (styleNameValue: string, styleModuleImportMap: StyleModuleImportM }) .map((styleName) => { if (isNamespacedStyleName(styleName)) { - return getClassNameForNamespacedStyleName(styleName, styleModuleImportMap, silenceStyleNameErrors); + return getClassNameForNamespacedStyleName(styleName, styleModuleImportMap, options.handleMissingStyleName); } if (styleModuleImportMapKeys.length === 0) { @@ -72,14 +87,20 @@ export default (styleNameValue: string, styleModuleImportMap: StyleModuleImportM const styleModuleMap: StyleModuleMapType = styleModuleImportMap[styleModuleImportMapKeys[0]]; - if (!styleModuleMap[styleName] && !silenceStyleNameErrors) { - throw new Error('Could not resolve the styleName \'' + styleName + '\'.'); + if (!styleModuleMap[styleName]) { + if (options.handleMissingStyleName === 'throw') { + throw new Error('Could not resolve the styleName \'' + styleName + '\'.'); + } + if (options.handleMissingStyleName === 'warn') { + // eslint-disable-next-line no-console + console.warn('Could not resolve the styleName \'' + styleName + '\'.'); + } } return styleModuleMap[styleName]; }) .filter((className) => { - // Remove any styles which could not be found (if silenceStyleNameErrors) + // Remove any styles which could not be found (if handleMissingStyleName === 'ignore') return className; }) .join(' '); diff --git a/src/index.js b/src/index.js index 233a2f2..3c86f1e 100644 --- a/src/index.js +++ b/src/index.js @@ -9,6 +9,7 @@ import BabelTypes from 'babel-types'; import ajvKeywords from 'ajv-keywords'; import Ajv from 'ajv'; import optionsSchema from './schemas/optionsSchema.json'; +import optionsDefaults from './schemas/optionsDefaults'; import createObjectExpression from './createObjectExpression'; import requireCssModule from './requireCssModule'; import resolveStringLiteral from './resolveStringLiteral'; @@ -188,7 +189,7 @@ export default ({ path, filenameMap[filename].styleModuleImportMap, styleNameAttribute, - {silenceStyleNameErrors: Boolean(stats.opts.silenceStyleNameErrors)} + {handleMissingStyleName: stats.opts.handleMissingStyleName || optionsDefaults.handleMissingStyleName} ); return; @@ -204,7 +205,7 @@ export default ({ styleNameAttribute, filenameMap[filename].importedHelperIndentifier, filenameMap[filename].styleModuleImportMapIdentifier, - {silenceStyleNameErrors: Boolean(stats.opts.silenceStyleNameErrors)} + {handleMissingStyleName: stats.opts.handleMissingStyleName || optionsDefaults.handleMissingStyleName} ); } }, diff --git a/src/replaceJsxExpressionContainer.js b/src/replaceJsxExpressionContainer.js index fd9dc7e..41b613f 100644 --- a/src/replaceJsxExpressionContainer.js +++ b/src/replaceJsxExpressionContainer.js @@ -9,11 +9,15 @@ import BabelTypes, { jSXExpressionContainer, jSXIdentifier } from 'babel-types'; +import type { + HandleMissingStyleNameOptionType +} from './types'; import conditionalClassMerge from './conditionalClassMerge'; import createObjectExpression from './createObjectExpression'; +import optionsDefaults from './schemas/optionsDefaults'; type OptionsType = {| - silenceStyleNameErrors: boolean + handleMissingStyleName: HandleMissingStyleNameOptionType |}; export default ( @@ -42,7 +46,9 @@ export default ( styleModuleImportMapIdentifier ]; - if (options.silenceStyleNameErrors) { + // Only provide options argument if the options are something other than default + // This helps save a few bits in the generated user code + if (options.handleMissingStyleName !== optionsDefaults.handleMissingStyleName) { args.push(createObjectExpression(t, options)); } diff --git a/src/resolveStringLiteral.js b/src/resolveStringLiteral.js index c2add9e..ea1c88c 100644 --- a/src/resolveStringLiteral.js +++ b/src/resolveStringLiteral.js @@ -9,11 +9,12 @@ import { import conditionalClassMerge from './conditionalClassMerge'; import getClassName from './getClassName'; import type { - StyleModuleImportMapType + StyleModuleImportMapType, + HandleMissingStyleNameOptionType } from './types'; type OptionsType = {| - silenceStyleNameErrors: boolean + handleMissingStyleName: HandleMissingStyleNameOptionType |}; /** diff --git a/src/schemas/optionsDefaults.js b/src/schemas/optionsDefaults.js new file mode 100644 index 0000000..bf89b63 --- /dev/null +++ b/src/schemas/optionsDefaults.js @@ -0,0 +1,5 @@ +const optionsDefaults = { + handleMissingStyleName: 'throw' +}; + +export default optionsDefaults; diff --git a/src/schemas/optionsSchema.json b/src/schemas/optionsSchema.json index 92cbe11..b1319b2 100644 --- a/src/schemas/optionsSchema.json +++ b/src/schemas/optionsSchema.json @@ -44,8 +44,8 @@ "webpackHotModuleReloading": { "type": "boolean" }, - "silenceStyleNameErrors": { - "type": "boolean" + "handleMissingStyleName": { + "enum": ["throw", "warn", "ignore"] } }, "type": "object" diff --git a/src/types.js b/src/types.js index ef203bb..76cb076 100644 --- a/src/types.js +++ b/src/types.js @@ -11,3 +11,5 @@ export type StyleModuleImportMapType = { export type GenerateScopedNameType = (localName: string, resourcePath: string) => string; export type GenerateScopedNameConfigurationType = GenerateScopedNameType | string; + +export type HandleMissingStyleNameOptionType = 'throw' | 'warn' | 'ignore'; diff --git a/test/fixtures/react-css-modules/does not throw error for stylename not found when silenceStyleNameErrors is true/actual.js b/test/fixtures/react-css-modules/does not throw error for stylename not found when handleMissingStyleName is warn/actual.js similarity index 100% rename from test/fixtures/react-css-modules/does not throw error for stylename not found when silenceStyleNameErrors is true/actual.js rename to test/fixtures/react-css-modules/does not throw error for stylename not found when handleMissingStyleName is warn/actual.js diff --git a/test/fixtures/react-css-modules/does not throw error for stylename not found when silenceStyleNameErrors is true/expected.js b/test/fixtures/react-css-modules/does not throw error for stylename not found when handleMissingStyleName is warn/expected.js similarity index 100% rename from test/fixtures/react-css-modules/does not throw error for stylename not found when silenceStyleNameErrors is true/expected.js rename to test/fixtures/react-css-modules/does not throw error for stylename not found when handleMissingStyleName is warn/expected.js diff --git a/test/fixtures/react-css-modules/does not throw error for stylename not found when silenceStyleNameErrors is true/foo.css b/test/fixtures/react-css-modules/does not throw error for stylename not found when handleMissingStyleName is warn/foo.css similarity index 100% rename from test/fixtures/react-css-modules/does not throw error for stylename not found when silenceStyleNameErrors is true/foo.css rename to test/fixtures/react-css-modules/does not throw error for stylename not found when handleMissingStyleName is warn/foo.css diff --git a/test/fixtures/react-css-modules/does not throw error for stylename not found when silenceStyleNameErrors is true/options.json b/test/fixtures/react-css-modules/does not throw error for stylename not found when handleMissingStyleName is warn/options.json similarity index 74% rename from test/fixtures/react-css-modules/does not throw error for stylename not found when silenceStyleNameErrors is true/options.json rename to test/fixtures/react-css-modules/does not throw error for stylename not found when handleMissingStyleName is warn/options.json index 70362a1..4ab2a61 100644 --- a/test/fixtures/react-css-modules/does not throw error for stylename not found when silenceStyleNameErrors is true/options.json +++ b/test/fixtures/react-css-modules/does not throw error for stylename not found when handleMissingStyleName is warn/options.json @@ -4,7 +4,7 @@ "../../../../src", { "generateScopedName": "[name]__[local]", - "silenceStyleNameErrors": true + "handleMissingStyleName": "ignore" } ] ] diff --git a/test/fixtures/react-css-modules/provides silenceStyleNameErrors to getClassName at runtime/actual.js b/test/fixtures/react-css-modules/provides handleMissingStyleName to getClassName at runtime/actual.js similarity index 100% rename from test/fixtures/react-css-modules/provides silenceStyleNameErrors to getClassName at runtime/actual.js rename to test/fixtures/react-css-modules/provides handleMissingStyleName to getClassName at runtime/actual.js diff --git a/test/fixtures/react-css-modules/provides silenceStyleNameErrors to getClassName at runtime/expected.js b/test/fixtures/react-css-modules/provides handleMissingStyleName to getClassName at runtime/expected.js similarity index 88% rename from test/fixtures/react-css-modules/provides silenceStyleNameErrors to getClassName at runtime/expected.js rename to test/fixtures/react-css-modules/provides handleMissingStyleName to getClassName at runtime/expected.js index a127586..a3e55b9 100644 --- a/test/fixtures/react-css-modules/provides silenceStyleNameErrors to getClassName at runtime/expected.js +++ b/test/fixtures/react-css-modules/provides handleMissingStyleName to getClassName at runtime/expected.js @@ -9,5 +9,5 @@ const _styleModuleImportMap = { const styleNameFoo = 'a-c';
; diff --git a/test/fixtures/react-css-modules/provides silenceStyleNameErrors to getClassName at runtime/foo.css b/test/fixtures/react-css-modules/provides handleMissingStyleName to getClassName at runtime/foo.css similarity index 100% rename from test/fixtures/react-css-modules/provides silenceStyleNameErrors to getClassName at runtime/foo.css rename to test/fixtures/react-css-modules/provides handleMissingStyleName to getClassName at runtime/foo.css diff --git a/test/fixtures/react-css-modules/provides silenceStyleNameErrors to getClassName at runtime/options.json b/test/fixtures/react-css-modules/provides handleMissingStyleName to getClassName at runtime/options.json similarity index 74% rename from test/fixtures/react-css-modules/provides silenceStyleNameErrors to getClassName at runtime/options.json rename to test/fixtures/react-css-modules/provides handleMissingStyleName to getClassName at runtime/options.json index 70362a1..4ab2a61 100644 --- a/test/fixtures/react-css-modules/provides silenceStyleNameErrors to getClassName at runtime/options.json +++ b/test/fixtures/react-css-modules/provides handleMissingStyleName to getClassName at runtime/options.json @@ -4,7 +4,7 @@ "../../../../src", { "generateScopedName": "[name]__[local]", - "silenceStyleNameErrors": true + "handleMissingStyleName": "ignore" } ] ]