|                                                                                                                                                                                                                                                                       |  | /** * @fileoverview Translates tokens between Acorn format and Esprima format. * @author Nicholas C. Zakas */
//------------------------------------------------------------------------------
// Requirements
//------------------------------------------------------------------------------
// none!
//------------------------------------------------------------------------------
// Private
//------------------------------------------------------------------------------
// Esprima Token Types
const Token = {    Boolean: "Boolean",    EOF: "<end>",    Identifier: "Identifier",    PrivateIdentifier: "PrivateIdentifier",    Keyword: "Keyword",    Null: "Null",    Numeric: "Numeric",    Punctuator: "Punctuator",    String: "String",    RegularExpression: "RegularExpression",    Template: "Template",    JSXIdentifier: "JSXIdentifier",    JSXText: "JSXText"};
/** * Converts part of a template into an Esprima token. * @param {AcornToken[]} tokens The Acorn tokens representing the template. * @param {string} code The source code. * @returns {EsprimaToken} The Esprima equivalent of the template token. * @private */function convertTemplatePart(tokens, code) {    const firstToken = tokens[0],        lastTemplateToken = tokens[tokens.length - 1];
    const token = {        type: Token.Template,        value: code.slice(firstToken.start, lastTemplateToken.end)    };
    if (firstToken.loc) {        token.loc = {            start: firstToken.loc.start,            end: lastTemplateToken.loc.end        };    }
    if (firstToken.range) {        token.start = firstToken.range[0];        token.end = lastTemplateToken.range[1];        token.range = [token.start, token.end];    }
    return token;}
/** * Contains logic to translate Acorn tokens into Esprima tokens. * @param {Object} acornTokTypes The Acorn token types. * @param {string} code The source code Acorn is parsing. This is necessary *      to correct the "value" property of some tokens. * @constructor */function TokenTranslator(acornTokTypes, code) {
    // token types
    this._acornTokTypes = acornTokTypes;
    // token buffer for templates
    this._tokens = [];
    // track the last curly brace
    this._curlyBrace = null;
    // the source code
    this._code = code;
}
TokenTranslator.prototype = {    constructor: TokenTranslator,
    /**     * Translates a single Esprima token to a single Acorn token. This may be     * inaccurate due to how templates are handled differently in Esprima and     * Acorn, but should be accurate for all other tokens.     * @param {AcornToken} token The Acorn token to translate.     * @param {Object} extra Espree extra object.     * @returns {EsprimaToken} The Esprima version of the token.     */    translate(token, extra) {
        const type = token.type,            tt = this._acornTokTypes;
        if (type === tt.name) {            token.type = Token.Identifier;
            // TODO: See if this is an Acorn bug
            if (token.value === "static") {                token.type = Token.Keyword;            }
            if (extra.ecmaVersion > 5 && (token.value === "yield" || token.value === "let")) {                token.type = Token.Keyword;            }
        } else if (type === tt.privateId) {            token.type = Token.PrivateIdentifier;
        } else if (type === tt.semi || type === tt.comma ||                 type === tt.parenL || type === tt.parenR ||                 type === tt.braceL || type === tt.braceR ||                 type === tt.dot || type === tt.bracketL ||                 type === tt.colon || type === tt.question ||                 type === tt.bracketR || type === tt.ellipsis ||                 type === tt.arrow || type === tt.jsxTagStart ||                 type === tt.incDec || type === tt.starstar ||                 type === tt.jsxTagEnd || type === tt.prefix ||                 type === tt.questionDot ||                 (type.binop && !type.keyword) ||                 type.isAssign) {
            token.type = Token.Punctuator;            token.value = this._code.slice(token.start, token.end);        } else if (type === tt.jsxName) {            token.type = Token.JSXIdentifier;        } else if (type.label === "jsxText" || type === tt.jsxAttrValueToken) {            token.type = Token.JSXText;        } else if (type.keyword) {            if (type.keyword === "true" || type.keyword === "false") {                token.type = Token.Boolean;            } else if (type.keyword === "null") {                token.type = Token.Null;            } else {                token.type = Token.Keyword;            }        } else if (type === tt.num) {            token.type = Token.Numeric;            token.value = this._code.slice(token.start, token.end);        } else if (type === tt.string) {
            if (extra.jsxAttrValueToken) {                extra.jsxAttrValueToken = false;                token.type = Token.JSXText;            } else {                token.type = Token.String;            }
            token.value = this._code.slice(token.start, token.end);        } else if (type === tt.regexp) {            token.type = Token.RegularExpression;            const value = token.value;
            token.regex = {                flags: value.flags,                pattern: value.pattern            };            token.value = `/${value.pattern}/${value.flags}`;        }
        return token;    },
    /**     * Function to call during Acorn's onToken handler.     * @param {AcornToken} token The Acorn token.     * @param {Object} extra The Espree extra object.     * @returns {void}     */    onToken(token, extra) {
        const tt = this._acornTokTypes,            tokens = extra.tokens,            templateTokens = this._tokens;
        /**         * Flushes the buffered template tokens and resets the template         * tracking.         * @returns {void}         * @private         */        const translateTemplateTokens = () => {            tokens.push(convertTemplatePart(this._tokens, this._code));            this._tokens = [];        };
        if (token.type === tt.eof) {
            // might be one last curlyBrace
            if (this._curlyBrace) {                tokens.push(this.translate(this._curlyBrace, extra));            }
            return;        }
        if (token.type === tt.backQuote) {
            // if there's already a curly, it's not part of the template
            if (this._curlyBrace) {                tokens.push(this.translate(this._curlyBrace, extra));                this._curlyBrace = null;            }
            templateTokens.push(token);
            // it's the end
            if (templateTokens.length > 1) {                translateTemplateTokens();            }
            return;        }        if (token.type === tt.dollarBraceL) {            templateTokens.push(token);            translateTemplateTokens();            return;        }        if (token.type === tt.braceR) {
            // if there's already a curly, it's not part of the template
            if (this._curlyBrace) {                tokens.push(this.translate(this._curlyBrace, extra));            }
            // store new curly for later
            this._curlyBrace = token;            return;        }        if (token.type === tt.template || token.type === tt.invalidTemplate) {            if (this._curlyBrace) {                templateTokens.push(this._curlyBrace);                this._curlyBrace = null;            }
            templateTokens.push(token);            return;        }
        if (this._curlyBrace) {            tokens.push(this.translate(this._curlyBrace, extra));            this._curlyBrace = null;        }
        tokens.push(this.translate(token, extra));    }};
//------------------------------------------------------------------------------
// Public
//------------------------------------------------------------------------------
export default TokenTranslator;
 |