/pcssmdts

Generates type definitions for your PostCSS powered CSS Modules

Primary LanguageJavaScriptMIT LicenseMIT

Release npm

pcssmdts

pcssmdts helps you ensure that you use classNames that actually exists when you write your PostCSS powered CSS Modules.

Installation

It is faster to use this utility through npx without installation.

npx pcssmdts "src/**/*.module.css"

Alternatively you can add it as devDependency to your project and use it in the same way. Both options are fine.

yarn add pcssmdts -D

In your package.json's scripts

{
  "scripts": {
    "generate:style-defs": "pcssmdts \"src/**/*.module.css\""
  }
}

Motivation

Let's assume that you have your project setup with PostCSS, CSS modules and TypeScript. You want to write your css code using some nice features that provided by PostCSS plugins (like postcss-nested) or use one of supported PostCSS syntax.

During development you actually face with two issues:

  • How to know which classNames are available in styles.module.css right in your IDE for faster development (get intellisense)?
  • How to make sure that all used classNames actually exists and safe when you do type checking?

Of course you want camelCased classNames to avoid something like styles['foo-bar'] and have styles.fooBar instead.

The first issue is solvable by typescript-plugin-css-modules typescript plugin that provides type information to IDEs and any other tools that work with TypeScript language service plugins.

However TypeScript at this time, does not support plugins during compilation. This means that typescript-plugin-css-modules plugin cannot provide errors during compilation.

But with pcssmdts dreams comes true, and from now on you can generate *.module.d.ts from your *.module.css.

npx pcssmdts "src/**/*.module.css"

By running command above command a few things will happen:

  • Any *.module.css files under src folder will be compiled to normal css using PostCSS under the hood.
  • TypeDefinitions are generated by typed-css-modules from compiled css files.
  • Compiled css files are deleted in the end of d.ts generation.

Showcase

Let's imagine simple project, particularly some react component folder like src/components/Component:

  src/
  ├─ components/
  │  ├─ Component/
  │  │  ├─ styles.module.css
  │  │  ├─ Component.tsx
  │  │  ├─ index.ts

styles.module.css

.foo {
  display: flex;

  &__bar {
    width: 10px;

    &--baz {
      color: blue;
    }
  }
}

.my-class-name {
  color: red;
}

Component.tsx

import styles from './styles.module.css';

export const Component: FC = () => {
  return (
    <div className={styles.foo}>
      <span className={styles.fooBar}>
        <a className={styles.fooBarBaz} href="#">
          Link
        </a>
      </span>
      <p className={styles.myClassName}>Some text</p>
    </div>
  );
};

index.ts

export * from './Component.ts';

When you run pcssmdts new file, styles.module.d.ts, will be generated and added to your Component's folder.

styles.module.d.ts

declare const styles: {
  readonly foo: string;
  readonly fooBar: string;
  readonly fooBarBaz: string;
  readonly myClassName: string;
};
export = styles;

In the end your folder structure will look like this:

  src/
  ├─ components/
  │  ├─ Component/
  │  │  ├─ styles.module.css.d.ts <- generated type definitions
  │  │  ├─ styles.module.css
  │  │  ├─ Component.tsx
  │  │  ├─ index.ts

So, when you run tsc for type checking it will pass because all classNames that you reference to in your component actually exists.

If you reference to className that doesn't exist on styles.module.css, like styles.someAwesomeClassName

<div className={styles.someAwesomeClassName} />

You will get type error on tsc type check:

Property 'someAwesomeClassName' does not exist on type "{ readonly foo: string; readonly fooBar: string; readonly fooBarBaz: string; readonly myClassName: string; }"

If you want to keep compiled css files for some reason use -k option, see CLI usage section.

npx pcssmdts "src/**/*.module.css" -k

Your Component folder structure with -k option:

  src/
  ├─ components/
  │  ├─ Component/
  │  │  ├─ styles.module.css.d.ts <- generated type definitions
  │  │  ├─ __postcss__styles.module.css <- compiled css file
  │  │  ├─ styles.module.css
  │  │  ├─ Component.tsx
  │  │  ├─ index.ts

After tsc type check you may want remove all generated *.module.css.d.ts, it can be easily done with rimraf:

npx rimraf \"src/**/*.css.d.ts\"

CLI Usage

pcssmdts <source> [options]

Generate d.ts files for PostCSS powered css modules

Basic options:
  -v, --verbose  Run with verbose logging                              [boolean]
  -c, --config   Optionally provide custom path to your PostCSS config  [string]
  -k, --keep     Keep compiled css files                               [boolean]

typed-css-modules options:
  -n, --namedExports  Enables named export for generated d.ts files
                                                      [boolean] [default: false]

Positionals:
  source  source pattern to your css modules files location             [string]

Options:
      --help     Show help                                             [boolean]
      --version  Show version number                                   [boolean]

Examples:
  pcssmdts "src/**/*.module.css"            Basic usage, in the end next to each
                                            found css file corresponding d.ts
                                            file will be generated


  pcssmdts "src/**/*.module.css" -k         In this case compiled files will be
                                            preserved, all compiled files are
                                            prefixed by __postcss__


  pcssmdts "src/**/*.module.css" -c         Custom PostCSS config location path
  configs/postcss.config.js

  pcssmdts "src/**/*.module.css" -n         Named exports is used for generated
                                            files