
如果你像我一样,你可能每天都会使用很多开源工具,而不会去想它们是如何开始的。很少有项目会分享它们创建的“原因”:它们试图解决的实际问题,以及它们第一次遇到这个问题的时间。当然,你可以在不了解开源项目起源故事的情况下从中受益,但我总觉得了解一切是如何开始的很有趣。
🌐 If you’re like me, you probably use a lot of open source tools every day without thinking about how they got started. Few projects share the “why” of their creation: the actual problem they were trying to solve and when they first came across that problem. You can, of course, benefit from open source projects without understanding their origin story, but I always find it interesting to hear about how it all started.
我最近意识到我从未分享过 ESLint 的起源故事。我在之前的帖子中分享过一些我在过程中做出的决定,但从未讲过最初触发 ESLint 创建的那个多米诺骨牌。如你将看到的,ESLint 并不是通过某种神圣的干预或灵机一动而创建的,而是通过一系列事件逐渐积累,最终促成了 ESLint 的诞生。
🌐 I recently realized that I’d never shared the origin story of ESLint. I’ve shared some of the decisions I made along the way in previous posts but never the initial domino that fell and led to ESLint’s creation. As you will see, ESLint wasn’t created through some divine intervention or stroke of insight, but rather through a series of events that eventually built up to ESLint’s creation.
虫子
🌐 The bug
当我在 Box 还比较新的时候,一个队友正在处理一个奇怪的错误。一个客户报告在 Internet Explorer 7 中使用网页应用时遇到问题(那时候我们可能是为数不多仍支持 IE7 的公司)。一名开发者显然在一些 JavaScript 代码中使用了原生的 XMLHttpRequest 对象,而不是我们内部的封装。这对其他浏览器没有问题,在内部用 IE7 测试也没有问题。问题出现的原因是客户有一个内部安全策略,禁止在 Internet Explorer 中使用 ActiveX,而由于 IE7 中的原生 XMLHttpRequest 对象实际上只是 ActiveX 对象的一个封装,它也被阻止了。
🌐 I was still fairly new at Box when a teammate was working on a strange bug. A client had reported problems using the web application in Internet Explorer 7 (we were probably one of the last companies supporting IE7 at that point). A developer had apparently used the native XMLHttpRequest object in some JavaScript code instead of our in-house wrapper. This wasn’t a problem for any other browser, and there wasn’t a problem testing with IE7 internally. The problem occurred because the client had an internal security policy that disabled ActiveX in Internet Explorer, and since the native XMLHttpRequest object in IE7 was really just a wrapper around the ActiveX object, it was blocked as well.
解决方案相当简单,只需确保每个人都知道要使用内部的 Ajax 封装器,而不是原生的 XMLHttpRequest 对象。但我们如何强制执行这一点呢?结果发现 Box 的构建系统中包含一个 JavaScript “代码检查器”。我把代码检查器这个词放在引号中,因为它实际上只是针对 JavaScript 代码运行的一系列正则表达式。在这种情况下,我的队友为“XMLHttpRequest”添加了一个正则表达式,这就是解决方案。如果有人试图提交匹配该模式的 JavaScript 文件,构建将会中断。
🌐 The solution was easy enough, just make sure everyone knows to use the in-house Ajax wrapper instead of the native XMLHttpRequest object. But how could we enforce this? It turned out that Box had a JavaScript “linter” as part of the build system. I put the word linter in quotes because it was really just a series of regular expressions that were run against JavaScript code. For this case, my teammate added a regular expression for “XMLHttpRequest” and that was the solution. The build would break going forward if someone tried to commit a JavaScript file matching that pattern.
根据我的经验,在源代码上使用正则表达式从来都不是一个好主意。我希望在构建过程中有一种更好的方法来进行像这样的检查。我想肯定有人已经解决了这个问题,于是我开始寻找解决方案。
🌐 In my experience, using regular expressions on source code was never a good idea. I wished that there was a better way to do checks like this one during the build. I figured that someone must have already solved this problem and so I started looking for solutions.
会是 JSHint 吗?
🌐 Could it be JSHint?
我做的第一件事是给当时的 JSHint 维护者 Anton Kovalyov[1] 发邮件。我记得曾读过一篇博客文章[2],上面说 JSHint 计划支持插件,但我找不到任何关于该功能已实现的信息。根据我过去为 JSHint 做贡献的经验,以及在 Yahoo 的一个项目中修改 JSLint 的经验,我知道 JSHint 起初并未设置为支持插件,而没有正式的支持,就无法轻松修改 JSHint 来实现我想要的功能。
🌐 The first thing I did was email the maintainer of JSHint at that time, Anton Kovalyov[1]. I had remembered reading a blog post[2] that said JSHint was planning to support plugins but couldn’t find any information about that feature being implemented. From past experience contributing to JSHint and from modifying JSLint for a project at Yahoo, I knew JSHint hadn’t initially been setup to support plugins, and without formal support there wouldn’t be an easy way to modify JSHint to do what I wanted.
安东告诉我,插件提案已经停滞不前,看起来不会被实现。我感到很失望,因为这似乎是解决问题的最直接途径。我感谢他,并请他不要介意,如果我创建一个能满足我需求的代码检查工具。我本想支持 JSHint,但我觉得这是一个无论是否使用 JSHint 都需要解决的问题。
🌐 Anton informed me that the plugin proposal had stalled and didn’t look like it would be implemented. I was disappointed, as this seemed like the most direct path to solving the problem. I thanked him and asked him to please not be offended if I create a linter that did what I needed. I wanted to support JSHint, but I felt like this was a problem that needed to be solved with JSHint or without it.
灵感
🌐 The inspiration
在研究 Box 的构建系统后,我发现除了临时的 JavaScript linter 外,实际上还有一个 PHP linter 在运行。然而,PHP linter 比 JavaScript linter 要复杂得多。它不是使用正则表达式,而是将代码解析为抽象语法树(AST),然后检查 AST 中的模式以生成报告。
🌐 After digging around in the build system at Box, I found there was actually a PHP linter running in addition to the makeshift JavaScript linter. The PHP linter, however, was a lot more involved that the JavaScript one. Instead of using regular expressions, the PHP linter parsed the code into an abstract syntax tree (AST) and then inspected the AST for the patterns to report.
我可能在读那个代码时不停地点头“是”。我立刻意识到,这正是我在 JavaScript 中需要做的事情。要是有办法把 JavaScript 解析成 AST,然后检查 AST 中的问题就好了。
🌐 I was probably shaking my head “yes” as I read through that code. Immediately I realized that this was exactly what I needed to do for JavaScript. If only there was some way to parse JavaScript into an AST and then inspect the AST for problems.
基础工作
🌐 The groundwork
在我脑子里浮现出这些思绪时,我邀请了 Ariya Hidayat[3] 到 Box 做一次演讲,主题随他选择。碰巧的是,他做了一场关于 Esprima[4] 的演讲,这是他用 JavaScript 编写的 JavaScript 解析器。在那次演讲中,Ariya 讨论了为何拥有 JavaScript 的 AST 表示是有用的,并提到了几个已经基于 Esprima 构建的工具。其中包括用于遍历 AST 的 estraverse 和用于作用域分析的 escope,这两个工具都是由 Yusuke Suzuki 编写的。
🌐 With all of this floating around in my brain, I invited Ariya Hidayat[3] to give a talk at Box about whatever topic he pleased. It just so happened that he gave a talk on Esprima[4], the JavaScript parser he wrote in JavaScript. During that talk, Ariya discussed why having an AST representation of JavaScript was useful and referenced several already-existing tools built on top of Esprima. Among those tools were estraverse for traversing the AST and escope for scope analysis, both written by Yusuke Suzuki.
当Ariya继续讲解并举例说明AST可以解决的各种问题时,一个新工具的想法在我脑海中形成。对我来说,很合理的是应该有一个工具可以执行Ariya提到的所有评估。毕竟,它们只是为了不同的目的使用AST。为什么不能有一个AST让它们都能使用呢?
🌐 As Ariya continued talking and giving examples of the types of problems an AST can solve, the idea for a new tool formed in my head. It made sense to me that there should be one tool that could perform all of the evaluations Ariya mentioned. After all, they are all just using the AST for different purposes. Why not have one AST they all can use?
开始
🌐 The beginning
多亏了 Esprima、estraverse 和 escope 的可用性,我才能在几个周末内拼凑出 ESLint 的第一个原型。对我来说,这三种工具代表了我创建一个可以轻松发现 JavaScript 代码中问题模式的新工具所需的一切。如果我必须从零开始创建这些工具,我毫不怀疑今天的 ESLint 将不会存在。基于这些工具,我能够快速迭代,最终,大家所熟知的 ESLint 工具诞生了。
🌐 Thanks largely to the availability of Esprima, estraverse, and escope, I was able to put together the first prototype of ESLint over a couple of weekends. To me, these three utilities represented everything that I needed to create a new tool that could easily find problem patterns in JavaScript code. If I had to create those from scratch, I have no doubt that ESLint would not exist today. Building on top of those tools, I was able to iterate quickly, and eventually, the tool you know today as ESLint was born.
(我觉得有必要指出的是,当时我并不是唯一一个想要创建基于 AST 的代码检查工具的人。JSCS[5] 也在差不多同一时间开发中,而现任 ESLint 维护者 Ilya Volodin 在发现 ESLint 之前也在开发他自己的项目。如果我没有想出类似 ESLint 的东西,那么无疑会有其他人去做。多亏了 Ariya 和 Yusuke,所有的部分都已经存在了,只需要有人以有用的方式将它们组合在一起。)
参考文献
🌐 References
