Merge pull request #2970 from amasad/no-const

Convert the constants transform plugin to a validation plugin
This commit is contained in:
Sebastian McKenzie 2015-11-11 12:55:39 -08:00
commit e63ac82d80
46 changed files with 114 additions and 130 deletions

View File

@ -0,0 +1,39 @@
# babel-plugin-check-es2015-constants
Validate ES2015 constants
## Installation
```sh
$ npm install babel-plugin-check-es2015-constants
```
## Usage
### Via `.babelrc` (Recommended)
**.babelrc**
```json
{
"plugins": ["check-es2015-constants"]
}
```
### Via CLI
```sh
$ babel --plugins check-es2015-constants script.js
```
### Via Node API
```javascript
require("babel-core").transform("code", {
plugins: ["check-es2015-constants"]
});
```
## Note
This check will only validate consts. If you need it to compile down to `var` then you must also install and enable [`check-es2015-block-scoping`](../babel-plugin-check-es2015-block-scoping).

View File

@ -1,8 +1,8 @@
{
"name": "babel-plugin-transform-es2015-constants",
"name": "babel-plugin-check-es2015-constants",
"version": "6.1.4",
"description": "Compile ES2015 constants to ES5",
"repository": "https://github.com/babel/babel/tree/master/packages/babel-plugin-transform-es2015-constants",
"repository": "https://github.com/babel/babel/tree/master/packages/babel-plugin-check-es2015-constants",
"license": "MIT",
"main": "lib/index.js",
"keywords": [

View File

@ -0,0 +1,16 @@
export default function ({ messages }) {
return {
visitor: {
Scope({ scope }) {
for (let name in scope.bindings) {
let binding = scope.bindings[name];
if (binding.kind !== "const" && binding.kind !== "module") continue;
for (let violation of (binding.constantViolations: Array)) {
throw violation.buildCodeFrameError(messages.get("readOnly", name));
}
}
},
}
};
}

View File

@ -0,0 +1,3 @@
{
"plugins": ["check-es2015-constants", "transform-es2015-block-scoping", "transform-es2015-destructuring"]
}

View File

@ -13,9 +13,10 @@ export default function () {
visitor: {
VariableDeclaration(path, file) {
let { node, parent, scope } = path;
if (!isLet(node, parent, scope)) return;
if (!isBlockScoped(node)) return;
convertBlockScopedToVar(node, parent, scope);
if (isLetInitable(node) && node._tdzThis) {
if (node._tdzThis) {
let nodes = [node];
for (let i = 0; i < node.declarations.length; i++) {
@ -42,13 +43,7 @@ export default function () {
Loop(path, file) {
let { node, parent, scope } = path;
let init = node.left || node.init;
if (isLet(init, node, scope)) {
t.ensureBlock(node);
node.body._letDeclarators = [init];
}
t.ensureBlock(node);
let blockScoping = new BlockScoping(path, path.get("body"), parent, scope, file);
let replace = blockScoping.run();
if (replace) path.replaceWith(replace);
@ -68,36 +63,28 @@ let buildRetCheck = template(`
if (typeof RETURN === "object") return RETURN.v;
`);
function isLet(node, parent, scope) {
function isBlockScoped(node) {
if (!t.isVariableDeclaration(node)) return false;
if (node._let) return true;
if (node.kind !== "let") return false;
if (node[t.BLOCK_SCOPED_SYMBOL]) return true;
if (node.kind !== "let" && node.kind !== "const") return false;
return true;
}
function convertBlockScopedToVar(node, parent, scope) {
// https://github.com/babel/babel/issues/255
if (isLetInitable(node, parent)) {
if (!t.isFor(parent)) {
for (let i = 0; i < node.declarations.length; i++) {
let declar = node.declarations[i];
declar.init = declar.init || scope.buildUndefinedNode();
}
}
node._let = true;
node[t.BLOCK_SCOPED_SYMBOL] = true;
node.kind = "var";
return true;
}
function isLetInitable(node, parent) {
return !t.isFor(parent) || !t.isFor(parent, { left: node });
}
function isVar(node, parent, scope) {
return t.isVariableDeclaration(node, { kind: "var" }) && !isLet(node, parent, scope);
}
function standardizeLets(declars) {
for (let declar of (declars: Array)) {
delete declar._let;
}
function isVar(node) {
return t.isVariableDeclaration(node, { kind: "var" }) && !isBlockScoped(node);
}
function replace(path, node, scope, remaps) {
@ -167,10 +154,10 @@ let letReferenceFunctionVisitor = traverse.visitors.merge([{
let hoistVarDeclarationsVisitor = {
enter(path, self) {
let { node, parent, scope } = path;
let { node, parent } = path;
if (path.isForStatement()) {
if (isVar(node.init, node, scope)) {
if (isVar(node.init, node)) {
let nodes = self.pushDeclar(node.init);
if (nodes.length === 1) {
node.init = nodes[0];
@ -179,11 +166,11 @@ let hoistVarDeclarationsVisitor = {
}
}
} else if (path.isFor()) {
if (isVar(node.left, node, scope)) {
if (isVar(node.left, node)) {
self.pushDeclar(node.left);
node.left = node.left.declarations[0].id;
}
} else if (isVar(node, parent, scope)) {
} else if (isVar(node, parent)) {
path.replaceWithMultiple(self.pushDeclar(node).map(expr => t.expressionStatement(expr)));
} else if (path.isFunction()) {
return path.skip();
@ -498,19 +485,22 @@ class BlockScoping {
getLetReferences() {
let block = this.block;
let declarators = block._letDeclarators || [];
let declarators = [];
//
for (let i = 0; i < declarators.length; i++) {
let declar = declarators[i];
extend(this.outsideLetReferences, t.getBindingIdentifiers(declar));
if (this.loop) {
let init = this.loop.left || this.loop.init;
if (isBlockScoped(init)) {
declarators.push(init);
extend(this.outsideLetReferences, t.getBindingIdentifiers(init));
}
}
//
if (block.body) {
for (let i = 0; i < block.body.length; i++) {
let declar = block.body[i];
if (t.isClassDeclaration(declar) || t.isFunctionDeclaration(declar) || isLet(declar, block, this.scope)) {
if (t.isClassDeclaration(declar) || t.isFunctionDeclaration(declar) || isBlockScoped(declar)) {
if (isBlockScoped(declar)) convertBlockScopedToVar(declar, block, this.scope);
declarators = declarators.concat(declar.declarations || declar);
}
}
@ -527,9 +517,6 @@ class BlockScoping {
// no let references so we can just quit
if (!this.hasLetReferences) return;
// set let references to plain let references
standardizeLets(declarators);
let state = {
letReferences: this.letReferences,
closurify: false,

View File

@ -1,4 +1,4 @@
const foo = "foo";
var foo = "foo";
function foobar() {
for (var item of [1, 2, 3]) {

View File

@ -0,0 +1,6 @@
for (let i = 0; i < 5; i++) {
const l = i;
setTimeout(function() {
console.log(l);
}, 1);
}

View File

@ -0,0 +1,10 @@
var _loop = function (i) {
var l = i;
setTimeout(function () {
console.log(l);
}, 1);
};
for (var i = 0; i < 5; i++) {
_loop(i);
}

View File

@ -1,3 +1,3 @@
{
"plugins": ["transform-es2015-constants", "transform-es2015-block-scoping", "transform-es2015-parameters", "transform-es2015-destructuring", "transform-es2015-modules-commonjs"]
"plugins": ["check-es2015-constants", "transform-es2015-block-scoping", "transform-es2015-parameters", "transform-es2015-destructuring", "transform-es2015-modules-commonjs"]
}

View File

@ -1,39 +0,0 @@
# babel-plugin-transform-es2015-constants
Compile ES2015 constants to ES5
## Installation
```sh
$ npm install babel-plugin-transform-es2015-constants
```
## Usage
### Via `.babelrc` (Recommended)
**.babelrc**
```json
{
"plugins": ["transform-es2015-constants"]
}
```
### Via CLI
```sh
$ babel --plugins transform-es2015-constants script.js
```
### Via Node API
```javascript
require("babel-core").transform("code", {
plugins: ["transform-es2015-constants"]
});
```
## Note
This transform on its own will compile `const` to `let`. If you need it to compile down to `var` then you must also install and enable [`transform-es2015-block-scoping`](../babel-plugin-transform-es2015-block-scoping).

View File

@ -1,38 +0,0 @@
export default function ({ messages, types: t }) {
function check(node) {
if (t.isVariableDeclaration(node, { kind: "const" })) {
node.kind = "let";
}
}
return {
visitor: {
Scope({ scope }) {
for (let name in scope.bindings) {
let binding = scope.bindings[name];
if (binding.kind !== "const" && binding.kind !== "module") continue;
for (let violation of (binding.constantViolations: Array)) {
throw violation.buildCodeFrameError(messages.get("readOnly", name));
}
}
},
VariableDeclaration({ node }) {
check(node);
},
ForXStatement({ node: { left } }) {
check(left);
},
ForStatement({ node: { init } }) {
check(init);
},
"BlockStatement|Program"({ node: { body } }) {
for (let node of body) check(node);
}
}
};
}

View File

@ -1,3 +0,0 @@
{
"plugins": ["transform-es2015-constants", "transform-es2015-block-scoping", "transform-es2015-destructuring"]
}

View File

@ -1,4 +1,4 @@
const deepAssign = function () {
var deepAssign = function () {
for (var _len = arguments.length, args = Array(_len), _key = 0; _key < _len; _key++) {
args[_key] = arguments[_key];
}

View File

@ -12,7 +12,7 @@ module.exports = {
require("babel-plugin-transform-es2015-for-of"),
require("babel-plugin-transform-es2015-sticky-regex"),
require("babel-plugin-transform-es2015-unicode-regex"),
require("babel-plugin-transform-es2015-constants"),
require("babel-plugin-check-es2015-constants"),
require("babel-plugin-transform-es2015-spread"),
require("babel-plugin-transform-es2015-parameters"),
require("babel-plugin-transform-es2015-destructuring"),

View File

@ -20,7 +20,7 @@
"babel-plugin-transform-es2015-for-of": "^6.1.4",
"babel-plugin-transform-es2015-sticky-regex": "^6.1.4",
"babel-plugin-transform-es2015-unicode-regex": "^6.1.4",
"babel-plugin-transform-es2015-constants": "^6.1.4",
"babel-plugin-check-es2015-constants": "^6.1.4",
"babel-plugin-transform-es2015-spread": "^6.1.4",
"babel-plugin-transform-es2015-parameters": "^6.1.4",
"babel-plugin-transform-es2015-destructuring": "^6.1.4",

View File

@ -14,7 +14,7 @@
"transform-es2015-for-of",
"transform-es2015-sticky-regex",
"transform-es2015-unicode-regex",
"transform-es2015-constants",
"check-es2015-constants",
"transform-es2015-spread",
"transform-es2015-parameters",
"transform-es2015-destructuring",

View File

@ -22,3 +22,5 @@ export const INHERIT_KEYS = {
optional: ["typeAnnotation", "typeParameters", "returnType"],
force: ["start", "loc", "end"]
};
export const BLOCK_SCOPED_SYMBOL = Symbol.for("var used to be block scoped");

View File

@ -3,6 +3,7 @@
import { getBindingIdentifiers } from "./retrievers";
import esutils from "esutils";
import * as t from "./index";
import { BLOCK_SCOPED_SYMBOL } from "./constants";
/**
* Check if the input `node` is a binding identifier.
@ -169,7 +170,7 @@ export function isValidIdentifier(name: string): boolean {
*/
export function isLet(node: Object): boolean {
return t.isVariableDeclaration(node) && (node.kind !== "var" || node._let);
return t.isVariableDeclaration(node) && (node.kind !== "var" || node[BLOCK_SCOPED_SYMBOL]);
}
/**
@ -185,7 +186,7 @@ export function isBlockScoped(node: Object): boolean {
*/
export function isVar(node: Object): boolean {
return t.isVariableDeclaration(node, { kind: "var" }) && !node._let;
return t.isVariableDeclaration(node, { kind: "var" }) && !node[BLOCK_SCOPED_SYMBOL];
}
/**