more classes!

This commit is contained in:
Sebastian McKenzie 2015-02-26 12:19:28 +11:00
parent f7186980e5
commit 553eb2d45e
13 changed files with 2125 additions and 2117 deletions

View File

@ -1,76 +1,75 @@
module.exports = Buffer;
var repeating = require("repeating"); var repeating = require("repeating");
var trimRight = require("trim-right"); var trimRight = require("trim-right");
var isBoolean = require("lodash/lang/isBoolean"); var isBoolean = require("lodash/lang/isBoolean");
var includes = require("lodash/collection/includes"); var includes = require("lodash/collection/includes");
var isNumber = require("lodash/lang/isNumber"); var isNumber = require("lodash/lang/isNumber");
function Buffer(position, format) { export default class Buffer {
constructor(position, format) {
this.position = position; this.position = position;
this._indent = format.indent.base; this._indent = format.indent.base;
this.format = format; this.format = format;
this.buf = ""; this.buf = "";
} }
Buffer.prototype.get = function () { get() {
return trimRight(this.buf); return trimRight(this.buf);
}; }
Buffer.prototype.getIndent = function () { getIndent() {
if (this.format.compact || this.format.concise) { if (this.format.compact || this.format.concise) {
return ""; return "";
} else { } else {
return repeating(this.format.indent.style, this._indent); return repeating(this.format.indent.style, this._indent);
} }
}; }
Buffer.prototype.indentSize = function () { indentSize() {
return this.getIndent().length; return this.getIndent().length;
}; }
Buffer.prototype.indent = function () { indent() {
this._indent++; this._indent++;
}; }
Buffer.prototype.dedent = function () { dedent() {
this._indent--; this._indent--;
}; }
Buffer.prototype.semicolon = function () { semicolon() {
this.push(";"); this.push(";");
}; }
Buffer.prototype.ensureSemicolon = function () { ensureSemicolon() {
if (!this.isLast(";")) this.semicolon(); if (!this.isLast(";")) this.semicolon();
}; }
Buffer.prototype.rightBrace = function () { rightBrace() {
this.newline(true); this.newline(true);
this.push("}"); this.push("}");
}; }
Buffer.prototype.keyword = function (name) { keyword(name) {
this.push(name); this.push(name);
this.space(); this.space();
}; }
Buffer.prototype.space = function () { space() {
if (this.format.compact) return; if (this.format.compact) return;
if (this.buf && !this.isLast(" ") && !this.isLast("\n")) { if (this.buf && !this.isLast(" ") && !this.isLast("\n")) {
this.push(" "); this.push(" ");
} }
}; }
Buffer.prototype.removeLast = function (cha) { removeLast(cha) {
if (this.format.compact) return; if (this.format.compact) return;
if (!this.isLast(cha)) return; if (!this.isLast(cha)) return;
this.buf = this.buf.substr(0, this.buf.length - 1); this.buf = this.buf.substr(0, this.buf.length - 1);
this.position.unshift(cha); this.position.unshift(cha);
}; }
Buffer.prototype.newline = function (i, removeLast) { newline(i, removeLast) {
if (this.format.compact) return; if (this.format.compact) return;
if (this.format.concise) { if (this.format.concise) {
@ -98,9 +97,9 @@ Buffer.prototype.newline = function (i, removeLast) {
} }
this._newline(removeLast); this._newline(removeLast);
}; }
Buffer.prototype._newline = function (removeLast) { _newline(removeLast) {
// never allow more than two lines // never allow more than two lines
if (this.endsWith("\n\n")) return; if (this.endsWith("\n\n")) return;
@ -110,13 +109,13 @@ Buffer.prototype._newline = function (removeLast) {
this.removeLast(" "); this.removeLast(" ");
this._removeSpacesAfterLastNewline(); this._removeSpacesAfterLastNewline();
this._push("\n"); this._push("\n");
}; }
/** /**
* If buffer ends with a newline and some spaces after it, trim those spaces. * If buffer ends with a newline and some spaces after it, trim those spaces.
*/ */
Buffer.prototype._removeSpacesAfterLastNewline = function () { _removeSpacesAfterLastNewline() {
var lastNewlineIndex = this.buf.lastIndexOf("\n"); var lastNewlineIndex = this.buf.lastIndexOf("\n");
if (lastNewlineIndex === -1) if (lastNewlineIndex === -1)
return; return;
@ -133,9 +132,9 @@ Buffer.prototype._removeSpacesAfterLastNewline = function () {
if (index === lastNewlineIndex) { if (index === lastNewlineIndex) {
this.buf = this.buf.substring(0, index + 1); this.buf = this.buf.substring(0, index + 1);
} }
}; }
Buffer.prototype.push = function (str, noIndent) { push(str, noIndent) {
if (!this.format.compact && this._indent && !noIndent && str !== "\n") { if (!this.format.compact && this._indent && !noIndent && str !== "\n") {
// we have an indent level and we aren't pushing a newline // we have an indent level and we aren't pushing a newline
var indent = this.getIndent(); var indent = this.getIndent();
@ -148,18 +147,18 @@ Buffer.prototype.push = function (str, noIndent) {
} }
this._push(str); this._push(str);
}; }
Buffer.prototype._push = function (str) { _push(str) {
this.position.push(str); this.position.push(str);
this.buf += str; this.buf += str;
}; }
Buffer.prototype.endsWith = function (str) { endsWith(str) {
return this.buf.slice(-str.length) === str; return this.buf.slice(-str.length) === str;
}; }
Buffer.prototype.isLast = function (cha) { isLast(cha) {
if (this.format.compact) return false; if (this.format.compact) return false;
var buf = this.buf; var buf = this.buf;
@ -170,4 +169,5 @@ Buffer.prototype.isLast = function (cha) {
} else { } else {
return cha === last; return cha === last;
} }
}; }
}

View File

@ -1,10 +1,3 @@
module.exports = function (ast, opts, code) {
var gen = new CodeGenerator(ast, opts, code);
return gen.generate();
};
module.exports.CodeGenerator = CodeGenerator;
var detectIndent = require("detect-indent"); var detectIndent = require("detect-indent");
var Whitespace = require("./whitespace"); var Whitespace = require("./whitespace");
var repeating = require("repeating"); var repeating = require("repeating");
@ -17,7 +10,8 @@ var each = require("lodash/collection/each");
var n = require("./node"); var n = require("./node");
var t = require("../types"); var t = require("../types");
function CodeGenerator(ast, opts, code) { class CodeGenerator {
constructor(ast, opts, code) {
opts ||= {}; opts ||= {};
this.comments = ast.comments || []; this.comments = ast.comments || [];
@ -30,15 +24,9 @@ function CodeGenerator(ast, opts, code) {
this.position = new Position; this.position = new Position;
this.map = new SourceMap(this.position, opts, code); this.map = new SourceMap(this.position, opts, code);
this.buffer = new Buffer(this.position, this.format); this.buffer = new Buffer(this.position, this.format);
} }
each(Buffer.prototype, function (fn, key) { static normalizeOptions(code, opts) {
CodeGenerator.prototype[key] = function () {
return fn.apply(this.buffer, arguments);
};
});
CodeGenerator.normalizeOptions = function (code, opts) {
var style = " "; var style = " ";
if (code) { if (code) {
var indent = detectIndent(code).indent; var indent = detectIndent(code).indent;
@ -64,9 +52,9 @@ CodeGenerator.normalizeOptions = function (code, opts) {
} }
return format; return format;
}; }
CodeGenerator.generators = { static generators = {
templateLiterals: require("./generators/template-literals"), templateLiterals: require("./generators/template-literals"),
comprehensions: require("./generators/comprehensions"), comprehensions: require("./generators/comprehensions"),
expressions: require("./generators/expressions"), expressions: require("./generators/expressions"),
@ -79,13 +67,9 @@ CodeGenerator.generators = {
flow: require("./generators/flow"), flow: require("./generators/flow"),
base: require("./generators/base"), base: require("./generators/base"),
jsx: require("./generators/jsx") jsx: require("./generators/jsx")
}; };
each(CodeGenerator.generators, function (generator) { generate() {
extend(CodeGenerator.prototype, generator);
});
CodeGenerator.prototype.generate = function () {
var ast = this.ast; var ast = this.ast;
this.print(ast); this.print(ast);
@ -100,9 +84,9 @@ CodeGenerator.prototype.generate = function () {
map: this.map.get(), map: this.map.get(),
code: this.buffer.get() code: this.buffer.get()
}; };
}; }
CodeGenerator.prototype.buildPrint = function (parent) { buildPrint(parent) {
var print = (node, opts) => { var print = (node, opts) => {
return this.print(node, parent, opts); return this.print(node, parent, opts);
}; };
@ -132,9 +116,9 @@ CodeGenerator.prototype.buildPrint = function (parent) {
}; };
return print; return print;
}; }
CodeGenerator.prototype.print = function (node, parent, opts) { print(node, parent, opts) {
if (!node) return ""; if (!node) return "";
if (parent && parent._compact) { if (parent && parent._compact) {
@ -211,9 +195,9 @@ CodeGenerator.prototype.print = function (node, parent, opts) {
} }
this.format.concise = oldConcise; this.format.concise = oldConcise;
}; }
CodeGenerator.prototype.printJoin = function (print, nodes, opts) { printJoin(print, nodes, opts) {
if (!nodes || !nodes.length) return; if (!nodes || !nodes.length) return;
opts ||= {}; opts ||= {};
@ -239,25 +223,25 @@ CodeGenerator.prototype.printJoin = function (print, nodes, opts) {
}); });
if (opts.indent) this.dedent(); if (opts.indent) this.dedent();
}; }
CodeGenerator.prototype.printAndIndentOnComments = function (print, node) { printAndIndentOnComments(print, node) {
var indent = !!node.leadingComments; var indent = !!node.leadingComments;
if (indent) this.indent(); if (indent) this.indent();
print(node); print(node);
if (indent) this.dedent(); if (indent) this.dedent();
}; }
CodeGenerator.prototype.printBlock = function (print, node) { printBlock(print, node) {
if (t.isEmptyStatement(node)) { if (t.isEmptyStatement(node)) {
this.semicolon(); this.semicolon();
} else { } else {
this.push(" "); this.push(" ");
print(node); print(node);
} }
}; }
CodeGenerator.prototype.generateComment = function (comment) { generateComment(comment) {
var val = comment.value; var val = comment.value;
if (comment.type === "Line") { if (comment.type === "Line") {
val = "//" + val; val = "//" + val;
@ -265,17 +249,17 @@ CodeGenerator.prototype.generateComment = function (comment) {
val = "/*" + val + "*/"; val = "/*" + val + "*/";
} }
return val; return val;
}; }
CodeGenerator.prototype.printTrailingComments = function (node, parent) { printTrailingComments(node, parent) {
this._printComments(this.getComments("trailingComments", node, parent)); this._printComments(this.getComments("trailingComments", node, parent));
}; }
CodeGenerator.prototype.printLeadingComments = function (node, parent) { printLeadingComments(node, parent) {
this._printComments(this.getComments("leadingComments", node, parent)); this._printComments(this.getComments("leadingComments", node, parent));
}; }
CodeGenerator.prototype.getComments = function (key, node, parent) { getComments(key, node, parent) {
if (t.isExpressionStatement(parent)) { if (t.isExpressionStatement(parent)) {
return []; return [];
} }
@ -292,13 +276,13 @@ CodeGenerator.prototype.getComments = function (key, node, parent) {
}); });
return comments; return comments;
}; }
CodeGenerator.prototype._getComments = function (key, node) { _getComments(key, node) {
return (node && node[key]) || []; return (node && node[key]) || [];
}; }
CodeGenerator.prototype._printComments = function (comments) { _printComments(comments) {
if (this.format.compact) return; if (this.format.compact) return;
if (!this.format.comments) return; if (!this.format.comments) return;
@ -355,4 +339,22 @@ CodeGenerator.prototype._printComments = function (comments) {
// whitespace after // whitespace after
this.newline(this.whitespace.getNewlinesAfter(comment)); this.newline(this.whitespace.getNewlinesAfter(comment));
}); });
}
}
each(Buffer.prototype, function (fn, key) {
CodeGenerator.prototype[key] = function () {
return fn.apply(this.buffer, arguments);
};
});
each(CodeGenerator.generators, function (generator) {
extend(CodeGenerator.prototype, generator);
});
module.exports = function (ast, opts, code) {
var gen = new CodeGenerator(ast, opts, code);
return gen.generate();
}; };
module.exports.CodeGenerator = CodeGenerator;

