Skip to content

Commit 09f9685

Browse files
authored
Add named exports for valid classes (#22)
1 parent 996e931 commit 09f9685

File tree

9 files changed

+91
-23
lines changed

9 files changed

+91
-23
lines changed

.npmignore

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

README.md

Lines changed: 21 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

@@ -36,6 +36,26 @@ Once installed, add this plugin to your `tsconfig.json`:
3636
}
3737
```
3838

39+
### Importing CSS
40+
41+
A default export is always provided for your CSS module.
42+
43+
```tsx
44+
import styles from 'my.module.css';
45+
46+
const a = styles.myClass;
47+
const b = styles['my_other-class'];
48+
```
49+
50+
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.
51+
52+
```tsx
53+
import styles, { myClass } from 'my.module.css';
54+
55+
const a = myClass;
56+
const b = styles['my_other-class'];
57+
```
58+
3959
### Options
4060

4161
| 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",
@@ -47,13 +52,14 @@
4752
"lodash": "^4.17.11",
4853
"postcss": "^7.0.16",
4954
"postcss-icss-selectors": "^2.0.3",
55+
"reserved-words": "^0.1.2",
5056
"sass": "^1.20.1"
5157
},
5258
"devDependencies": {
5359
"@types/jest": "^24.0.13",
5460
"@types/less": "^3.0.0",
5561
"@types/lodash": "^4.14.132",
56-
"@types/node": "^10.12.18",
62+
"@types/node": "^10.0.0",
5763
"@types/sass": "^1.16.0",
5864
"husky": "^2.3.0",
5965
"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
@@ -25,12 +25,19 @@ exports[`utils / cssSnapshots with file 'test.module.css' createExports should c
2525
'classA': string;
2626
'ClassB': string;
2727
'class-c': string;
28+
'class_d': string;
2829
'parent': string;
2930
'childA': string;
3031
'childB': string;
3132
'nestedChild': string;
3233
};
3334
export default classes;
35+
export const classA: string;
36+
export const ClassB: string;
37+
export const parent: string;
38+
export const childA: string;
39+
export const childB: string;
40+
export const nestedChild: string;
3441
"
3542
`;
3643

@@ -41,6 +48,7 @@ Object {
4148
"childB": "file__childB---pq4Ks",
4249
"class-c": "file__class-c---DZ1TD",
4350
"classA": "file__classA---2xcnJ",
51+
"class_d": "file__class_d---1mwNi",
4452
"nestedChild": "file__nestedChild---2d15b",
4553
"parent": "file__parent---1ATMj",
4654
}
@@ -84,6 +92,9 @@ exports[`utils / cssSnapshots with file 'test.module.scss' createExports should
8492
'local-class': string;
8593
'local-class-2': string;
8694
'local-class-inside-local': string;
95+
'reserved-words': string;
96+
'default': string;
97+
'const': string;
8798
'nested-class-parent': string;
8899
'child-class': string;
89100
'nested-class-parent--extended': string;
@@ -104,12 +115,15 @@ export default classes;
104115
exports[`utils / cssSnapshots with file 'test.module.scss' getClasses should return an object matching expected CSS 1`] = `
105116
Object {
106117
"child-class": "file__child-class---1QWYM",
118+
"const": "file__const---MIe_0",
119+
"default": "file__default---2RWlj",
107120
"local-class": "file__local-class---3SW3k",
108121
"local-class-2": "file__local-class-2----c5z7",
109122
"local-class-inside-global": "file__local-class-inside-global---1T0um",
110123
"local-class-inside-local": "file__local-class-inside-local---1Z9pB",
111124
"nested-class-parent": "file__nested-class-parent---3qXdF",
112125
"nested-class-parent--extended": "file__nested-class-parent--extended---qsVau",
126+
"reserved-words": "file__reserved-words---_rrID",
113127
"section-1": "file__section-1---1IHCS",
114128
"section-2": "file__section-2---cLFhf",
115129
"section-3": "file__section-3---1ldKa",

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: 26 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2,14 +2,18 @@ import { extractICSS, IICSSExports } from 'icss-utils';
22
import * as postcss from 'postcss';
33
import * as postcssIcssSelectors from 'postcss-icss-selectors';
44
import * as ts_module from 'typescript/lib/tsserverlibrary';
5-
import * as sass from 'sass';
65
import * as less from 'less';
6+
import * as sass from 'sass';
7+
import * as reserved from 'reserved-words';
78
import { transformClasses } from './classTransforms';
89
import { Options } from '../options';
910

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

1214
const classNameToProperty = (className: string) => `'${className}': string;`;
15+
const classNameToNamedExport = (className: string) =>
16+
`export const ${className}: string;`;
1317

1418
const flattenClassNames = (
1519
previousValue: string[] = [],
@@ -45,17 +49,32 @@ export const getClasses = (
4549
}
4650
};
4751

48-
export const createExports = (classes: IICSSExports, options: Options) => `\
49-
declare const classes: {
50-
${Object.keys(classes)
52+
export const createExports = (classes: IICSSExports, options: Options) => {
53+
const isCamelCase = (className: string) =>
54+
!NOT_CAMELCASE_REGEXP.test(className);
55+
const isReservedWord = (className: string) => !reserved.check(className);
56+
57+
const processedClasses = Object.keys(classes)
5158
.map(transformClasses(options.camelCase))
52-
.reduce(flattenClassNames, [])
53-
.map(classNameToProperty)
54-
.join('\n ')}
59+
.reduce(flattenClassNames, []);
60+
const camelCasedKeys = processedClasses
61+
.filter(isCamelCase)
62+
.filter(isReservedWord)
63+
.map(classNameToNamedExport);
64+
65+
const defaultExport = `\
66+
declare const classes: {
67+
${processedClasses.map(classNameToProperty).join('\n ')}
5568
};
5669
export default classes;
5770
`;
5871

72+
if (camelCasedKeys.length) {
73+
return defaultExport + camelCasedKeys.join('\n') + '\n';
74+
}
75+
return defaultExport;
76+
};
77+
5978
export const getDtsSnapshot = (
6079
ts: typeof ts_module,
6180
scriptSnapshot: ts.IScriptSnapshot,

yarn.lock

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

368-
"@types/node@^10.12.18":
368+
"@types/node@^10.0.0":
369369
version "10.14.7"
370370
resolved "https://registry.yarnpkg.com/@types/node/-/node-10.14.7.tgz#1854f0a9aa8d2cd6818d607b3d091346c6730362"
371371
integrity sha512-on4MmIDgHXiuJDELPk1NFaKVUxxCFr37tm8E9yN6rAiF5Pzp/9bBfBHkoexqRiY+hk/Z04EJU9kKEb59YqJ82A==
@@ -3479,6 +3479,11 @@ require-main-filename@^2.0.0:
34793479
resolved "https://registry.yarnpkg.com/require-main-filename/-/require-main-filename-2.0.0.tgz#d0b329ecc7cc0f61649f62215be69af54aa8989b"
34803480
integrity sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==
34813481

3482+
reserved-words@^0.1.2:
3483+
version "0.1.2"
3484+
resolved "https://registry.yarnpkg.com/reserved-words/-/reserved-words-0.1.2.tgz#00a0940f98cd501aeaaac316411d9adc52b31ab1"
3485+
integrity sha1-AKCUD5jNUBrqqsMWQR2a3FKzGrE=
3486+
34823487
resolve-cwd@^2.0.0:
34833488
version "2.0.0"
34843489
resolved "https://registry.yarnpkg.com/resolve-cwd/-/resolve-cwd-2.0.0.tgz#00a9f7387556e27038eae232caa372a6a59b665a"

0 commit comments

Comments
 (0)