Skip to content

Support a custom TS transformer for final DTS output #73

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 5 commits into from
Feb 27, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
34 changes: 31 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,7 @@ Please note that no options are required. However, depending on your configurati
| `classnameTransform` | `asIs` | See [`classnameTransform`](#classnameTransform) below. |
| `customMatcher` | `"\\.module\\.(c\|le\|sa\|sc)ss$"` | Changes the file extensions that this plugin processes. |
| `customRenderer` | `false` | See [`customRenderer`](#customRenderer) below. |
| `customTemplate` | `false` | See [`customTemplate`](#customTemplate) below. |
| `dotenvOptions` | `{}` | Provides options for [`dotenv`](https://github.com/motdotla/dotenv#options). |
| `postCssOptions` | `{}` | See [`postCssOptions`](#postCssOptions) below. |
| `rendererOptions` | `{}` | See [`rendererOptions`](#rendererOptions) below. |
Expand Down Expand Up @@ -112,12 +113,12 @@ When a custom renderer is provided, not other renderers will be used.

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

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.
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.

```js
module.exports = (css, { fileName, logger }) => {
try {
// ...process css here
// ...process your css here.
return renderedCss;
} catch (error) {
logger.error(error.message);
Expand All @@ -129,6 +130,33 @@ You can find an example custom renderer in our test fixtures ([`customRenderer.j

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

#### `customTemplate`

The `customTemplate` is an advanced option, letting you provide a template for the generated TypeScript declarations.

When a custom template is provided, its output is used as the virtual declaration (`*.d.ts`) file.

The path to the `customTemplate` must be relative to the project root (i.e. `./customTemplate.js`).

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 a valid TypeScript declaration (as found in a `.d.ts` file).

```js
module.exports = (dts, { classes, fileName, logger }) => {
try {
// ...generate your template here.
return customTemplate;
} catch (error) {
logger.error(error.message);
}
};
```

You can find an example custom template in our test fixtures ([`customTemplate.js`](https://github.com/mrmckeb/typescript-plugin-css-modules/blob/master/src/helpers/__tests__/fixtures/customTemplate.js)).

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

The `classes` object represents all the classnames extracted from the CSS Module. They are available if you want to add a custom representation of the CSS classes.

#### `postCssOptions`

| Option | Default value | Description |
Expand Down Expand Up @@ -171,7 +199,7 @@ If your project doesn't already have global declarations for CSS Modules, you wi

Where you store global declarations is up to you. An example might look like: `./src/custom.d.ts`.

The below is an example that you can copy or modify. If you use a [`customMatcher`], you'll need to modify this.
The below is an example that you can copy or modify. If you use a `customMatcher`, you'll need to modify this.

```ts
declare module '*.module.css' {
Expand Down
36 changes: 17 additions & 19 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@
],
"scripts": {
"build": "rm -rf ./lib && tsc",
"lint": "eslint './src/**/*.ts' --max-warnings 0 && yarn prettier './**/*.{json,md,ts,yml}' -c",
"lint": "eslint './src/**/*.{js,ts}' --max-warnings 0 && yarn prettier './**/*.{json,md,js,ts,yml}' -c",
"prepublishOnly": "yarn build",
"test": "jest ./src"
},
Expand All @@ -50,12 +50,10 @@
"lint-staged": {
"./src/**/*.ts": [
"eslint --fix",
"prettier --write",
"git add"
"prettier --write"
],
"./**/*.{json,md,yml}": [
"prettier --write",
"git add"
"prettier --write"
]
},
"prettier": {
Expand All @@ -64,42 +62,42 @@
"trailingComma": "all"
},
"resolutions": {
"postcss": "7.0.23"
"postcss": "7.0.27"
},
"dependencies": {
"dotenv": "^8.2.0",
"icss-utils": "^4.1.1",
"less": "^3.9.0",
"less": "^3.11.1",
"lodash.camelcase": "^4.3.0",
"postcss": "^7.0.23",
"postcss": "^7.0.27",
"postcss-filter-plugins": "^3.0.1",
"postcss-icss-selectors": "^2.0.3",
"postcss-load-config": "^2.1.0",
"reserved-words": "^0.1.2",
"sass": "^1.23.7"
"sass": "^1.26.1"
},
"devDependencies": {
"@types/icss-utils": "^4.1.0",
"@types/jest": "^24.0.23",
"@types/jest": "^25.1.3",
"@types/less": "^3.0.1",
"@types/lodash.camelcase": "^4.3.6",
"@types/node": "^12.12.17",
"@types/postcss-load-config": "^2.0.1",
"@types/postcss-nested": "^4.1.0",
"@types/reserved-words": "^0.1.0",
"@types/sass": "^1.16.0",
"@typescript-eslint/eslint-plugin": "^2.11.0",
"@typescript-eslint/parser": "^2.11.0",
"@typescript-eslint/eslint-plugin": "^2.21.0",
"@typescript-eslint/parser": "^2.21.0",
"bootstrap": "4.4.1",
"eslint": "^6.7.2",
"eslint-config-prettier": "^6.7.0",
"husky": "^3.1.0",
"jest": "^24.8.0",
"lint-staged": "^9.5.0",
"eslint": "^6.8.0",
"eslint-config-prettier": "^6.10.0",
"husky": "^4.2.3",
"jest": "^25.1.0",
"lint-staged": "^10.0.8",
"postcss-import-sync2": "^1.1.0",
"prettier": "^1.19.1",
"ts-jest": "^24.2.0",
"typescript": "^3.7.3"
"ts-jest": "^25.2.1",
"typescript": "^3.8.2"
},
"peerDependencies": {
"typescript": "^3.0.0"
Expand Down
181 changes: 181 additions & 0 deletions src/helpers/__tests__/__snapshots__/getDtsSnapshot.test.ts.snap
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,17 @@ export default classes;

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

exports[`utils / cssSnapshots with file 'empty.module.less' with a custom typescript transformer should transform the generated dts 1`] = `
"/* eslint-disable */
declare const classes: {

};
export default classes;

export const __cssModule: true;
export type AllClassNames = '';"
`;

exports[`utils / cssSnapshots with file 'empty.module.sass' createExports should create an exports file 1`] = `
"declare const classes: {

Expand All @@ -29,6 +40,17 @@ export default classes;

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

exports[`utils / cssSnapshots with file 'empty.module.sass' with a custom typescript transformer should transform the generated dts 1`] = `
"/* eslint-disable */
declare const classes: {

};
export default classes;

export const __cssModule: true;
export type AllClassNames = '';"
`;

exports[`utils / cssSnapshots with file 'empty.module.scss' createExports should create an exports file 1`] = `
"declare const classes: {

Expand All @@ -39,6 +61,17 @@ export default classes;

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

exports[`utils / cssSnapshots with file 'empty.module.scss' with a custom typescript transformer should transform the generated dts 1`] = `
"/* eslint-disable */
declare const classes: {

};
export default classes;

export const __cssModule: true;
export type AllClassNames = '';"
`;

exports[`utils / cssSnapshots with file 'import.module.css' createExports should create an exports file 1`] = `
"declare const classes: {
'classA': string;
Expand Down Expand Up @@ -73,6 +106,30 @@ Object {
}
`;

exports[`utils / cssSnapshots with file 'import.module.css' with a custom typescript transformer should transform the generated dts 1`] = `
"/* eslint-disable */
declare const classes: {
'classA': string;
'ClassB': string;
'class-c': string;
'class_d': string;
'parent': string;
'childA': string;
'childB': string;
'nestedChild': string;
};
export default classes;
export const classA: string;
export const ClassB: string;
export const parent: string;
export const childA: string;
export const childB: string;
export const nestedChild: string;

export const __cssModule: true;
export type AllClassNames = 'classA' | 'ClassB' | 'class-c' | 'class_d' | 'parent' | 'childA' | 'childB' | 'nestedChild';"
`;

exports[`utils / cssSnapshots with file 'import.module.less' createExports should create an exports file 1`] = `
"declare const classes: {
'nested-class-parent': string;
Expand Down Expand Up @@ -105,6 +162,26 @@ Object {
}
`;

exports[`utils / cssSnapshots with file 'import.module.less' with a custom typescript transformer should transform the generated dts 1`] = `
"/* eslint-disable */
declare const classes: {
'nested-class-parent': string;
'child-class': string;
'selector-blue': string;
'selector-green': string;
'selector-red': string;
'column-1': string;
'column-2': string;
'column-3': string;
'column-4': string;
'color-set': string;
};
export default classes;

export const __cssModule: true;
export type AllClassNames = 'nested-class-parent' | 'child-class' | 'selector-blue' | 'selector-green' | 'selector-red' | 'column-1' | 'column-2' | 'column-3' | 'column-4' | 'color-set';"
`;

exports[`utils / cssSnapshots with file 'test.module.css' createExports should create an exports file 1`] = `
"declare const classes: {
'classA': string;
Expand Down Expand Up @@ -139,6 +216,30 @@ Object {
}
`;

exports[`utils / cssSnapshots with file 'test.module.css' with a custom typescript transformer should transform the generated dts 1`] = `
"/* eslint-disable */
declare const classes: {
'classA': string;
'ClassB': string;
'class-c': string;
'class_d': string;
'parent': string;
'childA': string;
'childB': string;
'nestedChild': string;
};
export default classes;
export const classA: string;
export const ClassB: string;
export const parent: string;
export const childA: string;
export const childB: string;
export const nestedChild: string;

export const __cssModule: true;
export type AllClassNames = 'classA' | 'ClassB' | 'class-c' | 'class_d' | 'parent' | 'childA' | 'childB' | 'nestedChild';"
`;

exports[`utils / cssSnapshots with file 'test.module.less' createExports should create an exports file 1`] = `
"declare const classes: {
'nested-class-parent': string;
Expand Down Expand Up @@ -171,6 +272,26 @@ Object {
}
`;

exports[`utils / cssSnapshots with file 'test.module.less' with a custom typescript transformer should transform the generated dts 1`] = `
"/* eslint-disable */
declare const classes: {
'nested-class-parent': string;
'child-class': string;
'selector-blue': string;
'selector-green': string;
'selector-red': string;
'column-1': string;
'column-2': string;
'column-3': string;
'column-4': string;
'color-set': string;
};
export default classes;

export const __cssModule: true;
export type AllClassNames = 'nested-class-parent' | 'child-class' | 'selector-blue' | 'selector-green' | 'selector-red' | 'column-1' | 'column-2' | 'column-3' | 'column-4' | 'color-set';"
`;

exports[`utils / cssSnapshots with file 'test.module.sass' createExports should create an exports file 1`] = `
"declare const classes: {
'local-class-inside-global': string;
Expand Down Expand Up @@ -223,6 +344,36 @@ Object {
}
`;

exports[`utils / cssSnapshots with file 'test.module.sass' with a custom typescript transformer should transform the generated dts 1`] = `
"/* eslint-disable */
declare const classes: {
'local-class-inside-global': string;
'local-class': string;
'local-class-2': string;
'local-class-inside-local': string;
'reserved-words': string;
'default': string;
'const': string;
'nested-class-parent': string;
'child-class': string;
'nested-class-parent--extended': string;
'section-1': string;
'section-2': string;
'section-3': string;
'section-4': string;
'section-5': string;
'section-6': string;
'section-7': string;
'section-8': string;
'section-9': string;
'class-with-mixin': string;
};
export default classes;

export const __cssModule: true;
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';"
`;

exports[`utils / cssSnapshots with file 'test.module.scss' createExports should create an exports file 1`] = `
"declare const classes: {
'local-class-inside-global': string;
Expand Down Expand Up @@ -275,6 +426,36 @@ Object {
}
`;

exports[`utils / cssSnapshots with file 'test.module.scss' with a custom typescript transformer should transform the generated dts 1`] = `
"/* eslint-disable */
declare const classes: {
'local-class-inside-global': string;
'local-class': string;
'local-class-2': string;
'local-class-inside-local': string;
'reserved-words': string;
'default': string;
'const': string;
'nested-class-parent': string;
'child-class': string;
'nested-class-parent--extended': string;
'section-1': string;
'section-2': string;
'section-3': string;
'section-4': string;
'section-5': string;
'section-6': string;
'section-7': string;
'section-8': string;
'section-9': string;
'class-with-mixin': string;
};
export default classes;

export const __cssModule: true;
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';"
`;

exports[`utils / cssSnapshots with includePaths in sass options should find external file from includePaths 1`] = `
Object {
"big-font": "include-path-module__big-font---Td7hY",
Expand Down
Loading