| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800 | /** * @fileoverview Main Espree file that converts Acorn into Esprima output. * * This file contains code from the following MIT-licensed projects: * 1. Acorn * 2. Babylon * 3. Babel-ESLint * * This file also contains code from Esprima, which is BSD licensed. * * Acorn is Copyright 2012-2015 Acorn Contributors (https://github.com/marijnh/acorn/blob/master/AUTHORS) * Babylon is Copyright 2014-2015 various contributors (https://github.com/babel/babel/blob/master/packages/babylon/AUTHORS) * Babel-ESLint is Copyright 2014-2015 Sebastian McKenzie <sebmck@gmail.com> * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * * Redistributions of source code must retain the above copyright *   notice, this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above copyright *   notice, this list of conditions and the following disclaimer in the *   documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * Esprima is Copyright (c) jQuery Foundation, Inc. and Contributors, All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * *   * Redistributions of source code must retain the above copyright *     notice, this list of conditions and the following disclaimer. *   * Redistributions in binary form must reproduce the above copyright *     notice, this list of conditions and the following disclaimer in the *     documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. *//* eslint no-undefined:0, no-use-before-define: 0 */"use strict";var astNodeTypes = require("./lib/ast-node-types"),    commentAttachment = require("./lib/comment-attachment"),    TokenTranslator = require("./lib/token-translator"),    acornJSX = require("acorn-jsx/inject"),    rawAcorn = require("acorn");var acorn = acornJSX(rawAcorn);var DEFAULT_ECMA_VERSION = 5;var lookahead,    extra,    lastToken;/** * Object.assign polyfill for Node < 4 * @param {Object} target The target object * @param {...Object} sources Sources for the object * @returns {Object} `target` after being mutated */var assign = Object.assign || function assign(target) {    for (var argIndex = 1; argIndex < arguments.length; argIndex++) {        if (arguments[argIndex] !== null && typeof arguments[argIndex] === "object") {            var keys = Object.keys(arguments[argIndex]);            for (var keyIndex = 0; keyIndex < keys.length; keyIndex++) {                target[keys[keyIndex]] = arguments[argIndex][keys[keyIndex]];            }        }    }    return target;};/** * Resets the extra object to its default. * @returns {void} * @private */function resetExtra() {    extra = {        tokens: null,        range: false,        loc: false,        comment: false,        comments: [],        tolerant: false,        errors: [],        strict: false,        ecmaFeatures: {},        ecmaVersion: DEFAULT_ECMA_VERSION,        isModule: false    };}var tt = acorn.tokTypes,    getLineInfo = acorn.getLineInfo;// custom type for JSX attribute valuestt.jsxAttrValueToken = {};/** * Normalize ECMAScript version from the initial config * @param  {number} ecmaVersion ECMAScript version from the initial config * @returns {number} normalized ECMAScript version */function normalizeEcmaVersion(ecmaVersion) {    if (typeof ecmaVersion === "number") {        var version = ecmaVersion;        // Calculate ECMAScript edition number from official year version starting with        // ES2015, which corresponds with ES6 (or a difference of 2009).        if (version >= 2015) {            version -= 2009;        }        switch (version) {            case 3:            case 5:            case 6:            case 7:            case 8:            case 9:                return version;            default:                throw new Error("Invalid ecmaVersion.");        }    } else {        return DEFAULT_ECMA_VERSION;    }}/** * Determines if a node is valid given the set of ecmaFeatures. * @param {ASTNode} node The node to check. * @returns {boolean} True if the node is allowed, false if not. * @private */function isValidNode(node) {    var ecma = extra.ecmaFeatures;    switch (node.type) {        case "ExperimentalSpreadProperty":        case "ExperimentalRestProperty":            return ecma.experimentalObjectRestSpread;        case "ImportDeclaration":        case "ExportNamedDeclaration":        case "ExportDefaultDeclaration":        case "ExportAllDeclaration":            return extra.isModule;        default:            return true;    }}/** * Performs last-minute Esprima-specific compatibility checks and fixes. * @param {ASTNode} result The node to check. * @returns {ASTNode} The finished node. * @private * @this acorn.Parser */function esprimaFinishNode(result) {    // ensure that parsed node was allowed through ecmaFeatures    if (!isValidNode(result)) {        this.unexpected(result.start);    }    // https://github.com/marijnh/acorn/issues/323    if (result.type === "TryStatement") {        delete result.guardedHandlers;    } else if (result.type === "CatchClause") {        delete result.guard;    }    // Acorn doesn't count the opening and closing backticks as part of templates    // so we have to adjust ranges/locations appropriately.    if (result.type === "TemplateElement") {        // additional adjustment needed if ${ is the last token        var terminalDollarBraceL = this.input.slice(result.end, result.end + 2) === "${";        if (result.range) {            result.range[0]--;            result.range[1] += (terminalDollarBraceL ? 2 : 1);        }        if (result.loc) {            result.loc.start.column--;            result.loc.end.column += (terminalDollarBraceL ? 2 : 1);        }    }    // Acorn uses undefined instead of null, which affects serialization    if (result.type === "Literal" && result.value === undefined) {        result.value = null;    }    if (extra.attachComment) {        commentAttachment.processComment(result);    }    if (result.type.indexOf("Function") > -1 && !result.generator) {        result.generator = false;    }    return result;}/** * Determines if a token is valid given the set of ecmaFeatures. * @param {acorn.Parser} parser The parser to check. * @returns {boolean} True if the token is allowed, false if not. * @private */function isValidToken(parser) {    var ecma = extra.ecmaFeatures;    var type = parser.type;    switch (type) {        case tt.jsxName:        case tt.jsxText:        case tt.jsxTagStart:        case tt.jsxTagEnd:            return ecma.jsx;        // https://github.com/ternjs/acorn/issues/363        case tt.regexp:            if (extra.ecmaVersion < 6 && parser.value.flags && parser.value.flags.indexOf("y") > -1) {                return false;            }            return true;        default:            return true;    }}/** * Injects esprimaFinishNode into the finishNode process. * @param {Function} finishNode Original finishNode function. * @returns {ASTNode} The finished node. * @private */function wrapFinishNode(finishNode) {    return /** @this acorn.Parser */ function(node, type, pos, loc) {        var result = finishNode.call(this, node, type, pos, loc);        return esprimaFinishNode.call(this, result);    };}acorn.plugins.espree = function(instance) {    instance.extend("finishNode", wrapFinishNode);    instance.extend("finishNodeAt", wrapFinishNode);    instance.extend("next", function(next) {        return /** @this acorn.Parser */ function() {            if (!isValidToken(this)) {                this.unexpected();            }            return next.call(this);        };    });    // needed for experimental object rest/spread    instance.extend("checkLVal", function(checkLVal) {        return /** @this acorn.Parser */ function(expr, isBinding, checkClashes) {            if (extra.ecmaFeatures.experimentalObjectRestSpread && expr.type === "ObjectPattern") {                for (var i = 0; i < expr.properties.length; i++) {                    if (expr.properties[i].type.indexOf("Experimental") === -1) {                        this.checkLVal(expr.properties[i].value, isBinding, checkClashes);                    }                }                return undefined;            }            return checkLVal.call(this, expr, isBinding, checkClashes);        };    });    instance.extend("parseTopLevel", function(parseTopLevel) {        return /** @this acorn.Parser */ function(node) {            if (extra.ecmaFeatures.impliedStrict && this.options.ecmaVersion >= 5) {                this.strict = true;            }            return parseTopLevel.call(this, node);        };    });    instance.extend("toAssignable", function(toAssignable) {        return /** @this acorn.Parser */ function(node, isBinding, refDestructuringErrors) {            if (extra.ecmaFeatures.experimentalObjectRestSpread &&                    node.type === "ObjectExpression"            ) {                node.type = "ObjectPattern";                for (var i = 0; i < node.properties.length; i++) {                    var prop = node.properties[i];                    if (prop.type === "ExperimentalSpreadProperty") {                        prop.type = "ExperimentalRestProperty";                    } else if (prop.kind !== "init") {                        this.raise(prop.key.start, "Object pattern can't contain getter or setter");                    } else {                        this.toAssignable(prop.value, isBinding);                    }                }                return node;            } else {                return toAssignable.call(this, node, isBinding, refDestructuringErrors);            }        };    });    /**     * Method to parse an object rest or object spread.     * @returns {ASTNode} The node representing object rest or object spread.     * @this acorn.Parser     */    instance.parseObjectRest = function() {        var node = this.startNode();        this.next();        node.argument = this.parseIdent();        if (this.type === tt.comma) {            this.raise(this.start, "Unexpected trailing comma after rest property");        }        return this.finishNode(node, "ExperimentalRestProperty");    };    instance.extend("parseProperty", function(parseProperty) {        /**         * Override `parseProperty` method to parse rest/spread properties.         * @param {boolean} isPattern True if the object is a destructuring pattern.         * @param {Object} refDestructuringErrors ?         * @returns {ASTNode} The node representing a rest/spread property.         * @this acorn.Parser         */        return function(isPattern, refDestructuringErrors) {            if (extra.ecmaFeatures.experimentalObjectRestSpread && this.type === tt.ellipsis) {                var prop;                if (isPattern) {                    prop = this.parseObjectRest();                } else {                    prop = this.parseSpread();                    prop.type = "ExperimentalSpreadProperty";                }                return prop;            }            return parseProperty.call(this, isPattern, refDestructuringErrors);        };    });    instance.extend("checkPropClash", function(checkPropClash) {        /**         * Override `checkPropClash` method to avoid clash on rest/spread properties.         * @param {ASTNode} prop A property node to check.         * @param {Object} propHash Names map.         * @param {Object} refDestructuringErrors Destructuring error information.         * @returns {void}         * @this acorn.Parser         */        return function(prop, propHash, refDestructuringErrors) {            if (prop.type === "ExperimentalRestProperty" || prop.type === "ExperimentalSpreadProperty") {                return;            }            checkPropClash.call(this, prop, propHash, refDestructuringErrors);        };    });    /**     * Overwrites the default raise method to throw Esprima-style errors.     * @param {int} pos The position of the error.     * @param {string} message The error message.     * @throws {SyntaxError} A syntax error.     * @returns {void}     */    instance.raise = instance.raiseRecoverable = function(pos, message) {        var loc = getLineInfo(this.input, pos);        var err = new SyntaxError(message);        err.index = pos;        err.lineNumber = loc.line;        err.column = loc.column + 1; // acorn uses 0-based columns        throw err;    };    /**     * Overwrites the default unexpected method to throw Esprima-style errors.     * @param {int} pos The position of the error.     * @throws {SyntaxError} A syntax error.     * @returns {void}     */    instance.unexpected = function(pos) {        var message = "Unexpected token";        if (pos !== null && pos !== undefined) {            this.pos = pos;            if (this.options.locations) {                while (this.pos < this.lineStart) {                    this.lineStart = this.input.lastIndexOf("\n", this.lineStart - 2) + 1;                    --this.curLine;                }            }            this.nextToken();        }        if (this.end > this.start) {            message += " " + this.input.slice(this.start, this.end);        }        this.raise(this.start, message);    };    /*    * Esprima-FB represents JSX strings as tokens called "JSXText", but Acorn-JSX    * uses regular tt.string without any distinction between this and regular JS    * strings. As such, we intercept an attempt to read a JSX string and set a flag    * on extra so that when tokens are converted, the next token will be switched    * to JSXText via onToken.    */    instance.extend("jsx_readString", function(jsxReadString) {        return /** @this acorn.Parser */ function(quote) {            var result = jsxReadString.call(this, quote);            if (this.type === tt.string) {                extra.jsxAttrValueToken = true;            }            return result;        };    });};//------------------------------------------------------------------------------// Tokenizer//------------------------------------------------------------------------------/** * Tokenizes the given code. * @param {string} code The code to tokenize. * @param {Object} options Options defining how to tokenize. * @returns {Token[]} An array of tokens. * @throws {SyntaxError} If the input code is invalid. * @private */function tokenize(code, options) {    var toString,        tokens,        impliedStrict,        translator = new TokenTranslator(tt, code);    toString = String;    if (typeof code !== "string" && !(code instanceof String)) {        code = toString(code);    }    lookahead = null;    // Options matching.    options = assign({}, options);    var acornOptions = {        ecmaVersion: DEFAULT_ECMA_VERSION,        plugins: {            espree: true        }    };    resetExtra();    // Of course we collect tokens here.    options.tokens = true;    extra.tokens = [];    extra.range = (typeof options.range === "boolean") && options.range;    acornOptions.ranges = extra.range;    extra.loc = (typeof options.loc === "boolean") && options.loc;    acornOptions.locations = extra.loc;    extra.comment = typeof options.comment === "boolean" && options.comment;    if (extra.comment) {        acornOptions.onComment = function() {            var comment = convertAcornCommentToEsprimaComment.apply(this, arguments);            extra.comments.push(comment);        };    }    extra.tolerant = typeof options.tolerant === "boolean" && options.tolerant;    acornOptions.ecmaVersion = extra.ecmaVersion = normalizeEcmaVersion(options.ecmaVersion);    // apply parsing flags    if (options.ecmaFeatures && typeof options.ecmaFeatures === "object") {        extra.ecmaFeatures = assign({}, options.ecmaFeatures);        impliedStrict = extra.ecmaFeatures.impliedStrict;        extra.ecmaFeatures.impliedStrict = typeof impliedStrict === "boolean" && impliedStrict;    }    try {        var tokenizer = acorn.tokenizer(code, acornOptions);        while ((lookahead = tokenizer.getToken()).type !== tt.eof) {            translator.onToken(lookahead, extra);        }        // filterTokenLocation();        tokens = extra.tokens;        if (extra.comment) {            tokens.comments = extra.comments;        }        if (extra.tolerant) {            tokens.errors = extra.errors;        }    } catch (e) {        throw e;    }    return tokens;}//------------------------------------------------------------------------------// Parser//------------------------------------------------------------------------------/** * Converts an Acorn comment to a Esprima comment. * @param {boolean} block True if it's a block comment, false if not. * @param {string} text The text of the comment. * @param {int} start The index at which the comment starts. * @param {int} end The index at which the comment ends. * @param {Location} startLoc The location at which the comment starts. * @param {Location} endLoc The location at which the comment ends. * @returns {Object} The comment object. * @private */function convertAcornCommentToEsprimaComment(block, text, start, end, startLoc, endLoc) {    var comment = {        type: block ? "Block" : "Line",        value: text    };    if (typeof start === "number") {        comment.start = start;        comment.end = end;        comment.range = [start, end];    }    if (typeof startLoc === "object") {        comment.loc = {            start: startLoc,            end: endLoc        };    }    return comment;}/** * Parses the given code. * @param {string} code The code to tokenize. * @param {Object} options Options defining how to tokenize. * @returns {ASTNode} The "Program" AST node. * @throws {SyntaxError} If the input code is invalid. * @private */function parse(code, options) {    var program,        toString = String,        translator,        impliedStrict,        acornOptions = {            ecmaVersion: DEFAULT_ECMA_VERSION,            plugins: {                espree: true            }        };    lastToken = null;    if (typeof code !== "string" && !(code instanceof String)) {        code = toString(code);    }    resetExtra();    commentAttachment.reset();    if (typeof options !== "undefined") {        extra.range = (typeof options.range === "boolean") && options.range;        extra.loc = (typeof options.loc === "boolean") && options.loc;        extra.attachComment = (typeof options.attachComment === "boolean") && options.attachComment;        if (extra.loc && options.source !== null && options.source !== undefined) {            extra.source = toString(options.source);        }        if (typeof options.tokens === "boolean" && options.tokens) {            extra.tokens = [];            translator = new TokenTranslator(tt, code);        }        if (typeof options.comment === "boolean" && options.comment) {            extra.comment = true;            extra.comments = [];        }        if (typeof options.tolerant === "boolean" && options.tolerant) {            extra.errors = [];        }        if (extra.attachComment) {            extra.range = true;            extra.comments = [];            commentAttachment.reset();        }        acornOptions.ecmaVersion = extra.ecmaVersion = normalizeEcmaVersion(options.ecmaVersion);        if (options.sourceType === "module") {            extra.isModule = true;            // modules must be in 6 at least            if (acornOptions.ecmaVersion < 6) {                acornOptions.ecmaVersion = 6;                extra.ecmaVersion = 6;            }            acornOptions.sourceType = "module";        }        // apply parsing flags after sourceType to allow overriding        if (options.ecmaFeatures && typeof options.ecmaFeatures === "object") {            extra.ecmaFeatures = assign({}, options.ecmaFeatures);            impliedStrict = extra.ecmaFeatures.impliedStrict;            extra.ecmaFeatures.impliedStrict = typeof impliedStrict === "boolean" && impliedStrict;            if (options.ecmaFeatures.globalReturn) {                acornOptions.allowReturnOutsideFunction = true;            }        }        acornOptions.onToken = function(token) {            if (extra.tokens) {                translator.onToken(token, extra);            }            if (token.type !== tt.eof) {                lastToken = token;            }        };        if (extra.attachComment || extra.comment) {            acornOptions.onComment = function() {                var comment = convertAcornCommentToEsprimaComment.apply(this, arguments);                extra.comments.push(comment);                if (extra.attachComment) {                    commentAttachment.addComment(comment);                }            };        }        if (extra.range) {            acornOptions.ranges = true;        }        if (extra.loc) {            acornOptions.locations = true;        }        if (extra.ecmaFeatures.jsx) {            // Should process jsx plugin before espree plugin.            acornOptions.plugins = {                jsx: true,                espree: true            };        }    }    program = acorn.parse(code, acornOptions);    program.sourceType = extra.isModule ? "module" : "script";    if (extra.comment || extra.attachComment) {        program.comments = extra.comments;    }    if (extra.tokens) {        program.tokens = extra.tokens;    }    /*     * Adjust opening and closing position of program to match Esprima.     * Acorn always starts programs at range 0 whereas Esprima starts at the     * first AST node's start (the only real difference is when there's leading     * whitespace or leading comments). Acorn also counts trailing whitespace     * as part of the program whereas Esprima only counts up to the last token.     */    if (program.range) {        program.range[0] = program.body.length ? program.body[0].range[0] : program.range[0];        program.range[1] = lastToken ? lastToken.range[1] : program.range[1];    }    if (program.loc) {        program.loc.start = program.body.length ? program.body[0].loc.start : program.loc.start;        program.loc.end = lastToken ? lastToken.loc.end : program.loc.end;    }    return program;}//------------------------------------------------------------------------------// Public//------------------------------------------------------------------------------exports.version = require("./package.json").version;exports.tokenize = tokenize;exports.parse = parse;// Deep copy./* istanbul ignore next */exports.Syntax = (function() {    var name, types = {};    if (typeof Object.create === "function") {        types = Object.create(null);    }    for (name in astNodeTypes) {        if (astNodeTypes.hasOwnProperty(name)) {            types[name] = astNodeTypes[name];        }    }    if (typeof Object.freeze === "function") {        Object.freeze(types);    }    return types;}());/* istanbul ignore next */exports.VisitorKeys = (function() {    var visitorKeys = require("./lib/visitor-keys");    var name,        keys = {};    if (typeof Object.create === "function") {        keys = Object.create(null);    }    for (name in visitorKeys) {        if (visitorKeys.hasOwnProperty(name)) {            keys[name] = visitorKeys[name];        }    }    if (typeof Object.freeze === "function") {        Object.freeze(keys);    }    return keys;}());
 |