| 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060 | // Astring is a tiny and fast JavaScript code generator from an ESTree-compliant AST.//// Astring was written by David Bonnet and released under an MIT license.//// The Git repository for Astring is available at:// https://github.com/davidbonnet/astring.git//// Please use the GitHub bug tracker to report issues:// https://github.com/davidbonnet/astring/issuesconst { stringify } = JSON/* istanbul ignore if */if (!String.prototype.repeat) {    /* istanbul ignore next */    throw new Error(        'String.prototype.repeat is undefined, see https://github.com/davidbonnet/astring#installation',    )}/* istanbul ignore if */if (!String.prototype.endsWith) {    /* istanbul ignore next */    throw new Error(        'String.prototype.endsWith is undefined, see https://github.com/davidbonnet/astring#installation',    )}const OPERATOR_PRECEDENCE = {    '||': 3,    '&&': 4,    '|': 5,    '^': 6,    '&': 7,    '==': 8,    '!=': 8,    '===': 8,    '!==': 8,    '<': 9,    '>': 9,    '<=': 9,    '>=': 9,    in: 9,    instanceof: 9,    '<<': 10,    '>>': 10,    '>>>': 10,    '+': 11,    '-': 11,    '*': 12,    '%': 12,    '/': 12,    '**': 13,}// Enables parenthesis regardless of precedenceconst NEEDS_PARENTHESES = 17const EXPRESSIONS_PRECEDENCE = {    // Definitions    ArrayExpression: 20,    TaggedTemplateExpression: 20,    ThisExpression: 20,    Identifier: 20,    Literal: 18,    TemplateLiteral: 20,    Super: 20,    SequenceExpression: 20,    // Operations    MemberExpression: 19,    CallExpression: 19,    NewExpression: 19,    // Other definitions    ArrowFunctionExpression: NEEDS_PARENTHESES,    ClassExpression: NEEDS_PARENTHESES,    FunctionExpression: NEEDS_PARENTHESES,    ObjectExpression: NEEDS_PARENTHESES,    // Other operations    UpdateExpression: 16,    UnaryExpression: 15,    BinaryExpression: 14,    LogicalExpression: 13,    ConditionalExpression: 4,    AssignmentExpression: 3,    AwaitExpression: 2,    YieldExpression: 2,    RestElement: 1,}function formatSequence(state, nodes) {    /*    Writes into `state` a sequence of `nodes`.    */    const { generator } = state    state.write('(')    if (nodes != null && nodes.length > 0) {        generator[nodes[0].type](nodes[0], state)        const { length } = nodes        for (let i = 1; i < length; i++) {            const param = nodes[i]            state.write(', ')            generator[param.type](param, state)        }    }    state.write(')')}function expressionNeedsParenthesis(node, parentNode, isRightHand) {    const nodePrecedence = EXPRESSIONS_PRECEDENCE[node.type]    if (nodePrecedence === NEEDS_PARENTHESES) {        return true    }    const parentNodePrecedence = EXPRESSIONS_PRECEDENCE[parentNode.type]    if (nodePrecedence !== parentNodePrecedence) {        // Different node types        return (            (!isRightHand &&                nodePrecedence === 15 &&                parentNodePrecedence === 14 &&                parentNode.operator === '**') ||            nodePrecedence < parentNodePrecedence        )    }    if (nodePrecedence !== 13 && nodePrecedence !== 14) {        // Not a `LogicalExpression` or `BinaryExpression`        return false    }    if (node.operator === '**' && parentNode.operator === '**') {        // Exponentiation operator has right-to-left associativity        return !isRightHand    }    if (isRightHand) {        // Parenthesis are used if both operators have the same precedence        return (            OPERATOR_PRECEDENCE[node.operator] <=            OPERATOR_PRECEDENCE[parentNode.operator]        )    }    return (        OPERATOR_PRECEDENCE[node.operator] <        OPERATOR_PRECEDENCE[parentNode.operator]    )}function formatBinaryExpressionPart(state, node, parentNode, isRightHand) {    /*    Writes into `state` a left-hand or right-hand expression `node`    from a binary expression applying the provided `operator`.    The `isRightHand` parameter should be `true` if the `node` is a right-hand argument.    */    const { generator } = state    if (expressionNeedsParenthesis(node, parentNode, isRightHand)) {        state.write('(')        generator[node.type](node, state)        state.write(')')    } else {        generator[node.type](node, state)    }}function reindent(state, text, indent, lineEnd) {    /*    Writes into `state` the `text` string reindented with the provided `indent`.    */    const lines = text.split('\n')    const end = lines.length - 1    state.write(lines[0].trim())    if (end > 0) {        state.write(lineEnd)        for (let i = 1; i < end; i++) {            state.write(indent + lines[i].trim() + lineEnd)        }        state.write(indent + lines[end].trim())    }}function formatComments(state, comments, indent, lineEnd) {    /*    Writes into `state` the provided list of `comments`, with the given `indent` and `lineEnd` strings.    Line comments will end with `"\n"` regardless of the value of `lineEnd`.    Expects to start on a new unindented line.    */    const { length } = comments    for (let i = 0; i < length; i++) {        const comment = comments[i]        state.write(indent)        if (comment.type[0] === 'L') {            // Line comment            state.write('// ' + comment.value.trim() + '\n')        } else {            // Block comment            state.write('/*')            reindent(state, comment.value, indent, lineEnd)            state.write('*/' + lineEnd)        }    }}function hasCallExpression(node) {    /*    Returns `true` if the provided `node` contains a call expression and `false` otherwise.    */    let currentNode = node    while (currentNode != null) {        const { type } = currentNode        if (type[0] === 'C' && type[1] === 'a') {            // Is CallExpression            return true        } else if (type[0] === 'M' && type[1] === 'e' && type[2] === 'm') {            // Is MemberExpression            currentNode = currentNode.object        } else {            return false        }    }}function formatVariableDeclaration(state, node) {    /*    Writes into `state` a variable declaration.    */    const { generator } = state    const { declarations } = node    state.write(node.kind + ' ')    const { length } = declarations    if (length > 0) {        generator.VariableDeclarator(declarations[0], state)        for (let i = 1; i < length; i++) {            state.write(', ')            generator.VariableDeclarator(declarations[i], state)        }    }}let ForInStatement,    FunctionDeclaration,    RestElement,    BinaryExpression,    ArrayExpression,    BlockStatementconst baseGenerator = {    Program(node, state) {        const indent = state.indent.repeat(state.indentLevel)        const { lineEnd, writeComments } = state        if (writeComments && node.comments != null) {            formatComments(state, node.comments, indent, lineEnd)        }        const statements = node.body        const { length } = statements        for (let i = 0; i < length; i++) {            const statement = statements[i]            if (writeComments && statement.comments != null) {                formatComments(state, statement.comments, indent, lineEnd)            }            state.write(indent)            this[statement.type](statement, state)            state.write(lineEnd)        }        if (writeComments && node.trailingComments != null) {            formatComments(state, node.trailingComments, indent, lineEnd)        }    },    BlockStatement: (BlockStatement = function(node, state) {        const indent = state.indent.repeat(state.indentLevel++)        const { lineEnd, writeComments } = state        const statementIndent = indent + state.indent        state.write('{')        const statements = node.body        if (statements != null && statements.length > 0) {            state.write(lineEnd)            if (writeComments && node.comments != null) {                formatComments(state, node.comments, statementIndent, lineEnd)            }            const { length } = statements            for (let i = 0; i < length; i++) {                const statement = statements[i]                if (writeComments && statement.comments != null) {                    formatComments(state, statement.comments, statementIndent, lineEnd)                }                state.write(statementIndent)                this[statement.type](statement, state)                state.write(lineEnd)            }            state.write(indent)        } else {            if (writeComments && node.comments != null) {                state.write(lineEnd)                formatComments(state, node.comments, statementIndent, lineEnd)                state.write(indent)            }        }        if (writeComments && node.trailingComments != null) {            formatComments(state, node.trailingComments, statementIndent, lineEnd)        }        state.write('}')        state.indentLevel--    }),    ClassBody: BlockStatement,    EmptyStatement(node, state) {        state.write(';')    },    ExpressionStatement(node, state) {        const precedence = EXPRESSIONS_PRECEDENCE[node.expression.type]        if (            precedence === NEEDS_PARENTHESES ||            (precedence === 3 && node.expression.left.type[0] === 'O')        ) {            // Should always have parentheses or is an AssignmentExpression to an ObjectPattern            state.write('(')            this[node.expression.type](node.expression, state)            state.write(')')        } else {            this[node.expression.type](node.expression, state)        }        state.write(';')    },    IfStatement(node, state) {        state.write('if (')        this[node.test.type](node.test, state)        state.write(') ')        this[node.consequent.type](node.consequent, state)        if (node.alternate != null) {            state.write(' else ')            this[node.alternate.type](node.alternate, state)        }    },    LabeledStatement(node, state) {        this[node.label.type](node.label, state)        state.write(': ')        this[node.body.type](node.body, state)    },    BreakStatement(node, state) {        state.write('break')        if (node.label != null) {            state.write(' ')            this[node.label.type](node.label, state)        }        state.write(';')    },    ContinueStatement(node, state) {        state.write('continue')        if (node.label != null) {            state.write(' ')            this[node.label.type](node.label, state)        }        state.write(';')    },    WithStatement(node, state) {        state.write('with (')        this[node.object.type](node.object, state)        state.write(') ')        this[node.body.type](node.body, state)    },    SwitchStatement(node, state) {        const indent = state.indent.repeat(state.indentLevel++)        const { lineEnd, writeComments } = state        state.indentLevel++        const caseIndent = indent + state.indent        const statementIndent = caseIndent + state.indent        state.write('switch (')        this[node.discriminant.type](node.discriminant, state)        state.write(') {' + lineEnd)        const { cases: occurences } = node        const { length: occurencesCount } = occurences        for (let i = 0; i < occurencesCount; i++) {            const occurence = occurences[i]            if (writeComments && occurence.comments != null) {                formatComments(state, occurence.comments, caseIndent, lineEnd)            }            if (occurence.test) {                state.write(caseIndent + 'case ')                this[occurence.test.type](occurence.test, state)                state.write(':' + lineEnd)            } else {                state.write(caseIndent + 'default:' + lineEnd)            }            const { consequent } = occurence            const { length: consequentCount } = consequent            for (let i = 0; i < consequentCount; i++) {                const statement = consequent[i]                if (writeComments && statement.comments != null) {                    formatComments(state, statement.comments, statementIndent, lineEnd)                }                state.write(statementIndent)                this[statement.type](statement, state)                state.write(lineEnd)            }        }        state.indentLevel -= 2        state.write(indent + '}')    },    ReturnStatement(node, state) {        state.write('return')        if (node.argument) {            state.write(' ')            this[node.argument.type](node.argument, state)        }        state.write(';')    },    ThrowStatement(node, state) {        state.write('throw ')        this[node.argument.type](node.argument, state)        state.write(';')    },    TryStatement(node, state) {        state.write('try ')        this[node.block.type](node.block, state)        if (node.handler) {            const { handler } = node            if (handler.param == null) {                state.write(' catch ')            } else {                state.write(' catch (')                this[handler.param.type](handler.param, state)                state.write(') ')            }            this[handler.body.type](handler.body, state)        }        if (node.finalizer) {            state.write(' finally ')            this[node.finalizer.type](node.finalizer, state)        }    },    WhileStatement(node, state) {        state.write('while (')        this[node.test.type](node.test, state)        state.write(') ')        this[node.body.type](node.body, state)    },    DoWhileStatement(node, state) {        state.write('do ')        this[node.body.type](node.body, state)        state.write(' while (')        this[node.test.type](node.test, state)        state.write(');')    },    ForStatement(node, state) {        state.write('for (')        if (node.init != null) {            const { init } = node            if (init.type[0] === 'V') {                formatVariableDeclaration(state, init)            } else {                this[init.type](init, state)            }        }        state.write('; ')        if (node.test) {            this[node.test.type](node.test, state)        }        state.write('; ')        if (node.update) {            this[node.update.type](node.update, state)        }        state.write(') ')        this[node.body.type](node.body, state)    },    ForInStatement: (ForInStatement = function(node, state) {        state.write(`for ${node.await ? 'await ' : ''}(`)        const { left } = node        if (left.type[0] === 'V') {            formatVariableDeclaration(state, left)        } else {            this[left.type](left, state)        }        // Identifying whether node.type is `ForInStatement` or `ForOfStatement`        state.write(node.type[3] === 'I' ? ' in ' : ' of ')        this[node.right.type](node.right, state)        state.write(') ')        this[node.body.type](node.body, state)    }),    ForOfStatement: ForInStatement,    DebuggerStatement(node, state) {        state.write('debugger;' + state.lineEnd)    },    FunctionDeclaration: (FunctionDeclaration = function(node, state) {        state.write(            (node.async ? 'async ' : '') +            (node.generator ? 'function* ' : 'function ') +            (node.id ? node.id.name : ''),            node,        )        formatSequence(state, node.params)        state.write(' ')        this[node.body.type](node.body, state)    }),    FunctionExpression: FunctionDeclaration,    VariableDeclaration(node, state) {        formatVariableDeclaration(state, node)        state.write(';')    },    VariableDeclarator(node, state) {        this[node.id.type](node.id, state)        if (node.init != null) {            state.write(' = ')            this[node.init.type](node.init, state)        }    },    ClassDeclaration(node, state) {        state.write('class ' + (node.id ? `${node.id.name} ` : ''), node)        if (node.superClass) {            state.write('extends ')            this[node.superClass.type](node.superClass, state)            state.write(' ')        }        this.ClassBody(node.body, state)    },    ImportDeclaration(node, state) {        state.write('import ')        const { specifiers } = node        const { length } = specifiers        // NOTE: Once babili is fixed, put this after condition        // https://github.com/babel/babili/issues/430        let i = 0        if (length > 0) {            for (; i < length; ) {                if (i > 0) {                    state.write(', ')                }                const specifier = specifiers[i]                const type = specifier.type[6]                if (type === 'D') {                    // ImportDefaultSpecifier                    state.write(specifier.local.name, specifier)                    i++                } else if (type === 'N') {                    // ImportNamespaceSpecifier                    state.write('* as ' + specifier.local.name, specifier)                    i++                } else {                    // ImportSpecifier                    break                }            }            if (i < length) {                state.write('{')                for (;;) {                    const specifier = specifiers[i]                    const { name } = specifier.imported                    state.write(name, specifier)                    if (name !== specifier.local.name) {                        state.write(' as ' + specifier.local.name)                    }                    if (++i < length) {                        state.write(', ')                    } else {                        break                    }                }                state.write('}')            }            state.write(' from ')        }        this.Literal(node.source, state)        state.write(';')    },    ExportDefaultDeclaration(node, state) {        state.write('export default ')        this[node.declaration.type](node.declaration, state)        if (            EXPRESSIONS_PRECEDENCE[node.declaration.type] &&            node.declaration.type[0] !== 'F'        ) {            // All expression nodes except `FunctionExpression`            state.write(';')        }    },    ExportNamedDeclaration(node, state) {        state.write('export ')        if (node.declaration) {            this[node.declaration.type](node.declaration, state)        } else {            state.write('{')            const { specifiers } = node,                { length } = specifiers            if (length > 0) {                for (let i = 0; ; ) {                    const specifier = specifiers[i]                    const { name } = specifier.local                    state.write(name, specifier)                    if (name !== specifier.exported.name) {                        state.write(' as ' + specifier.exported.name)                    }                    if (++i < length) {                        state.write(', ')                    } else {                        break                    }                }            }            state.write('}')            if (node.source) {                state.write(' from ')                this.Literal(node.source, state)            }            state.write(';')        }    },    ExportAllDeclaration(node, state) {        state.write('export * from ')        this.Literal(node.source, state)        state.write(';')    },    MethodDefinition(node, state) {        if (node.static) {            state.write('static ')        }        const kind = node.kind[0]        if (kind === 'g' || kind === 's') {            // Getter or setter            state.write(node.kind + ' ')        }        if (node.value.async) {            state.write('async ')        }        if (node.value.generator) {            state.write('*')        }        if (node.computed) {            state.write('[')            this[node.key.type](node.key, state)            state.write(']')        } else {            this[node.key.type](node.key, state)        }        formatSequence(state, node.value.params)        state.write(' ')        this[node.value.body.type](node.value.body, state)    },    ClassExpression(node, state) {        this.ClassDeclaration(node, state)    },    ArrowFunctionExpression(node, state) {        state.write(node.async ? 'async ' : '', node)        const { params } = node        if (params != null) {            // Omit parenthesis if only one named parameter            if (params.length === 1 && params[0].type[0] === 'I') {                // If params[0].type[0] starts with 'I', it can't be `ImportDeclaration` nor `IfStatement` and thus is `Identifier`                state.write(params[0].name, params[0])            } else {                formatSequence(state, node.params)            }        }        state.write(' => ')        if (node.body.type[0] === 'O') {            // Body is an object expression            state.write('(')            this.ObjectExpression(node.body, state)            state.write(')')        } else {            this[node.body.type](node.body, state)        }    },    ThisExpression(node, state) {        state.write('this', node)    },    Super(node, state) {        state.write('super', node)    },    RestElement: (RestElement = function(node, state) {        state.write('...')        this[node.argument.type](node.argument, state)    }),    SpreadElement: RestElement,    YieldExpression(node, state) {        state.write(node.delegate ? 'yield*' : 'yield')        if (node.argument) {            state.write(' ')            this[node.argument.type](node.argument, state)        }    },    AwaitExpression(node, state) {        state.write('await ')        if (node.argument) {            this[node.argument.type](node.argument, state)        }    },    TemplateLiteral(node, state) {        const { quasis, expressions } = node        state.write('`')        const { length } = expressions        for (let i = 0; i < length; i++) {            const expression = expressions[i]            this.TemplateElement(quasis[i], state)            state.write('${')            this[expression.type](expression, state)            state.write('}')        }        state.write(quasis[quasis.length - 1].value.raw)        state.write('`')    },    TemplateElement(node, state) {        state.write(node.value.raw)    },    TaggedTemplateExpression(node, state) {        this[node.tag.type](node.tag, state)        this[node.quasi.type](node.quasi, state)    },    ArrayExpression: (ArrayExpression = function(node, state) {        state.write('[')        if (node.elements.length > 0) {            const { elements } = node,                { length } = elements            for (let i = 0; ; ) {                const element = elements[i]                if (element != null) {                    this[element.type](element, state)                }                if (++i < length) {                    state.write(', ')                } else {                    if (element == null) {                        state.write(', ')                    }                    break                }            }        }        state.write(']')    }),    ArrayPattern: ArrayExpression,    ObjectExpression(node, state) {        const indent = state.indent.repeat(state.indentLevel++)        const { lineEnd, writeComments } = state        const propertyIndent = indent + state.indent        state.write('{')        if (node.properties.length > 0) {            state.write(lineEnd)            if (writeComments && node.comments != null) {                formatComments(state, node.comments, propertyIndent, lineEnd)            }            const comma = ',' + lineEnd            const { properties } = node,                { length } = properties            for (let i = 0; ; ) {                const property = properties[i]                if (writeComments && property.comments != null) {                    formatComments(state, property.comments, propertyIndent, lineEnd)                }                state.write(propertyIndent)                this[property.type](property, state)                if (++i < length) {                    state.write(comma)                } else {                    break                }            }            state.write(lineEnd)            if (writeComments && node.trailingComments != null) {                formatComments(state, node.trailingComments, propertyIndent, lineEnd)            }            state.write(indent + '}')        } else if (writeComments) {            if (node.comments != null) {                state.write(lineEnd)                formatComments(state, node.comments, propertyIndent, lineEnd)                if (node.trailingComments != null) {                    formatComments(state, node.trailingComments, propertyIndent, lineEnd)                }                state.write(indent + '}')            } else if (node.trailingComments != null) {                state.write(lineEnd)                formatComments(state, node.trailingComments, propertyIndent, lineEnd)                state.write(indent + '}')            } else {                state.write('}')            }        } else {            state.write('}')        }        state.indentLevel--    },    Property(node, state) {        if (node.method || node.kind[0] !== 'i') {            // Either a method or of kind `set` or `get` (not `init`)            this.MethodDefinition(node, state)        } else {            if (!node.shorthand) {                if (node.computed) {                    state.write('[')                    this[node.key.type](node.key, state)                    state.write(']')                } else {                    this[node.key.type](node.key, state)                }                state.write(': ')            }            this[node.value.type](node.value, state)        }    },    ObjectPattern(node, state) {        state.write('{')        if (node.properties.length > 0) {            const { properties } = node,                { length } = properties            for (let i = 0; ; ) {                this[properties[i].type](properties[i], state)                if (++i < length) {                    state.write(', ')                } else {                    break                }            }        }        state.write('}')    },    SequenceExpression(node, state) {        formatSequence(state, node.expressions)    },    UnaryExpression(node, state) {        if (node.prefix) {            state.write(node.operator)            if (node.operator.length > 1) {                state.write(' ')            }            if (                EXPRESSIONS_PRECEDENCE[node.argument.type] <                EXPRESSIONS_PRECEDENCE.UnaryExpression            ) {                state.write('(')                this[node.argument.type](node.argument, state)                state.write(')')            } else {                this[node.argument.type](node.argument, state)            }        } else {            // FIXME: This case never occurs            this[node.argument.type](node.argument, state)            state.write(node.operator)        }    },    UpdateExpression(node, state) {        // Always applied to identifiers or members, no parenthesis check needed        if (node.prefix) {            state.write(node.operator)            this[node.argument.type](node.argument, state)        } else {            this[node.argument.type](node.argument, state)            state.write(node.operator)        }    },    AssignmentExpression(node, state) {        this[node.left.type](node.left, state)        state.write(' ' + node.operator + ' ')        this[node.right.type](node.right, state)    },    AssignmentPattern(node, state) {        this[node.left.type](node.left, state)        state.write(' = ')        this[node.right.type](node.right, state)    },    BinaryExpression: (BinaryExpression = function(node, state) {        const isIn = node.operator === 'in'        if (isIn) {            // Avoids confusion in `for` loops initializers            state.write('(')        }        formatBinaryExpressionPart(state, node.left, node, false)        state.write(' ' + node.operator + ' ')        formatBinaryExpressionPart(state, node.right, node, true)        if (isIn) {            state.write(')')        }    }),    LogicalExpression: BinaryExpression,    ConditionalExpression(node, state) {        if (            EXPRESSIONS_PRECEDENCE[node.test.type] >            EXPRESSIONS_PRECEDENCE.ConditionalExpression        ) {            this[node.test.type](node.test, state)        } else {            state.write('(')            this[node.test.type](node.test, state)            state.write(')')        }        state.write(' ? ')        this[node.consequent.type](node.consequent, state)        state.write(' : ')        this[node.alternate.type](node.alternate, state)    },    NewExpression(node, state) {        state.write('new ')        if (            EXPRESSIONS_PRECEDENCE[node.callee.type] <            EXPRESSIONS_PRECEDENCE.CallExpression ||            hasCallExpression(node.callee)        ) {            state.write('(')            this[node.callee.type](node.callee, state)            state.write(')')        } else {            this[node.callee.type](node.callee, state)        }        formatSequence(state, node['arguments'])    },    CallExpression(node, state) {        if (            EXPRESSIONS_PRECEDENCE[node.callee.type] <            EXPRESSIONS_PRECEDENCE.CallExpression        ) {            state.write('(')            this[node.callee.type](node.callee, state)            state.write(')')        } else {            this[node.callee.type](node.callee, state)        }        formatSequence(state, node['arguments'])    },    MemberExpression(node, state) {        if (            EXPRESSIONS_PRECEDENCE[node.object.type] <            EXPRESSIONS_PRECEDENCE.MemberExpression        ) {            state.write('(')            this[node.object.type](node.object, state)            state.write(')')        } else {            this[node.object.type](node.object, state)        }        if (node.computed) {            state.write('[')            this[node.property.type](node.property, state)            state.write(']')        } else {            state.write('.')            this[node.property.type](node.property, state)        }    },    MetaProperty(node, state) {        state.write(node.meta.name + '.' + node.property.name, node)    },    Identifier(node, state) {        state.write(node.name, node)    },    Literal(node, state) {        if (node.raw != null) {            state.write(node.raw, node)        } else if (node.regex != null) {            this.RegExpLiteral(node, state)        } else {            state.write(stringify(node.value), node)        }    },    RegExpLiteral(node, state) {        const { regex } = node        state.write(`/${regex.pattern}/${regex.flags}`, node)    },}const EMPTY_OBJECT = {}class State {    constructor(options) {        const setup = options == null ? EMPTY_OBJECT : options        this.output = ''        // Functional options        if (setup.output != null) {            this.output = setup.output            this.write = this.writeToStream        } else {            this.output = ''        }        this.generator = setup.generator != null ? setup.generator : baseGenerator        // Formating setup        this.indent = setup.indent != null ? setup.indent : '  '        this.lineEnd = setup.lineEnd != null ? setup.lineEnd : '\n'        this.indentLevel =            setup.startingIndentLevel != null ? setup.startingIndentLevel : 0        this.writeComments = setup.comments ? setup.comments : false        // Source map        if (setup.sourceMap != null) {            this.write =                setup.output == null ? this.writeAndMap : this.writeToStreamAndMap            this.sourceMap = setup.sourceMap            this.line = 1            this.column = 0            this.lineEndSize = this.lineEnd.split('\n').length - 1            this.mapping = {                original: null,                generated: this,                name: undefined,                source: setup.sourceMap.file || setup.sourceMap._file,            }        }    }    write(code) {        this.output += code    }    writeToStream(code) {        this.output.write(code)    }    writeAndMap(code, node) {        this.output += code        this.map(code, node)    }    writeToStreamAndMap(code, node) {        this.output.write(code)        this.map(code, node)    }    map(code, node) {        if (node != null && node.loc != null) {            const { mapping } = this            mapping.original = node.loc.start            mapping.name = node.name            this.sourceMap.addMapping(mapping)        }        if (code.length > 0) {            if (this.lineEndSize > 0) {                if (code.endsWith(this.lineEnd)) {                    this.line += this.lineEndSize                    this.column = 0                } else if (code[code.length - 1] === '\n') {                    // Case of inline comment                    this.line++                    this.column = 0                } else {                    this.column += code.length                }            } else {                if (code[code.length - 1] === '\n') {                    // Case of inline comment                    this.line++                    this.column = 0                } else {                    this.column += code.length                }            }        }    }    toString() {        return this.output    }}function generate(node, options) {    /*    Returns a string representing the rendered code of the provided AST `node`.    The `options` are:    - `indent`: string to use for indentation (defaults to `␣␣`)    - `lineEnd`: string to use for line endings (defaults to `\n`)    - `startingIndentLevel`: indent level to start from (defaults to `0`)    - `comments`: generate comments if `true` (defaults to `false`)    - `output`: output stream to write the rendered code to (defaults to `null`)    - `generator`: custom code generator (defaults to `baseGenerator`)    */    const state = new State(options)    // Travel through the AST node and generate the code    state.generator[node.type](node, state)    return state.output}
 |