检测 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 插件。
它可以使用可用键的类型信息填充 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
构建您的发布代码,否则当用户访问您的站点时它可能会崩溃...
在我的 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 插件。
它可以使用可用键的类型信息填充 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
构建您的发布代码,否则当用户访问您的站点时它可能会崩溃...