View File

@ -1,5 +1,3 @@
module.exports = Node;
var whitespace = require("./whitespace"); var whitespace = require("./whitespace");
var parens = require("./parentheses"); var parens = require("./parentheses");
var each = require("lodash/collection/each"); var each = require("lodash/collection/each");
@ -24,16 +22,17 @@ var find = function (obj, node, parent) {
return result; return result;
}; };
function Node(node, parent) { export default class Node {
constructor(node, parent) {
this.parent = parent; this.parent = parent;
this.node = node; this.node = node;
} }
Node.isUserWhitespacable = function (node) { static isUserWhitespacable(node) {
return t.isUserWhitespacable(node); return t.isUserWhitespacable(node);
}; }
Node.needsWhitespace = function (node, parent, type) { static needsWhitespace(node, parent, type) {
if (!node) return 0; if (!node) return 0;
if (t.isExpressionStatement(node)) { if (t.isExpressionStatement(node)) {
@ -53,17 +52,17 @@ Node.needsWhitespace = function (node, parent, type) {
} }
return (linesInfo && linesInfo[type]) || 0; return (linesInfo && linesInfo[type]) || 0;
}; }
Node.needsWhitespaceBefore = function (node, parent) { static needsWhitespaceBefore(node, parent) {
return Node.needsWhitespace(node, parent, "before"); return Node.needsWhitespace(node, parent, "before");
}; }
Node.needsWhitespaceAfter = function (node, parent) { static needsWhitespaceAfter(node, parent) {
return Node.needsWhitespace(node, parent, "after"); return Node.needsWhitespace(node, parent, "after");
}; }
Node.needsParens = function (node, parent) { static needsParens(node, parent) {
if (!parent) return false; if (!parent) return false;
if (t.isNewExpression(parent) && parent.callee === node) { if (t.isNewExpression(parent) && parent.callee === node) {
@ -76,9 +75,9 @@ Node.needsParens = function (node, parent) {
} }
return find(parens, node, parent); return find(parens, node, parent);
}; }
Node.needsParensNoLineTerminator = function (node, parent) { static needsParensNoLineTerminator(node, parent) {
if (!parent) return false; if (!parent) return false;
// no comments // no comments
@ -96,7 +95,8 @@ Node.needsParensNoLineTerminator = function (node, parent) {
} }
return false; return false;
}; }
}
each(Node, function (fn, key) { each(Node, function (fn, key) {
Node.prototype[key] = function () { Node.prototype[key] = function () {

View File

@ -1,11 +1,10 @@
module.exports = Position; export default class Position {
constructor() {
function Position() {
this.line = 1; this.line = 1;
this.column = 0; this.column = 0;
} }
Position.prototype.push = function (str) { push(str) {
for (var i = 0; i < str.length; i++) { for (var i = 0; i < str.length; i++) {
if (str[i] === "\n") { if (str[i] === "\n") {
this.line++; this.line++;
@ -14,9 +13,9 @@ Position.prototype.push = function (str) {
this.column++; this.column++;
} }
} }
}; }
Position.prototype.unshift = function (str) { unshift(str) {
for (var i = 0; i < str.length; i++) { for (var i = 0; i < str.length; i++) {
if (str[i] === "\n") { if (str[i] === "\n") {
this.line--; this.line--;
@ -24,4 +23,5 @@ Position.prototype.unshift = function (str) {
this.column--; this.column--;
} }
} }
}; }
}

View File

@ -1,9 +1,8 @@
module.exports = SourceMap;
var sourceMap = require("source-map"); var sourceMap = require("source-map");
var t = require("../types"); var t = require("../types");
function SourceMap(position, opts, code) { export default class SourceMap {
constructor(position, opts, code) {
this.position = position; this.position = position;
this.opts = opts; this.opts = opts;
@ -17,18 +16,18 @@ function SourceMap(position, opts, code) {
} else { } else {
this.map = null; this.map = null;
} }
} }
SourceMap.prototype.get = function () { get() {
var map = this.map; var map = this.map;
if (map) { if (map) {
return map.toJSON(); return map.toJSON();
} else { } else {
return map; return map;
} }
}; }
SourceMap.prototype.mark = function (node, type) { mark(node, type) {
var loc = node.loc; var loc = node.loc;
if (!loc) return; // no location info if (!loc) return; // no location info
@ -51,4 +50,5 @@ SourceMap.prototype.mark = function (node, type) {
generated: generated, generated: generated,
original: original original: original
}); });
}; }
}

View File

@ -1,5 +1,3 @@
module.exports = Whitespace;
var sortBy = require("lodash/collection/sortBy"); var sortBy = require("lodash/collection/sortBy");
/** /**
@ -22,7 +20,8 @@ function getLookupIndex(i, base, max) {
return i; return i;
} }
function Whitespace(tokens, comments) { export default class Whitespace {
constructor(tokens, comments) {
this.tokens = sortBy(tokens.concat(comments), "start"); this.tokens = sortBy(tokens.concat(comments), "start");
this.used = {}; this.used = {};
@ -35,9 +34,9 @@ function Whitespace(tokens, comments) {
// case will be much faster. // case will be much faster.
this._lastFoundIndex = 0; this._lastFoundIndex = 0;
} }
Whitespace.prototype.getNewlinesBefore = function (node) { getNewlinesBefore(node) {
var startToken; var startToken;
var endToken; var endToken;
var tokens = this.tokens; var tokens = this.tokens;
@ -59,9 +58,9 @@ Whitespace.prototype.getNewlinesBefore = function (node) {
} }
return this.getNewlinesBetween(startToken, endToken); return this.getNewlinesBetween(startToken, endToken);
}; }
Whitespace.prototype.getNewlinesAfter = function (node) { getNewlinesAfter(node) {
var startToken; var startToken;
var endToken; var endToken;
var tokens = this.tokens; var tokens = this.tokens;
@ -93,9 +92,9 @@ Whitespace.prototype.getNewlinesAfter = function (node) {
return lines; return lines;
} }
} }
}; }
Whitespace.prototype.getNewlinesBetween = function (startToken, endToken) { getNewlinesBetween(startToken, endToken) {
if (!endToken || !endToken.loc) return 0; if (!endToken || !endToken.loc) return 0;
var start = startToken ? startToken.loc.end.line : 1; var start = startToken ? startToken.loc.end.line : 1;
@ -110,4 +109,5 @@ Whitespace.prototype.getNewlinesBetween = function (startToken, endToken) {
} }
return lines; return lines;
}; }
}

View File

@ -1,5 +1,3 @@
module.exports = File;
var sourceMapToComment = require("source-map-to-comment"); var sourceMapToComment = require("source-map-to-comment");
var shebangRegex = require("shebang-regex"); var shebangRegex = require("shebang-regex");
var isFunction = require("lodash/lang/isFunction"); var isFunction = require("lodash/lang/isFunction");
@ -16,7 +14,21 @@ var path = require("path");
var each = require("lodash/collection/each"); var each = require("lodash/collection/each");
var t = require("../types"); var t = require("../types");
function File(opts) { var checkTransformerVisitor = {
enter(node, parent, scope, state) {
checkNode(state.stack, node, scope);
}
};
var checkNode = function (stack, node, scope) {
each(stack, function (pass) {
if (pass.shouldRun) return;
pass.checkNode(node, scope);
});
};
export default class File {
constructor(opts) {
this.dynamicImportedNoDefault = []; this.dynamicImportedNoDefault = [];
this.dynamicImportIds = {}; this.dynamicImportIds = {};
this.dynamicImported = []; this.dynamicImported = [];
@ -31,9 +43,9 @@ function File(opts) {
this.ast = {}; this.ast = {};
this.buildTransformers(); this.buildTransformers();
} }
File.helpers = [ static helpers = [
"inherits", "inherits",
"defaults", "defaults",
"prototype-properties", "prototype-properties",
@ -60,9 +72,9 @@ File.helpers = [
"temporal-undefined", "temporal-undefined",
"temporal-assert-defined", "temporal-assert-defined",
"self-global" "self-global"
]; ];
File.validOptions = [ static validOptions = [
"filename", "filename",
"filenameRelative", "filenameRelative",
"blacklist", "blacklist",
@ -99,9 +111,9 @@ File.validOptions = [
"only", "only",
"extensions", "extensions",
"accept" "accept"
]; ];
File.prototype.normalizeOptions = function (opts) { normalizeOptions(opts) {
opts = assign({}, opts); opts = assign({}, opts);
for (var key in opts) { for (var key in opts) {
@ -200,13 +212,13 @@ File.prototype.normalizeOptions = function (opts) {
each(opts.optional, ensureEnabled); each(opts.optional, ensureEnabled);
return opts; return opts;
}; };
File.prototype.isLoose = function (key) { isLoose(key) {
return includes(this.opts.loose, key); return includes(this.opts.loose, key);
}; }
File.prototype.buildTransformers = function () { buildTransformers() {
var file = this; var file = this;
var transformers = {}; var transformers = {};
@ -232,15 +244,15 @@ File.prototype.buildTransformers = function () {
this.transformerStack = stack.concat(secondaryStack); this.transformerStack = stack.concat(secondaryStack);
this.transformers = transformers; this.transformers = transformers;
}; }
File.prototype.debug = function (msg) { debug(msg) {
var parts = this.opts.filename; var parts = this.opts.filename;
if (msg) parts += ": " + msg; if (msg) parts += ": " + msg;
util.debug(parts); util.debug(parts);
}; }
File.prototype.getModuleFormatter = function (type) { getModuleFormatter(type) {
var ModuleFormatter = isFunction(type) ? type : transform.moduleFormatters[type]; var ModuleFormatter = isFunction(type) ? type : transform.moduleFormatters[type];
if (!ModuleFormatter) { if (!ModuleFormatter) {
@ -253,9 +265,9 @@ File.prototype.getModuleFormatter = function (type) {
} }
return new ModuleFormatter(this); return new ModuleFormatter(this);
}; }
File.prototype.parseShebang = function (code) { parseShebang(code) {
var shebangMatch = shebangRegex.exec(code); var shebangMatch = shebangRegex.exec(code);
if (shebangMatch) { if (shebangMatch) {
@ -266,17 +278,17 @@ File.prototype.parseShebang = function (code) {
} }
return code; return code;
}; }
File.prototype.set = function (key, val) { set(key, val) {
return this.data[key] = val; return this.data[key] = val;
}; };
File.prototype.setDynamic = function (key, fn) { setDynamic(key, fn) {
this.dynamicData[key] = fn; this.dynamicData[key] = fn;
}; }
File.prototype.get = function (key) { get(key) {
var data = this.data[key]; var data = this.data[key];
if (data) { if (data) {
return data; return data;
@ -286,9 +298,9 @@ File.prototype.get = function (key) {
return this.set(key, dynamic()); return this.set(key, dynamic());
} }
} }
}; }
File.prototype.addImport = function (source, name, noDefault) { addImport(source, name, noDefault) {
name ||= source; name ||= source;
var id = this.dynamicImportIds[name]; var id = this.dynamicImportIds[name];
@ -306,13 +318,13 @@ File.prototype.addImport = function (source, name, noDefault) {
} }
return id; return id;
}; }
File.prototype.isConsequenceExpressionStatement = function (node) { isConsequenceExpressionStatement(node) {
return t.isExpressionStatement(node) && this.lastStatements.indexOf(node) >= 0; return t.isExpressionStatement(node) && this.lastStatements.indexOf(node) >= 0;
}; }
File.prototype.attachAuxiliaryComment = function (node) { attachAuxiliaryComment(node) {
var comment = this.opts.auxiliaryComment; var comment = this.opts.auxiliaryComment;
if (comment) { if (comment) {
node.leadingComments ||= []; node.leadingComments ||= [];
@ -322,9 +334,9 @@ File.prototype.attachAuxiliaryComment = function (node) {
}); });
} }
return node; return node;
}; }
File.prototype.addHelper = function (name) { addHelper(name) {
if (!includes(File.helpers, name)) { if (!includes(File.helpers, name)) {
throw new ReferenceError("Unknown helper " + name); throw new ReferenceError("Unknown helper " + name);
} }
@ -351,28 +363,28 @@ File.prototype.addHelper = function (name) {
}); });
return uid; return uid;
} }
}; }
File.prototype.logDeopt = function () { logDeopt() {
// todo, (node, msg) // todo, (node, msg)
}; }
File.prototype.errorWithNode = function (node, msg, Error) { errorWithNode(node, msg, Error) {
Error ||= SyntaxError; Error ||= SyntaxError;
var loc = node.loc.start; var loc = node.loc.start;
var err = new Error("Line " + loc.line + ": " + msg); var err = new Error("Line " + loc.line + ": " + msg);
err.loc = loc; err.loc = loc;
return err; return err;
}; }
File.prototype.addCode = function (code) { addCode(code) {
code = (code || "") + ""; code = (code || "") + "";
this.code = code; this.code = code;
return this.parseShebang(code); return this.parseShebang(code);
}; }
File.prototype.parse = function (code) { parse(code) {
code = this.addCode(code); code = this.addCode(code);
var opts = this.opts; var opts = this.opts;
@ -384,9 +396,9 @@ File.prototype.parse = function (code) {
this.transform(tree); this.transform(tree);
return this.generate(); return this.generate();
}); });
}; }
File.prototype.transform = function (ast) { transform(ast) {
this.debug(); this.debug();
this.ast = ast; this.ast = ast;
@ -407,9 +419,9 @@ File.prototype.transform = function (ast) {
}); });
this.call("post"); this.call("post");
}; }
File.prototype.call = function (key) { call(key) {
var stack = this.transformerStack; var stack = this.transformerStack;
for (var i = 0; i < stack.length; i++) { for (var i = 0; i < stack.length; i++) {
var transformer = stack[i].transformer; var transformer = stack[i].transformer;
@ -417,22 +429,9 @@ File.prototype.call = function (key) {
transformer[key](this); transformer[key](this);
} }
} }
};
var checkTransformerVisitor = {
enter(node, parent, scope, state) {
checkNode(state.stack, node, scope);
} }
};
var checkNode = function (stack, node, scope) { checkNode(node, scope) {
each(stack, function (pass) {
if (pass.shouldRun) return;
pass.checkNode(node, scope);
});
};
File.prototype.checkNode = function (node, scope) {
var stack = this.transformerStack; var stack = this.transformerStack;
scope ||= this.scope; scope ||= this.scope;
@ -441,9 +440,9 @@ File.prototype.checkNode = function (node, scope) {
scope.traverse(node, checkTransformerVisitor, { scope.traverse(node, checkTransformerVisitor, {
stack: stack stack: stack
}); });
}; }
File.prototype.generate = function () { generate() {
var opts = this.opts; var opts = this.opts;
var ast = this.ast; var ast = this.ast;
@ -475,4 +474,5 @@ File.prototype.generate = function () {
} }
return result; return result;
}; }
}

View File

@ -3,94 +3,16 @@ module.exports = ReplaceSupers;
var messages = require("../../messages"); var messages = require("../../messages");
var t = require("../../types"); var t = require("../../types");
/**
* Description
*
* @param {Object} opts
* @param {Boolean} [inClass]
*/
function ReplaceSupers(opts, inClass) { var isIllegalBareSuper = function (node, parent) {
this.topLevelThisReference = opts.topLevelThisReference; if (!isSuper(node, parent)) return false;
this.methodNode = opts.methodNode; if (t.isMemberExpression(parent, { computed: false })) return false;
this.className = opts.className; if (t.isCallExpression(parent, { callee: node })) return false;
this.superName = opts.superName; return true;
this.isStatic = opts.isStatic;
this.hasSuper = false;
this.inClass = inClass;
this.isLoose = opts.isLoose;
this.scope = opts.scope;
this.file = opts.file;
}
/**
* Sets a super class value of the named property.
*
* @example
*
* _set(Object.getPrototypeOf(CLASS.prototype), "METHOD", "VALUE", this)
*
* @param {Node} property
* @param {Node} value
* @param {Boolean} isComputed
* @param {Node} thisExpression
*
* @returns {Node}
*/
ReplaceSupers.prototype.setSuperProperty = function (property, value, isComputed, thisExpression) {
return t.callExpression(
this.file.addHelper("set"),
[
t.callExpression(
t.memberExpression(t.identifier("Object"), t.identifier("getPrototypeOf")),
[
this.isStatic ? this.className : t.memberExpression(this.className, t.identifier("prototype"))
]
),
isComputed ? property : t.literal(property.name),
value,
thisExpression
]
);
}; };
/** var isSuper = function (node, parent) {
* Gets a node representing the super class value of the named property. return t.isIdentifier(node, { name: "super" }) && t.isReferenced(node, parent);
*
* @example
*
* _get(Object.getPrototypeOf(CLASS.prototype), "METHOD", this)
*
* @param {Node} property
* @param {Boolean} isComputed
* @param {Node} thisExpression
*
* @returns {Node}
*/
ReplaceSupers.prototype.getSuperProperty = function (property, isComputed, thisExpression) {
return t.callExpression(
this.file.addHelper("get"),
[
t.callExpression(
t.memberExpression(t.identifier("Object"), t.identifier("getPrototypeOf")),
[
this.isStatic ? this.className : t.memberExpression(this.className, t.identifier("prototype"))
]
),
isComputed ? property : t.literal(property.name),
thisExpression
]
);
};
/**
* Description
*/
ReplaceSupers.prototype.replace = function () {
this.traverseLevel(this.methodNode.value, true);
}; };
var visitor = { var visitor = {
@ -121,23 +43,115 @@ var visitor = {
} }
}; };
/** export default class ReplaceSupers {
/**
* Description
*
* @param {Object} opts
* @param {Boolean} [inClass]
*/
constructor(opts, inClass) {
this.topLevelThisReference = opts.topLevelThisReference;
this.methodNode = opts.methodNode;
this.className = opts.className;
this.superName = opts.superName;
this.isStatic = opts.isStatic;
this.hasSuper = false;
this.inClass = inClass;
this.isLoose = opts.isLoose;
this.scope = opts.scope;
this.file = opts.file;
}
/**
* Sets a super class value of the named property.
*
* @example
*
* _set(Object.getPrototypeOf(CLASS.prototype), "METHOD", "VALUE", this)
*
* @param {Node} property
* @param {Node} value
* @param {Boolean} isComputed
* @param {Node} thisExpression
*
* @returns {Node}
*/
setSuperProperty(property, value, isComputed, thisExpression) {
return t.callExpression(
this.file.addHelper("set"),
[
t.callExpression(
t.memberExpression(t.identifier("Object"), t.identifier("getPrototypeOf")),
[
this.isStatic ? this.className : t.memberExpression(this.className, t.identifier("prototype"))
]
),
isComputed ? property : t.literal(property.name),
value,
thisExpression
]
);
}
/**
* Gets a node representing the super class value of the named property.
*
* @example
*
* _get(Object.getPrototypeOf(CLASS.prototype), "METHOD", this)
*
* @param {Node} property
* @param {Boolean} isComputed
* @param {Node} thisExpression
*
* @returns {Node}
*/
getSuperProperty(property, isComputed, thisExpression) {
return t.callExpression(
this.file.addHelper("get"),
[
t.callExpression(
t.memberExpression(t.identifier("Object"), t.identifier("getPrototypeOf")),
[
this.isStatic ? this.className : t.memberExpression(this.className, t.identifier("prototype"))
]
),
isComputed ? property : t.literal(property.name),
thisExpression
]
);
}
/**
* Description
*/
replace() {
this.traverseLevel(this.methodNode.value, true);
}
/**
* Description * Description
* *
* @param {Object} node * @param {Object} node
* @param {Boolean} topLevel * @param {Boolean} topLevel
*/ */
ReplaceSupers.prototype.traverseLevel = function (node, topLevel) { traverseLevel(node, topLevel) {
var state = { self: this, topLevel: topLevel }; var state = { self: this, topLevel: topLevel };
this.scope.traverse(node, visitor, state); this.scope.traverse(node, visitor, state);
}; }
/** /**
* Description * Description
*/ */
ReplaceSupers.prototype.getThisReference = function () { getThisReference() {
if (this.topLevelThisReference) { if (this.topLevelThisReference) {
return this.topLevelThisReference; return this.topLevelThisReference;
} else { } else {
@ -147,9 +161,9 @@ ReplaceSupers.prototype.getThisReference = function () {
])); ]));
return ref; return ref;
} }
}; }
/** /**
* Description * Description
* *
* @param {Object} node * @param {Object} node
@ -158,7 +172,7 @@ ReplaceSupers.prototype.getThisReference = function () {
* @returns {Object} * @returns {Object}
*/ */
ReplaceSupers.prototype.getLooseSuperProperty = function (id, parent) { getLooseSuperProperty(id, parent) {
var methodNode = this.methodNode; var methodNode = this.methodNode;
var methodName = methodNode.key; var methodName = methodNode.key;
var superName = this.superName || t.identifier("Function"); var superName = this.superName || t.identifier("Function");
@ -189,9 +203,9 @@ ReplaceSupers.prototype.getLooseSuperProperty = function (id, parent) {
} else { } else {
return superName; return superName;
} }
}; }
/** /**
* Description * Description
* *
* @param {Function} getThisReference * @param {Function} getThisReference
@ -199,7 +213,7 @@ ReplaceSupers.prototype.getLooseSuperProperty = function (id, parent) {
* @param {Object} parent * @param {Object} parent
*/ */
ReplaceSupers.prototype.looseHandle = function (getThisReference, node, parent) { looseHandle(getThisReference, node, parent) {
if (t.isIdentifier(node, { name: "super" })) { if (t.isIdentifier(node, { name: "super" })) {
this.hasSuper = true; this.hasSuper = true;
return this.getLooseSuperProperty(node, parent); return this.getLooseSuperProperty(node, parent);
@ -213,9 +227,9 @@ ReplaceSupers.prototype.looseHandle = function (getThisReference, node, parent)
t.appendToMemberExpression(callee, t.identifier("call")); t.appendToMemberExpression(callee, t.identifier("call"));
node.arguments.unshift(getThisReference()); node.arguments.unshift(getThisReference());
} }
}; }
/** /**
* Description * Description
* *
* @param {Function} getThisReference * @param {Function} getThisReference
@ -223,7 +237,7 @@ ReplaceSupers.prototype.looseHandle = function (getThisReference, node, parent)
* @param {Object} parent * @param {Object} parent
*/ */
ReplaceSupers.prototype.specHandle = function (getThisReference, node, parent) { specHandle(getThisReference, node, parent) {
var methodNode = this.methodNode; var methodNode = this.methodNode;
var property; var property;
var computed; var computed;
@ -287,15 +301,5 @@ ReplaceSupers.prototype.specHandle = function (getThisReference, node, parent) {
} else { } else {
return superProperty; return superProperty;
} }
}; }
}
var isIllegalBareSuper = function (node, parent) {
if (!isSuper(node, parent)) return false;
if (t.isMemberExpression(parent, { computed: false })) return false;
if (t.isCallExpression(parent, { callee: node })) return false;
return true;
};
var isSuper = function (node, parent) {
return t.isIdentifier(node, { name: "super" }) && t.isReferenced(node, parent);
};

View File

@ -1,5 +1,3 @@
module.exports = TransformerPass;
var includes = require("lodash/collection/includes"); var includes = require("lodash/collection/includes");
/** /**
@ -7,14 +5,15 @@ var includes = require("lodash/collection/includes");
* AST and running it's parent transformers handlers over it. * AST and running it's parent transformers handlers over it.
*/ */
function TransformerPass(file, transformer) { export default class TransformerPass {
constructor(file, transformer) {
this.transformer = transformer; this.transformer = transformer;
this.shouldRun = !transformer.check; this.shouldRun = !transformer.check;
this.handlers = transformer.handlers; this.handlers = transformer.handlers;
this.file = file; this.file = file;
} }
TransformerPass.prototype.canRun = function () { canRun() {
var transformer = this.transformer; var transformer = this.transformer;
var opts = this.file.opts; var opts = this.file.opts;
@ -41,18 +40,18 @@ TransformerPass.prototype.canRun = function () {
if (transformer.playground && !opts.playground) return false; if (transformer.playground && !opts.playground) return false;
return true; return true;
}; }
TransformerPass.prototype.checkNode = function (node) { checkNode(node) {
var check = this.transformer.check; var check = this.transformer.check;
if (check) { if (check) {
return this.shouldRun = check(node); return this.shouldRun = check(node);
} else { } else {
return true; return true;
} }
}; }
TransformerPass.prototype.transform = function () { transform() {
if (!this.shouldRun) return; if (!this.shouldRun) return;
var file = this.file; var file = this.file;
@ -60,4 +59,5 @@ TransformerPass.prototype.transform = function () {
file.debug("Running transformer " + this.transformer.key); file.debug("Running transformer " + this.transformer.key);
file.scope.traverse(file.ast, this.handlers, file); file.scope.traverse(file.ast, this.handlers, file);
}; }
}

View File

@ -1,5 +1,3 @@
module.exports = Transformer;
var TransformerPass = require("./transformer-pass"); var TransformerPass = require("./transformer-pass");
var isFunction = require("lodash/lang/isFunction"); var isFunction = require("lodash/lang/isFunction");
var traverse = require("../traversal"); var traverse = require("../traversal");
@ -13,7 +11,8 @@ var each = require("lodash/collection/each");
* actually running the transformer over the provided `File`. * actually running the transformer over the provided `File`.
*/ */
function Transformer(key, transformer, opts) { export default class Transformer {
constructor(key, transformer, opts) {
transformer = assign({}, transformer); transformer = assign({}, transformer);
var take = function (key) { var take = function (key) {
@ -35,9 +34,9 @@ function Transformer(key, transformer, opts) {
this.handlers = this.normalize(transformer); this.handlers = this.normalize(transformer);
this.opts ||= {}; this.opts ||= {};
this.key = key; this.key = key;
} }
Transformer.prototype.normalize = function (transformer) { normalize(transformer) {
if (isFunction(transformer)) { if (isFunction(transformer)) {
transformer = { ast: transformer }; transformer = { ast: transformer };
} }
@ -64,8 +63,9 @@ Transformer.prototype.normalize = function (transformer) {
}); });
return transformer; return transformer;
}; }
Transformer.prototype.buildPass = function (file) { buildPass(file) {
return new TransformerPass(file, this); return new TransformerPass(file, this);
}; }
}

View File

@ -1,28 +1,27 @@
module.exports = TraversalContext;
var TraversalPath = require("./path"); var TraversalPath = require("./path");
var flatten = require("lodash/array/flatten"); var flatten = require("lodash/array/flatten");
var compact = require("lodash/array/compact"); var compact = require("lodash/array/compact");
function TraversalContext(scope, opts, state, parentPath) { export default class TraversalConext {
constructor(scope, opts, state, parentPath) {
this.shouldFlatten = false; this.shouldFlatten = false;
this.parentPath = parentPath; this.parentPath = parentPath;
this.scope = scope; this.scope = scope;
this.state = state; this.state = state;
this.opts = opts; this.opts = opts;
} }
TraversalContext.prototype.flatten = function () { flatten() {
this.shouldFlatten = true; this.shouldFlatten = true;
}; }
TraversalContext.prototype.visitNode = function (node, obj, key) { visitNode(node, obj, key) {
var iteration = new TraversalPath(this, node, obj, key); var iteration = new TraversalPath(this, node, obj, key);
return iteration.visit(); return iteration.visit();
}; }
TraversalContext.prototype.visit = function (node, key) { visit(node, key) {
var nodes = node[key]; var nodes = node[key];
if (!nodes) return; if (!nodes) return;
@ -49,4 +48,5 @@ TraversalContext.prototype.visit = function (node, key) {
node[key] = compact(node[key]); node[key] = compact(node[key]);
} }
} }
}; }
}

View File

@ -1,11 +1,10 @@
module.exports = TraversalPath;
var traverse = require("./index"); var traverse = require("./index");
var includes = require("lodash/collection/includes"); var includes = require("lodash/collection/includes");
var Scope = require("./scope"); var Scope = require("./scope");
var t = require("../types"); var t = require("../types");
function TraversalPath(context, parent, container, key) { export default class TraversalPath {
constructor(context, parent, container, key) {
this.shouldRemove = false; this.shouldRemove = false;
this.shouldSkip = false; this.shouldSkip = false;
this.shouldStop = false; this.shouldStop = false;
@ -22,9 +21,9 @@ function TraversalPath(context, parent, container, key) {
this.state = context.state; this.state = context.state;
this.setScope(); this.setScope();
} }
TraversalPath.getScope = function (node, parent, scope) { static getScope(node, parent, scope) {
var ourScope = scope; var ourScope = scope;
// we're entering a new scope so let's construct it! // we're entering a new scope so let's construct it!
@ -33,36 +32,35 @@ TraversalPath.getScope = function (node, parent, scope) {
} }
return ourScope; return ourScope;
}; }
TraversalPath.prototype.setScope = function () { setScope() {
this.scope = TraversalPath.getScope(this.node, this.parent, this.context.scope); this.scope = TraversalPath.getScope(this.node, this.parent, this.context.scope);
}; }
TraversalPath.prototype.remove = function () { remove() {
this.shouldRemove = true; this.shouldRemove = true;
this.shouldSkip = true; this.shouldSkip = true;
}; }
TraversalPath.prototype.skip = function () { skip() {
this.shouldSkip = true; this.shouldSkip = true;
}; }
TraversalPath.prototype.stop = function () { stop() {
this.shouldStop = true; this.shouldStop = true;
this.shouldSkip = true; this.shouldSkip = true;
}; }
TraversalPath.prototype.flatten = function () { flatten() {
this.context.flatten(); this.context.flatten();
}; }
Object.defineProperty(TraversalPath.prototype, "node", { get node() {
get() {
return this.container[this.key]; return this.container[this.key];
}, }
set(replacement) { set node(replacement) {
var isArray = Array.isArray(replacement); var isArray = Array.isArray(replacement);
// inherit comments from original node to the first replacement node // inherit comments from original node to the first replacement node
@ -95,9 +93,8 @@ Object.defineProperty(TraversalPath.prototype, "node", {
this.flatten(); this.flatten();
} }
} }
});
TraversalPath.prototype.call = function (key) { call(key) {
var node = this.node; var node = this.node;
if (!node) return; if (!node) return;
@ -115,9 +112,9 @@ TraversalPath.prototype.call = function (key) {
this.container[this.key] = null; this.container[this.key] = null;
this.flatten(); this.flatten();
} }
}; }
TraversalPath.prototype.visit = function () { visit() {
var opts = this.opts; var opts = this.opts;
var node = this.node; var node = this.node;
@ -146,8 +143,9 @@ TraversalPath.prototype.visit = function () {
} }
return this.shouldStop; return this.shouldStop;
}; }
TraversalPath.prototype.isReferencedIdentifier = function () { isReferencedIdentifier() {
return t.isReferencedIdentifier(this.node); return t.isReferencedIdentifier(this.node);
}; }
}

