简体   繁体   中英

Detect use of non-existent classNames in CSS Modules/React

In my react project if I use some non-existent className from css modules file,

 // mycss.modules.scss

.thing { color: red }

// index.jsx

import styles from mycss.modules.scss

<div className={styles.otherThing}>Some div</div>

// Browser would return:

<div>Some div</div>

it quietly fails without letting me know that this class does not exist. How I can check if this class name exist or not and throw an error. Would be great to receive an error during build time, when saving file.

If you are open to typescript solution, I found one TS plugin for you.

typescript-plugin-css-modules

It can populate the styles object with type information for available keys.

You don't have to switch whole project to typescript, you can add // @ts-check directive at the top of this file to enable the TS engine design time check.

Unless you want to put forth a pull request to add something like a strict mode option to the webpack loader itself, i dont think theres much you can do since its just a base object. An easy alternative is to just do styles.styleName.toString() , this way if styleName is undefined, it will throw an error.

Actually it is possible in javascript code. But I think that className exist check is not good idea.

document.styleSheets[].rules[].selectorText

original link How can you determine if a css class exists with Javascript?

add this function to the top:

// index.jsx
import styles from mycss.modules.scss

function strictStyles (clsName){
  if(styles[clsName]){
    return styles[clsName]
  }else{
    throw "CSS class doesn't exist";
  }
}

...
<div className={strictStyles(otherThing)}>Some div</div>
...

NOTE: This solution does not require you to change any of your code, just add the loader and it should work out of the box. Please note the caveat about production builds at the end, or check the source for full instructions at Github.

I have created a Webpack loader that works with CSS/LESS/Other CSS module loaders.

The full source and readme can be found on GitHub .

For those who just want to add the loader to their project, it can be used like this:

Add this webpack loader source file somewhere, eg /webpack/loaders/css-module-proxy.js

/**
 * A CSS/LESS/Style module loader that prepends a proxy in non-production builds.
 *
 * The proxy checks if the loaded style module actually contains the style we are trying to fetch.
 * If it doesn't exist (its accessor returns undefined), we crash on debug (non-production) builds!
 *
 * Inspired by https://github.com/royriojas/css-local-loader
 */
module.exports = function cssLocalLoader(source, map) {
    this.cacheable();
    if (process.env.NODE_ENV !== "production") {
        // noMatch:
        // Makes sure that any access prefixed with underscore are filtered out
        // otherwise it will crash at runtime when Webpack is probing the locals export.
        // toJsonMatch:
        // Makes sure that toJSON access on the locals object gets proxied to the correct
        // toJSON function.
        const requireWrapper = `
            // If the access matches this regexp, skip it
            const oldLocals = exports.locals;
            const noMatch = /^[_]+/;
            const toJsonMatch = /^toJSON$/;
            const proxy = new Proxy(oldLocals, {
              get: function(target, name) {
                if (noMatch.test(name)) {
                    return undefined;
                }
                if (toJsonMatch.test(name)) {
                    return oldLocals.toJSON;
                }
                const clz = target[name];
                if (clz === undefined) {
                    throw new Error("Error: LESS / CSS class named \\"" + name + "\\" does not exist");
                }
                return clz;
              }
            });
            exports.locals = proxy;
        `;
        const newSource = `${source}\n\n${requireWrapper}`;
        this.callback(null, newSource, map);
    } else {
        this.callback(null, source, map);
    }
};

And then use it from your webpack config, example below is for LESS:

{
    test: /\.module\.less$/,
    use: [
        { loader: path.resolve("webpack/loaders/css-module-proxy.js") },
        {
            loader: "css-loader",
            options: {
                modules: true,
                importLoaders: 1,
                localIdentName: "[name]__[local]__[hash:base64:5]",
            },
        },
        { loader: "less-loader" },
    ],
},

Don't forget to build your release code with NODE_ENV=production or it may crash when a user visits your site...

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM