UNPKG

4.78 kBMarkdownView Raw
1# defs.js
2Static scope analysis and transpilation of ES6 block scoped `const` and `let`
3variables, to ES3.
4
5Node already supports `const` and `let` so you can use that today
6(run `node --harmony` and `"use strict"`). `defs.js` enables you to do the same
7for browser code. While developing you can rely on the experimental support
8in Chrome (chrome://flags, check Enable experimental JavaScript). `defs.js` is
9also a pretty decent static scope analyzer/linter.
10
11The slides for the talk
12[LET's CONST together, right now (with ES3)](http://blog.lassus.se/files/lets_const_together_ft2013.pdf)
13from Front-Trends 2013 includes more information about `let`, `const` and `defs.js`.
14
15
16## Installation and usage
17 npm install -g defs
18
19Then run it as `defs file.js`. The errors (if any) will go to stderr,
20the transpiled source to `stdout`, so redirect it like `defs file.js > output.js`.
21More command line options is coming.
22
23
24## Configuration
25`defs` looks for a `defs-config.json` configuration file in your current
26directory. It will search for it in parent directories soon as you'd expect.
27
28Example `defs-config.json`:
29
30 {
31 "environments": ["node", "browser"],
32
33 "globals": {
34 "my": false,
35 "hat": true
36 },
37 "disallowVars": false,
38 "disallowDuplicated": true,
39 "disallowUnknownReferences": true
40 }
41
42`globals` lets you list your program's globals, and indicate whether they are
43writable (`true`) or read-only (`false`), just like `jshint`.
44
45`environments` lets you import a set of pre-defined globals, here `node` and
46`browser`. These default environments are borrowed from `jshint` (see
47[jshint_globals/vars.js](https://github.com/olov/defs/blob/master/jshint_globals/vars.js)).
48
49`disallowVars` (defaults to `false`) can be enabled to make
50usage of `var` an error.
51
52`disallowDuplicated` (defaults to `true`) errors on duplicated
53`var` definitions in the same function scope.
54
55`disallowUnknownReferences` (defaults to `true`) errors on references to
56unknown global variables.
57
58`ast` (defaults to `false`) produces an AST instead of source code
59(experimental).
60
61
62## Example
63
64Input `example.js`:
65
66```javascript
67"use strict";
68function fn() {
69 const y = 0;
70 for (let x = 0; x < 10; x++) {
71 const y = x * 2;
72 const z = y;
73 }
74 console.log(y); // prints 0
75}
76fn();
77```
78
79Output from running `defs example.js`:
80
81```javascript
82"use strict";
83function fn() {
84 var y = 0;
85 for (var x = 0; x < 10; x++) {
86 var y$0 = x * 2;
87 var z = y$0;
88 }
89 console.log(y); // prints 0
90}
91fn();
92```
93
94
95## Compatibility
96`defs.js` strives to transpile your program as true to the ES6 block scope semantics as possible,
97 while being as unintrusive as possible. The only textual differences you'll find between your
98 original and transpiled program is that the latter uses `var` and occasional variable renames.
99
100### Loop closures limitation
101`defs.js` won't transpile a closure-that-captures-a-block-scoped-variable-inside-a-loop, such
102as the following example:
103
104```javascript
105for (let x = 0; x < 10; x++) {
106 let y = x;
107 arr.push(function() { return y; });
108}
109```
110
111With ES6 semantics `y` is bound fresh per loop iteration, so each closure captures a seperate
112instance of `y`, unlike if `y` would have been a `var`. [Actually, even `x` is bound per
113iteration, but v8 (so node) has an
114[open bug](https://code.google.com/p/v8/issues/detail?id=2560) for that].
115
116To transpile this example, an IIFE or `try-catch` must be inserted, which isn't non-intrusive.
117`defs.js` will detect this case and spit out an error instead, like so:
118
119 line 3: can't transform closure. y is defined outside closure, inside loop
120
121You need to manually handle this the way we've always done pre-`ES6`,
122for instance like so:
123
124```javascript
125for (let x = 0; x < 10; x++) {
126 (function(y) {
127 arr.push(function() { return y; });
128 })(x);
129}
130```
131
132I'm interested in feedback on this based on real-world usage of `defs.js`.
133
134### Referenced (inside closure) before declaration
135`defs.js` detects the vast majority of cases where a variable is referenced prior to
136its declaration. The one case it cannot detect is the following:
137
138```javascript
139function printx() { console.log(x); }
140printx(); // illegal
141let x = 1;
142printx(); // legal
143```
144
145The first call to `printx` is not legal because `x` hasn't been initialized at that point
146of *time*, which is impossible to catch reliably with statical analysis.
147`v8 --harmony` will detect and error on this via run-time checking. `defs.js` will
148happily transpile this example (`let` => `var` and that's it), and the transpiled code
149will print `undefined` on the first call to `printx`. This difference should be a very
150minor problem in practice.
151
152
153## License
154`MIT`, see LICENSE file.