TypeScript definitions files for scss module doesnt work

253 views Asked by At

I just create a React project use rollup, below is my rollup config file:

rollup.config.js

import serve from "rollup-plugin-serve";
import livereload from "rollup-plugin-livereload";
import babel from "@rollup/plugin-babel";
import { nodeResolve } from "@rollup/plugin-node-resolve";
import commonjs from "@rollup/plugin-commonjs";
import replace from "@rollup/plugin-replace";
import image from "@rollup/plugin-image";
import postcss from "rollup-plugin-postcss";

const isDevelopment = process.env.NODE_ENV === 'development';

const extensions = [".js", ".jsx", ".ts", ".tsx"];

export default {
  input: isDevelopment ? "src/index.tsx" : "src/index.ts",
  output: [
    {
      file: "dist/iife/bundle.js",
      format: "iife", // for local dev
      sourcemap: true,
    },
    {
      file: "dist/esm/bundle.js",
      format: "esm", // for esm
      sourcemap: true,
    },
  ],
  plugins: [
    image(),
    postcss({
      autoModules: true,
      extensions: [".css", ".scss"],
    }),
    nodeResolve({
      extensions,
    }),
    replace({
      preventAssignment: true,
      "process.env.NODE_ENV": JSON.stringify("development"),
    }),
    commonjs(),
    babel({
      babelHelpers: "bundled",
      extensions,
    }),
    serve({
      open: true,
      verbose: true,
      contentBase: ["", "public"],
      host: "localhost",
      port: 3000,
    }),
    livereload({ watch: "dist" }),
  ],
};

and I use typed-scss-modules package to generate TypeScript definition files for scss file.

styles.module.scss

.test-test {
  color: red;
} 

the generated types file like this:

styles.module.d.ts

export type Styles = {
  testTest: string;
};

export type ClassNames = keyof Styles;

declare const styles: Styles;

export default styles;

And when I use the styles in my React component, it seems that the styles.testTest is not passed to the dom correctly.

App.tsx

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

const App: React.FC = () => {
  return <div className={styles.testTest} {...restProps}>{children}</div>;
};

enter image description here

The div element receive an undefined instead of test-test class. Does anyone know the reason?

2

There are 2 answers

2
Lin Du On BEST ANSWER

This library only generates TypeScript definitions, types will be erased after compiling. it will not touch the runtime code. The runtime CSS class name is still test-test. That's why you got undefined.

Try the --nameFormat: none option, the default value is camel.

none: do not modify the given class names

typed-scss-modules ./styles.module.scss --nameFormat none --exportType default

Output:

export type Styles = {
    'test-test': string;
};

export type ClassNames = keyof Styles;

declare const styles: Styles;

export default styles;

Use it like className={styles['test-test']}

0
Richard Tyler Miles On

I figure I'll update this with more naming availability. The code below will allow the camel case convention and the natural non changing locals (can cause style overlap). This allows for dot access syntax on all class names classes.colMd6 which is, IMHO, preferable over classes['col-md-6'].

rollup.config.mjs

import resolve from '@rollup/plugin-node-resolve';
import commonjs from '@rollup/plugin-commonjs';
import typescript from '@rollup/plugin-typescript';
import postcss from 'rollup-plugin-postcss';
import includePaths from 'rollup-plugin-includepaths';
import simplevars from 'postcss-simple-vars';
import nested from 'postcss-nested';
import autoprefixer from 'autoprefixer';

import {readFileSync} from "fs";

const pkg = JSON.parse(readFileSync('package.json', {encoding: 'utf8'}));
const config = JSON.parse(readFileSync('tsconfig.json', {encoding: 'utf8'}));

// @link https://stackoverflow.com/questions/63128597/how-to-get-rid-of-the-rollup-plugin-typescript-rollup-sourcemap-option-must
//const production = !process.env.ROLLUP_WATCH;

const postCss = postcss({
    sourceMap: true,

    plugins: [
        autoprefixer(),
        simplevars(),
        nested()
    ],
    extensions: ['.css', '.scss'],
    extract: true,
    modules: {
        localsConvention: 'camelCase',
    },
    syntax: 'postcss-scss',
    use: ['sass'],
})


const plugins = [
    includePaths({paths: [config.compilerOptions.baseUrl]}),
    resolve({
        extensions: ['.js', '.jsx', '.ts', '.tsx']
    }),
    commonjs({
        namedExports: {
            // This is needed because react/jsx-runtime exports jsx on the module export.
            // Without this mapping the transformed import {jsx as _jsx} from 'react/jsx-runtime' will fail.
            'react/jsx-runtime': ['jsx', 'jsxs', 'Fragment'],
        },
    }),
    typescript({
        sourceMap: true, // !production,
        inlineSources: false, // !production
    }),
    postCss
]

// noinspection JSUnresolvedReference
const externals = [
    ...Object.keys(pkg.dependencies || {}),
    ...Object.keys(pkg.devDependencies || {}),
    ...Object.keys(pkg.peerDependencies || {}),
    ///.*\\.scss$/
]

console.log('externals', externals)

const globals = []

externals.forEach((external) => {

    globals[external] = external

})

console.log(globals)

export default [
    // browser-friendly UMD build
    {

        input: 'src/index.ts',
        external: externals,
        output: {
            name: 'CarbonReact',
            file: pkg.browser,
            format: 'umd',
            globals: globals,
            sourcemap: true
        },
        plugins: plugins
    },

    // CommonJS (for Node) and ES module (for bundlers) build.
    // (We could have three entries in the configuration array
    // instead of two, but it's quicker to generate multiple
    // builds from a single configuration where possible, using
    // an array for the `output` option, where we can specify
    // `file` and `format` for each target)
    {

        input: 'src/index.ts',
        external: externals,
        plugins: plugins,
        output: [
            {file: pkg.main, format: 'cjs', sourcemap: true},
            {preserveModules: true, dir: pkg.module, format: 'es', sourcemap: true}
        ]
    }
];

npm build The build command below will be required to be in your package.json to run.

"build": "sass ./src:./src && typed-scss-modules 'src/**/*.?(s)css' --nameFormat camel --nameFormat none --exportType default src && rollup -c"

The package typed-scss-modules supports live reloads/watches using the flag -w or --watch