Skip to content

Commit 0aa583c

Browse files
committed
Add named exports for valid classes
1 parent 5463b4f commit 0aa583c

File tree

9 files changed

+92
-22
lines changed

9 files changed

+92
-22
lines changed

.npmignore

Lines changed: 0 additions & 12 deletions
This file was deleted.

README.md

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ for [CSS Modules](https://github.com/css-modules/css-modules).
1212
This project was inspired by this [`create-react-app` issue](https://github.com/facebook/create-react-app/issues/5677)
1313
and was based on [`css-module-types`](https://github.com/timothykang/css-module-types).
1414

15-
## Usage
15+
## Installation
1616

1717
To install with Yarn:
1818

@@ -26,6 +26,8 @@ To install with npm:
2626
npm install --save typescript-plugin-css-modules
2727
```
2828

29+
## Adding the plugin
30+
2931
Once installed, add this plugin to your `tsconfig.json`:
3032

3133
```json
@@ -36,6 +38,26 @@ Once installed, add this plugin to your `tsconfig.json`:
3638
}
3739
```
3840

41+
### Importing CSS
42+
43+
A default export is always provided for your CSS module.
44+
45+
```tsx
46+
import styles from 'my.module.css';
47+
48+
const a = styles.myClass;
49+
const b = styles['my_other-class'];
50+
```
51+
52+
As of version 1.1.0, you can also use named exports for classes that don't contain hyphens or underscores. You can still access other classes via the default export.
53+
54+
```tsx
55+
import styles, { myClass } from 'my.module.css';
56+
57+
const a = myClass;
58+
const b = styles['my_other-class'];
59+
```
60+
3961
### Options
4062

4163
| Option | Default value | Description |

package.json

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,12 +12,17 @@
1212
},
1313
"keywords": [
1414
"css",
15+
"scss",
16+
"sass",
17+
"less",
1518
"modules",
1619
"plugin",
1720
"postcss",
18-
"sass",
1921
"typescript"
2022
],
23+
"files": [
24+
"lib"
25+
],
2126
"scripts": {
2227
"build": "rm -rf ./lib && tsc",
2328
"prepublishOnly": "yarn build",
@@ -46,12 +51,13 @@
4651
"lodash": "^4.17.11",
4752
"postcss": "^7.0.16",
4853
"postcss-icss-selectors": "^2.0.3",
54+
"reserved-words": "^0.1.2",
4955
"sass": "^1.20.1"
5056
},
5157
"devDependencies": {
5258
"@types/jest": "^24.0.13",
5359
"@types/lodash": "^4.14.132",
54-
"@types/node": "^10.12.18",
60+
"@types/node": "^10.0.0",
5561
"@types/sass": "^1.16.0",
5662
"husky": "^2.3.0",
5763
"jest": "^24.8.0",

src/@types/reserved-words.d.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
declare module 'reserved-words' {
2+
export const check: (string: string, esVersion?: string) => boolean;
3+
}

src/helpers/__tests__/__snapshots__/cssSnapshots.test.ts.snap

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,12 +15,19 @@ exports[`utils / cssSnapshots with file 'test.module.css' createExports should c
1515
'classA': string;
1616
'ClassB': string;
1717
'class-c': string;
18+
'class_d': string;
1819
'parent': string;
1920
'childA': string;
2021
'childB': string;
2122
'nestedChild': string;
2223
};
2324
export default classes;
25+
export const classA: string;
26+
export const ClassB: string;
27+
export const parent: string;
28+
export const childA: string;
29+
export const childB: string;
30+
export const nestedChild: string;
2431
"
2532
`;
2633

@@ -31,6 +38,7 @@ Object {
3138
"childB": "file__childB---pq4Ks",
3239
"class-c": "file__class-c---DZ1TD",
3340
"classA": "file__classA---2xcnJ",
41+
"class_d": "file__class_d---1mwNi",
3442
"nestedChild": "file__nestedChild---2d15b",
3543
"parent": "file__parent---1ATMj",
3644
}
@@ -42,6 +50,9 @@ exports[`utils / cssSnapshots with file 'test.module.scss' createExports should
4250
'local-class': string;
4351
'local-class-2': string;
4452
'local-class-inside-local': string;
53+
'reserved-words': string;
54+
'default': string;
55+
'const': string;
4556
'nested-class-parent': string;
4657
'child-class': string;
4758
'nested-class-parent--extended': string;
@@ -62,12 +73,15 @@ export default classes;
6273
exports[`utils / cssSnapshots with file 'test.module.scss' getClasses should return an object matching expected CSS 1`] = `
6374
Object {
6475
"child-class": "file__child-class---1mwoB",
76+
"const": "file__const---1uG5q",
77+
"default": "file__default---3wY_M",
6578
"local-class": "file__local-class---3KegX",
6679
"local-class-2": "file__local-class-2---2h6qz",
6780
"local-class-inside-global": "file__local-class-inside-global---2xH_Y",
6881
"local-class-inside-local": "file__local-class-inside-local---QdL6b",
6982
"nested-class-parent": "file__nested-class-parent---_ft7G",
7083
"nested-class-parent--extended": "file__nested-class-parent--extended---1642l",
84+
"reserved-words": "file__reserved-words---rsDKy",
7185
"section-1": "file__section-1---2EiKX",
7286
"section-2": "file__section-2---2f4aZ",
7387
"section-3": "file__section-3---R_Ilj",

src/helpers/__tests__/fixtures/test.module.css

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,10 @@
1010
color: rebeccapurple;
1111
}
1212

13+
.class_d {
14+
color: rebeccapurple;
15+
}
16+
1317
.parent {
1418
.childA {
1519
color: rebeccapurple;

src/helpers/__tests__/fixtures/test.module.scss

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,15 @@
1818
}
1919
}
2020

21+
.reserved-words {
22+
.default {
23+
color: rebeccapurple;
24+
}
25+
.const {
26+
color: rebeccapurple;
27+
}
28+
}
29+
2130
.nested-class-parent {
2231
.child-class {
2332
color: rebeccapurple;

src/helpers/cssSnapshots.ts

Lines changed: 25 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3,18 +3,26 @@ import * as postcss from 'postcss';
33
import * as postcssIcssSelectors from 'postcss-icss-selectors';
44
import * as ts_module from 'typescript/lib/tsserverlibrary';
55
import * as sass from 'sass';
6+
import * as reserved from 'reserved-words';
67
import { transformClasses } from './classTransforms';
78
import { Options } from '../options';
89

10+
const NOT_CAMELCASE_REGEXP = /[\-_]/;
911
const processor = postcss(postcssIcssSelectors({ mode: 'local' }));
1012

1113
const classNameToProperty = (className: string) => `'${className}': string;`;
14+
const classNameToNamedExport = (className: string) =>
15+
`export const ${className}: string;`;
1216

1317
const flattenClassNames = (
1418
previousValue: string[] = [],
1519
currentValue: string[],
1620
) => previousValue.concat(currentValue);
1721

22+
const isCamelCase = (className: string) =>
23+
!NOT_CAMELCASE_REGEXP.test(className);
24+
const isReservedWord = (className: string) => !reserved.check(className);
25+
1826
export const getClasses = (css: string, isLess: boolean = false) => {
1927
try {
2028
let transformedCss: string;
@@ -30,17 +38,28 @@ export const getClasses = (css: string, isLess: boolean = false) => {
3038
}
3139
};
3240

33-
export const createExports = (classes: IICSSExports, options: Options) => `\
34-
declare const classes: {
35-
${Object.keys(classes)
41+
export const createExports = (classes: IICSSExports, options: Options) => {
42+
const processedClasses = Object.keys(classes)
3643
.map(transformClasses(options.camelCase))
37-
.reduce(flattenClassNames, [])
38-
.map(classNameToProperty)
39-
.join('\n ')}
44+
.reduce(flattenClassNames, []);
45+
const camelCasedKeys = processedClasses
46+
.filter(isCamelCase)
47+
.filter(isReservedWord)
48+
.map(classNameToNamedExport);
49+
50+
const defaultExport = `\
51+
declare const classes: {
52+
${processedClasses.map(classNameToProperty).join('\n ')}
4053
};
4154
export default classes;
4255
`;
4356

57+
if (camelCasedKeys.length) {
58+
return defaultExport + camelCasedKeys.join('\n') + '\n';
59+
}
60+
return defaultExport;
61+
};
62+
4463
export const getDtsSnapshot = (
4564
ts: typeof ts_module,
4665
scriptSnapshot: ts.IScriptSnapshot,

yarn.lock

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -360,7 +360,7 @@
360360
resolved "https://registry.yarnpkg.com/@types/node/-/node-12.0.2.tgz#3452a24edf9fea138b48fad4a0a028a683da1e40"
361361
integrity sha512-5tabW/i+9mhrfEOUcLDu2xBPsHJ+X5Orqy9FKpale3SjDA17j5AEpYq5vfy3oAeAHGcvANRCO3NV3d2D6q3NiA==
362362

363-
"@types/node@^10.12.18":
363+
"@types/node@^10.0.0":
364364
version "10.14.7"
365365
resolved "https://registry.yarnpkg.com/@types/node/-/node-10.14.7.tgz#1854f0a9aa8d2cd6818d607b3d091346c6730362"
366366
integrity sha512-on4MmIDgHXiuJDELPk1NFaKVUxxCFr37tm8E9yN6rAiF5Pzp/9bBfBHkoexqRiY+hk/Z04EJU9kKEb59YqJ82A==
@@ -3419,6 +3419,11 @@ require-main-filename@^2.0.0:
34193419
resolved "https://registry.yarnpkg.com/require-main-filename/-/require-main-filename-2.0.0.tgz#d0b329ecc7cc0f61649f62215be69af54aa8989b"
34203420
integrity sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==
34213421

3422+
reserved-words@^0.1.2:
3423+
version "0.1.2"
3424+
resolved "https://registry.yarnpkg.com/reserved-words/-/reserved-words-0.1.2.tgz#00a0940f98cd501aeaaac316411d9adc52b31ab1"
3425+
integrity sha1-AKCUD5jNUBrqqsMWQR2a3FKzGrE=
3426+
34223427
resolve-cwd@^2.0.0:
34233428
version "2.0.0"
34243429
resolved "https://registry.yarnpkg.com/resolve-cwd/-/resolve-cwd-2.0.0.tgz#00a9f7387556e27038eae232caa372a6a59b665a"

0 commit comments

Comments
 (0)