记一次使用`stylus-converter`将stylus转scss的过程

作者&投稿:东郭宙 (若有异议请与网页底部的电邮联系)
~

公司的前端项目基本上都是用的scss预处理器,但是有两个Vue项目因为历史原因是stylus和scss混用的情况,所以有了将stylus转为scss的需求。经过一番搜索,我们找到了stylus-converter这个插件,但是仍然还存在一些需要手动机解决的问题。

发现问题

首先按照教程操作一波目录的转译:

#下载stylus-converternpminstall-gstylus-converter#进入项目目录mvsrcsrc-temp#运行cli转换目录stylus-conver-dyes-isrc-temp-osrc

很快就转译结束了,赶紧run一下,结果马上出问题:

Theserelativemoduleswerenotfound:*xxx/xxx.stylin./src/main.js

好家伙,原来在入口文件引入的一个styl文件被改成scss文件后找不到了,这个插件只编译了.stylus后缀的文件和.vue中的包含stylus字符串的<style>标签,源码中对应的正则是/\.styl$/,/\.vue$/和/<style(.*)>([\w\W]*?)<\/style>/g。

手动把import的stylus文件都改成对应的scss文件后,再次查看控制台,发现还是报错,有一堆的mixin找不到import,转译前后的import应该是不会变更的,为什么在转移后就找不到了呢?猜想是配置了stylus的全局导入,一看webpack的配置果然如此,项目用来style-resources-loader将stylus样式预导入了全局的mixin,将其改成对应的scss导入即可。

计算表达式转译错误

再次运行,这回是新的报错:

SassError:Expectedexpression.?78│flex:00,100/$n%;│^?

转译前是:flex:00(100/n)%转移后:flex:00,100/$n%;

很显然这在sass是一个语法错误,看了下这类数量不多,就手动改掉了,比如这个改成了:flex:00,(100/$n)*1%;

样式穿透错误

再次运行,发现了新的报错:

SassError:expectedselector.?5│margin:20px00;/deep/.table{│^?

转译前是:

.xxclassnamemargin:20px00>>>.table//...

看了下源码是这样子替换深度选择器的:

result=result.replace(/(.*)>>>(.*)/g,`$1/deep/$2`)

我看了下代码里的深度选择器,都是可以直接通过字符串替换解决的,也就是/deep/替换成::v-deep,于是一键替换了。

这里还有个注意点,就是可能有这样的代码:/deep/div{,如果简单的替换成::v-deepdiv{那肯定有问题,所以替换的::v-deep是包含一个空格符的。

Mixin签名差异

再次运行,发现了新的报错:

SassError:Only0argumentsallowed,but2werepassed.┌──>src/views/trace/detail.scss6│@includefontHeight(30px,12px);│^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^invocation?┌──>/Users/grapevine/Documents/chan/cmm-wap/src/views/trace/detail.vue12│@mixinfontHeight(){│━━━━━━━━━━━━declaration?

看下源码:

fontHeight()if(length(arguments)==1)height:arguments[0]line-height:arguments[0]font-size:arguments[0]elseheight:arguments[0]line-height:arguments[0]font-size:arguments[1]

转移后:

@mixinfontHeight(){@iflength($arguments)==1{height:map-get(arguments,0);line-height:map-get(arguments,0);font-size:map-get(arguments,0);}@else{height:map-get(arguments,0);line-height:map-get(arguments,0);font-size:map-get(arguments,1);}}

使用这个mxin的地方:

@includefontHeight(30px,12px);

好家伙,原来是stylus的@mixin的签名里参数可以为空,实际使用时可以传入参数,通过arguments关键字去获取参数。而scss必须在mixin签名显示指定参数,于是乎查阅scss文档然后改造了一番:

Theserelativemoduleswerenotfound:*xxx/xxx.stylin./src/main.js0

scss可以使用剩余参数,然而比较不符合直觉的是这个剩余参数是index是从1开始的……

好了,到这里控制台就没有报错了,但是还不能高兴太早,run起来看看。

果然最担心的事情还是发生了,没有报错,但是界面上的样式明显是有问题的,经过一番定位,发现了几个问题。

CSS属性同名的Mixin问题

首先发现的是css属性同名的Mixin问题,因为stylus全局定义了一些Mixin,并且用webpack插件进行全局导入,而stylus使用Mixin是不需要显示指定@include,所以会出现如下情况:

转译前:

Theserelativemoduleswerenotfound:*xxx/xxx.stylin./src/main.js1

Theserelativemoduleswerenotfound:*xxx/xxx.stylin./src/main.js2

而在转译时,检测到border-top是一个合法css属性,所以不会将其解析成mixin。真是令人头疼。

一个比较好的解决方法是在stylus语法分析时,将和我们定义的mixin同名的css属性标记成mixin而不是普通的属性,但是鉴于对stylus语法分析流程不熟悉,所以退而求其次:先遍历一次找到所有的mixin,收集到和css同名的mixin集合中;然后在遍历第二次,在遍历css属性时判断该属性是否在和css同名的mixin集合中,如果是则打印当前文件名和代码位置,然后手动去修改代码。

说干就干,首先将项目clone到本地,然后用vscode进行调试,编写launch.json:

Theserelativemoduleswerenotfound:*xxx/xxx.stylin./src/main.js3

通过debug很快就能找到stylus的解析器:node_modules/stylus/lib/parser.js,在Parser这个函数上定义了状态机针对不同词法的解析,我们首先需要找到mixin节点在解析后的节点类型是什么样的。

在lib/index.js中,我们可以找到这样一段代码:

Theserelativemoduleswerenotfound:*xxx/xxx.stylin./src/main.js4

取消这个注释,我们写一段简单的stylus代码看看ast是什么样的,定义一个mixin:

Theserelativemoduleswerenotfound:*xxx/xxx.stylin./src/main.js5

打印结果:

Theserelativemoduleswerenotfound:*xxx/xxx.stylin./src/main.js6

可以看到这个节点就是我们要的:

Theserelativemoduleswerenotfound:*xxx/xxx.stylin./src/main.js7

接下来去paser.js中寻找语句的解析:

Theserelativemoduleswerenotfound:*xxx/xxx.stylin./src/main.js8

打印结果:

Theserelativemoduleswerenotfound:*xxx/xxx.stylin./src/main.js9

可以发现和整棵ast树中对应的节点是一致的。

我们也能发现mixin节点的特性,所以我们在每次statement解析后收集它:

SassError:Expectedexpression.?78│flex:00,100/$n%;│^?0

一运行,结果都是空的,咋回事呢?通过debug我们发现,其实它的节点长这样:

SassError:Expectedexpression.?78│flex:00,100/$n%;│^?1

仔细一番查看发现是这些节点都重写了原型上的toJSON方法,导致打印出来的节点有所差异,可以在node_modules/stylus/lib/nodes查看这些节点。

所以我们需要修改代码:

SassError:Expectedexpression.?78│flex:00,100/$n%;│^?2

再次运行,可以发现其正确收集到了所有的mixin:

SassError:Expectedexpression.?78│flex:00,100/$n%;│^?3

我们需要从这个集合中挑出和css属性同名的mixin。

接下来需要找到属性是否包含mixin,需要在parser的indent中的atrule情况判断:

case'atrule':constp=this.property();//检测所有的属性是否包含指定的MixinconstMixins=SassError:Expectedexpression.?78│flex:00,100/$n%;│^?3letcurrentMixin=[];if(Array.isArray(p.segments)&&p.segments.some(e=>{currentMixin=Mixins.filter(mixin=>mixin==e.name)returncurrentMixin.length})){//输出当前文件位置和currentMixin}}returnp

那么问题来了,如何获取当前文件位置呢?在Parser遍历节点的过程中并没有上下文,无法自上而下传递信息,我想到的一个方法是用全局属性,在读取文件的时候把文件名挂载到global.DIR属性上,但是这样子必须确保文件的IO操作是同步的,所以我将源码中的文件API从异步改成了同步,比如fs.readFile改成fs.readFileSync等。

在文件读取的入口bin/convertStylus.js中的functionconvertStylus中fs.readFileSync后记录当前文件的位置:global.DIR=input;。

回到我们节点属性的判断:

SassError:Expectedexpression.?78│flex:00,100/$n%;│^?5

运行之后,成功获取到每个和css属性同名mixin的引用位置,可以手动去修改它。

插件去括号的问题

在一桶猛如虎的操作后,接着又发现了一个样式问题,在浏览器元素审查后发现这个报错:

transform:translate(-50%,-50%)scale(10/12,10/12);

除法符号没有计算成功,查看代码:

转译前:

SassError:Expectedexpression.?78│flex:00,100/$n%;│^?6

转移后:

SassError:Expectedexpression.?78│flex:00,100/$n%;│^?7

唉我括号呢?

这回我们在插件的代码中去寻找解决方法,因为我发现lib/index.js中的visitNode()会遍历每个节点,在这里寻找stylusAST中有/计算符号的节点:

SassError:Expectedexpression.?78│flex:00,100/$n%;│^?8

然后手动去把括号加上。

总结

总结一下遇到的3个问题:样式穿透选择器只能转译成/deep/是有问题的,应该提供一个可选项让用户选择什么样的选择器;Mixin签名差异、计算表达式和去括号的操作有明显的bug;而CSS属性同名的Mixin问题我觉得比较特殊,因为我们的开发小伙伴当初应该是为了解决1px的问题而做的,虽然这种做法极其不推荐,但是插件在这方面还是有优化空间的。

一开始没有想到会踩这些坑,早知道如此,就在一开始从AST的角度去解决问题,而不是这种半自动化的操作。如果时间充足,倒是想给作者提一下PR,但是限于时间关系,只能止步于此。

最后还是感谢sylus-convert这个插件给我们的业务带来的便利。

原文:https://juejin.cn/post/7097491392854753287


怎样去除产后妊辰纹 - 百度宝宝知道
求问一下谁上海的啊郁闷问问产后妊辰纹怎么去除? 有效去斑?怎样能去除脸上的斑?怎样能快速去产后黄褐斑? 产后怎样去除黑色素,怎样才能恢复原来的体 育儿知识宝宝天生都爱吃糖,妈妈不用谈糖色变 让宝宝吃奶吃得舒服是门技术活 给宝宝的辅食添加过程中要避免3大误区 清除影响宝宝食欲的拦路虎让宝宝爱吃饭 宝宝...

css预处理器有哪些
Stylus的语法花样多一些,它的文件扩展名是“.styl”,Stylus也接受标准的CSS语法,但是他也像Sass老的语法规则,使用缩进控制,同时Stylus也接受不带大括号({})和分号的语法,如下所示: \/*style.styl*\/\/*类似于CSS标准语法*\/h1 { color: #963;background-color:#333;}\/*省略大括号({})*\/h1color: #963; bac...

在Vue中如何使用JSX,就这么简单!
(不想用stylus可跳过,样式这块可随意)<anchored-heading:level="1">Helloworld!<\/anchored-heading>0 各种style安装见:https:\/\/www.cnblogs.com\/jimc\/p\/10265198.html 安装完成后新建HelloWorld.styl,然后引入。 stylus的使用:https:\/\/www.jianshu.com\/p\/5fb15984f22d stylus官网:https:\/\/stylus.zcopy.site...

专访沃尔沃大中华区销售公司售后服务副总裁方锡智先生
换位思考下,日常用车一般1星期充电1次到1.5次,而沃尔沃车主享受到的免费充电频率远超日常需求。 第二,纯电车也有特别道路救援,如果电车抛锚,我们的拖车可以...要强调的一点就是,我们的承诺不是虚的广告语,过去一年时间,我们让这件事情初步落地了,阶段性成果得以展现,未来一年还要把这个服务在全国270多<font styl...

求游戏情侣名字要好听的
╭这一次、我们生死相许 | ╭这一次、我们不弃不离ツ痛苦在延伸゛ | ツ思恋在蔓延゛Hi,treasure。 | Hey,darling。 已赞过 已踩过< 你对这个回答的评价是? 评论 收起 匿名用户 2019-01-07 展开全部 推荐你用这个,可以根据包含的字,字数、类型找到所有好听的游戏名字。游戏名字大全(https:\/\/name.ku7...

时尚的英文单词怎么拼?
通管针一种外科用探针 Obsolete 【废语】A pen.钢笔style v.tr. styled; styl.ing; styles;To call or name; designate: 称呼称为,叫做;指称:George VI styled his brother Duke of Windsor.乔治六世称他哥哥为温莎公爵To make consistent with rules of style: 整理使按一定格式连贯:style a manuscript.整...

迈克尔杰克逊的娘娘腔怎么回事?
是他唱歌的特色,不是什么娘娘腔,应该算是假声唱法吧。所谓“假声”是指演唱时通过有意识的控制而只使部分声带发生振动所发出来的声音,这种声音比由整个声带都振动的“真声”要高要弱,而且音色有一种晶亮透明的感觉。大部分流行歌手(无论男女)是用真声演唱的,以获得一种自然、亲切、贴近口语的风格...

语言运用
styl�93A�20.下面是某省气象台发布的寒潮蓝色预警的部分内容,其中有五处词语使用不当,请找出并作修改。要求修改后语意准确,语体风格一致。(5分) 省气象台24日发布寒潮蓝色预警信号。受强冷空气不断影响,估计今天夜间至26日,我省将出现大风和很冷天气。气温将明显下降,过程降温幅度大:内陆地区-8℃左右,沿海...

...沙柳等植物.某人一次种植了n株沙柳.各株沙柳的成活与
(2) 由题意知, 服从二项分布B(n,p),P( =k)= (1-p) n - k ,k=0,1,…,n.(1)由E( )=np=3, 2 =np(1-p)= ,得1-p= ,从而n=6,p= . 的概率分布为 0 1 2 3 4 5 6 ...

我的老师初三作文
记得有一次语文考试,老师说:“凡是古诗文默写错了的,一个罚抄一百遍”。我很荣幸地中招了。当天晚上...他可以津津有味地用一节课给我们讲一首诗,也可以用一节课来给我们讲作文,只是因为作文是最后一节...我的英语老师唯一的爱好就是跳江南styl,他喜欢笑,讲起课来很随意,讲着课随便都可以插入5分钟的笑话...

尚志市15283799592: 一种计数器,设置为每隔4.5小时记一次数,笫9次计数时,时间为11:00? -
项注疏血: 4.5*9=40.5 40.5-24=16.5 11往前 16.5个小时 开始时间是7:30

尚志市15283799592: 英语里介词的用法怎么最好记
项注疏血: 一、体会它们大致的、原始的意思,比如“in”的意思是“在......里面”,“from”是“从......来” 二、了解它们的基本用法,“for”是“为了、给”通常用来表示某人给某人做什么,或某人做某事为了什么

尚志市15283799592: 小石潭记第一次段运用了什么修辞方法.有什么表达效果? -
项注疏血: 小石潭记第一次段运用了什么修辞方法.有什么表达效果? 第一段,作者采用的是“移步换形”的手法,在移动变换中引导我们去领略各种不同的景致,具有极强的动态的画面感. “从小丘西行百二十步,隔篁竹,闻水声,如鸣佩环,心乐之...

尚志市15283799592: 如何记住药物使用方法 -
项注疏血: 首先要有医药学基础`然后见过很明显适用于该药的症状`用过一次后`下次就很快反应过来的`这个过程是慢慢积累的`没有捷径`只能靠平时用心`多接触`

尚志市15283799592: 1~5月实现的利润是属于哪个会计科目? -
项注疏血: 1-5月实现的利润属于哪个会计科目? 属于利润表中的“利润总额”或者“净利润”科目,具体属于哪个要看是需要净利润数据还是税前利润的数据.同时,1-5月实现的利润可以从每月的利润表中的本期数提取数据相加得到. 或者在5月份利润表中的本年累计数就是1-5月份的利润数据了.

尚志市15283799592: 驾车宝典科目一考试 -
项注疏血: 科目一考试技巧技巧之一:罚款的选择题:有200—2000选“200—2000”,没有就选“20—200”,若二者都没有就选“2000”技巧之二:违章处罚:有警告选警告;没有警告的:(1)与车有关或伪造证的选扣机动车(无车则吊销驾驶证...

尚志市15283799592: C#.CS中如何实现记住账号和密码 -
项注疏血: C#.CS中实现记住账号和密码 ,可以有以下的几种方式1:可以采取在本地存储的方式 如本地使用config.ini文件存储记录用户名和加密过的密码2:云端存储的方式 通过api访问云端数据存储,存储用户名和加密过的密码 那么接下来,在登录的过程中先从本地或者云端读取用户名和密码 ,来实现自动登陆.

本站内容来自于网友发表,不代表本站立场,仅表示其个人看法,不对其真实性、正确性、有效性作任何的担保
相关事宜请发邮件给我们
© 星空见康网