View File

@ -1,4 +1,4 @@
module.exports = Scope;
var includes = require("lodash/collection/includes"); var includes = require("lodash/collection/includes");
var traverse = require("./index"); var traverse = require("./index");
@ -11,7 +11,59 @@ var object = require("../helpers/object");
var each = require("lodash/collection/each"); var each = require("lodash/collection/each");
var t = require("../types"); var t = require("../types");
/**
var functionVariableVisitor = {
enter(node, parent, scope, state) {
if (t.isFor(node)) {
each(t.FOR_INIT_KEYS, function (key) {
var declar = node[key];
if (t.isVar(declar)) state.scope.registerBinding("var", declar);
});
}
// this block is a function so we'll stop since none of the variables
// declared within are accessible
if (t.isFunction(node)) return this.skip();
// function identifier doesn't belong to this scope
if (state.blockId && node === state.blockId) return;
// delegate block scope handling to the `blockVariableVisitor`
if (t.isBlockScoped(node)) return;
// this will be hit again once we traverse into it after this iteration
if (t.isExportDeclaration(node) && t.isDeclaration(node.declaration)) return;
// we've ran into a declaration!
if (t.isDeclaration(node)) state.scope.registerDeclaration(node);
}
};
var programReferenceVisitor = {
enter(node, parent, scope, state) {
if (t.isReferencedIdentifier(node, parent) && !scope.hasBinding(node.name)) {
state.addGlobal(node);
} else if (t.isLabeledStatement(node)) {
state.addGlobal(node);
} else if (t.isAssignmentExpression(node) || t.isUpdateExpression(node) || (t.isUnaryExpression(node) && node.operator === "delete")) {
scope.registerBindingReassignment(node);
}
}
};
var blockVariableVisitor = {
enter(node, parent, scope, state) {
if (t.isFunctionDeclaration(node) || t.isBlockScoped(node)) {
state.registerDeclaration(node);
} else if (t.isScope(node, parent)) {
this.skip();
}
}
};
export default class Scope {
/**
* This searches the current "scope" and collects all references/bindings * This searches the current "scope" and collects all references/bindings
* within. * within.
* *
@ -21,7 +73,7 @@ var t = require("../types");
* @param {File} [file] * @param {File} [file]
*/ */
function Scope(block, parentBlock, parent, file) { constructor(block, parentBlock, parent, file) {
this.parent = parent; this.parent = parent;
this.file = parent ? parent.file : file; this.file = parent ? parent.file : file;
@ -29,11 +81,11 @@ function Scope(block, parentBlock, parent, file) {
this.block = block; this.block = block;
this.crawl(); this.crawl();
} }
Scope.globals = flatten([globals.builtin, globals.browser, globals.node].map(Object.keys)); static globals = flatten([globals.builtin, globals.browser, globals.node].map(Object.keys));
/** /**
* Description * Description
* *
* @param {Object} node * @param {Object} node
@ -41,44 +93,44 @@ Scope.globals = flatten([globals.builtin, globals.browser, globals.node].map(Obj
* @param [state] * @param [state]
*/ */
Scope.prototype.traverse = function (node, opts, state) { traverse(node, opts, state) {
traverse(node, opts, this, state); traverse(node, opts, this, state);
}; }
/** /**
* Description * Description
* *
* @param {String} [name="temp"] * @param {String} [name="temp"]
*/ */
Scope.prototype.generateTemp = function (name) { generateTemp(name) {
var id = this.generateUidIdentifier(name || "temp"); var id = this.generateUidIdentifier(name || "temp");
this.push({ this.push({
key: id.name, key: id.name,
id: id id: id
}); });
return id; return id;
}; }
/** /**
* Description * Description
* *
* @param {String} name * @param {String} name
*/ */
Scope.prototype.generateUidIdentifier = function (name) { generateUidIdentifier(name) {
var id = t.identifier(this.generateUid(name)); var id = t.identifier(this.generateUid(name));
this.getFunctionParent().registerBinding("uid", id); this.getFunctionParent().registerBinding("uid", id);
return id; return id;
}; }
/** /**
* Description * Description
* *
* @param {String} name * @param {String} name
*/ */
Scope.prototype.generateUid = function (name) { generateUid(name) {
name = t.toIdentifier(name).replace(/^_+/, ""); name = t.toIdentifier(name).replace(/^_+/, "");
var uid; var uid;
@ -88,22 +140,22 @@ Scope.prototype.generateUid = function (name) {
i++; i++;
} while (this.hasBinding(uid) || this.hasGlobal(uid)); } while (this.hasBinding(uid) || this.hasGlobal(uid));
return uid; return uid;
}; }
Scope.prototype._generateUid = function (name, i) { _generateUid(name, i) {
var id = name; var id = name;
if (i > 1) id += i; if (i > 1) id += i;
return "_" + id; return "_" + id;
}; }
/* /*
* Description * Description
* *
* @param {Object} parent * @param {Object} parent
* @returns {Object} * @returns {Object}
*/ */
Scope.prototype.generateUidBasedOnNode = function (parent) { generateUidBasedOnNode(parent) {
var node = parent; var node = parent;
if (t.isAssignmentExpression(parent)) { if (t.isAssignmentExpression(parent)) {
@ -135,16 +187,16 @@ Scope.prototype.generateUidBasedOnNode = function (parent) {
id = id.replace(/^_/, "") || "ref"; id = id.replace(/^_/, "") || "ref";
return this.generateUidIdentifier(id); return this.generateUidIdentifier(id);
}; }
/** /**
* Description * Description
* *
* @param {Object} node * @param {Object} node
* @returns {Object} * @returns {Object}
*/ */
Scope.prototype.generateTempBasedOnNode = function (node) { generateTempBasedOnNode(node) {
if (t.isIdentifier(node) && this.hasBinding(node.name)) { if (t.isIdentifier(node) && this.hasBinding(node.name)) {
return null; return null;
} }
@ -155,9 +207,9 @@ Scope.prototype.generateTempBasedOnNode = function (node) {
id: id id: id
}); });
return id; return id;
}; }
Scope.prototype.checkBlockScopedCollisions = function (kind, name, id) { checkBlockScopedCollisions(kind, name, id) {
var local = this.getOwnBindingInfo(name); var local = this.getOwnBindingInfo(name);
if (!local) return; if (!local) return;
@ -167,9 +219,9 @@ Scope.prototype.checkBlockScopedCollisions = function (kind, name, id) {
if (local.kind === "let" || local.kind === "const" || local.kind === "module") { if (local.kind === "let" || local.kind === "const" || local.kind === "module") {
throw this.file.errorWithNode(id, messages.get("scopeDuplicateDeclaration", name), TypeError); throw this.file.errorWithNode(id, messages.get("scopeDuplicateDeclaration", name), TypeError);
} }
}; }
Scope.prototype.rename = function (oldName, newName) { rename(oldName, newName) {
newName ||= this.generateUidIdentifier(oldName).name; newName ||= this.generateUidIdentifier(oldName).name;
var info = this.getBindingInfo(oldName); var info = this.getBindingInfo(oldName);
@ -199,9 +251,9 @@ Scope.prototype.rename = function (oldName, newName) {
scope.bindings[newName] = info; scope.bindings[newName] = info;
binding.name = newName; binding.name = newName;
}; }
Scope.prototype.inferType = function (node) { inferType(node) {
var target; var target;
if (t.isVariableDeclarator(node)) { if (t.isVariableDeclarator(node)) {
@ -231,28 +283,28 @@ Scope.prototype.inferType = function (node) {
if (t.isIdentifier(target)) { if (t.isIdentifier(target)) {
return; return;
} }
}; }
Scope.prototype.isTypeGeneric = function (name, genericName) { isTypeGeneric(name, genericName) {
var info = this.getBindingInfo(name); var info = this.getBindingInfo(name);
if (!info) return false; if (!info) return false;
var type = info.typeAnnotation; var type = info.typeAnnotation;
return t.isGenericTypeAnnotation(type) && t.isIdentifier(type.id, { name: genericName }); return t.isGenericTypeAnnotation(type) && t.isIdentifier(type.id, { name: genericName });
}; }
Scope.prototype.assignTypeGeneric = function (name, type) { assignTypeGeneric(name, type) {
this.assignType(name, t.genericTypeAnnotation(t.identifier(type))); this.assignType(name, t.genericTypeAnnotation(t.identifier(type)));
}; }
Scope.prototype.assignType = function (name, type) { assignType(name, type) {
var info = this.getBindingInfo(name); var info = this.getBindingInfo(name);
if (!info) return; if (!info) return;
info.identifier.typeAnnotation = info.typeAnnotation = type; info.identifier.typeAnnotation = info.typeAnnotation = type;
}; }
Scope.prototype.getTypeAnnotation = function (name, id, node) { getTypeAnnotation(name, id, node) {
var info = { var info = {
annotation: null, annotation: null,
inferred: false inferred: false
@ -275,9 +327,9 @@ Scope.prototype.getTypeAnnotation = function (name, id, node) {
} }
return info; return info;
}; }
Scope.prototype.toArray = function (node, i) { toArray(node, i) {
var file = this.file; var file = this.file;
if (t.isIdentifier(node) && this.isTypeGeneric(node.name, "Array")) { if (t.isIdentifier(node) && this.isTypeGeneric(node.name, "Array")) {
@ -301,13 +353,13 @@ Scope.prototype.toArray = function (node, i) {
helperName = "sliced-to-array"; helperName = "sliced-to-array";
} }
return t.callExpression(file.addHelper(helperName), args); return t.callExpression(file.addHelper(helperName), args);
}; }
Scope.prototype.clearOwnBinding = function (name) { clearOwnBinding(name) {
delete this.bindings[name]; delete this.bindings[name];
}; }
Scope.prototype.registerDeclaration = function (node) { registerDeclaration(node) {
if (t.isFunctionDeclaration(node)) { if (t.isFunctionDeclaration(node)) {
this.registerBinding("hoisted", node); this.registerBinding("hoisted", node);
} else if (t.isVariableDeclaration(node)) { } else if (t.isVariableDeclaration(node)) {
@ -321,9 +373,9 @@ Scope.prototype.registerDeclaration = function (node) {
} else { } else {
this.registerBinding("unknown", node); this.registerBinding("unknown", node);
} }
}; }
Scope.prototype.registerBindingReassignment = function (node) { registerBindingReassignment(node) {
var ids = t.getBindingIdentifiers(node); var ids = t.getBindingIdentifiers(node);
for (var name in ids) { for (var name in ids) {
var info = this.getBindingInfo(name); var info = this.getBindingInfo(name);
@ -336,9 +388,9 @@ Scope.prototype.registerBindingReassignment = function (node) {
} }
} }
} }
}; }
Scope.prototype.registerBinding = function (kind, node) { registerBinding(kind, node) {
if (!kind) throw new ReferenceError("no `kind`"); if (!kind) throw new ReferenceError("no `kind`");
var ids = t.getBindingIdentifiers(node); var ids = t.getBindingIdentifiers(node);
@ -360,47 +412,20 @@ Scope.prototype.registerBinding = function (kind, node) {
kind: kind kind: kind
}; };
} }
}; }
Scope.prototype.registerVariableDeclaration = function (declar) { registerVariableDeclaration(declar) {
var declars = declar.declarations; var declars = declar.declarations;
for (var i = 0; i < declars.length; i++) { for (var i = 0; i < declars.length; i++) {
this.registerBinding(declars[i], declar.kind); this.registerBinding(declars[i], declar.kind);
} }
};
var functionVariableVisitor = {
enter(node, parent, scope, state) {
if (t.isFor(node)) {
each(t.FOR_INIT_KEYS, function (key) {
var declar = node[key];
if (t.isVar(declar)) state.scope.registerBinding("var", declar);
});
} }
// this block is a function so we'll stop since none of the variables addGlobal(node) {
// declared within are accessible
if (t.isFunction(node)) return this.skip();
// function identifier doesn't belong to this scope
if (state.blockId && node === state.blockId) return;
// delegate block scope handling to the `blockVariableVisitor`
if (t.isBlockScoped(node)) return;
// this will be hit again once we traverse into it after this iteration
if (t.isExportDeclaration(node) && t.isDeclaration(node.declaration)) return;
// we've ran into a declaration!
if (t.isDeclaration(node)) state.scope.registerDeclaration(node);
}
};
Scope.prototype.addGlobal = function (node) {
this.globals[node.name] = node; this.globals[node.name] = node;
}; };
Scope.prototype.hasGlobal = function (name) { hasGlobal(name) {
var scope = this; var scope = this;
do { do {
@ -408,31 +433,9 @@ Scope.prototype.hasGlobal = function (name) {
} while (scope = scope.parent); } while (scope = scope.parent);
return false; return false;
}; }
var programReferenceVisitor = { crawl() {
enter(node, parent, scope, state) {
if (t.isReferencedIdentifier(node, parent) && !scope.hasBinding(node.name)) {
state.addGlobal(node);
} else if (t.isLabeledStatement(node)) {
state.addGlobal(node);
} else if (t.isAssignmentExpression(node) || t.isUpdateExpression(node) || (t.isUnaryExpression(node) && node.operator === "delete")) {
scope.registerBindingReassignment(node);
}
}
};
var blockVariableVisitor = {
enter(node, parent, scope, state) {
if (t.isFunctionDeclaration(node) || t.isBlockScoped(node)) {
state.registerDeclaration(node);
} else if (t.isScope(node, parent)) {
this.skip();
}
}
};
Scope.prototype.crawl = function () {
var block = this.block; var block = this.block;
var i; var i;
@ -513,15 +516,15 @@ Scope.prototype.crawl = function () {
if (t.isProgram(block)) { if (t.isProgram(block)) {
this.traverse(block, programReferenceVisitor, this); this.traverse(block, programReferenceVisitor, this);
} }
}; }
/** /**
* Description * Description
* *
* @param {Object} opts * @param {Object} opts
*/ */
Scope.prototype.push = function (opts) { push(opts) {
var block = this.block; var block = this.block;
if (t.isLoop(block) || t.isCatchClause(block) || t.isFunction(block)) { if (t.isLoop(block) || t.isCatchClause(block) || t.isFunction(block)) {
@ -539,28 +542,28 @@ Scope.prototype.push = function (opts) {
} else { } else {
throw new TypeError("cannot add a declaration here in node type " + block.type); throw new TypeError("cannot add a declaration here in node type " + block.type);
} }
}; }
/** /**
* Walk up the scope tree until we hit either a Function or reach the * Walk up the scope tree until we hit either a Function or reach the
* very top and hit Program. * very top and hit Program.
*/ */
Scope.prototype.getFunctionParent = function () { getFunctionParent() {
var scope = this; var scope = this;
while (scope.parent && !t.isFunction(scope.block)) { while (scope.parent && !t.isFunction(scope.block)) {
scope = scope.parent; scope = scope.parent;
} }
return scope; return scope;
}; }
/** /**
* Walks the scope tree and gathers **all** bindings. * Walks the scope tree and gathers **all** bindings.
* *
* @returns {Object} * @returns {Object}
*/ */
Scope.prototype.getAllBindings = function () { getAllBindings() {
var ids = object(); var ids = object();
var scope = this; var scope = this;
@ -570,16 +573,16 @@ Scope.prototype.getAllBindings = function () {
} while (scope); } while (scope);
return ids; return ids;
}; }
/** /**
* Walks the scope tree and gathers all declarations of `kind`. * Walks the scope tree and gathers all declarations of `kind`.
* *
* @param {String} kind * @param {String} kind
* @returns {Object} * @returns {Object}
*/ */
Scope.prototype.getAllBindingsOfKind = function (kind) { getAllBindingsOfKind(kind) {
var ids = object(); var ids = object();
var scope = this; var scope = this;
@ -592,53 +595,54 @@ Scope.prototype.getAllBindingsOfKind = function (kind) {
} while (scope); } while (scope);
return ids; return ids;
}; }
// misc // misc
Scope.prototype.bindingIdentifierEquals = function (name, node) { bindingIdentifierEquals(name, node) {
return this.getBindingIdentifier(name) === node; return this.getBindingIdentifier(name) === node;
}; }
// get // get
Scope.prototype.getBindingInfo = function (name) { getBindingInfo(name) {
var scope = this; var scope = this;
do { do {
var binding = scope.getOwnBindingInfo(name); var binding = scope.getOwnBindingInfo(name);
if (binding) return binding; if (binding) return binding;
} while (scope = scope.parent); } while (scope = scope.parent);
}; }
Scope.prototype.getOwnBindingInfo = function (name) { getOwnBindingInfo(name) {
return this.bindings[name]; return this.bindings[name];
}; }
Scope.prototype.getBindingIdentifier = function (name) { getBindingIdentifier(name) {
var info = this.getBindingInfo(name); var info = this.getBindingInfo(name);
return info && info.identifier; return info && info.identifier;
}; }
Scope.prototype.getOwnBindingIdentifier = function (name) { getOwnBindingIdentifier(name) {
var binding = this.bindings[name]; var binding = this.bindings[name];
return binding && binding.identifier; return binding && binding.identifier;
}; }
// has // has
Scope.prototype.hasOwnBinding = function (name) { hasOwnBinding(name) {
return !!this.getOwnBindingInfo(name); return !!this.getOwnBindingInfo(name);
}; }
Scope.prototype.hasBinding = function (name) { hasBinding(name) {
if (!name) return false; if (!name) return false;
if (this.hasOwnBinding(name)) return true; if (this.hasOwnBinding(name)) return true;
if (this.parentHasBinding(name)) return true; if (this.parentHasBinding(name)) return true;
if (includes(Scope.globals, name)) return true; if (includes(Scope.globals, name)) return true;
return false; return false;
}; }
Scope.prototype.parentHasBinding = function (name) { parentHasBinding(name) {
return this.parent && this.parent.hasBinding(name); return this.parent && this.parent.hasBinding(name);
}; }
}