背景
可能看到这个题目,你会觉得scss编译出错还用检测吗?
的确,scss出错的时候(比如@import路径出错),编译器会自动帮你检测,只要你注视着命令行,或者提交的时候检查一下,就没什么问题。
不过在我们组里,还是遇到过线上的scss编译出错问题,就像这样:
可能是某个程序员没有注视命令行,也没有在提交的时候人工检查一下,最后出了问题...
再加上,我们这边有个组件池,里面几十个组件repo,维护的人超过20个,大大提高了犯错的概率。所以急需工具解决这个问题。
分析
这里涉及到两个问题:
- 在什么阶段去检测
- 如何检测
比如在提交之前,去重新编译一遍scss,如果报错则不提交,否则提交。这里的在提交之前即是阶段,后面的则是检测手法。
在什么阶段去检测
我最先想到的是git hooks,这种方式需要在每个工程的.git文件里加上相应的hook,可以在pre-commit阶段,也就是提交之前进行检测。但是由于有几十个repo,个个都要加上很是麻烦。
或者加在gitlab-ci里,在push到远程仓库(挂在gitlab上的)时会触发CI,如果出错会收到报警邮件。不过,这同样存在上述的问题。
还有一种就是在部署的时候做检测。由于部署的时候会把引用到的组件repo全部拉下来,所以可以在这个阶段针对每个repo都做一下检测,如此只用改一下部署脚本。
如何检测
最简单的是直接用scss命令行重新编译一下。
另一种就是检测css里面有没有报错。下面是一段编译出错产生的css的报错信息:
/*
Error: Invalid CSS after "... width: 100%": expected "{", was ";"
on line 26 of src/slideModal/wap/component.scss
21: @include transition(0.25s opacity ease-out);
22: }
23:
24: .container{
25: position: fixed;sdf
26: width: 100%;
27: bottom: rem(-800);
28: height: rem(800);
29: background: white;
30: @include transition(0.25s bottom ease-out);
31: }
Backtrace:
src/slideModal/wap/component.scss:26
/Library/Ruby/Gems/2.0.0/gems/sass-3.4.22/lib/sass/scss/parser.rb:1189:in `expected'
/Library/Ruby/Gems/2.0.0/gems/sass-3.4.22/lib/sass/scss/parser.rb:1125:in `expected'
/Library/Ruby/Gems/2.0.0/gems/sass-3.4.22/lib/sass/scss/parser.rb:1120:in `tok!'
/Library/Ruby/Gems/2.0.0/gems/sass-3.4.22/lib/sass/scss/parser.rb:656:in `block'
/Library/Ruby/Gems/2.0.0/gems/sass-3.4.22/lib/sass/scss/parser.rb:726:in `declaration_or_ruleset'
/Library/Ruby/Gems/2.0.0/gems/sass-3.4.22/lib/sass/scss/parser.rb:676:in `block_child'
/Library/Ruby/Gems/2.0.0/gems/sass-3.4.22/lib/sass/scss/parser.rb:668:in `block_contents'
/Library/Ruby/Gems/2.0.0/gems/sass-3.4.22/lib/sass/scss/parser.rb:657:in `block'
/Library/Ruby/Gems/2.0.0/gems/sass-3.4.22/lib/sass/scss/parser.rb:726:in `declaration_or_ruleset'
/Library/Ruby/Gems/2.0.0/gems/sass-3.4.22/lib/sass/scss/parser.rb:676:in `block_child'
/Library/Ruby/Gems/2.0.0/gems/sass-3.4.22/lib/sass/scss/parser.rb:668:in `block_contents'
/Library/Ruby/Gems/2.0.0/gems/sass-3.4.22/lib/sass/scss/parser.rb:657:in `block'
/Library/Ruby/Gems/2.0.0/gems/sass-3.4.22/lib/sass/scss/parser.rb:649:in `ruleset'
/Library/Ruby/Gems/2.0.0/gems/sass-3.4.22/lib/sass/scss/parser.rb:675:in `block_child'
/Library/Ruby/Gems/2.0.0/gems/sass-3.4.22/lib/sass/scss/parser.rb:668:in `block_contents'
/Library/Ruby/Gems/2.0.0/gems/sass-3.4.22/lib/sass/scss/parser.rb:125:in `stylesheet'
/Library/Ruby/Gems/2.0.0/gems/sass-3.4.22/lib/sass/scss/parser.rb:41:in `parse'
/Library/Ruby/Gems/2.0.0/gems/sass-3.4.22/lib/sass/engine.rb:406:in `_to_tree'
/Library/Ruby/Gems/2.0.0/gems/sass-3.4.22/lib/sass/engine.rb:381:in `_render_with_sourcemap'
/Library/Ruby/Gems/2.0.0/gems/sass-3.4.22/lib/sass/engine.rb:298:in `render_with_sourcemap'
/Library/Ruby/Gems/2.0.0/gems/sass-3.4.22/lib/sass/plugin/compiler.rb:492:in `update_stylesheet'
/Library/Ruby/Gems/2.0.0/gems/sass-3.4.22/lib/sass/plugin/compiler.rb:215:in `block in update_stylesheets'
/Library/Ruby/Gems/2.0.0/gems/sass-3.4.22/lib/sass/plugin/compiler.rb:209:in `each'
/Library/Ruby/Gems/2.0.0/gems/sass-3.4.22/lib/sass/plugin/compiler.rb:209:in `update_stylesheets'
/Library/Ruby/Gems/2.0.0/gems/sass-3.4.22/lib/sass/plugin/compiler.rb:473:in `on_file_changed'
/Library/Ruby/Gems/2.0.0/gems/sass-3.4.22/lib/sass/plugin/compiler.rb:331:in `block in watch'
/Library/Ruby/Gems/2.0.0/gems/sass-3.4.22/vendor/listen/lib/listen/listener.rb:252:in `call'
/Library/Ruby/Gems/2.0.0/gems/sass-3.4.22/vendor/listen/lib/listen/listener.rb:252:in `on_change'
/Library/Ruby/Gems/2.0.0/gems/sass-3.4.22/vendor/listen/lib/listen/listener.rb:290:in `block in initialize_adapter'
/Library/Ruby/Gems/2.0.0/gems/sass-3.4.22/vendor/listen/lib/listen/adapter.rb:254:in `call'
/Library/Ruby/Gems/2.0.0/gems/sass-3.4.22/vendor/listen/lib/listen/adapter.rb:254:in `report_changes'
/Library/Ruby/Gems/2.0.0/gems/sass-3.4.22/vendor/listen/lib/listen/adapter.rb:323:in `poll_changed_directories'
/Library/Ruby/Gems/2.0.0/gems/sass-3.4.22/vendor/listen/lib/listen/adapter.rb:299:in `block in start_poller'
*/
对比几个出错的css文件,可以从中提取出特征:都有Error:
和Backtrace:
这两个字符串。那么可以通过这个特征来检测css文件是否出错。
回过头来想一下,其实用第一种方法是比较冗余的,因为我们只是为了找错,而不是想再编译一遍,这会产生性能上的差异,尤其当集中对每一个repo进行编译的时候,性能问题凸显,除非分发到pre-commit或者CI阶段,但是又会产生之前提到过的问题。
所以最终选择的方案是:在部署阶段,检测指定的css是否出错,若出错进而反推出相应的scss文件。
实现
文件遍历部分就不提了,用gulp实现很简单。
css检测脚本(基于gulp):
var through = require('through2');
var gutil = require('gulp-util');
var path = require('path');
var PluginError = gutil.PluginError;
const PLUGIN_NAME = 'gulp-isErrorInCSS';
function isErrorInCSS(prefixText) {
var errors = 0;
// 这里添加highWaterMark是因为默认只能遍历16个还是32个文件
return through.obj({highWaterMark: 999999}, function (file, enc, cb) {
if (file.isNull()) {
return cb(null, file);
}
if (file.isBuffer()) {
// 判断css文件内容里是否同时存在Error:和Backtrace:字符串
if (file.contents.toString().includes('Error:') && file.contents.toString().includes('Backtrace:')) {
// 给出出错css文件的相对路径
console.warn('Error In:' + path.relative('.', file.path));
errors++;
}
}
cb(null, file);
}, function (cb) {
if (errors) {
throw new PluginError(PLUGIN_NAME, errors + '个css出错!请到对应的源文件改正');
} else {
console.log('css没有出错');
cb();
}
});
}
module.exports = isErrorInCSS;
总结
最终的解决方案跟我们的开发环境有关,所以请慎重选择。
如有需要优化或错误的地方,感谢指出!