Skip to content

Commit 4b523dc

Browse files
author
Joel Jeske
committed
Support a custom TS transformer for final DTS output
1 parent e548afe commit 4b523dc

File tree

8 files changed

+291
-16
lines changed

8 files changed

+291
-16
lines changed

README.md

Lines changed: 37 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -69,14 +69,15 @@ const b = styles['my_other-class'];
6969

7070
Please note that no options are required. However, depending on your configuration, you may need to customise these options.
7171

72-
| Option | Default value | Description |
73-
| -------------------- | ---------------------------------- | ---------------------------------------------------------------------------- |
74-
| `classnameTransform` | `asIs` | See [`classnameTransform`](#classnameTransform) below. |
75-
| `customMatcher` | `"\\.module\\.(c\|le\|sa\|sc)ss$"` | Changes the file extensions that this plugin processes. |
76-
| `customRenderer` | `false` | See [`customRenderer`](#customRenderer) below. |
77-
| `dotenvOptions` | `{}` | Provides options for [`dotenv`](https://github.com/motdotla/dotenv#options). |
78-
| `postCssOptions` | `{}` | See [`postCssOptions`](#postCssOptions) below. |
79-
| `rendererOptions` | `{}` | See [`rendererOptions`](#rendererOptions) below. |
72+
| Option | Default value | Description |
73+
| ----------------------------- | ---------------------------------- | ---------------------------------------------------------------------------- |
74+
| `classnameTransform` | `asIs` | See [`classnameTransform`](#classnameTransform) below. |
75+
| `customMatcher` | `"\\.module\\.(c\|le\|sa\|sc)ss$"` | Changes the file extensions that this plugin processes. |
76+
| `customRenderer` | `false` | See [`customRenderer`](#customRenderer) below. |
77+
| `customTypescriptTransformer` | `false` | See [`customTypescriptTransformer`](#customTypescriptTransformer) below. |
78+
| `dotenvOptions` | `{}` | Provides options for [`dotenv`](https://github.com/motdotla/dotenv#options). |
79+
| `postCssOptions` | `{}` | See [`postCssOptions`](#postCssOptions) below. |
80+
| `rendererOptions` | `{}` | See [`rendererOptions`](#rendererOptions) below. |
8081

8182
```json
8283
{
@@ -112,7 +113,7 @@ When a custom renderer is provided, not other renderers will be used.
112113

113114
The path to the `customRenderer` must be relative to the project root (i.e. `./myRenderer.js`).
114115

115-
The custom renderer itself should be a JavaScript file. The function will be called with two arguments: a `css` string, and an `options` object (see [`options.ts`](https://github.com/mrmckeb/typescript-plugin-css-modules/blob/master/src/options.ts#L36-L39)). It must be synchronous, and must return valid CSS.
116+
The custom renderer itself should be a JavaScript file. The function will be called with two arguments: a `css` string, and an `options` object (see [`options.ts`](https://github.com/mrmckeb/typescript-plugin-css-modules/blob/master/src/options.ts#L33-L41)). It must be synchronous, and must return valid CSS.
116117

117118
```js
118119
module.exports = (css, { fileName, logger }) => {
@@ -129,6 +130,33 @@ You can find an example custom renderer in our test fixtures ([`customRenderer.j
129130

130131
The [internal `logger`](https://github.com/mrmckeb/typescript-plugin-css-modules/blob/master/src/helpers/logger.ts) is provided for [debugging](#troubleshooting).
131132

133+
#### `customTypescriptTransformer`
134+
135+
The `customTypescriptTransformer` is an advanced option, letting you provide a transformer of the generated typescript declarations.
136+
137+
When a custom typescript transformer is provided, its output is used as the virtual typescript file.
138+
139+
The path to the `customTypescriptTransformer` must be relative to the project root (i.e. `./myTypescriptTransformer.js`).
140+
141+
The custom renderer itself should be a JavaScript file. The function will be called with two arguments: a `dts` string, and an `options` object (see [`options.ts`](https://github.com/mrmckeb/typescript-plugin-css-modules/blob/master/src/options.ts#L43-L52)). It must be synchronous, and must return valid TypeScript Declaration code (code found in a .d.ts file only).
142+
143+
```js
144+
module.exports = (dts, { classes, fileName, logger }) => {
145+
try {
146+
// ...transform the dts here
147+
return transformedDts;
148+
} catch (error) {
149+
logger.error(error.message);
150+
}
151+
};
152+
```
153+
154+
You can find an example custom typescript transformer in our test fixtures ([`customTypescriptTransformer.js`](https://github.com/mrmckeb/typescript-plugin-css-modules/blob/master/src/helpers/__tests__/fixtures/customTypescriptTransformer.js)).
155+
156+
The [internal `logger`](https://github.com/mrmckeb/typescript-plugin-css-modules/blob/master/src/helpers/logger.ts) is provided for [debugging](#troubleshooting).
157+
158+
The `classes` object represents all the classnames extracted form the CSS Module. They are available if you want to add a custom representation of the CSS classes.
159+
132160
#### `postCssOptions`
133161

134162
| Option | Default value | Description |

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

Lines changed: 181 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,17 @@ export default classes;
1919

2020
exports[`utils / cssSnapshots with file 'empty.module.less' getClasses should return an object matching expected CSS 1`] = `Object {}`;
2121

22+
exports[`utils / cssSnapshots with file 'empty.module.less' with a custom typescript transformer should transform the generated dts 1`] = `
23+
"/* eslint-disable */
24+
declare const classes: {
25+
26+
};
27+
export default classes;
28+
29+
export const __cssModule: true;
30+
export type AllClassNames = '';"
31+
`;
32+
2233
exports[`utils / cssSnapshots with file 'empty.module.sass' createExports should create an exports file 1`] = `
2334
"declare const classes: {
2435
@@ -29,6 +40,17 @@ export default classes;
2940

3041
exports[`utils / cssSnapshots with file 'empty.module.sass' getClasses should return an object matching expected CSS 1`] = `Object {}`;
3142

43+
exports[`utils / cssSnapshots with file 'empty.module.sass' with a custom typescript transformer should transform the generated dts 1`] = `
44+
"/* eslint-disable */
45+
declare const classes: {
46+
47+
};
48+
export default classes;
49+
50+
export const __cssModule: true;
51+
export type AllClassNames = '';"
52+
`;
53+
3254
exports[`utils / cssSnapshots with file 'empty.module.scss' createExports should create an exports file 1`] = `
3355
"declare const classes: {
3456
@@ -39,6 +61,17 @@ export default classes;
3961

4062
exports[`utils / cssSnapshots with file 'empty.module.scss' getClasses should return an object matching expected CSS 1`] = `Object {}`;
4163

64+
exports[`utils / cssSnapshots with file 'empty.module.scss' with a custom typescript transformer should transform the generated dts 1`] = `
65+
"/* eslint-disable */
66+
declare const classes: {
67+
68+
};
69+
export default classes;
70+
71+
export const __cssModule: true;
72+
export type AllClassNames = '';"
73+
`;
74+
4275
exports[`utils / cssSnapshots with file 'import.module.css' createExports should create an exports file 1`] = `
4376
"declare const classes: {
4477
'classA': string;
@@ -73,6 +106,30 @@ Object {
73106
}
74107
`;
75108

109+
exports[`utils / cssSnapshots with file 'import.module.css' with a custom typescript transformer should transform the generated dts 1`] = `
110+
"/* eslint-disable */
111+
declare const classes: {
112+
'classA': string;
113+
'ClassB': string;
114+
'class-c': string;
115+
'class_d': string;
116+
'parent': string;
117+
'childA': string;
118+
'childB': string;
119+
'nestedChild': string;
120+
};
121+
export default classes;
122+
export const classA: string;
123+
export const ClassB: string;
124+
export const parent: string;
125+
export const childA: string;
126+
export const childB: string;
127+
export const nestedChild: string;
128+
129+
export const __cssModule: true;
130+
export type AllClassNames = 'classA' | 'ClassB' | 'class-c' | 'class_d' | 'parent' | 'childA' | 'childB' | 'nestedChild';"
131+
`;
132+
76133
exports[`utils / cssSnapshots with file 'import.module.less' createExports should create an exports file 1`] = `
77134
"declare const classes: {
78135
'nested-class-parent': string;
@@ -105,6 +162,26 @@ Object {
105162
}
106163
`;
107164

165+
exports[`utils / cssSnapshots with file 'import.module.less' with a custom typescript transformer should transform the generated dts 1`] = `
166+
"/* eslint-disable */
167+
declare const classes: {
168+
'nested-class-parent': string;
169+
'child-class': string;
170+
'selector-blue': string;
171+
'selector-green': string;
172+
'selector-red': string;
173+
'column-1': string;
174+
'column-2': string;
175+
'column-3': string;
176+
'column-4': string;
177+
'color-set': string;
178+
};
179+
export default classes;
180+
181+
export const __cssModule: true;
182+
export type AllClassNames = 'nested-class-parent' | 'child-class' | 'selector-blue' | 'selector-green' | 'selector-red' | 'column-1' | 'column-2' | 'column-3' | 'column-4' | 'color-set';"
183+
`;
184+
108185
exports[`utils / cssSnapshots with file 'test.module.css' createExports should create an exports file 1`] = `
109186
"declare const classes: {
110187
'classA': string;
@@ -139,6 +216,30 @@ Object {
139216
}
140217
`;
141218

219+
exports[`utils / cssSnapshots with file 'test.module.css' with a custom typescript transformer should transform the generated dts 1`] = `
220+
"/* eslint-disable */
221+
declare const classes: {
222+
'classA': string;
223+
'ClassB': string;
224+
'class-c': string;
225+
'class_d': string;
226+
'parent': string;
227+
'childA': string;
228+
'childB': string;
229+
'nestedChild': string;
230+
};
231+
export default classes;
232+
export const classA: string;
233+
export const ClassB: string;
234+
export const parent: string;
235+
export const childA: string;
236+
export const childB: string;
237+
export const nestedChild: string;
238+
239+
export const __cssModule: true;
240+
export type AllClassNames = 'classA' | 'ClassB' | 'class-c' | 'class_d' | 'parent' | 'childA' | 'childB' | 'nestedChild';"
241+
`;
242+
142243
exports[`utils / cssSnapshots with file 'test.module.less' createExports should create an exports file 1`] = `
143244
"declare const classes: {
144245
'nested-class-parent': string;
@@ -171,6 +272,26 @@ Object {
171272
}
172273
`;
173274

275+
exports[`utils / cssSnapshots with file 'test.module.less' with a custom typescript transformer should transform the generated dts 1`] = `
276+
"/* eslint-disable */
277+
declare const classes: {
278+
'nested-class-parent': string;
279+
'child-class': string;
280+
'selector-blue': string;
281+
'selector-green': string;
282+
'selector-red': string;
283+
'column-1': string;
284+
'column-2': string;
285+
'column-3': string;
286+
'column-4': string;
287+
'color-set': string;
288+
};
289+
export default classes;
290+
291+
export const __cssModule: true;
292+
export type AllClassNames = 'nested-class-parent' | 'child-class' | 'selector-blue' | 'selector-green' | 'selector-red' | 'column-1' | 'column-2' | 'column-3' | 'column-4' | 'color-set';"
293+
`;
294+
174295
exports[`utils / cssSnapshots with file 'test.module.sass' createExports should create an exports file 1`] = `
175296
"declare const classes: {
176297
'local-class-inside-global': string;
@@ -223,6 +344,36 @@ Object {
223344
}
224345
`;
225346

347+
exports[`utils / cssSnapshots with file 'test.module.sass' with a custom typescript transformer should transform the generated dts 1`] = `
348+
"/* eslint-disable */
349+
declare const classes: {
350+
'local-class-inside-global': string;
351+
'local-class': string;
352+
'local-class-2': string;
353+
'local-class-inside-local': string;
354+
'reserved-words': string;
355+
'default': string;
356+
'const': string;
357+
'nested-class-parent': string;
358+
'child-class': string;
359+
'nested-class-parent--extended': string;
360+
'section-1': string;
361+
'section-2': string;
362+
'section-3': string;
363+
'section-4': string;
364+
'section-5': string;
365+
'section-6': string;
366+
'section-7': string;
367+
'section-8': string;
368+
'section-9': string;
369+
'class-with-mixin': string;
370+
};
371+
export default classes;
372+
373+
export const __cssModule: true;
374+
export type AllClassNames = 'local-class-inside-global' | 'local-class' | 'local-class-2' | 'local-class-inside-local' | 'reserved-words' | 'default' | 'const' | 'nested-class-parent' | 'child-class' | 'nested-class-parent--extended' | 'section-1' | 'section-2' | 'section-3' | 'section-4' | 'section-5' | 'section-6' | 'section-7' | 'section-8' | 'section-9' | 'class-with-mixin';"
375+
`;
376+
226377
exports[`utils / cssSnapshots with file 'test.module.scss' createExports should create an exports file 1`] = `
227378
"declare const classes: {
228379
'local-class-inside-global': string;
@@ -275,6 +426,36 @@ Object {
275426
}
276427
`;
277428

429+
exports[`utils / cssSnapshots with file 'test.module.scss' with a custom typescript transformer should transform the generated dts 1`] = `
430+
"/* eslint-disable */
431+
declare const classes: {
432+
'local-class-inside-global': string;
433+
'local-class': string;
434+
'local-class-2': string;
435+
'local-class-inside-local': string;
436+
'reserved-words': string;
437+
'default': string;
438+
'const': string;
439+
'nested-class-parent': string;
440+
'child-class': string;
441+
'nested-class-parent--extended': string;
442+
'section-1': string;
443+
'section-2': string;
444+
'section-3': string;
445+
'section-4': string;
446+
'section-5': string;
447+
'section-6': string;
448+
'section-7': string;
449+
'section-8': string;
450+
'section-9': string;
451+
'class-with-mixin': string;
452+
};
453+
export default classes;
454+
455+
export const __cssModule: true;
456+
export type AllClassNames = 'local-class-inside-global' | 'local-class' | 'local-class-2' | 'local-class-inside-local' | 'reserved-words' | 'default' | 'const' | 'nested-class-parent' | 'child-class' | 'nested-class-parent--extended' | 'section-1' | 'section-2' | 'section-3' | 'section-4' | 'section-5' | 'section-6' | 'section-7' | 'section-8' | 'section-9' | 'class-with-mixin';"
457+
`;
458+
278459
exports[`utils / cssSnapshots with includePaths in sass options should find external file from includePaths 1`] = `
279460
Object {
280461
"big-font": "include-path-module__big-font---Td7hY",
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
module.exports = (dts, { classes, logger }) => {
2+
logger.log('Example log');
3+
return [
4+
'/* eslint-disable */',
5+
dts,
6+
'export const __cssModule: true;',
7+
`export type AllClassNames = '${Object.keys(classes).join("' | '")}';`
8+
].join('\n');
9+
}

src/helpers/__tests__/getDtsSnapshot.test.ts

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,10 +58,29 @@ describe('utils / cssSnapshots', () => {
5858

5959
describe('createExports', () => {
6060
it('should create an exports file', () => {
61-
const exports = createExports(classes, {});
61+
const exports = createExports(classes, {}, fullFileName, mockLogger);
6262
expect(exports).toMatchSnapshot();
6363
});
6464
});
65+
66+
describe('with a custom typescript transformer', () => {
67+
it('should transform the generated dts', () => {
68+
const customTypescriptTransformer = join(
69+
__dirname,
70+
'fixtures',
71+
'customTypescriptTransformer.js',
72+
);
73+
const overrideOptions: Options = { customTypescriptTransformer };
74+
75+
const dts = createExports(
76+
classes,
77+
overrideOptions,
78+
fullFileName,
79+
mockLogger,
80+
);
81+
expect(dts).toMatchSnapshot();
82+
});
83+
});
6584
});
6685
});
6786

0 commit comments

Comments
 (0)