Skip to content

TasukuUno/typed-classnames-loader

Repository files navigation

typed-classnames-loader

Webpack loader for classnames auto bind and d.ts auto generation inspired by typings-for-css-modules-loader, typed-css-modules-loader and classnames-loader.

example

How to install

npm i -D typed-classnames-loader

Concept

When you import css-like files (css, scss, less, stylus etc.), you can access them like:

// Button.tsx
import * as React from 'react';
import { cx, cn } from './style.css'; 

interface Props {
  className?: string;
  primary?: boolean;
  children?: React.ReactNode;
}

const Button: React.SFC<Props> = ({ className, primary, children }) => {
  const classNames = cx('button', { primary });
  return (
    <button className={cn(className, classNames)}>
      {children}
    </button>
  );
};

export default Button;

At the same time, typed-classnames-loader will generate d.ts file like:

// style.css.d.ts
interface Style {
  button: string;
  primary: string;
}

declare namespace ClassNames {
  type DictionaryValue = boolean | undefined | null;
  interface Dictionary {
    [id: string]: DictionaryValue;
  }
  type Value = string | Dictionary | Values | null | undefined;
  interface Values extends Array<Value> {}
  type Fn = (...classes: Value[]) => string;
}

declare namespace ClassNamesBind {
  type Names = keyof Style;
  type DictionaryValue = boolean | undefined | null;
  type Dictionary = {
    [key in Names]?: DictionaryValue;
  };
  type Value = Names | Dictionary | Values | null | undefined;
  interface Values extends Array<Value> {}
  type Fn = (...classes: Value[]) => string;
}

export declare const style: Style;
export declare const classNames: ClassNames.Fn;
export declare const classNamesBind: ClassNamesBind.Fn;
export declare const cn: typeof classNames;
export declare const cx: typeof classNamesBind;

export default cx;

Configuration

It may be easier to see basic example.
typed-classnames-loader actually provides 2 loaders. We will realize the function by using both of them.

  1. typed-classnames-loader/classname
  • This provides auto bind import using classnames/bind.
  • This should be connected to top of loaders (using pitch phase).
  1. typed-classnames-loader/type
  • This generates *.d.ts file according to the result of css-loader.
  • This should be connected to next to css-loader

So, an excerpt from webpack.config may be like this:

rules: [
 {
   test: [
     /\.css$/,
   ],
   use: [
     {
       // 1. loader for classnames auto bind
       loader: 'typed-classnames-loader/classnames',
     },
     {
       loader: 'style-loader',
       options: {
         sourceMap: true,
       },
     },
     {
       // 2. loader for d.ts auto generation
       loader: 'typed-classnames-loader/type',
     },
     {
       loader: 'css-loader',
       options: {
         sourceMap: true,
         modules: true,
         localIdentName: '[hash:base64]',
       },
     },
   ],
 },
],

Options

It may be easier to see customize example.

typed-classnames-loader/classnames

there are no options currently.

typed-classnames-loader/type

key type default description
indent object specify indent of *.d.ts
indent.type 'space' | 'tab' 'space' space or tab
indent.size number space: 2
tab: 1
length of indent character
banner string undefined some text added to top of *.d.ts
template function undefined a function to build custom format of *.d.ts

So, an excerpt from webpack.config may be like this:

{
  loader: 'typed-classnames-loader/type',
  options: {
    indent: {
      type: 'tab',
      size: 1
    },
    banner: '// auto generated by typed-classnames-loader',
    template: (classNames, original) => {
      return `// there are ${classNames.length} classNames.\n${original}`;
    },
  },
}

Contributing

The author is new to publish npm. Please feel free to make PRs or issues! Thank you for your help! 😄