from http://jlongster.com/Compiling-JSX-with-Sweet.js-using-Readtableshtml
JSX http://facebook.github.io/react/docs/jsx-in-depth.html 是一个 Facebook 项目,给 js 嵌入了 xml-like 语言,它是 React http://facebook.github.io/react/ 中标志性的使用特点。不少人喜欢它而且发现它很是好用。不幸的是他须要独立的编译器,而且不能和其余语言混合或者扩展。我用 sweet.js 宏 http://sweetjs.org/ 实现了一个 JSX “编译器” https://github.com/jlongster/jsx-reader,于是你能够把 jsx 和其余任何宏语言扩展一块儿放手边随时供使用。node
我预见,预见有人能给 js 语言添加一些难懂的特性,好比模式匹配 https://github.com/natefaubion/sparkler ,而后我只须要安装模块而后使用它。在 js 影响深远的今天,我认为这种语言扩展能力是很是重要滴。react
这很重要,不只仅是由于我或者你能拥有原生 goroutines https://gobyexample.com/goroutines 或者 原生持久数据解构 http://swannodette.github.io/mori/ 语法 。它更使人难以置信的是,咱们用的野生的特性,可能成为 ES 标准的一部分,甚至成为标准的 JS 的一部分。将来的 js 会因为咱们的反馈变得更好。咱们须要模块化的方式来扩展 js,一但这个可行,会无缝的与无数的扩展共享成果。webpack
我不许备解释为啥 sweet.js 宏是这个目标的答案。若是你想听更多,看个人 JSConf 2014 演讲 https://www.youtube.com/watch?v=wTkcGprt5rU 。若是你准备写负面的评论,请先阅读这篇 http://jlongster.com/Compiling-JSX-with-Sweet.js-using-Readtables#concernedgit
我为啥在 sweet.js 中实现一个 JSX 呢?若是你使用 JSX,如今你能够在任何其余的可用宏 https://www.npmjs.org/search?q=sweet-macros 边上使用 jsx-reader https://github.com/jlongster/jsx-reader。想要原生语法给持久化数据结构?继续读下去。。。es6
jsx 这么工做:xml 元素转换为简单的 js 对象。github
var div = <div> <h1>{ header }</h1> </div>;
这转化为:web
var div = React.Dom.div(null, React.DOM.h1(null, header));
http://sweetjs.org/ 直到这周 jsx 都没法把它实现,是什么缘由让这作到了? 可读表 Readtables http://sweetjs.org/doc/main/sweet.html#reader-extensions正则表达式
我简单解释下 sweet.js 的一些技术环境。Sweet.js 主要工做在口令层面,不是 AST,AST是惟一能扩展语言的组成部分的方式 见 *。其中的算法大量的来自 10 多年的 Lisp Scheme 社区尤为是 Racket http://docs.racket-lang.org/ 的工做基础之上算法
咱们的想法是,你在一个轻量的口令树之上提供语言的宏定义,而后展开这些口令。定制的模式匹配 http://sweetjs.org/doc/main/sweet.html#rule-macros 和 全自动卫生 http://sweetjs.org/doc/main/sweet.html#hygiene 让它能够作很是复杂的扩展,而且你能够从 sourcemaps 上得到更多。
生成的管道看起来像这样:
expand 阶段本质上是给语言解析器添加了展开性。这对不少特性都很好。好比 类型 模块 等那些须要整个程序的信息,还有那些在解析 parse 阶段或者生成阶段最好有 AST 支持的特性。如今咱们尚未能展开到那个阶段。
不多有特性须要扩展到 read 阶段, JSX 就是其中之一。 Lisp 早就有一个叫作 readtables 的东西了,我最近意识到咱们的 js 须要相似的东西,以支持 JSX 的实现。所以我实现了它 https://github.com/mozilla/sweet.js/pull/340 !
有几个缘由 jsx 须要做为一个 reader 生效而不是做为宏:
Reader 扩展容许你安装一个自定义的 reader 当在源码中遇到一个特殊的单词的时候调用。你能读取你须要的足够多的源码,而后返回一个口令组。reader 扩展只能被用标点的 punctuators 触发。(好比 < % 等符号),所以你不能作好比改变引号起做用的方式这类可怕的事情。
一个可读表是一个给 reader 扩展用的字符表。在这里 http://sweetjs.org/doc/main/sweet.html#reader-extensions 的这个文档中阅读更多来深刻了解。
- 关于这点我有许多思考和论点,我会在将来的文章中展开来讲。在 AST 层面工做是一个很棒的特性,这须要整个程序,好比类型和代码分析的优化。看这篇 http://blog.fogus.me/2012/04/25/the-clojurescript-compilation-pipeline/ 来了解关于这个想法的一个很是好的解释。
如今咱们有了可读表,咱们能够实现 jsx 啦!我已经用 jsx-reader https://github.com/jlongster/jsx-reader 作到了。他是一个文字的 jsx 编译器端口,机智的作到了全部的空白规则以及其余的边缘状况。(但愿如此)
加载一个 reader 扩展,传入模块名给 sweet.js 用 -l 编译 sjs 。这是全部的步骤:
$ npm install sweet.js $ npm install jsx-reader $ sjs -l jsx-reader file.js
固然你也能够加载任何其余的宏,一块儿做用在你的文件上。这是一个组合的很漂亮的语言扩展(试试 es6-macroshttps://github.com/jlongster/es6-macros)
我已经建立了一个 webpack loader https://github.com/jlongster/sweetjs-loader 和一个 gulp loader https://github.com/jlongster/gulp-sweetjs 并且是最新的支持 readtable 加载的。
这是测试版的软件我在一些小的原生的 jsx 编译器的测试用例上测试经过了,此外它也在一些大文件上工做良好。However,依然有一些小 bugs 和边缘状况须要被人们发现他以后修补。
你不只仅能得到可靠的 sourcemaps (传入 -c 给 sjs),并且你也会比原生 jsx complier 有更好的错误提示。例如:若是你忘了关闭一个标签:
var div = <div> <h1>Title</h1> <p> </div>
你会获得一个漂亮的,感受的错误信息,明确的指出你的代码中的错误
SyntaxError: [JSX] Expected corresponding closing tag for p 4: </div> ^ at Object.readtables.readerAPI.throwSyntaxError (/Users/james/tmp/poop/node_modules/sweet.js/lib/parser.js:5075:23)
有一个退步就是,这比原生的 jsx compiler 慢。一个 2000 行代码的大文件会花费 ~.7s 来编译(排序了预热时间,由于你会在大多数项目中使用监听器 watcher),原生的只要 ~0.4s。实际上,这不是特别明显,由于大多数文件是不少的更小的东西,大部分在几百毫秒就编译完了。固然,sweet.js 会更努力滴优化这个时间滴。
React 和持久化数据结构一块儿工做会表现得更好。问题是 js 原生没有它们,可是幸运的是有 mori http://swannodette.github.io/mori/ 这些库可用。只是你不再能用对象字面值了;你不得不 mori.vector(1,2,3) 来代替 [1,2,3];
若是咱们给 mori 实现一个字面的语法会怎样?可能你就用 #[1,2,3] 来建立持久的矢量,而 #{x: 1, y: 2} 来建立持久的表 map 。那也太棒了吧!(不幸的是我如今还没作到,但我很是渴望作到这点。。。)
如今每一个用 jsx 的人均可以在 React 中用个人字面语法来持久化数据结构。这真的是一个给力的工具包。
给 js 添加新的语法,特别拥有大块展现区域的好比 jsx。必须仔细的实现。它必须 100% 向后兼容,而且和 ES.next 特性同样在脑中完成。
jsx-reader 和 原生 jsx 编译器都查找 < 口令并触发一个 jsx 表达式的解析。虽然这个有一个关键的不一样。你可能注意到 jsx-reader 做为一个 reader,被源代码调用的时候是没有环境的。原生的 jsx 编译器用被猴子们打了补丁 monkeypatches 的 esprima 来调用 < ,当解析一个表达式的时候,所以它更容易保证正确的解析。 < 在 js 表达式中从不会有效,所以它能这么用它。
jsx-reader 也是用 < 调用,当它在源码的任何地方出现的时候,甚至是比较大小的操做符的时候。这让人提心吊胆的;咱们须要更仔细。可是我想到了一个可行的算法。你不会一直须要一个 ast。
jsx-reader 开始解析 < 而后任何它后面的东西做为 jsx 表达式,若是它遇到一些肯定不指望的点,它停下来。大多数时候,它能很早的发觉 < 以后是或者不是 jsx 表达式。算法来了:
我很快的打出了这些,可能有更好的方式啦,可是你应该了解了这思路。主要是通常状况下很容易消除歧义,可是全部的边缘状况都须要生效。边缘状况不是特别高性能,由于咱们的 reader 可能要作不少工做而后丢弃它,可是 99.9% 的状况下不会发生这种事情。
在咱们的算法中,若是咱们说 read 而不说相应的的 “若是失败了 bail”,它会抛出一个错误。咱们依然能够给出好的错误提示,当遇到二义性的 js 边缘状况的时候。
这儿有几个咱们的 reader bails的地儿:
咱们获取这个实际的优势:表达式像 x < y foo 在 js 中不会存在。这儿,它既不找 = 来解析属性,也不找 > 来关闭元素,而是直接报错,若是它没找到的话。
宏调用,弄混了一些人,还有不少迟疑的认为它们是好用的东西。你可能也这么想,并且争论一件事情:像 readtables 这样的东西标志着咱们进入了很深的地方。
我请大家仔细想一想 sweet.js。给它 5 分钟。也许几个小时。玩玩它:设置一个 gulp 监控 wathcer,从 npm 安装一些宏而且用它。别直接反对它你实际上除非你理解了咱们尝试用它来解决的一些问题。许多人们给出的争论都没有很容易体会到那种场景(但有一些有!)
无论那些,甚至你认为这是错误的方法。他固然是有效的。软件工业对我老说最困惑的一件事情就是:咱们对其余人到底能有多恶毒,因此请作些建设性的事情。
今天就试试 jsx-reader https://github.com/jlongster/jsx-reader 若是发现 bugs https://github.com/jlongster/jsx-reader/issues
以上;