From 4f451ce474f7589362002e5380688b19156aa1dc Mon Sep 17 00:00:00 2001 From: Ben Warbrooke Date: Thu, 12 Oct 2017 13:25:06 +1300 Subject: [PATCH 1/3] Add orderAlphabetically to the loader options. Use this option to sort the module key strings alphabetically --- src/cssModuleToInterface.js | 22 ++++++++++++++-------- src/index.js | 6 ++++-- 2 files changed, 18 insertions(+), 10 deletions(-) diff --git a/src/cssModuleToInterface.js b/src/cssModuleToInterface.js index 272e431..cb3dd00 100644 --- a/src/cssModuleToInterface.js +++ b/src/cssModuleToInterface.js @@ -6,18 +6,24 @@ const filenameToInterfaceName = (filename) => { .replace(/\W+(\w)/g, (_, c) => c.toUpperCase()); }; -const cssModuleToTypescriptInterfaceProperties = (cssModuleKeys, indent = ' ') => { - return cssModuleKeys +const cssModuleToTypescriptInterfaceProperties = (cssModuleKeys, orderAlphabetically, indent = ' ') => { + return sortCssModuleKeys(cssModuleKeys, orderAlphabetically) .map((key) => `${indent}'${key}': string;`) .join('\n'); }; -const cssModuleToNamedExports = (cssModuleKeys) => { - return cssModuleKeys +const cssModuleToNamedExports = (cssModuleKeys, orderAlphabetically) => { + return sortCssModuleKeys(cssModuleKeys, orderAlphabetically) .map((key) => `export const ${key}: string;`) .join('\n'); }; +const sortCssModuleKeys = (cssModuleKeys, orderAlphabetically) => { + return orderAlphabetically + ? [...cssModuleKeys].sort() + : [...cssModuleKeys] +}; + const allWordsRegexp = /^\w+$/i; export const filterNonWordClasses = (cssModuleKeys) => { const filteredClassNames = cssModuleKeys.filter(classname => allWordsRegexp.test(classname)); @@ -80,15 +86,15 @@ export const filenameToTypingsFilename = (filename) => { return path.join(dirName, `${baseName}.d.ts`); }; -export const generateNamedExports = (cssModuleKeys) => { - const namedExports = cssModuleToNamedExports(cssModuleKeys); +export const generateNamedExports = (cssModuleKeys, orderAlphabetically) => { + const namedExports = cssModuleToNamedExports(cssModuleKeys, orderAlphabetically); return (`${namedExports} `); }; -export const generateGenericExportInterface = (cssModuleKeys, filename, indent) => { +export const generateGenericExportInterface = (cssModuleKeys, filename, orderAlphabetically, indent) => { const interfaceName = filenameToInterfaceName(filename); - const interfaceProperties = cssModuleToTypescriptInterfaceProperties(cssModuleKeys, indent); + const interfaceProperties = cssModuleToTypescriptInterfaceProperties(cssModuleKeys, orderAlphabetically, indent); return ( `export interface ${interfaceName} { ${interfaceProperties} diff --git a/src/index.js b/src/index.js index 269c2d8..208b857 100644 --- a/src/index.js +++ b/src/index.js @@ -51,9 +51,11 @@ module.exports = function(...input) { } } + query.orderAlphabetically = !!query.orderAlphabetically; + let cssModuleDefinition; if (!query.namedExport) { - cssModuleDefinition = generateGenericExportInterface(cssModuleKeys, filename); + cssModuleDefinition = generateGenericExportInterface(cssModuleKeys, filename, query.orderAlphabetically); } else { const [cleanedDefinitions, skippedDefinitions,] = filterNonWordClasses(cssModuleKeys); if (skippedDefinitions.length > 0 && !query.camelCase) { @@ -72,7 +74,7 @@ These can be accessed using the object literal syntax; eg styles['delete'] inste `.yellow); } - cssModuleDefinition = generateNamedExports(nonReservedWordDefinitions); + cssModuleDefinition = generateNamedExports(nonReservedWordDefinitions, query.orderAlphabetically); } if (cssModuleDefinition.trim() === '') { // Ensure empty CSS modules export something From 14c52f7fab3fff3941064c5d8a35812116e0cb06 Mon Sep 17 00:00:00 2001 From: Ben Warbrooke Date: Thu, 12 Oct 2017 13:25:40 +1300 Subject: [PATCH 2/3] Add test coverage --- test/entry.ts | 11 +++++++++++ test/example-namedexport-orderalphabetically.css | 12 ++++++++++++ test/example-orderalphabetically.css | 12 ++++++++++++ ...-example-namedexport-orderalphabetically.css.d.ts | 3 +++ test/expected-example-orderalphabetically.css.d.ts | 7 +++++++ test/webpack.config.babel.js | 4 +++- 6 files changed, 48 insertions(+), 1 deletion(-) create mode 100644 test/example-namedexport-orderalphabetically.css create mode 100644 test/example-orderalphabetically.css create mode 100644 test/expected-example-namedexport-orderalphabetically.css.d.ts create mode 100644 test/expected-example-orderalphabetically.css.d.ts diff --git a/test/entry.ts b/test/entry.ts index 6c7341e..9e5f878 100644 --- a/test/entry.ts +++ b/test/entry.ts @@ -1,10 +1,13 @@ import {locals as stylesBase} from './example.css'; import {locals as stylesCamelCase} from './example-camelcase.css'; +import {locals as stylesOrderAlphabetically} from './example-orderalphabetically.css'; import * as stylesNamedExport from './example-namedexport.css'; import * as stylesCamelCasedNamedExport from './example-camelcase-namedexport.css'; +import * as stylesNamedExportOrderAlphabetically from './example-namedexport-orderalphabetically.css'; import './example-no-css-modules.css'; import * as compose from './example-compose.css'; + const foo = stylesBase.foo; const barBaz = stylesBase['bar-baz']; @@ -17,4 +20,12 @@ const fooNamedExport = stylesNamedExport.foo; const fooCamelCaseNamedExport = stylesCamelCasedNamedExport.foo; const barBazCamelCaseNamedExport = stylesCamelCasedNamedExport.barBaz; +const fooStylesOrderAlphabetically = stylesOrderAlphabetically.foo; +const barStylesOrderAlphabetically = stylesOrderAlphabetically.bar; +const bazStylesOrderAlphabetically = stylesOrderAlphabetically.baz; + +const fooNamedExportOrderAlhpabetically = stylesNamedExportOrderAlphabetically.foo; +const barNamedExportOrderAlhpabetically = stylesNamedExportOrderAlphabetically.bar; +const bazNamedExportOrderAlhpabetically = stylesNamedExportOrderAlphabetically.baz; + const composed = compose.test; diff --git a/test/example-namedexport-orderalphabetically.css b/test/example-namedexport-orderalphabetically.css new file mode 100644 index 0000000..892307a --- /dev/null +++ b/test/example-namedexport-orderalphabetically.css @@ -0,0 +1,12 @@ +.foo { + color: white; +} + +.baz { + color: red; +} + +.bar { + color: green; +} + diff --git a/test/example-orderalphabetically.css b/test/example-orderalphabetically.css new file mode 100644 index 0000000..892307a --- /dev/null +++ b/test/example-orderalphabetically.css @@ -0,0 +1,12 @@ +.foo { + color: white; +} + +.baz { + color: red; +} + +.bar { + color: green; +} + diff --git a/test/expected-example-namedexport-orderalphabetically.css.d.ts b/test/expected-example-namedexport-orderalphabetically.css.d.ts new file mode 100644 index 0000000..e71c92d --- /dev/null +++ b/test/expected-example-namedexport-orderalphabetically.css.d.ts @@ -0,0 +1,3 @@ +export const bar: string; +export const baz: string; +export const foo: string; diff --git a/test/expected-example-orderalphabetically.css.d.ts b/test/expected-example-orderalphabetically.css.d.ts new file mode 100644 index 0000000..bb37115 --- /dev/null +++ b/test/expected-example-orderalphabetically.css.d.ts @@ -0,0 +1,7 @@ +export interface IExampleOrderalphabeticallyCss { + 'bar': string; + 'baz': string; + 'foo': string; +} + +export const locals: IExampleOrderalphabeticallyCss; diff --git a/test/webpack.config.babel.js b/test/webpack.config.babel.js index d69e970..2f40be4 100644 --- a/test/webpack.config.babel.js +++ b/test/webpack.config.babel.js @@ -12,7 +12,9 @@ module.exports = { { test: /example-namedexport\.css$/, loader: '../src/index.js?modules&namedExport' }, { test: /example-camelcase-namedexport\.css$/, loader: '../src/index.js?modules&camelCase&namedExport' }, { test: /example-no-css-modules\.css$/, loader: '../src/index.js' }, - { test: /example-compose\.css$/, loader: '../src/index.js?modules&camelCase&namedExport' } + { test: /example-compose\.css$/, loader: '../src/index.js?modules&camelCase&namedExport' }, + { test: /example-orderalphabetically\.css$/, loader: '../src/index.js?modules&orderAlphabetically' }, + { test: /example-namedexport-orderalphabetically\.css$/, loader: '../src/index.js?modules&namedExport&orderAlphabetically' } ] } }; From 3137d469b4f810b0a70fb29f5bf3416984cf1dc2 Mon Sep 17 00:00:00 2001 From: Ben Warbrooke Date: Thu, 12 Oct 2017 13:25:59 +1300 Subject: [PATCH 3/3] Update readme describing new option --- README.md | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/README.md b/README.md index f4de421..d027aaa 100644 --- a/README.md +++ b/README.md @@ -57,6 +57,14 @@ export const barBaz: string; `css-loader` exports mappings to `exports.locals` which is incompatible with the `namedExport`-option unless paired with `extract-text-webpack-plugin` or `style-loader`. They move the exported properties from `exports.locals` to `exports` making them required for `namedExport` to work, and `namedExport` required for them to work. *Always combine usage of `extract-text-webpack-plugin` or `style-loader` with the `namedExport`-option.* +### `orderAlphabetically`-option +Orders generated exports or interface properties alphabetically. This is useful when committing the .d.ts files as the default ordering is not always consistent and can change from commit to commit. +e.g.: + +```js + { test: /\.css$/, loader: 'typings-for-css-modules-loader?modules&orderAlphabetically' } +``` + ### `silent`-option To silence the loader because you get annoyed by its warnings or for other reasons, you can simply pass the "silent" query to the loader and it will shut up. e.g.: