检测 CSS Modules/React 中不存在的类名的使用

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

在我的 React 项目中,如果我使用 css 模块文件中不存在的 className,

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

它悄悄地失败了,却没有让我知道这个 class 不存在。我如何检查此 class 名称是否存在并抛出错误。保存文件时在生成期间收到错误会很棒。

实际上在javascript代码中是可能的。但我认为 className 是否存在检查不是个好主意。

document.styleSheets[].rules[].selectorText

原创linkHow can you determine if a css class exists with Javascript?

将此功能添加到顶部:

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

除非您想提出拉取请求以向 webpack 加载程序本身添加类似严格模式选项的内容,否则我认为您无能为力,因为它只是一个基础对象。一个简单的替代方法是只执行 styles.styleName.toString(),这样如果 styleName 未定义,它将抛出错误。

如果您对 typescript 解决方案持开放态度,我为您找到了一个 TS 插件。

typescript-plugin-css-modules

它可以使用可用键的类型信息填充 styles 对象。

您不必将整个项目切换到 TypeScript,您可以在此文件的顶部添加 // @ts-check 指令以启用 TS 引擎设计时检查。

注意:此解决方案不需要您更改任何代码,只需添加加载程序,它应该开箱即用。请注意最后关于生产构建的警告,或在 Github.

查看源代码以获取完整说明

我创建了一个与 CSS/LESS/Other CSS 模块加载器一起工作的 Webpack 加载器。

The full source and readme can be found on GitHub.

对于那些只想将加载器添加到他们的项目中的人,可以这样使用:

在某处添加这个 webpack 加载器源文件,例如 /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);
    }
};

然后从你的 webpack 配置中使用它,下面的例子是 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" },
    ],
},

不要忘记使用 NODE_ENV=production 构建您的发布代码,否则当用户访问您的站点时它可能会崩溃...