1 | # typed-css-modules [![github actions](https://github.com/Quramy/typed-css-modules/workflows/build/badge.svg)](https://github.com/Quramy/typed-css-modules/actions) [![npm version](https://badge.fury.io/js/typed-css-modules.svg)](http://badge.fury.io/js/typed-css-modules)
|
2 |
|
3 | Creates TypeScript definition files from [CSS Modules](https://github.com/css-modules/css-modules) .css files.
|
4 |
|
5 | If you have the following css,
|
6 |
|
7 | ```css
|
8 | /* styles.css */
|
9 |
|
10 | @value primary: red;
|
11 |
|
12 | .myClass {
|
13 | color: primary;
|
14 | }
|
15 | ```
|
16 |
|
17 | typed-css-modules creates the following .d.ts files from the above css:
|
18 |
|
19 | ```ts
|
20 | /* styles.css.d.ts */
|
21 | declare const styles: {
|
22 | readonly primary: string;
|
23 | readonly myClass: string;
|
24 | };
|
25 | export = styles;
|
26 | ```
|
27 |
|
28 | So, you can import CSS modules' class or variable into your TypeScript sources:
|
29 |
|
30 | ```ts
|
31 | /* app.ts */
|
32 | import styles from './styles.css';
|
33 | console.log(`<div class="${styles.myClass}"></div>`);
|
34 | console.log(`<div style="color: ${styles.primary}"></div>`);
|
35 | ```
|
36 |
|
37 | ## CLI
|
38 |
|
39 | ```sh
|
40 | npm install -g typed-css-modules
|
41 | ```
|
42 |
|
43 | And exec `tcm <input directory>` command.
|
44 | For example, if you have .css files under `src` directory, exec the following:
|
45 |
|
46 | ```sh
|
47 | tcm src
|
48 | ```
|
49 |
|
50 | Then, this creates `*.css.d.ts` files under the directory which has the original .css file.
|
51 |
|
52 | ```text
|
53 | (your project root)
|
54 | - src/
|
55 | | myStyle.css
|
56 | | myStyle.css.d.ts [created]
|
57 | ```
|
58 |
|
59 | #### output directory
|
60 |
|
61 | Use `-o` or `--outDir` option.
|
62 |
|
63 | For example:
|
64 |
|
65 | ```sh
|
66 | tcm -o dist src
|
67 | ```
|
68 |
|
69 | ```text
|
70 | (your project root)
|
71 | - src/
|
72 | | myStyle.css
|
73 | - dist/
|
74 | | myStyle.css.d.ts [created]
|
75 | ```
|
76 |
|
77 | #### file name pattern
|
78 |
|
79 | By default, this tool searches `**/*.css` files under `<input directory>`.
|
80 | If you can customize the glob pattern, you can use `--pattern` or `-p` option.
|
81 | Note the quotes around the glob to `-p` (they are required, so that your shell does not perform the expansion).
|
82 |
|
83 | ```sh
|
84 | tcm -p 'src/**/*.css' .
|
85 | ```
|
86 |
|
87 | #### watch
|
88 |
|
89 | With `-w` or `--watch`, this CLI watches files in the input directory.
|
90 |
|
91 | #### validating type files
|
92 |
|
93 | With `-l` or `--listDifferent`, list any files that are different than those that would be generated.
|
94 | If any are different, exit with a status code 1.
|
95 |
|
96 | #### camelize CSS token
|
97 |
|
98 | With `-c` or `--camelCase`, kebab-cased CSS classes(such as `.my-class {...}`) are exported as camelized TypeScript variable name(`export const myClass: string`).
|
99 |
|
100 | You can pass `--camelCase dashes` to only camelize dashes in the class name. Since version `0.27.1` in the
|
101 | webpack `css-loader`. This will keep upperCase class names intact, e.g.:
|
102 |
|
103 | ```css
|
104 | .SomeComponent {
|
105 | height: 10px;
|
106 | }
|
107 | ```
|
108 |
|
109 | becomes
|
110 |
|
111 | ```typescript
|
112 | declare const styles: {
|
113 | readonly SomeComponent: string;
|
114 | };
|
115 | export = styles;
|
116 | ```
|
117 |
|
118 | See also [webpack css-loader's camelCase option](https://github.com/webpack/css-loader#camelcase).
|
119 |
|
120 | #### named exports (enable tree shaking)
|
121 |
|
122 | With `-e` or `--namedExports`, types are exported as named exports as opposed to default exports.
|
123 | This enables support for the `namedExports` css-loader feature, required for webpack to tree shake the final CSS (learn more [here](https://webpack.js.org/loaders/css-loader/#namedexport)).
|
124 |
|
125 | Use this option in combination with https://webpack.js.org/loaders/css-loader/#namedexport and https://webpack.js.org/loaders/style-loader/#namedexport (if you use `style-loader`).
|
126 |
|
127 | When this option is enabled, the type definition changes to support named exports.
|
128 |
|
129 | _NOTE: this option enables camelcase by default._
|
130 |
|
131 | ```css
|
132 | .SomeComponent {
|
133 | height: 10px;
|
134 | }
|
135 | ```
|
136 |
|
137 | **Standard output:**
|
138 |
|
139 | ```typescript
|
140 | declare const styles: {
|
141 | readonly SomeComponent: string;
|
142 | };
|
143 | export = styles;
|
144 | ```
|
145 |
|
146 | **Named exports output:**
|
147 |
|
148 | ```typescript
|
149 | export const someComponent: string;
|
150 | ```
|
151 |
|
152 | #### arbitrary file extensions
|
153 |
|
154 | With `-a` or `--allowArbitraryExtensions`, output filenames will be compatible with the "arbitrary file extensions" feature that was introduce in TypeScript 5.0. See [the docs](https://www.typescriptlang.org/tsconfig#allowArbitraryExtensions) for more info.
|
155 |
|
156 | In essence, the `*.css.d.ts` extension now becomes `*.d.css.ts` so that you can import CSS modules in projects using ESM module resolution.
|
157 |
|
158 | ## API
|
159 |
|
160 | ```sh
|
161 | npm install typed-css-modules
|
162 | ```
|
163 |
|
164 | ```js
|
165 | import DtsCreator from 'typed-css-modules';
|
166 | let creator = new DtsCreator();
|
167 | creator.create('src/style.css').then(content => {
|
168 | console.log(content.tokens); // ['myClass']
|
169 | console.log(content.formatted); // 'export const myClass: string;'
|
170 | content.writeFile(); // writes this content to "src/style.css.d.ts"
|
171 | });
|
172 | ```
|
173 |
|
174 | ### class DtsCreator
|
175 |
|
176 | DtsCreator instance processes the input CSS and creates TypeScript definition contents.
|
177 |
|
178 | #### `new DtsCreator(option)`
|
179 |
|
180 | You can set the following options:
|
181 |
|
182 | - `option.rootDir`: Project root directory(default: `process.cwd()`).
|
183 | - `option.searchDir`: Directory which includes target `*.css` files(default: `'./'`).
|
184 | - `option.outDir`: Output directory(default: `option.searchDir`).
|
185 | - `option.camelCase`: Camelize CSS class tokens.
|
186 | - `option.namedExports`: Use named exports as opposed to default exports to enable tree shaking. Requires `import * as style from './file.module.css';` (default: `false`)
|
187 | - `option.allowArbitraryExtensions`: Output filenames that will be compatible with the "arbitrary file extensions" TypeScript feature
|
188 | - `option.EOL`: EOL (end of line) for the generated `d.ts` files. Possible values `'\n'` or `'\r\n'`(default: `os.EOL`).
|
189 |
|
190 | #### `create(filepath, contents) => Promise(dtsContent)`
|
191 |
|
192 | Returns `DtsContent` instance.
|
193 |
|
194 | - `filepath`: path of target .css file.
|
195 | - `contents`(optional): the CSS content of the `filepath`. If set, DtsCreator uses the contents instead of the original contents of the `filepath`.
|
196 |
|
197 | ### class DtsContent
|
198 |
|
199 | DtsContent instance has `*.d.ts` content, final output path, and function to write the file.
|
200 |
|
201 | #### `writeFile(postprocessor) => Promise(dtsContent)`
|
202 |
|
203 | Writes the DtsContent instance's content to a file. Returns the DtsContent instance.
|
204 |
|
205 | - `postprocessor` (optional): a function that takes the formatted definition string and returns a modified string that will be the final content written to the file.
|
206 |
|
207 | You could use this, for example, to pass generated definitions through a formatter like Prettier, or to add a comment to the top of generated files:
|
208 |
|
209 | ```js
|
210 | dtsContent.writeFile(definition => `// Generated automatically, do not edit\n${definition}`);
|
211 | ```
|
212 |
|
213 | #### `tokens`
|
214 |
|
215 | An array of tokens is retrieved from the input CSS file.
|
216 | e.g. `['myClass']`
|
217 |
|
218 | #### `contents`
|
219 |
|
220 | An array of TypeScript definition expressions.
|
221 | e.g. `['export const myClass: string;']`.
|
222 |
|
223 | #### `formatted`
|
224 |
|
225 | A string of TypeScript definition expressions.
|
226 |
|
227 | e.g.
|
228 |
|
229 | ```ts
|
230 | export const myClass: string;
|
231 | ```
|
232 |
|
233 | #### `messageList`
|
234 |
|
235 | An array of messages. The messages contain invalid token information.
|
236 | e.g. `['my-class is not valid TypeScript variable name.']`.
|
237 |
|
238 | #### `outputFilePath`
|
239 |
|
240 | Final output file path.
|
241 |
|
242 | ## Remarks
|
243 |
|
244 | If your input CSS file has the following class names, these invalid tokens are not written to output `.d.ts` file.
|
245 |
|
246 | ```css
|
247 | /* TypeScript reserved word */
|
248 | .while {
|
249 | color: red;
|
250 | }
|
251 |
|
252 | /* invalid TypeScript variable */
|
253 | /* If camelCase option is set, this token will be converted to 'myClass' */
|
254 | .my-class {
|
255 | color: red;
|
256 | }
|
257 |
|
258 | /* it's ok */
|
259 | .myClass {
|
260 | color: red;
|
261 | }
|
262 | ```
|
263 |
|
264 | ## Example
|
265 |
|
266 | There is a minimum example in this repository `example` folder. Clone this repository and run `cd example; npm i; npm start`.
|
267 |
|
268 | Or please see [https://github.com/Quramy/typescript-css-modules-demo](https://github.com/Quramy/typescript-css-modules-demo). It's a working demonstration of CSS Modules with React and TypeScript.
|
269 |
|
270 | ## License
|
271 |
|
272 | This software is released under the MIT License, see LICENSE.txt.
|