Module refinery.lib.scripts.js.model

The JavaScript syntax tree: one node type per production of the grammar, and the few predicates that answer a question about a node from the node alone.

A node states what was written and nothing about what it means. strip_parens() and names_a_property() live here because the answer is in the shape; anything needing a scope, a binding or an effect belongs in refinery.lib.scripts.js.analysis instead, which is why nothing here imports that package. Nothing here holds state either, so a pass may ask any of it at any point of a rewrite.

Expand source code Browse git
"""
The JavaScript syntax tree: one node type per production of the grammar, and the few predicates that
answer a question about a node from the node alone.

A node states what was written and nothing about what it means. `strip_parens` and `names_a_property`
live here because the answer is in the shape; anything needing a scope, a binding or an effect
belongs in `refinery.lib.scripts.js.analysis` instead, which is why nothing here imports that package.
Nothing here holds state either, so a pass may ask any of it at any point of a rewrite.
"""
from __future__ import annotations

import enum
import functools

from dataclasses import dataclass, field, replace
from typing import TYPE_CHECKING

from refinery.lib.scripts import Expression, Node, Statement
from refinery.lib.scripts.js.numbers import integer_of_numeral, to_js_number

if TYPE_CHECKING:
    from refinery.lib.scripts.js.strict import StrictViolation


class JsPropertyKind(enum.Enum):
    INIT = 'init'
    GET  = 'get'   # noqa
    SET  = 'set'   # noqa


class JsMethodKind(enum.Enum):
    METHOD      = 'method'       # noqa
    GET         = 'get'          # noqa
    SET         = 'set'          # noqa
    CONSTRUCTOR = 'constructor'  # noqa


class JsVarKind(enum.Enum):
    VAR   = 'var'    # noqa
    LET   = 'let'    # noqa
    CONST = 'const'  # noqa


@dataclass(repr=False, eq=False)
class JsErrorNode(Expression, Statement, unparsed=True):
    """
    A span of source the parser could not read, kept verbatim so that what an analyst gets back
    still contains what was written. It prints as `text` and so reads back as whatever that text
    parses to, which is why a tree holding one states nothing about synthesizer fidelity.

    It stands in either position, because the recovery that builds it does not know which was
    expected. In statement position it is the statement, and is deliberately not wrapped in an
    expression statement: the wrapper prints a semicolon that nobody wrote, which grows the file by
    one character every time the tool reads its own output.
    """
    text: str = ''
    message: str = ''


@dataclass(repr=False, eq=False)
class JsIdentifier(Expression, spelling='raw'):
    """
    A name. `name` is the name the source denotes and `raw` is the text it was written with, which
    part ways wherever a unicode escape stands between them: `\\u0061bc` and `abc` are one binding
    written two ways, and every question about names is asked of `name`.

    `raw` is empty wherever the two would be the same text, so it holds something only for a name
    the source wrote some other way. What it holds is trusted only for as long as it still spells
    `name`, which is what leaves a pass renaming a node nothing to maintain: the synthesizer asks
    whether the spelling it was handed spells the name it is printing, and writes the name itself
    where it does not.
    """
    name: str = ''
    raw: str = ''

    def has_spelling(self) -> bool:
        """
        There is no name spelled by nothing. Printing one writes whatever stands around it and
        closes up over the gap, so `a.` becomes `a` and a program loses a member read; the parser
        builds an error node where it finds no name, and this is what says so if a transform ever
        assembles one anyway.
        """
        return bool(self.name)


@dataclass(repr=False, eq=False)
class JsPrivateIdentifier(Expression, spelling='raw'):
    """
    A private name, written with the `#` that opens it left out of `name` and out of `raw` alike.
    An escape spells one of these as it spells any other name, so `this.#\\u0061` reads the member
    `#a` declares.
    """
    name: str = ''
    raw: str = ''


@dataclass(repr=False, eq=False)
class JsNumericLiteral(Expression, spelling='raw'):
    """
    A Number literal. `value` is the double the source denotes and `raw` is how that source spelled
    it; the two are independent because a spelling carries information the value does not, such as
    the base of `0xFF` or the sign of `-0`. Coercion happens here rather than at the call sites so
    that no construction anywhere can introduce a value JavaScript cannot hold.
    """
    value: float = 0.0
    raw: str = '0'

    def __post_init__(self):
        super().__post_init__()
        self.value = to_js_number(self.value)


@dataclass(repr=False, eq=False)
class JsBigIntLiteral(Expression, spelling='raw'):
    """
    A BigInt literal. `raw` is how the source spelled it, and the exact integer it denotes is read
    off that spelling when it is asked for: converting a long decimal spelling is bounded by the
    interpreter's limit on the digits of an integer conversion, and reading a file must not be
    hostage to that limit for a value the file may never have read.
    """
    raw: str = '0n'

    @property
    def value(self) -> int:
        return integer_of_numeral(self.raw[:-1].replace('_', ''))


@dataclass(repr=False, eq=False)
class JsStringLiteral(Expression, spelling='raw'):
    """
    A String literal. `value` is the text it denotes and `raw` is how the source spelled it, which
    part ways wherever an escape stands between them.

    `value` is `None` where the literal denotes nothing, which is a literal written with a `\\x` or
    `\\u` escape that names no character. A string keeps the legacy escapes Annex B holds open for
    sloppy code, so those still denote text; an escape no literal has at all denotes none, the way
    the same escape leaves a `JsTemplateElement` denoting none.

    `terminated` reports whether the closing quote was there. A literal the source never closed is
    not a form the language has, so no text spells it: printing what was written runs the literal on
    into whatever the synthesizer prints next, and printing the quote that is missing turns a file
    that does not parse into a program that runs.
    """
    value: str | None = ''
    raw: str = "''"
    terminated: bool = True

    @property
    def body(self) -> str:
        """
        The source text between the quotes, every escape still spelled as it was written. A rule
        about how a literal was written reads this rather than `value`: a directive spelled with an
        escape in it denotes the text `use strict` and is not a Use Strict Directive, because what
        makes a directive is the spelling and not the value.
        """
        return self.raw[1:-1] if self.terminated else self.raw[1:]


@dataclass(repr=False, eq=False)
class JsRegExpLiteral(Expression, spelling='raw'):
    pattern: str = ''
    flags: str = ''
    raw: str = '//'
    terminated: bool = True


@dataclass(repr=False, eq=False)
class JsTemplateLiteral(Expression):
    quasis: list[JsTemplateElement] = field(default_factory=list)
    expressions: list[Expression] = field(default_factory=list)


@dataclass(repr=False, eq=False)
class JsTemplateElement(Node, spelling='raw'):
    """
    One run of text in a template literal. `raw` is that run as the source wrote it and `value` the
    text it denotes; neither holds the delimiters that separate the runs from the expressions,
    because the literal prints those itself and a run is what stands between them.

    `terminated` reports whether the delimiter that ends the run was there. Only a template can
    reach the end of the file unclosed, since no line terminator ends one, and a hole the source
    left open ends here as an empty run that closes nothing.

    `value` is `None` where the run denotes nothing, which is a run written with an escape the
    template grammar excludes. The language says the same thing by handing a tag `undefined` for
    such a run, and by refusing the untagged literal outright.
    """
    value: str | None = ''
    raw: str = ''
    tail: bool = False
    terminated: bool = True
    #: Whether the `}` that closes the hole in front of this run was there. Only the run behind a
    #: hole the file ended inside lacks it, and such a run holds no text at all.
    opened: bool = True


@dataclass(repr=False, eq=False)
class JsBooleanLiteral(Expression):
    value: bool = False


@dataclass(repr=False, eq=False)
class JsNullLiteral(Expression):
    @property
    def value(self):
        return None


@dataclass(repr=False, eq=False)
class JsThisExpression(Expression):
    pass


@dataclass(repr=False, eq=False)
class JsArrayExpression(Expression):
    elements: list[Expression | None] = field(default_factory=list)
    #: Whether the closing bracket was there; `file_ended_inside` says what its absence means.
    terminated: bool = True


@dataclass(repr=False, eq=False)
class JsObjectExpression(Expression):
    properties: list[JsProperty | JsSpreadElement | JsErrorNode] = field(default_factory=list)
    terminated: bool = True


@dataclass(repr=False, eq=False)
class JsProperty(Node):
    key: Expression | None = None
    value: Expression | None = None
    computed: bool = False
    shorthand: bool = False
    method: bool = False
    kind: JsPropertyKind = JsPropertyKind.INIT


@dataclass(repr=False, eq=False)
class JsSpreadElement(Expression):
    argument: Expression | None = None


@dataclass(repr=False, eq=False)
class JsFunctionExpression(Expression, spelling='source_text'):
    id: JsIdentifier | None = None
    params: list[Expression] = field(default_factory=list)
    body: JsBlockStatement | None = None
    generator: bool = False
    is_async: bool = False
    source_text: str | None = None


@dataclass(repr=False, eq=False)
class JsArrowFunctionExpression(Expression, spelling='source_text'):
    params: list[Expression] = field(default_factory=list)
    body: Expression | JsBlockStatement | None = None
    is_async: bool = False
    source_text: str | None = None


@dataclass(repr=False, eq=False)
class JsDecorator(Node):
    expression: Expression | None = None


@dataclass(repr=False, eq=False)
class JsClassExpression(Expression):
    id: JsIdentifier | None = None
    super_class: Expression | None = None
    body: JsClassBody | None = None
    decorators: list[JsDecorator] = field(default_factory=list)


@dataclass(repr=False, eq=False)
class JsUnaryExpression(Expression):
    operator: str = ''
    operand: Expression | None = None
    prefix: bool = True


@dataclass(repr=False, eq=False)
class JsUpdateExpression(Expression):
    operator: str = ''
    argument: Expression | None = None
    prefix: bool = True


@dataclass(repr=False, eq=False)
class JsBinaryExpression(Expression):
    left: Expression | None = None
    operator: str = ''
    right: Expression | None = None


@dataclass(repr=False, eq=False)
class JsLogicalExpression(Expression):
    left: Expression | None = None
    operator: str = ''
    right: Expression | None = None


@dataclass(repr=False, eq=False)
class JsAssignmentExpression(Expression):
    left: Expression | None = None
    operator: str = '='
    right: Expression | None = None


@dataclass(repr=False, eq=False)
class JsConditionalExpression(Expression):
    test: Expression | None = None
    consequent: Expression | None = None
    alternate: Expression | None = None


@dataclass(repr=False, eq=False)
class JsMemberExpression(Expression):
    object: Expression | None = None
    property: Expression | None = None
    computed: bool = False
    optional: bool = False


@dataclass(repr=False, eq=False)
class JsCallExpression(Expression):
    callee: Expression | None = None
    arguments: list[Expression] = field(default_factory=list)
    optional: bool = False
    terminated: bool = True


@dataclass(repr=False, eq=False)
class JsNewExpression(Expression):
    callee: Expression | None = None
    arguments: list[Expression] = field(default_factory=list)
    terminated: bool = True


@dataclass(repr=False, eq=False)
class JsSequenceExpression(Expression):
    expressions: list[Expression] = field(default_factory=list)


@dataclass(repr=False, eq=False)
class JsYieldExpression(Expression):
    argument: Expression | None = None
    delegate: bool = False


@dataclass(repr=False, eq=False)
class JsAwaitExpression(Expression):
    argument: Expression | None = None


@dataclass(repr=False, eq=False)
class JsTaggedTemplateExpression(Expression):
    tag: Expression | None = None
    quasi: JsTemplateLiteral | None = None


@dataclass(repr=False, eq=False)
class JsParenthesizedExpression(Expression):
    expression: Expression | None = None
    terminated: bool = True


@dataclass(repr=False, eq=False)
class JsArrayPattern(Expression):
    elements: list[Expression | None] = field(default_factory=list)
    terminated: bool = True


@dataclass(repr=False, eq=False)
class JsObjectPattern(Expression):
    properties: list[JsProperty | JsRestElement | JsErrorNode] = field(default_factory=list)
    terminated: bool = True


@dataclass(repr=False, eq=False)
class JsAssignmentPattern(Expression):
    left: Expression | None = None
    right: Expression | None = None


@dataclass(repr=False, eq=False)
class JsRestElement(Expression):
    argument: Expression | None = None


@dataclass(repr=False, eq=False)
class JsClassBody(Node):
    body: list[JsMethodDefinition | JsPropertyDefinition | JsStaticBlock | JsErrorNode] = field(default_factory=list)
    #: Whether the closing brace was there. The file may end inside a class body, and a body the
    #: file ends inside prints the elements it holds and nothing after them.
    terminated: bool = True


@dataclass(repr=False, eq=False)
class JsMethodDefinition(Node):
    key: Expression | None = None
    value: JsFunctionExpression | None = None
    kind: JsMethodKind = JsMethodKind.METHOD
    computed: bool = False
    is_static: bool = False
    decorators: list[JsDecorator] = field(default_factory=list)


@dataclass(repr=False, eq=False)
class JsPropertyDefinition(Node):
    key: Expression | None = None
    value: Expression | None = None
    computed: bool = False
    is_static: bool = False
    decorators: list[JsDecorator] = field(default_factory=list)


@dataclass(repr=False, eq=False)
class JsStaticBlock(Node):
    body: list[Statement] = field(default_factory=list)
    terminated: bool = True


@dataclass(repr=False, eq=False)
class JsExpressionStatement(Statement, spelling='directive'):
    expression: Expression | None = None
    #: Whether the source wrote this statement into a Directive Prologue. It records where the
    #: statement came from and not what it computes, which is why it is a spelling field: two trees
    #: that differ only here spell the same program. A clone carries it, deliberately — a directive
    #: that is copied elsewhere was still written as one, and whether it *is* one is decided by
    #: `refinery.lib.scripts.js.strict.is_prologue_host` at wherever it now stands.
    directive: bool = False


@dataclass(repr=False, eq=False)
class JsBlockStatement(Statement):
    body: list[Statement] = field(default_factory=list)
    #: Whether the closing brace was there. A file carved out of memory ends inside a block more
    #: often than anywhere else, and the block keeps the statements it holds and says that nothing
    #: closed it: printing what it holds and nothing after it is what keeps the file's end where the
    #: file had it. `early_errors` reports it.
    terminated: bool = True


@dataclass(repr=False, eq=False)
class JsEmptyStatement(Statement):
    pass


@dataclass(repr=False, eq=False)
class JsVariableDeclaration(Statement):
    declarations: list[JsVariableDeclarator] = field(default_factory=list)
    kind: JsVarKind = JsVarKind.VAR


@dataclass(repr=False, eq=False)
class JsVariableDeclarator(Node):
    id: Expression | None = None
    init: Expression | None = None


@dataclass(repr=False, eq=False)
class JsIfStatement(Statement):
    test: Expression | None = None
    consequent: Statement | None = None
    alternate: Statement | None = None


@dataclass(repr=False, eq=False)
class JsWhileStatement(Statement):
    test: Expression | None = None
    body: Statement | None = None


@dataclass(repr=False, eq=False)
class JsDoWhileStatement(Statement):
    test: Expression | None = None
    body: Statement | None = None


@dataclass(repr=False, eq=False)
class JsForStatement(Statement):
    init: Expression | Statement | None = None
    test: Expression | None = None
    update: Expression | None = None
    body: Statement | None = None


@dataclass(repr=False, eq=False)
class JsForInStatement(Statement):
    left: Expression | Statement | None = None
    right: Expression | None = None
    body: Statement | None = None


@dataclass(repr=False, eq=False)
class JsForOfStatement(Statement):
    left: Expression | Statement | None = None
    right: Expression | None = None
    body: Statement | None = None
    is_await: bool = False


@dataclass(repr=False, eq=False)
class JsSwitchStatement(Statement):
    discriminant: Expression | None = None
    cases: list[JsSwitchCase | JsErrorNode] = field(default_factory=list)
    terminated: bool = True


@dataclass(repr=False, eq=False)
class JsSwitchCase(Node):
    test: Expression | None = None
    body: list[Statement] = field(default_factory=list)


@dataclass(repr=False, eq=False)
class JsTryStatement(Statement):
    block: JsBlockStatement | None = None
    handler: JsCatchClause | None = None
    finalizer: JsBlockStatement | None = None


@dataclass(repr=False, eq=False)
class JsCatchClause(Node):
    param: Expression | None = None
    body: JsBlockStatement | None = None


@dataclass(repr=False, eq=False)
class JsThrowStatement(Statement):
    argument: Expression | None = None

    def has_spelling(self) -> bool:
        """
        A `throw` takes its expression on the same line, and nothing else: the parser refuses a
        `throw` with nothing behind it, and this is what says so if a transform assembles one.
        """
        return self.argument is not None


@dataclass(repr=False, eq=False)
class JsReturnStatement(Statement):
    argument: Expression | None = None


@dataclass(repr=False, eq=False)
class JsBreakStatement(Statement):
    label: JsIdentifier | None = None


@dataclass(repr=False, eq=False)
class JsContinueStatement(Statement):
    label: JsIdentifier | None = None


@dataclass(repr=False, eq=False)
class JsLabeledStatement(Statement):
    label: JsIdentifier | None = None
    body: Statement | None = None


@dataclass(repr=False, eq=False)
class JsWithStatement(Statement):
    object: Expression | None = None
    body: Statement | None = None


@dataclass(repr=False, eq=False)
class JsDebuggerStatement(Statement):
    pass


@dataclass(repr=False, eq=False)
class JsFunctionDeclaration(Statement, spelling='source_text'):
    id: JsIdentifier | None = None
    params: list[Expression] = field(default_factory=list)
    body: JsBlockStatement | None = None
    generator: bool = False
    is_async: bool = False
    source_text: str | None = None


@dataclass(repr=False, eq=False)
class JsClassDeclaration(Statement):
    id: JsIdentifier | None = None
    super_class: Expression | None = None
    body: JsClassBody | None = None
    decorators: list[JsDecorator] = field(default_factory=list)


@dataclass(repr=False, eq=False)
class JsImportSpecifier(Node):
    imported: Expression | None = None
    local: Expression | None = None


@dataclass(repr=False, eq=False)
class JsImportDefaultSpecifier(Node):
    local: Expression | None = None


@dataclass(repr=False, eq=False)
class JsImportNamespaceSpecifier(Node):
    local: Expression | None = None


@dataclass(repr=False, eq=False)
class JsImportAttribute(Node):
    key: Expression | None = None
    value: Expression | None = None


@dataclass(repr=False, eq=False)
class JsImportDeclaration(Statement):
    specifiers: list[
        JsImportSpecifier | JsImportDefaultSpecifier | JsImportNamespaceSpecifier | JsErrorNode
    ] = field(default_factory=list)
    source: JsStringLiteral | None = None
    attributes: list[JsImportAttribute | JsErrorNode] = field(default_factory=list)
    attributes_keyword: str = ''


@dataclass(repr=False, eq=False)
class JsImportExpression(Expression):
    source: Expression | None = None
    options: Expression | None = None


@dataclass(repr=False, eq=False)
class JsMetaProperty(Expression):
    meta: str = ''
    property: str = ''


@dataclass(repr=False, eq=False)
class JsExportSpecifier(Node):
    local: Expression | None = None
    exported: Expression | None = None


@dataclass(repr=False, eq=False)
class JsExportNamedDeclaration(Statement):
    declaration: Statement | None = None
    specifiers: list[JsExportSpecifier | JsErrorNode] = field(default_factory=list)
    source: JsStringLiteral | None = None
    attributes: list[JsImportAttribute | JsErrorNode] = field(default_factory=list)
    attributes_keyword: str = ''


@dataclass(repr=False, eq=False)
class JsExportDefaultDeclaration(Statement):
    declaration: Expression | Statement | None = None


@dataclass(repr=False, eq=False)
class JsExportAllDeclaration(Statement):
    source: JsStringLiteral | None = None
    exported: Expression | None = None
    attributes: list[JsImportAttribute | JsErrorNode] = field(default_factory=list)
    attributes_keyword: str = ''


@dataclass(repr=False, eq=False)
class JsScript(Statement, spelling=('module', 'recovered', 'html_comment')):
    body: list[Statement] = field(default_factory=list)
    #: Whether the source is module code, which the host decides (§16.1) and the syntax only reports:
    #: an `import` or `export` declaration, or `import.meta`, can appear in nothing else. It is a
    #: spelling field because two scripts differing only here hold the same text — what differs is how
    #: a host loads it — and because a pass that cuts the last import out of a body does not turn a
    #: module into a script.
    module: bool = False
    #: Whether the parser had to invent a token, or step over one, in order to read this file. It is
    #: a spelling field for the reason `module` is: it records where the tree came from rather than
    #: what it spells, so two scripts differing only here are the same program. Nothing ever clears
    #: it, because no later pass can put back a token the source never held.
    recovered: bool = False
    #: The offset of the first HTML-like comment delimiter the lexer read in this file (§B.1.1), or
    #: `None` where it read none. Module code refuses both delimiters, and the comment one opens may
    #: stand where no statement carries it, so the fact is kept on the script rather than looked for
    #: among the comments the tree holds. It is a spelling field for the reason `recovered` is.
    html_comment: int | None = None
    #: Whether the file ended outside every comment. A block comment the file ends inside runs to
    #: the end of the file, is the last of the file's `trailing_comments`, and makes the file no
    #: program, which `early_errors` reports.
    terminated: bool = True

    def is_recovered(self) -> bool:
        return self.recovered

    def early_errors(self) -> list[StrictViolation]:
        """
        What the language refuses in the file under its own mode and goal: the early errors
        `refinery.lib.scripts.js.strict.collect_strict_violations` reports over the tree read as a
        script, or as a module where the file spells module syntax, and every construct the file
        ended inside — a literal, a comment, a block, a class body or a switch — which an engine
        refuses as an unexpected end of input.
        """
        from refinery.lib.scripts.js.strict import collect_strict_violations
        return collect_strict_violations(self, module=self.module)


#: The three nodes that hold a function body. A class or object method holds a
#: `JsFunctionExpression` as its value, so it needs no entry of its own.
FUNCTION_NODES = (JsFunctionDeclaration, JsFunctionExpression, JsArrowFunctionExpression)

JsFunctionNode = JsFunctionDeclaration | JsFunctionExpression | JsArrowFunctionExpression


class AwaitReading(enum.Enum):
    """
    What the word `await` is where a piece of code stands. It names a binding or a reference in a
    script and in every function that is not `async`; it is the operator in an `async` function's
    parameter list and body; and it is neither in a class static block or a static field
    initializer, where the language refuses the word in every position.
    """
    NAME = enum.auto()
    OPERATOR = enum.auto()
    RESERVED = enum.auto()


@dataclass(frozen=True)
class CodeContext:
    """
    The grammar parameters a piece of code is read under: what `await` is, whether `yield` is the
    operator rather than a name, whether `arguments` may be referred to at all, and whether the
    code is the top level of a file. The parser descends it and the early-error collector threads
    it, both through `code_context_within`, so that the tree a text is read into and the errors
    reported over that tree agree on what every word is.

    The top level is set apart because its goal symbol is not known while parsing: `await` is read
    as a name there, as a script reads it, but a `for await` head is read as well, since that is
    one way a module spells its top-level `await`. Below a function or a class element the goal
    decides nothing, and a `for await` head stands only where `await` is the operator.
    """
    await_reading: AwaitReading
    yield_is_operator: bool
    arguments_reserved: bool
    top_level: bool

    @property
    def reads_for_await(self) -> bool:
        """
        Whether a `for await` head is read here: where `await` is the operator, and at the top
        level of a file, whose goal may make it one.
        """
        return self.await_reading is AwaitReading.OPERATOR or self.top_level


SCRIPT_CONTEXT = CodeContext(AwaitReading.NAME, False, False, True)
"""
The context the top level of a file is read under: both words are names, `arguments` is free, and
the goal symbol is open.
"""


@functools.cache
def function_context(is_async: bool, is_generator: bool) -> CodeContext:
    """
    The context the parameter list and the body of a function of the given kind are read under.
    """
    return CodeContext(
        AwaitReading.OPERATOR if is_async else AwaitReading.NAME,
        is_generator,
        False,
        False,
    )


@functools.cache
def class_element_context(static: bool) -> CodeContext:
    """
    The context a class element's own code is read under: a field initializer, or a static block.
    Each is a function context of its own that reserves `arguments` (§15.7.1) and reads `yield` as
    a name; a static one refuses `await` outright, where an instance initializer reads it as a name.
    """
    return CodeContext(
        AwaitReading.RESERVED if static else AwaitReading.NAME,
        False,
        True,
        False,
    )


def arrow_body_context(is_async: bool, enclosing: CodeContext) -> CodeContext:
    """
    The context an arrow function's body is read under, given that the arrow stands in
    *enclosing*: the context a function of its kind gives its body, except that an arrow has no
    `arguments` of its own (§15.7.1) and so keeps the reservation it stands in.
    """
    return replace(function_context(is_async, False), arguments_reserved=enclosing.arguments_reserved)


def code_context_within(parent: Node, child: Node, enclosing: CodeContext) -> CodeContext:
    """
    The context *child* is read under, given that *parent* is read under *enclosing*. This is the
    one transition every reader of the context takes, so that the parser descending the tree, the
    collector threading it, and `code_context_at` folding it agree on every node.

    A function body takes the readings its own kind gives it, and so does the name of a function
    expression, which is bound inside it (§15.2.1); a declaration's name is bound outside it and
    keeps the enclosure's. An arrow's parameters are still the enclosing code and keep its
    readings, except that an `async` arrow reads `await` as the operator there too; its body is
    what `arrow_body_context` says. A class element's own code is what `class_element_context`
    says, whatever encloses the class.
    """
    if isinstance(parent, JsStaticBlock):
        return class_element_context(static=True)
    if isinstance(parent, JsPropertyDefinition):
        return class_element_context(parent.is_static) if child is parent.value else enclosing
    if isinstance(parent, JsArrowFunctionExpression):
        if any(child is param for param in parent.params):
            if parent.is_async and enclosing.await_reading is not AwaitReading.OPERATOR:
                return replace(enclosing, await_reading=AwaitReading.OPERATOR)
            return enclosing
        return arrow_body_context(parent.is_async, enclosing)
    if isinstance(parent, JsFunctionDeclaration):
        return enclosing if child is parent.id else function_context(parent.is_async, parent.generator)
    if isinstance(parent, JsFunctionExpression):
        return function_context(parent.is_async, parent.generator)
    return enclosing


def code_context_at(node: Node, root_context: CodeContext = SCRIPT_CONTEXT) -> CodeContext:
    """
    The context *node* is read under, folded from the root of its tree down through
    `code_context_within`. *root_context* is what the root itself stands in: the top level of a
    file by default, and the splice site's context for a tree about to be inlined there.
    """
    chain: list[Node] = []
    cursor: Node | None = node
    while cursor is not None:
        chain.append(cursor)
        cursor = cursor.parent
    chain.reverse()
    context = root_context
    for parent, child in zip(chain, chain[1:]):
        context = code_context_within(parent, child, context)
    return context


def is_async_function(func: JsFunctionNode) -> bool:
    return func.is_async


def is_generator_function(func: JsFunctionNode) -> bool:
    """
    Whether *func* is written `function*`. An arrow holds no such field, because the language has no
    generator arrow to write, and answers `False` rather than raising: a caller deciding what kind of
    function to rebuild asks this about whatever it holds, and a raise there would be an arrow the
    rebuild refuses rather than one it rebuilds as an arrow.
    """
    return isinstance(func, (JsFunctionDeclaration, JsFunctionExpression)) and func.generator


def wraps_return(func: JsFunctionNode) -> bool:
    """
    Whether calling *func* answers something wrapped around what the body returned: a promise for an
    `async` function, a generator object for a generator, an async generator object for both. In none
    of the three is the call the value the body returned, so a pass that answers such a call with the
    body's return expression hands back a program computing something else.
    """
    return is_async_function(func) or is_generator_function(func)


def strip_parens(node: Node | None) -> Node | None:
    """
    The expression *node* denotes once any enclosing parentheses are removed, so that a parenthesized
    operand is classified by the operator that actually applies to it rather than by the redundant
    grouping the parser preserves. A grouping whose inner expression is absent strips to `None`, which
    every caller treats as "not the node being matched".
    """
    while isinstance(node, JsParenthesizedExpression):
        node = node.expression
    return node


def callee_form_sensitive(node: Node | None) -> bool:
    """
    Whether a call invoking *node* directly as its callee means something a call reaching the same
    value through a neutral spelling does not. The language has two such forms: a member access
    binds `this` to its object, and a bare `eval` performs a *direct* eval evaluated in the
    caller's own scope. Any other callee — a plain identifier or a value — invokes with no
    receiver and no direct-eval effect, exactly as the same value called behind `(0, ...)` does,
    so only these two forms constrain what may stand in a callee position.
    """
    inner = strip_parens(node)
    if isinstance(inner, JsMemberExpression):
        return True
    return isinstance(inner, JsIdentifier) and inner.name == 'eval'


def names_a_property(node: Node) -> bool:
    """
    Whether *node* spells the name of a property and reads nothing. A member written with a dot, a
    key of an object literal, the name of a class member, the key of an import attribute, and a
    name on the far side of a module boundary are the positions the language has for such a name,
    and in each of them the text is a name the value carries rather than one the program looks up.

    A computed key is not one of these: what stands inside the brackets is an expression and is
    read like any other. Neither is a shorthand property, which is written like a key and is both:
    `{ x }` means `{ x: x }`, and the one node the parser builds for it is the read as much as it
    is the key, so calling it a property name would excuse a reference the program really makes.

    An import attribute answers `True` for a key of any kind, a string as readily as a name,
    because a with-clause holds nothing a program could refer to:

        import d from 'm' with { 'type': 'json' }

    names an attribute and not a binding, and so does the same clause written without the quotes.

    A name across the module boundary is an IdentifierName the grammar takes wider than a name the
    file could bind: `import { yield as v }`, `export { v as yield }`, `export * as yield from`,
    and both words of `export { yield } from "m"` name what another module carries. The shorthand
    `import { yield }` is the one spelling that is the far side and the binding at once, and it
    is read as the binding.
    """
    parent = node.parent
    if isinstance(parent, JsMemberExpression):
        return parent.property is node and not parent.computed
    if isinstance(parent, JsProperty):
        return parent.key is node and not parent.computed and not parent.shorthand
    if isinstance(parent, (JsMethodDefinition, JsPropertyDefinition)):
        return parent.key is node and not parent.computed
    if isinstance(parent, JsImportAttribute):
        return parent.key is node
    if isinstance(parent, JsImportSpecifier):
        return parent.imported is node and parent.local is not node
    if isinstance(parent, JsExportSpecifier):
        declaration = parent.parent
        if isinstance(declaration, JsExportNamedDeclaration) and declaration.source is not None:
            return True
        return parent.exported is node and parent.local is not node
    if isinstance(parent, JsExportAllDeclaration):
        return parent.exported is node
    return False


ACCESSOR_INSTALL_METHODS = frozenset({
    'defineProperty',
    'defineProperties',
    '__defineGetter__',
    '__defineSetter__',
})


def static_string(node: Node | None) -> str | None:
    """
    The string *node* certainly evaluates to, or `None`. It reads a string literal, a
    substitution-free template, and concatenations of those — the forms a constant fold collapses
    to a literal.

    This exists so that an analysis answer cannot change as folds fire: a property key the
    simplifier will turn into `'defineProperty'` must already be read as that name, or a consumer
    holding the answer across a pass would be told there is no install and then have one appear.
    It is deliberately a *must* analysis — an unknown value yields `None`, and a key whose value
    is unknown names no method, since only a key a fold can collapse can reveal an install
    mid-pass.
    """
    node = strip_parens(node)
    if isinstance(node, JsStringLiteral):
        return node.value
    if isinstance(node, JsTemplateLiteral):
        if node.expressions:
            return None
        if any(quasi.value is None for quasi in node.quasis):
            return None
        return ''.join(quasi.value or '' for quasi in node.quasis)
    if isinstance(node, JsBinaryExpression) and node.operator == '+':
        left = static_string(node.left)
        if left is None:
            return None
        right = static_string(node.right)
        if right is None:
            return None
        return left + right
    return None


def static_property_key(node: JsMemberExpression) -> str | None:
    """
    The property name *node* accesses, or `None` where no single static name is known: the
    identifier of a dotted access, or the string a computed key certainly evaluates to
    (`static_string`), so a key a fold would collapse to a literal already reads as that name.
    """
    prop = node.property
    if node.computed:
        return static_string(prop)
    return prop.name if isinstance(prop, JsIdentifier) else None


def accessor_install_method(node: JsMemberExpression) -> str | None:
    """
    The accessor-install method *node* names, through a dotted property or a computed key whose
    string value is statically known, or `None`. Matching the dotted form alone is what let
    `Object['defineProperty']` slip past both callers, and a fold rewriting that key to a dot
    would then reveal the install only after the fact was consumed.
    """
    name = static_property_key(node)
    return name if name in ACCESSOR_INSTALL_METHODS else None


Closable = (
    JsStringLiteral
    | JsRegExpLiteral
    | JsTemplateElement
    | JsArrayExpression
    | JsObjectExpression
    | JsParenthesizedExpression
    | JsCallExpression
    | JsNewExpression
    | JsArrayPattern
    | JsObjectPattern
    | JsClassBody
    | JsStaticBlock
    | JsBlockStatement
    | JsSwitchStatement
    | JsScript
)
"""
The node kinds that carry `terminated`: a construct with a closing delimiter the file may end
before, which `file_ended_inside` reads.
"""


def file_ended_inside(node: Node) -> bool:
    """
    Whether *node* is a construct the file ended inside: a literal, a bracketed list, a block, a
    class body, a switch, or the file itself, whose closing delimiter the parser never saw because
    the file ended first. Such a node holds what the file held and says that nothing closed it: a
    printer writes what it holds and nothing after it, an engine refuses the file as an unexpected
    end of input, and the parser sets the flag at the end of the file and nowhere else.
    """
    return isinstance(node, Closable) and not node.terminated

Global variables

var FUNCTION_NODES

The three nodes that hold a function body. A class or object method holds a JsFunctionExpression as its value, so it needs no entry of its own.

var SCRIPT_CONTEXT

The context the top level of a file is read under: both words are names, arguments is free, and the goal symbol is open.

var Closable

The node kinds that carry terminated: a construct with a closing delimiter the file may end before, which file_ended_inside() reads.

Functions

def function_context(is_async, is_generator)

The context the parameter list and the body of a function of the given kind are read under.

Expand source code Browse git
@functools.cache
def function_context(is_async: bool, is_generator: bool) -> CodeContext:
    """
    The context the parameter list and the body of a function of the given kind are read under.
    """
    return CodeContext(
        AwaitReading.OPERATOR if is_async else AwaitReading.NAME,
        is_generator,
        False,
        False,
    )
def class_element_context(static)

The context a class element's own code is read under: a field initializer, or a static block. Each is a function context of its own that reserves arguments (§15.7.1) and reads yield as a name; a static one refuses await outright, where an instance initializer reads it as a name.

Expand source code Browse git
@functools.cache
def class_element_context(static: bool) -> CodeContext:
    """
    The context a class element's own code is read under: a field initializer, or a static block.
    Each is a function context of its own that reserves `arguments` (§15.7.1) and reads `yield` as
    a name; a static one refuses `await` outright, where an instance initializer reads it as a name.
    """
    return CodeContext(
        AwaitReading.RESERVED if static else AwaitReading.NAME,
        False,
        True,
        False,
    )
def arrow_body_context(is_async, enclosing)

The context an arrow function's body is read under, given that the arrow stands in enclosing: the context a function of its kind gives its body, except that an arrow has no arguments of its own (§15.7.1) and so keeps the reservation it stands in.

Expand source code Browse git
def arrow_body_context(is_async: bool, enclosing: CodeContext) -> CodeContext:
    """
    The context an arrow function's body is read under, given that the arrow stands in
    *enclosing*: the context a function of its kind gives its body, except that an arrow has no
    `arguments` of its own (§15.7.1) and so keeps the reservation it stands in.
    """
    return replace(function_context(is_async, False), arguments_reserved=enclosing.arguments_reserved)
def code_context_within(parent, child, enclosing)

The context child is read under, given that parent is read under enclosing. This is the one transition every reader of the context takes, so that the parser descending the tree, the collector threading it, and code_context_at() folding it agree on every node.

A function body takes the readings its own kind gives it, and so does the name of a function expression, which is bound inside it (§15.2.1); a declaration's name is bound outside it and keeps the enclosure's. An arrow's parameters are still the enclosing code and keep its readings, except that an async arrow reads await as the operator there too; its body is what arrow_body_context() says. A class element's own code is what class_element_context() says, whatever encloses the class.

Expand source code Browse git
def code_context_within(parent: Node, child: Node, enclosing: CodeContext) -> CodeContext:
    """
    The context *child* is read under, given that *parent* is read under *enclosing*. This is the
    one transition every reader of the context takes, so that the parser descending the tree, the
    collector threading it, and `code_context_at` folding it agree on every node.

    A function body takes the readings its own kind gives it, and so does the name of a function
    expression, which is bound inside it (§15.2.1); a declaration's name is bound outside it and
    keeps the enclosure's. An arrow's parameters are still the enclosing code and keep its
    readings, except that an `async` arrow reads `await` as the operator there too; its body is
    what `arrow_body_context` says. A class element's own code is what `class_element_context`
    says, whatever encloses the class.
    """
    if isinstance(parent, JsStaticBlock):
        return class_element_context(static=True)
    if isinstance(parent, JsPropertyDefinition):
        return class_element_context(parent.is_static) if child is parent.value else enclosing
    if isinstance(parent, JsArrowFunctionExpression):
        if any(child is param for param in parent.params):
            if parent.is_async and enclosing.await_reading is not AwaitReading.OPERATOR:
                return replace(enclosing, await_reading=AwaitReading.OPERATOR)
            return enclosing
        return arrow_body_context(parent.is_async, enclosing)
    if isinstance(parent, JsFunctionDeclaration):
        return enclosing if child is parent.id else function_context(parent.is_async, parent.generator)
    if isinstance(parent, JsFunctionExpression):
        return function_context(parent.is_async, parent.generator)
    return enclosing
def code_context_at(node, root_context=CodeContext(await_reading=<AwaitReading.NAME: 1>, yield_is_operator=False, arguments_reserved=False, top_level=True))

The context node is read under, folded from the root of its tree down through code_context_within(). root_context is what the root itself stands in: the top level of a file by default, and the splice site's context for a tree about to be inlined there.

Expand source code Browse git
def code_context_at(node: Node, root_context: CodeContext = SCRIPT_CONTEXT) -> CodeContext:
    """
    The context *node* is read under, folded from the root of its tree down through
    `code_context_within`. *root_context* is what the root itself stands in: the top level of a
    file by default, and the splice site's context for a tree about to be inlined there.
    """
    chain: list[Node] = []
    cursor: Node | None = node
    while cursor is not None:
        chain.append(cursor)
        cursor = cursor.parent
    chain.reverse()
    context = root_context
    for parent, child in zip(chain, chain[1:]):
        context = code_context_within(parent, child, context)
    return context
def is_async_function(func)
Expand source code Browse git
def is_async_function(func: JsFunctionNode) -> bool:
    return func.is_async
def is_generator_function(func)

Whether func is written function*. An arrow holds no such field, because the language has no generator arrow to write, and answers False rather than raising: a caller deciding what kind of function to rebuild asks this about whatever it holds, and a raise there would be an arrow the rebuild refuses rather than one it rebuilds as an arrow.

Expand source code Browse git
def is_generator_function(func: JsFunctionNode) -> bool:
    """
    Whether *func* is written `function*`. An arrow holds no such field, because the language has no
    generator arrow to write, and answers `False` rather than raising: a caller deciding what kind of
    function to rebuild asks this about whatever it holds, and a raise there would be an arrow the
    rebuild refuses rather than one it rebuilds as an arrow.
    """
    return isinstance(func, (JsFunctionDeclaration, JsFunctionExpression)) and func.generator
def wraps_return(func)

Whether calling func answers something wrapped around what the body returned: a promise for an async function, a generator object for a generator, an async generator object for both. In none of the three is the call the value the body returned, so a pass that answers such a call with the body's return expression hands back a program computing something else.

Expand source code Browse git
def wraps_return(func: JsFunctionNode) -> bool:
    """
    Whether calling *func* answers something wrapped around what the body returned: a promise for an
    `async` function, a generator object for a generator, an async generator object for both. In none
    of the three is the call the value the body returned, so a pass that answers such a call with the
    body's return expression hands back a program computing something else.
    """
    return is_async_function(func) or is_generator_function(func)
def strip_parens(node)

The expression node denotes once any enclosing parentheses are removed, so that a parenthesized operand is classified by the operator that actually applies to it rather than by the redundant grouping the parser preserves. A grouping whose inner expression is absent strips to None, which every caller treats as "not the node being matched".

Expand source code Browse git
def strip_parens(node: Node | None) -> Node | None:
    """
    The expression *node* denotes once any enclosing parentheses are removed, so that a parenthesized
    operand is classified by the operator that actually applies to it rather than by the redundant
    grouping the parser preserves. A grouping whose inner expression is absent strips to `None`, which
    every caller treats as "not the node being matched".
    """
    while isinstance(node, JsParenthesizedExpression):
        node = node.expression
    return node
def callee_form_sensitive(node)

Whether a call invoking node directly as its callee means something a call reaching the same value through a neutral spelling does not. The language has two such forms: a member access binds this to its object, and a bare eval performs a direct eval evaluated in the caller's own scope. Any other callee — a plain identifier or a value — invokes with no receiver and no direct-eval effect, exactly as the same value called behind (0, …) does, so only these two forms constrain what may stand in a callee position.

Expand source code Browse git
def callee_form_sensitive(node: Node | None) -> bool:
    """
    Whether a call invoking *node* directly as its callee means something a call reaching the same
    value through a neutral spelling does not. The language has two such forms: a member access
    binds `this` to its object, and a bare `eval` performs a *direct* eval evaluated in the
    caller's own scope. Any other callee — a plain identifier or a value — invokes with no
    receiver and no direct-eval effect, exactly as the same value called behind `(0, ...)` does,
    so only these two forms constrain what may stand in a callee position.
    """
    inner = strip_parens(node)
    if isinstance(inner, JsMemberExpression):
        return True
    return isinstance(inner, JsIdentifier) and inner.name == 'eval'
def names_a_property(node)

Whether node spells the name of a property and reads nothing. A member written with a dot, a key of an object literal, the name of a class member, the key of an import attribute, and a name on the far side of a module boundary are the positions the language has for such a name, and in each of them the text is a name the value carries rather than one the program looks up.

A computed key is not one of these: what stands inside the brackets is an expression and is read like any other. Neither is a shorthand property, which is written like a key and is both: { x } means { x: x }, and the one node the parser builds for it is the read as much as it is the key, so calling it a property name would excuse a reference the program really makes.

An import attribute answers True for a key of any kind, a string as readily as a name, because a with-clause holds nothing a program could refer to:

import d from 'm' with { 'type': 'json' }

names an attribute and not a binding, and so does the same clause written without the quotes.

A name across the module boundary is an IdentifierName the grammar takes wider than a name the file could bind: import { yield as v }, export { v as yield }, export * as yield from, and both words of export { yield } from "m" name what another module carries. The shorthand import { yield } is the one spelling that is the far side and the binding at once, and it is read as the binding.

Expand source code Browse git
def names_a_property(node: Node) -> bool:
    """
    Whether *node* spells the name of a property and reads nothing. A member written with a dot, a
    key of an object literal, the name of a class member, the key of an import attribute, and a
    name on the far side of a module boundary are the positions the language has for such a name,
    and in each of them the text is a name the value carries rather than one the program looks up.

    A computed key is not one of these: what stands inside the brackets is an expression and is
    read like any other. Neither is a shorthand property, which is written like a key and is both:
    `{ x }` means `{ x: x }`, and the one node the parser builds for it is the read as much as it
    is the key, so calling it a property name would excuse a reference the program really makes.

    An import attribute answers `True` for a key of any kind, a string as readily as a name,
    because a with-clause holds nothing a program could refer to:

        import d from 'm' with { 'type': 'json' }

    names an attribute and not a binding, and so does the same clause written without the quotes.

    A name across the module boundary is an IdentifierName the grammar takes wider than a name the
    file could bind: `import { yield as v }`, `export { v as yield }`, `export * as yield from`,
    and both words of `export { yield } from "m"` name what another module carries. The shorthand
    `import { yield }` is the one spelling that is the far side and the binding at once, and it
    is read as the binding.
    """
    parent = node.parent
    if isinstance(parent, JsMemberExpression):
        return parent.property is node and not parent.computed
    if isinstance(parent, JsProperty):
        return parent.key is node and not parent.computed and not parent.shorthand
    if isinstance(parent, (JsMethodDefinition, JsPropertyDefinition)):
        return parent.key is node and not parent.computed
    if isinstance(parent, JsImportAttribute):
        return parent.key is node
    if isinstance(parent, JsImportSpecifier):
        return parent.imported is node and parent.local is not node
    if isinstance(parent, JsExportSpecifier):
        declaration = parent.parent
        if isinstance(declaration, JsExportNamedDeclaration) and declaration.source is not None:
            return True
        return parent.exported is node and parent.local is not node
    if isinstance(parent, JsExportAllDeclaration):
        return parent.exported is node
    return False
def static_string(node)

The string node certainly evaluates to, or None. It reads a string literal, a substitution-free template, and concatenations of those — the forms a constant fold collapses to a literal.

This exists so that an analysis answer cannot change as folds fire: a property key the simplifier will turn into 'defineProperty' must already be read as that name, or a consumer holding the answer across a pass would be told there is no install and then have one appear. It is deliberately a must analysis — an unknown value yields None, and a key whose value is unknown names no method, since only a key a fold can collapse can reveal an install mid-pass.

Expand source code Browse git
def static_string(node: Node | None) -> str | None:
    """
    The string *node* certainly evaluates to, or `None`. It reads a string literal, a
    substitution-free template, and concatenations of those — the forms a constant fold collapses
    to a literal.

    This exists so that an analysis answer cannot change as folds fire: a property key the
    simplifier will turn into `'defineProperty'` must already be read as that name, or a consumer
    holding the answer across a pass would be told there is no install and then have one appear.
    It is deliberately a *must* analysis — an unknown value yields `None`, and a key whose value
    is unknown names no method, since only a key a fold can collapse can reveal an install
    mid-pass.
    """
    node = strip_parens(node)
    if isinstance(node, JsStringLiteral):
        return node.value
    if isinstance(node, JsTemplateLiteral):
        if node.expressions:
            return None
        if any(quasi.value is None for quasi in node.quasis):
            return None
        return ''.join(quasi.value or '' for quasi in node.quasis)
    if isinstance(node, JsBinaryExpression) and node.operator == '+':
        left = static_string(node.left)
        if left is None:
            return None
        right = static_string(node.right)
        if right is None:
            return None
        return left + right
    return None
def static_property_key(node)

The property name node accesses, or None where no single static name is known: the identifier of a dotted access, or the string a computed key certainly evaluates to (static_string()), so a key a fold would collapse to a literal already reads as that name.

Expand source code Browse git
def static_property_key(node: JsMemberExpression) -> str | None:
    """
    The property name *node* accesses, or `None` where no single static name is known: the
    identifier of a dotted access, or the string a computed key certainly evaluates to
    (`static_string`), so a key a fold would collapse to a literal already reads as that name.
    """
    prop = node.property
    if node.computed:
        return static_string(prop)
    return prop.name if isinstance(prop, JsIdentifier) else None
def accessor_install_method(node)

The accessor-install method node names, through a dotted property or a computed key whose string value is statically known, or None. Matching the dotted form alone is what let Object['defineProperty'] slip past both callers, and a fold rewriting that key to a dot would then reveal the install only after the fact was consumed.

Expand source code Browse git
def accessor_install_method(node: JsMemberExpression) -> str | None:
    """
    The accessor-install method *node* names, through a dotted property or a computed key whose
    string value is statically known, or `None`. Matching the dotted form alone is what let
    `Object['defineProperty']` slip past both callers, and a fold rewriting that key to a dot
    would then reveal the install only after the fact was consumed.
    """
    name = static_property_key(node)
    return name if name in ACCESSOR_INSTALL_METHODS else None
def file_ended_inside(node)

Whether node is a construct the file ended inside: a literal, a bracketed list, a block, a class body, a switch, or the file itself, whose closing delimiter the parser never saw because the file ended first. Such a node holds what the file held and says that nothing closed it: a printer writes what it holds and nothing after it, an engine refuses the file as an unexpected end of input, and the parser sets the flag at the end of the file and nowhere else.

Expand source code Browse git
def file_ended_inside(node: Node) -> bool:
    """
    Whether *node* is a construct the file ended inside: a literal, a bracketed list, a block, a
    class body, a switch, or the file itself, whose closing delimiter the parser never saw because
    the file ended first. Such a node holds what the file held and says that nothing closed it: a
    printer writes what it holds and nothing after it, an engine refuses the file as an unexpected
    end of input, and the parser sets the flag at the end of the file and nowhere else.
    """
    return isinstance(node, Closable) and not node.terminated

Classes

class JsPropertyKind (*args, **kwds)

Create a collection of name/value pairs.

Example enumeration:

>>> class Color(Enum):
...     RED = 1
...     BLUE = 2
...     GREEN = 3

Access them by:

  • attribute access:

Color.RED

  • value lookup:

Color(1)

  • name lookup:

Color['RED']

Enumerations can be iterated over, and know how many members they have:

>>> len(Color)
3
>>> list(Color)
[<Color.RED: 1>, <Color.BLUE: 2>, <Color.GREEN: 3>]

Methods can be added to enumerations, and members can have their own attributes – see the documentation for details.

Expand source code Browse git
class JsPropertyKind(enum.Enum):
    INIT = 'init'
    GET  = 'get'   # noqa
    SET  = 'set'   # noqa

Ancestors

  • enum.Enum

Class variables

var INIT

The type of the None singleton.

var GET

The type of the None singleton.

var SET

The type of the None singleton.

class JsMethodKind (*args, **kwds)

Create a collection of name/value pairs.

Example enumeration:

>>> class Color(Enum):
...     RED = 1
...     BLUE = 2
...     GREEN = 3

Access them by:

  • attribute access:

Color.RED

  • value lookup:

Color(1)

  • name lookup:

Color['RED']

Enumerations can be iterated over, and know how many members they have:

>>> len(Color)
3
>>> list(Color)
[<Color.RED: 1>, <Color.BLUE: 2>, <Color.GREEN: 3>]

Methods can be added to enumerations, and members can have their own attributes – see the documentation for details.

Expand source code Browse git
class JsMethodKind(enum.Enum):
    METHOD      = 'method'       # noqa
    GET         = 'get'          # noqa
    SET         = 'set'          # noqa
    CONSTRUCTOR = 'constructor'  # noqa

Ancestors

  • enum.Enum

Class variables

var METHOD

The type of the None singleton.

var GET

The type of the None singleton.

var SET

The type of the None singleton.

var CONSTRUCTOR

The type of the None singleton.

class JsVarKind (*args, **kwds)

Create a collection of name/value pairs.

Example enumeration:

>>> class Color(Enum):
...     RED = 1
...     BLUE = 2
...     GREEN = 3

Access them by:

  • attribute access:

Color.RED

  • value lookup:

Color(1)

  • name lookup:

Color['RED']

Enumerations can be iterated over, and know how many members they have:

>>> len(Color)
3
>>> list(Color)
[<Color.RED: 1>, <Color.BLUE: 2>, <Color.GREEN: 3>]

Methods can be added to enumerations, and members can have their own attributes – see the documentation for details.

Expand source code Browse git
class JsVarKind(enum.Enum):
    VAR   = 'var'    # noqa
    LET   = 'let'    # noqa
    CONST = 'const'  # noqa

Ancestors

  • enum.Enum

Class variables

var VAR

The type of the None singleton.

var LET

The type of the None singleton.

var CONST

The type of the None singleton.

class JsErrorNode (offset=-1, parent=None, leading_comments=<factory>, trailing_comments=<factory>, text='', message='')

A span of source the parser could not read, kept verbatim so that what an analyst gets back still contains what was written. It prints as text and so reads back as whatever that text parses to, which is why a tree holding one states nothing about synthesizer fidelity.

It stands in either position, because the recovery that builds it does not know which was expected. In statement position it is the statement, and is deliberately not wrapped in an expression statement: the wrapper prints a semicolon that nobody wrote, which grows the file by one character every time the tool reads its own output.

Expand source code Browse git
@dataclass(repr=False, eq=False)
class JsErrorNode(Expression, Statement, unparsed=True):
    """
    A span of source the parser could not read, kept verbatim so that what an analyst gets back
    still contains what was written. It prints as `text` and so reads back as whatever that text
    parses to, which is why a tree holding one states nothing about synthesizer fidelity.

    It stands in either position, because the recovery that builds it does not know which was
    expected. In statement position it is the statement, and is deliberately not wrapped in an
    expression statement: the wrapper prints a semicolon that nobody wrote, which grows the file by
    one character every time the tool reads its own output.
    """
    text: str = ''
    message: str = ''

Ancestors

Instance variables

var text

The type of the None singleton.

var message

The type of the None singleton.

Inherited members

class JsIdentifier (offset=-1, parent=None, leading_comments=<factory>, trailing_comments=<factory>, name='', raw='')

A name. name is the name the source denotes and raw is the text it was written with, which part ways wherever a unicode escape stands between them: \u0061bc and abc are one binding written two ways, and every question about names is asked of name.

raw is empty wherever the two would be the same text, so it holds something only for a name the source wrote some other way. What it holds is trusted only for as long as it still spells name, which is what leaves a pass renaming a node nothing to maintain: the synthesizer asks whether the spelling it was handed spells the name it is printing, and writes the name itself where it does not.

Expand source code Browse git
@dataclass(repr=False, eq=False)
class JsIdentifier(Expression, spelling='raw'):
    """
    A name. `name` is the name the source denotes and `raw` is the text it was written with, which
    part ways wherever a unicode escape stands between them: `\\u0061bc` and `abc` are one binding
    written two ways, and every question about names is asked of `name`.

    `raw` is empty wherever the two would be the same text, so it holds something only for a name
    the source wrote some other way. What it holds is trusted only for as long as it still spells
    `name`, which is what leaves a pass renaming a node nothing to maintain: the synthesizer asks
    whether the spelling it was handed spells the name it is printing, and writes the name itself
    where it does not.
    """
    name: str = ''
    raw: str = ''

    def has_spelling(self) -> bool:
        """
        There is no name spelled by nothing. Printing one writes whatever stands around it and
        closes up over the gap, so `a.` becomes `a` and a program loses a member read; the parser
        builds an error node where it finds no name, and this is what says so if a transform ever
        assembles one anyway.
        """
        return bool(self.name)

Ancestors

Instance variables

var name

The type of the None singleton.

var raw

The type of the None singleton.

Methods

def has_spelling(self)

There is no name spelled by nothing. Printing one writes whatever stands around it and closes up over the gap, so a. becomes a and a program loses a member read; the parser builds an error node where it finds no name, and this is what says so if a transform ever assembles one anyway.

Expand source code Browse git
def has_spelling(self) -> bool:
    """
    There is no name spelled by nothing. Printing one writes whatever stands around it and
    closes up over the gap, so `a.` becomes `a` and a program loses a member read; the parser
    builds an error node where it finds no name, and this is what says so if a transform ever
    assembles one anyway.
    """
    return bool(self.name)

Inherited members

class JsPrivateIdentifier (offset=-1, parent=None, leading_comments=<factory>, trailing_comments=<factory>, name='', raw='')

A private name, written with the # that opens it left out of name and out of raw alike. An escape spells one of these as it spells any other name, so this.#\u0061 reads the member #a declares.

Expand source code Browse git
@dataclass(repr=False, eq=False)
class JsPrivateIdentifier(Expression, spelling='raw'):
    """
    A private name, written with the `#` that opens it left out of `name` and out of `raw` alike.
    An escape spells one of these as it spells any other name, so `this.#\\u0061` reads the member
    `#a` declares.
    """
    name: str = ''
    raw: str = ''

Ancestors

Instance variables

var name

The type of the None singleton.

var raw

The type of the None singleton.

Inherited members

class JsNumericLiteral (offset=-1, parent=None, leading_comments=<factory>, trailing_comments=<factory>, value=0.0, raw='0')

A Number literal. value is the double the source denotes and raw is how that source spelled it; the two are independent because a spelling carries information the value does not, such as the base of 0xFF or the sign of -0. Coercion happens here rather than at the call sites so that no construction anywhere can introduce a value JavaScript cannot hold.

Expand source code Browse git
@dataclass(repr=False, eq=False)
class JsNumericLiteral(Expression, spelling='raw'):
    """
    A Number literal. `value` is the double the source denotes and `raw` is how that source spelled
    it; the two are independent because a spelling carries information the value does not, such as
    the base of `0xFF` or the sign of `-0`. Coercion happens here rather than at the call sites so
    that no construction anywhere can introduce a value JavaScript cannot hold.
    """
    value: float = 0.0
    raw: str = '0'

    def __post_init__(self):
        super().__post_init__()
        self.value = to_js_number(self.value)

Ancestors

Instance variables

var value

The type of the None singleton.

var raw

The type of the None singleton.

Inherited members

class JsBigIntLiteral (offset=-1, parent=None, leading_comments=<factory>, trailing_comments=<factory>, raw='0n')

A BigInt literal. raw is how the source spelled it, and the exact integer it denotes is read off that spelling when it is asked for: converting a long decimal spelling is bounded by the interpreter's limit on the digits of an integer conversion, and reading a file must not be hostage to that limit for a value the file may never have read.

Expand source code Browse git
@dataclass(repr=False, eq=False)
class JsBigIntLiteral(Expression, spelling='raw'):
    """
    A BigInt literal. `raw` is how the source spelled it, and the exact integer it denotes is read
    off that spelling when it is asked for: converting a long decimal spelling is bounded by the
    interpreter's limit on the digits of an integer conversion, and reading a file must not be
    hostage to that limit for a value the file may never have read.
    """
    raw: str = '0n'

    @property
    def value(self) -> int:
        return integer_of_numeral(self.raw[:-1].replace('_', ''))

Ancestors

Instance variables

var raw

The type of the None singleton.

var value
Expand source code Browse git
@property
def value(self) -> int:
    return integer_of_numeral(self.raw[:-1].replace('_', ''))

Inherited members

class JsStringLiteral (offset=-1, parent=None, leading_comments=<factory>, trailing_comments=<factory>, value='', raw="''", terminated=True)

A String literal. value is the text it denotes and raw is how the source spelled it, which part ways wherever an escape stands between them.

value is None where the literal denotes nothing, which is a literal written with a \x or \u escape that names no character. A string keeps the legacy escapes Annex B holds open for sloppy code, so those still denote text; an escape no literal has at all denotes none, the way the same escape leaves a JsTemplateElement denoting none.

terminated reports whether the closing quote was there. A literal the source never closed is not a form the language has, so no text spells it: printing what was written runs the literal on into whatever the synthesizer prints next, and printing the quote that is missing turns a file that does not parse into a program that runs.

Expand source code Browse git
@dataclass(repr=False, eq=False)
class JsStringLiteral(Expression, spelling='raw'):
    """
    A String literal. `value` is the text it denotes and `raw` is how the source spelled it, which
    part ways wherever an escape stands between them.

    `value` is `None` where the literal denotes nothing, which is a literal written with a `\\x` or
    `\\u` escape that names no character. A string keeps the legacy escapes Annex B holds open for
    sloppy code, so those still denote text; an escape no literal has at all denotes none, the way
    the same escape leaves a `JsTemplateElement` denoting none.

    `terminated` reports whether the closing quote was there. A literal the source never closed is
    not a form the language has, so no text spells it: printing what was written runs the literal on
    into whatever the synthesizer prints next, and printing the quote that is missing turns a file
    that does not parse into a program that runs.
    """
    value: str | None = ''
    raw: str = "''"
    terminated: bool = True

    @property
    def body(self) -> str:
        """
        The source text between the quotes, every escape still spelled as it was written. A rule
        about how a literal was written reads this rather than `value`: a directive spelled with an
        escape in it denotes the text `use strict` and is not a Use Strict Directive, because what
        makes a directive is the spelling and not the value.
        """
        return self.raw[1:-1] if self.terminated else self.raw[1:]

Ancestors

Instance variables

var value

The type of the None singleton.

var raw

The type of the None singleton.

var terminated

The type of the None singleton.

var body

The source text between the quotes, every escape still spelled as it was written. A rule about how a literal was written reads this rather than value: a directive spelled with an escape in it denotes the text use strict and is not a Use Strict Directive, because what makes a directive is the spelling and not the value.

Expand source code Browse git
@property
def body(self) -> str:
    """
    The source text between the quotes, every escape still spelled as it was written. A rule
    about how a literal was written reads this rather than `value`: a directive spelled with an
    escape in it denotes the text `use strict` and is not a Use Strict Directive, because what
    makes a directive is the spelling and not the value.
    """
    return self.raw[1:-1] if self.terminated else self.raw[1:]

Inherited members

class JsRegExpLiteral (offset=-1, parent=None, leading_comments=<factory>, trailing_comments=<factory>, pattern='', flags='', raw='//', terminated=True)

JsRegExpLiteral(offset: 'int' = -1, parent: 'Node | None' = None, leading_comments: 'list[str]' = , trailing_comments: 'list[str]' = , pattern: 'str' = '', flags: 'str' = '', raw: 'str' = '//', terminated: 'bool' = True)

Expand source code Browse git
@dataclass(repr=False, eq=False)
class JsRegExpLiteral(Expression, spelling='raw'):
    pattern: str = ''
    flags: str = ''
    raw: str = '//'
    terminated: bool = True

Ancestors

Instance variables

var pattern

The type of the None singleton.

var flags

The type of the None singleton.

var raw

The type of the None singleton.

var terminated

The type of the None singleton.

Inherited members

class JsTemplateLiteral (offset=-1, parent=None, leading_comments=<factory>, trailing_comments=<factory>, quasis=<factory>, expressions=<factory>)

JsTemplateLiteral(offset: 'int' = -1, parent: 'Node | None' = None, leading_comments: 'list[str]' = , trailing_comments: 'list[str]' = , quasis: 'list[JsTemplateElement]' = , expressions: 'list[Expression]' = )

Expand source code Browse git
@dataclass(repr=False, eq=False)
class JsTemplateLiteral(Expression):
    quasis: list[JsTemplateElement] = field(default_factory=list)
    expressions: list[Expression] = field(default_factory=list)

Ancestors

Instance variables

var quasis

The type of the None singleton.

var expressions

The type of the None singleton.

Inherited members

class JsTemplateElement (offset=-1, parent=None, leading_comments=<factory>, trailing_comments=<factory>, value='', raw='', tail=False, terminated=True, opened=True)

One run of text in a template literal. raw is that run as the source wrote it and value the text it denotes; neither holds the delimiters that separate the runs from the expressions, because the literal prints those itself and a run is what stands between them.

terminated reports whether the delimiter that ends the run was there. Only a template can reach the end of the file unclosed, since no line terminator ends one, and a hole the source left open ends here as an empty run that closes nothing.

value is None where the run denotes nothing, which is a run written with an escape the template grammar excludes. The language says the same thing by handing a tag undefined for such a run, and by refusing the untagged literal outright.

Expand source code Browse git
@dataclass(repr=False, eq=False)
class JsTemplateElement(Node, spelling='raw'):
    """
    One run of text in a template literal. `raw` is that run as the source wrote it and `value` the
    text it denotes; neither holds the delimiters that separate the runs from the expressions,
    because the literal prints those itself and a run is what stands between them.

    `terminated` reports whether the delimiter that ends the run was there. Only a template can
    reach the end of the file unclosed, since no line terminator ends one, and a hole the source
    left open ends here as an empty run that closes nothing.

    `value` is `None` where the run denotes nothing, which is a run written with an escape the
    template grammar excludes. The language says the same thing by handing a tag `undefined` for
    such a run, and by refusing the untagged literal outright.
    """
    value: str | None = ''
    raw: str = ''
    tail: bool = False
    terminated: bool = True
    #: Whether the `}` that closes the hole in front of this run was there. Only the run behind a
    #: hole the file ended inside lacks it, and such a run holds no text at all.
    opened: bool = True

Ancestors

Instance variables

var value

The type of the None singleton.

var raw

The type of the None singleton.

var tail

The type of the None singleton.

var terminated

The type of the None singleton.

var opened

Whether the } that closes the hole in front of this run was there. Only the run behind a hole the file ended inside lacks it, and such a run holds no text at all.

Inherited members

class JsBooleanLiteral (offset=-1, parent=None, leading_comments=<factory>, trailing_comments=<factory>, value=False)

JsBooleanLiteral(offset: 'int' = -1, parent: 'Node | None' = None, leading_comments: 'list[str]' = , trailing_comments: 'list[str]' = , value: 'bool' = False)

Expand source code Browse git
@dataclass(repr=False, eq=False)
class JsBooleanLiteral(Expression):
    value: bool = False

Ancestors

Instance variables

var value

The type of the None singleton.

Inherited members

class JsNullLiteral (offset=-1, parent=None, leading_comments=<factory>, trailing_comments=<factory>)

JsNullLiteral(offset: 'int' = -1, parent: 'Node | None' = None, leading_comments: 'list[str]' = , trailing_comments: 'list[str]' = )

Expand source code Browse git
@dataclass(repr=False, eq=False)
class JsNullLiteral(Expression):
    @property
    def value(self):
        return None

Ancestors

Instance variables

var value
Expand source code Browse git
@property
def value(self):
    return None

Inherited members

class JsThisExpression (offset=-1, parent=None, leading_comments=<factory>, trailing_comments=<factory>)

JsThisExpression(offset: 'int' = -1, parent: 'Node | None' = None, leading_comments: 'list[str]' = , trailing_comments: 'list[str]' = )

Expand source code Browse git
@dataclass(repr=False, eq=False)
class JsThisExpression(Expression):
    pass

Ancestors

Inherited members

class JsArrayExpression (offset=-1, parent=None, leading_comments=<factory>, trailing_comments=<factory>, elements=<factory>, terminated=True)

JsArrayExpression(offset: 'int' = -1, parent: 'Node | None' = None, leading_comments: 'list[str]' = , trailing_comments: 'list[str]' = , elements: 'list[Expression | None]' = , terminated: 'bool' = True)

Expand source code Browse git
@dataclass(repr=False, eq=False)
class JsArrayExpression(Expression):
    elements: list[Expression | None] = field(default_factory=list)
    #: Whether the closing bracket was there; `file_ended_inside` says what its absence means.
    terminated: bool = True

Ancestors

Instance variables

var elements

The type of the None singleton.

var terminated

Whether the closing bracket was there; file_ended_inside() says what its absence means.

Inherited members

class JsObjectExpression (offset=-1, parent=None, leading_comments=<factory>, trailing_comments=<factory>, properties=<factory>, terminated=True)

JsObjectExpression(offset: 'int' = -1, parent: 'Node | None' = None, leading_comments: 'list[str]' = , trailing_comments: 'list[str]' = , properties: 'list[JsProperty | JsSpreadElement | JsErrorNode]' = , terminated: 'bool' = True)

Expand source code Browse git
@dataclass(repr=False, eq=False)
class JsObjectExpression(Expression):
    properties: list[JsProperty | JsSpreadElement | JsErrorNode] = field(default_factory=list)
    terminated: bool = True

Ancestors

Instance variables

var properties

The type of the None singleton.

var terminated

The type of the None singleton.

Inherited members

class JsProperty (offset=-1, parent=None, leading_comments=<factory>, trailing_comments=<factory>, key=None, value=None, computed=False, shorthand=False, method=False, kind=JsPropertyKind.INIT)

JsProperty(offset: 'int' = -1, parent: 'Node | None' = None, leading_comments: 'list[str]' = , trailing_comments: 'list[str]' = , key: 'Expression | None' = None, value: 'Expression | None' = None, computed: 'bool' = False, shorthand: 'bool' = False, method: 'bool' = False, kind: 'JsPropertyKind' = )

Expand source code Browse git
@dataclass(repr=False, eq=False)
class JsProperty(Node):
    key: Expression | None = None
    value: Expression | None = None
    computed: bool = False
    shorthand: bool = False
    method: bool = False
    kind: JsPropertyKind = JsPropertyKind.INIT

Ancestors

Instance variables

var key

The type of the None singleton.

var value

The type of the None singleton.

var computed

The type of the None singleton.

var shorthand

The type of the None singleton.

var method

The type of the None singleton.

var kind

The type of the None singleton.

Inherited members

class JsSpreadElement (offset=-1, parent=None, leading_comments=<factory>, trailing_comments=<factory>, argument=None)

JsSpreadElement(offset: 'int' = -1, parent: 'Node | None' = None, leading_comments: 'list[str]' = , trailing_comments: 'list[str]' = , argument: 'Expression | None' = None)

Expand source code Browse git
@dataclass(repr=False, eq=False)
class JsSpreadElement(Expression):
    argument: Expression | None = None

Ancestors

Instance variables

var argument

The type of the None singleton.

Inherited members

class JsFunctionExpression (offset=-1, parent=None, leading_comments=<factory>, trailing_comments=<factory>, id=None, params=<factory>, body=None, generator=False, is_async=False, source_text=None)

JsFunctionExpression(offset: 'int' = -1, parent: 'Node | None' = None, leading_comments: 'list[str]' = , trailing_comments: 'list[str]' = , id: 'JsIdentifier | None' = None, params: 'list[Expression]' = , body: 'JsBlockStatement | None' = None, generator: 'bool' = False, is_async: 'bool' = False, source_text: 'str | None' = None)

Expand source code Browse git
@dataclass(repr=False, eq=False)
class JsFunctionExpression(Expression, spelling='source_text'):
    id: JsIdentifier | None = None
    params: list[Expression] = field(default_factory=list)
    body: JsBlockStatement | None = None
    generator: bool = False
    is_async: bool = False
    source_text: str | None = None

Ancestors

Instance variables

var params

The type of the None singleton.

var id

The type of the None singleton.

var body

The type of the None singleton.

var generator

The type of the None singleton.

var is_async

The type of the None singleton.

var source_text

The type of the None singleton.

Inherited members

class JsArrowFunctionExpression (offset=-1, parent=None, leading_comments=<factory>, trailing_comments=<factory>, params=<factory>, body=None, is_async=False, source_text=None)

JsArrowFunctionExpression(offset: 'int' = -1, parent: 'Node | None' = None, leading_comments: 'list[str]' = , trailing_comments: 'list[str]' = , params: 'list[Expression]' = , body: 'Expression | JsBlockStatement | None' = None, is_async: 'bool' = False, source_text: 'str | None' = None)

Expand source code Browse git
@dataclass(repr=False, eq=False)
class JsArrowFunctionExpression(Expression, spelling='source_text'):
    params: list[Expression] = field(default_factory=list)
    body: Expression | JsBlockStatement | None = None
    is_async: bool = False
    source_text: str | None = None

Ancestors

Instance variables

var params

The type of the None singleton.

var body

The type of the None singleton.

var is_async

The type of the None singleton.

var source_text

The type of the None singleton.

Inherited members

class JsDecorator (offset=-1, parent=None, leading_comments=<factory>, trailing_comments=<factory>, expression=None)

JsDecorator(offset: 'int' = -1, parent: 'Node | None' = None, leading_comments: 'list[str]' = , trailing_comments: 'list[str]' = , expression: 'Expression | None' = None)

Expand source code Browse git
@dataclass(repr=False, eq=False)
class JsDecorator(Node):
    expression: Expression | None = None

Ancestors

Instance variables

var expression

The type of the None singleton.

Inherited members

class JsClassExpression (offset=-1, parent=None, leading_comments=<factory>, trailing_comments=<factory>, id=None, super_class=None, body=None, decorators=<factory>)

JsClassExpression(offset: 'int' = -1, parent: 'Node | None' = None, leading_comments: 'list[str]' = , trailing_comments: 'list[str]' = , id: 'JsIdentifier | None' = None, super_class: 'Expression | None' = None, body: 'JsClassBody | None' = None, decorators: 'list[JsDecorator]' = )

Expand source code Browse git
@dataclass(repr=False, eq=False)
class JsClassExpression(Expression):
    id: JsIdentifier | None = None
    super_class: Expression | None = None
    body: JsClassBody | None = None
    decorators: list[JsDecorator] = field(default_factory=list)

Ancestors

Instance variables

var decorators

The type of the None singleton.

var id

The type of the None singleton.

var super_class

The type of the None singleton.

var body

The type of the None singleton.

Inherited members

class JsUnaryExpression (offset=-1, parent=None, leading_comments=<factory>, trailing_comments=<factory>, operator='', operand=None, prefix=True)

JsUnaryExpression(offset: 'int' = -1, parent: 'Node | None' = None, leading_comments: 'list[str]' = , trailing_comments: 'list[str]' = , operator: 'str' = '', operand: 'Expression | None' = None, prefix: 'bool' = True)

Expand source code Browse git
@dataclass(repr=False, eq=False)
class JsUnaryExpression(Expression):
    operator: str = ''
    operand: Expression | None = None
    prefix: bool = True

Ancestors

Instance variables

var operator

The type of the None singleton.

var operand

The type of the None singleton.

var prefix

The type of the None singleton.

Inherited members

class JsUpdateExpression (offset=-1, parent=None, leading_comments=<factory>, trailing_comments=<factory>, operator='', argument=None, prefix=True)

JsUpdateExpression(offset: 'int' = -1, parent: 'Node | None' = None, leading_comments: 'list[str]' = , trailing_comments: 'list[str]' = , operator: 'str' = '', argument: 'Expression | None' = None, prefix: 'bool' = True)

Expand source code Browse git
@dataclass(repr=False, eq=False)
class JsUpdateExpression(Expression):
    operator: str = ''
    argument: Expression | None = None
    prefix: bool = True

Ancestors

Instance variables

var operator

The type of the None singleton.

var argument

The type of the None singleton.

var prefix

The type of the None singleton.

Inherited members

class JsBinaryExpression (offset=-1, parent=None, leading_comments=<factory>, trailing_comments=<factory>, left=None, operator='', right=None)

JsBinaryExpression(offset: 'int' = -1, parent: 'Node | None' = None, leading_comments: 'list[str]' = , trailing_comments: 'list[str]' = , left: 'Expression | None' = None, operator: 'str' = '', right: 'Expression | None' = None)

Expand source code Browse git
@dataclass(repr=False, eq=False)
class JsBinaryExpression(Expression):
    left: Expression | None = None
    operator: str = ''
    right: Expression | None = None

Ancestors

Instance variables

var left

The type of the None singleton.

var operator

The type of the None singleton.

var right

The type of the None singleton.

Inherited members

class JsLogicalExpression (offset=-1, parent=None, leading_comments=<factory>, trailing_comments=<factory>, left=None, operator='', right=None)

JsLogicalExpression(offset: 'int' = -1, parent: 'Node | None' = None, leading_comments: 'list[str]' = , trailing_comments: 'list[str]' = , left: 'Expression | None' = None, operator: 'str' = '', right: 'Expression | None' = None)

Expand source code Browse git
@dataclass(repr=False, eq=False)
class JsLogicalExpression(Expression):
    left: Expression | None = None
    operator: str = ''
    right: Expression | None = None

Ancestors

Instance variables

var left

The type of the None singleton.

var operator

The type of the None singleton.

var right

The type of the None singleton.

Inherited members

class JsAssignmentExpression (offset=-1, parent=None, leading_comments=<factory>, trailing_comments=<factory>, left=None, operator='=', right=None)

JsAssignmentExpression(offset: 'int' = -1, parent: 'Node | None' = None, leading_comments: 'list[str]' = , trailing_comments: 'list[str]' = , left: 'Expression | None' = None, operator: 'str' = '=', right: 'Expression | None' = None)

Expand source code Browse git
@dataclass(repr=False, eq=False)
class JsAssignmentExpression(Expression):
    left: Expression | None = None
    operator: str = '='
    right: Expression | None = None

Ancestors

Instance variables

var left

The type of the None singleton.

var operator

The type of the None singleton.

var right

The type of the None singleton.

Inherited members

class JsConditionalExpression (offset=-1, parent=None, leading_comments=<factory>, trailing_comments=<factory>, test=None, consequent=None, alternate=None)

JsConditionalExpression(offset: 'int' = -1, parent: 'Node | None' = None, leading_comments: 'list[str]' = , trailing_comments: 'list[str]' = , test: 'Expression | None' = None, consequent: 'Expression | None' = None, alternate: 'Expression | None' = None)

Expand source code Browse git
@dataclass(repr=False, eq=False)
class JsConditionalExpression(Expression):
    test: Expression | None = None
    consequent: Expression | None = None
    alternate: Expression | None = None

Ancestors

Instance variables

var test

The type of the None singleton.

var consequent

The type of the None singleton.

var alternate

The type of the None singleton.

Inherited members

class JsMemberExpression (offset=-1, parent=None, leading_comments=<factory>, trailing_comments=<factory>, object=None, property=None, computed=False, optional=False)

JsMemberExpression(offset: 'int' = -1, parent: 'Node | None' = None, leading_comments: 'list[str]' = , trailing_comments: 'list[str]' = , object: 'Expression | None' = None, property: 'Expression | None' = None, computed: 'bool' = False, optional: 'bool' = False)

Expand source code Browse git
@dataclass(repr=False, eq=False)
class JsMemberExpression(Expression):
    object: Expression | None = None
    property: Expression | None = None
    computed: bool = False
    optional: bool = False

Ancestors

Instance variables

var object

The type of the None singleton.

var property

The type of the None singleton.

var computed

The type of the None singleton.

var optional

The type of the None singleton.

Inherited members

class JsCallExpression (offset=-1, parent=None, leading_comments=<factory>, trailing_comments=<factory>, callee=None, arguments=<factory>, optional=False, terminated=True)

JsCallExpression(offset: 'int' = -1, parent: 'Node | None' = None, leading_comments: 'list[str]' = , trailing_comments: 'list[str]' = , callee: 'Expression | None' = None, arguments: 'list[Expression]' = , optional: 'bool' = False, terminated: 'bool' = True)

Expand source code Browse git
@dataclass(repr=False, eq=False)
class JsCallExpression(Expression):
    callee: Expression | None = None
    arguments: list[Expression] = field(default_factory=list)
    optional: bool = False
    terminated: bool = True

Ancestors

Instance variables

var arguments

The type of the None singleton.

var callee

The type of the None singleton.

var optional

The type of the None singleton.

var terminated

The type of the None singleton.

Inherited members

class JsNewExpression (offset=-1, parent=None, leading_comments=<factory>, trailing_comments=<factory>, callee=None, arguments=<factory>, terminated=True)

JsNewExpression(offset: 'int' = -1, parent: 'Node | None' = None, leading_comments: 'list[str]' = , trailing_comments: 'list[str]' = , callee: 'Expression | None' = None, arguments: 'list[Expression]' = , terminated: 'bool' = True)

Expand source code Browse git
@dataclass(repr=False, eq=False)
class JsNewExpression(Expression):
    callee: Expression | None = None
    arguments: list[Expression] = field(default_factory=list)
    terminated: bool = True

Ancestors

Instance variables

var arguments

The type of the None singleton.

var callee

The type of the None singleton.

var terminated

The type of the None singleton.

Inherited members

class JsSequenceExpression (offset=-1, parent=None, leading_comments=<factory>, trailing_comments=<factory>, expressions=<factory>)

JsSequenceExpression(offset: 'int' = -1, parent: 'Node | None' = None, leading_comments: 'list[str]' = , trailing_comments: 'list[str]' = , expressions: 'list[Expression]' = )

Expand source code Browse git
@dataclass(repr=False, eq=False)
class JsSequenceExpression(Expression):
    expressions: list[Expression] = field(default_factory=list)

Ancestors

Instance variables

var expressions

The type of the None singleton.

Inherited members

class JsYieldExpression (offset=-1, parent=None, leading_comments=<factory>, trailing_comments=<factory>, argument=None, delegate=False)

JsYieldExpression(offset: 'int' = -1, parent: 'Node | None' = None, leading_comments: 'list[str]' = , trailing_comments: 'list[str]' = , argument: 'Expression | None' = None, delegate: 'bool' = False)

Expand source code Browse git
@dataclass(repr=False, eq=False)
class JsYieldExpression(Expression):
    argument: Expression | None = None
    delegate: bool = False

Ancestors

Instance variables

var argument

The type of the None singleton.

var delegate

The type of the None singleton.

Inherited members

class JsAwaitExpression (offset=-1, parent=None, leading_comments=<factory>, trailing_comments=<factory>, argument=None)

JsAwaitExpression(offset: 'int' = -1, parent: 'Node | None' = None, leading_comments: 'list[str]' = , trailing_comments: 'list[str]' = , argument: 'Expression | None' = None)

Expand source code Browse git
@dataclass(repr=False, eq=False)
class JsAwaitExpression(Expression):
    argument: Expression | None = None

Ancestors

Instance variables

var argument

The type of the None singleton.

Inherited members

class JsTaggedTemplateExpression (offset=-1, parent=None, leading_comments=<factory>, trailing_comments=<factory>, tag=None, quasi=None)

JsTaggedTemplateExpression(offset: 'int' = -1, parent: 'Node | None' = None, leading_comments: 'list[str]' = , trailing_comments: 'list[str]' = , tag: 'Expression | None' = None, quasi: 'JsTemplateLiteral | None' = None)

Expand source code Browse git
@dataclass(repr=False, eq=False)
class JsTaggedTemplateExpression(Expression):
    tag: Expression | None = None
    quasi: JsTemplateLiteral | None = None

Ancestors

Instance variables

var tag

The type of the None singleton.

var quasi

The type of the None singleton.

Inherited members

class JsParenthesizedExpression (offset=-1, parent=None, leading_comments=<factory>, trailing_comments=<factory>, expression=None, terminated=True)

JsParenthesizedExpression(offset: 'int' = -1, parent: 'Node | None' = None, leading_comments: 'list[str]' = , trailing_comments: 'list[str]' = , expression: 'Expression | None' = None, terminated: 'bool' = True)

Expand source code Browse git
@dataclass(repr=False, eq=False)
class JsParenthesizedExpression(Expression):
    expression: Expression | None = None
    terminated: bool = True

Ancestors

Instance variables

var expression

The type of the None singleton.

var terminated

The type of the None singleton.

Inherited members

class JsArrayPattern (offset=-1, parent=None, leading_comments=<factory>, trailing_comments=<factory>, elements=<factory>, terminated=True)

JsArrayPattern(offset: 'int' = -1, parent: 'Node | None' = None, leading_comments: 'list[str]' = , trailing_comments: 'list[str]' = , elements: 'list[Expression | None]' = , terminated: 'bool' = True)

Expand source code Browse git
@dataclass(repr=False, eq=False)
class JsArrayPattern(Expression):
    elements: list[Expression | None] = field(default_factory=list)
    terminated: bool = True

Ancestors

Instance variables

var elements

The type of the None singleton.

var terminated

The type of the None singleton.

Inherited members

class JsObjectPattern (offset=-1, parent=None, leading_comments=<factory>, trailing_comments=<factory>, properties=<factory>, terminated=True)

JsObjectPattern(offset: 'int' = -1, parent: 'Node | None' = None, leading_comments: 'list[str]' = , trailing_comments: 'list[str]' = , properties: 'list[JsProperty | JsRestElement | JsErrorNode]' = , terminated: 'bool' = True)

Expand source code Browse git
@dataclass(repr=False, eq=False)
class JsObjectPattern(Expression):
    properties: list[JsProperty | JsRestElement | JsErrorNode] = field(default_factory=list)
    terminated: bool = True

Ancestors

Instance variables

var properties

The type of the None singleton.

var terminated

The type of the None singleton.

Inherited members

class JsAssignmentPattern (offset=-1, parent=None, leading_comments=<factory>, trailing_comments=<factory>, left=None, right=None)

JsAssignmentPattern(offset: 'int' = -1, parent: 'Node | None' = None, leading_comments: 'list[str]' = , trailing_comments: 'list[str]' = , left: 'Expression | None' = None, right: 'Expression | None' = None)

Expand source code Browse git
@dataclass(repr=False, eq=False)
class JsAssignmentPattern(Expression):
    left: Expression | None = None
    right: Expression | None = None

Ancestors

Instance variables

var left

The type of the None singleton.

var right

The type of the None singleton.

Inherited members

class JsRestElement (offset=-1, parent=None, leading_comments=<factory>, trailing_comments=<factory>, argument=None)

JsRestElement(offset: 'int' = -1, parent: 'Node | None' = None, leading_comments: 'list[str]' = , trailing_comments: 'list[str]' = , argument: 'Expression | None' = None)

Expand source code Browse git
@dataclass(repr=False, eq=False)
class JsRestElement(Expression):
    argument: Expression | None = None

Ancestors

Instance variables

var argument

The type of the None singleton.

Inherited members

class JsClassBody (offset=-1, parent=None, leading_comments=<factory>, trailing_comments=<factory>, body=<factory>, terminated=True)

JsClassBody(offset: 'int' = -1, parent: 'Node | None' = None, leading_comments: 'list[str]' = , trailing_comments: 'list[str]' = , body: 'list[JsMethodDefinition | JsPropertyDefinition | JsStaticBlock | JsErrorNode]' = , terminated: 'bool' = True)

Expand source code Browse git
@dataclass(repr=False, eq=False)
class JsClassBody(Node):
    body: list[JsMethodDefinition | JsPropertyDefinition | JsStaticBlock | JsErrorNode] = field(default_factory=list)
    #: Whether the closing brace was there. The file may end inside a class body, and a body the
    #: file ends inside prints the elements it holds and nothing after them.
    terminated: bool = True

Ancestors

Instance variables

var body

The type of the None singleton.

var terminated

Whether the closing brace was there. The file may end inside a class body, and a body the file ends inside prints the elements it holds and nothing after them.

Inherited members

class JsMethodDefinition (offset=-1, parent=None, leading_comments=<factory>, trailing_comments=<factory>, key=None, value=None, kind=JsMethodKind.METHOD, computed=False, is_static=False, decorators=<factory>)

JsMethodDefinition(offset: 'int' = -1, parent: 'Node | None' = None, leading_comments: 'list[str]' = , trailing_comments: 'list[str]' = , key: 'Expression | None' = None, value: 'JsFunctionExpression | None' = None, kind: 'JsMethodKind' = , computed: 'bool' = False, is_static: 'bool' = False, decorators: 'list[JsDecorator]' = )

Expand source code Browse git
@dataclass(repr=False, eq=False)
class JsMethodDefinition(Node):
    key: Expression | None = None
    value: JsFunctionExpression | None = None
    kind: JsMethodKind = JsMethodKind.METHOD
    computed: bool = False
    is_static: bool = False
    decorators: list[JsDecorator] = field(default_factory=list)

Ancestors

Instance variables

var decorators

The type of the None singleton.

var key

The type of the None singleton.

var value

The type of the None singleton.

var kind

The type of the None singleton.

var computed

The type of the None singleton.

var is_static

The type of the None singleton.

Inherited members

class JsPropertyDefinition (offset=-1, parent=None, leading_comments=<factory>, trailing_comments=<factory>, key=None, value=None, computed=False, is_static=False, decorators=<factory>)

JsPropertyDefinition(offset: 'int' = -1, parent: 'Node | None' = None, leading_comments: 'list[str]' = , trailing_comments: 'list[str]' = , key: 'Expression | None' = None, value: 'Expression | None' = None, computed: 'bool' = False, is_static: 'bool' = False, decorators: 'list[JsDecorator]' = )

Expand source code Browse git
@dataclass(repr=False, eq=False)
class JsPropertyDefinition(Node):
    key: Expression | None = None
    value: Expression | None = None
    computed: bool = False
    is_static: bool = False
    decorators: list[JsDecorator] = field(default_factory=list)

Ancestors

Instance variables

var decorators

The type of the None singleton.

var key

The type of the None singleton.

var value

The type of the None singleton.

var computed

The type of the None singleton.

var is_static

The type of the None singleton.

Inherited members

class JsStaticBlock (offset=-1, parent=None, leading_comments=<factory>, trailing_comments=<factory>, body=<factory>, terminated=True)

JsStaticBlock(offset: 'int' = -1, parent: 'Node | None' = None, leading_comments: 'list[str]' = , trailing_comments: 'list[str]' = , body: 'list[Statement]' = , terminated: 'bool' = True)

Expand source code Browse git
@dataclass(repr=False, eq=False)
class JsStaticBlock(Node):
    body: list[Statement] = field(default_factory=list)
    terminated: bool = True

Ancestors

Instance variables

var body

The type of the None singleton.

var terminated

The type of the None singleton.

Inherited members

class JsExpressionStatement (offset=-1, parent=None, leading_comments=<factory>, trailing_comments=<factory>, expression=None, directive=False)

JsExpressionStatement(offset: 'int' = -1, parent: 'Node | None' = None, leading_comments: 'list[str]' = , trailing_comments: 'list[str]' = , expression: 'Expression | None' = None, directive: 'bool' = False)

Expand source code Browse git
@dataclass(repr=False, eq=False)
class JsExpressionStatement(Statement, spelling='directive'):
    expression: Expression | None = None
    #: Whether the source wrote this statement into a Directive Prologue. It records where the
    #: statement came from and not what it computes, which is why it is a spelling field: two trees
    #: that differ only here spell the same program. A clone carries it, deliberately — a directive
    #: that is copied elsewhere was still written as one, and whether it *is* one is decided by
    #: `refinery.lib.scripts.js.strict.is_prologue_host` at wherever it now stands.
    directive: bool = False

Ancestors

Instance variables

var expression

The type of the None singleton.

var directive

Whether the source wrote this statement into a Directive Prologue. It records where the statement came from and not what it computes, which is why it is a spelling field: two trees that differ only here spell the same program. A clone carries it, deliberately — a directive that is copied elsewhere was still written as one, and whether it is one is decided by is_prologue_host() at wherever it now stands.

Inherited members

class JsBlockStatement (offset=-1, parent=None, leading_comments=<factory>, trailing_comments=<factory>, body=<factory>, terminated=True)

JsBlockStatement(offset: 'int' = -1, parent: 'Node | None' = None, leading_comments: 'list[str]' = , trailing_comments: 'list[str]' = , body: 'list[Statement]' = , terminated: 'bool' = True)

Expand source code Browse git
@dataclass(repr=False, eq=False)
class JsBlockStatement(Statement):
    body: list[Statement] = field(default_factory=list)
    #: Whether the closing brace was there. A file carved out of memory ends inside a block more
    #: often than anywhere else, and the block keeps the statements it holds and says that nothing
    #: closed it: printing what it holds and nothing after it is what keeps the file's end where the
    #: file had it. `early_errors` reports it.
    terminated: bool = True

Ancestors

Instance variables

var body

The type of the None singleton.

var terminated

Whether the closing brace was there. A file carved out of memory ends inside a block more often than anywhere else, and the block keeps the statements it holds and says that nothing closed it: printing what it holds and nothing after it is what keeps the file's end where the file had it. early_errors reports it.

Inherited members

class JsEmptyStatement (offset=-1, parent=None, leading_comments=<factory>, trailing_comments=<factory>)

JsEmptyStatement(offset: 'int' = -1, parent: 'Node | None' = None, leading_comments: 'list[str]' = , trailing_comments: 'list[str]' = )

Expand source code Browse git
@dataclass(repr=False, eq=False)
class JsEmptyStatement(Statement):
    pass

Ancestors

Inherited members

class JsVariableDeclaration (offset=-1, parent=None, leading_comments=<factory>, trailing_comments=<factory>, declarations=<factory>, kind=JsVarKind.VAR)

JsVariableDeclaration(offset: 'int' = -1, parent: 'Node | None' = None, leading_comments: 'list[str]' = , trailing_comments: 'list[str]' = , declarations: 'list[JsVariableDeclarator]' = , kind: 'JsVarKind' = )

Expand source code Browse git
@dataclass(repr=False, eq=False)
class JsVariableDeclaration(Statement):
    declarations: list[JsVariableDeclarator] = field(default_factory=list)
    kind: JsVarKind = JsVarKind.VAR

Ancestors

Instance variables

var declarations

The type of the None singleton.

var kind

The type of the None singleton.

Inherited members

class JsVariableDeclarator (offset=-1, parent=None, leading_comments=<factory>, trailing_comments=<factory>, id=None, init=None)

JsVariableDeclarator(offset: 'int' = -1, parent: 'Node | None' = None, leading_comments: 'list[str]' = , trailing_comments: 'list[str]' = , id: 'Expression | None' = None, init: 'Expression | None' = None)

Expand source code Browse git
@dataclass(repr=False, eq=False)
class JsVariableDeclarator(Node):
    id: Expression | None = None
    init: Expression | None = None

Ancestors

Instance variables

var id

The type of the None singleton.

var init

The type of the None singleton.

Inherited members

class JsIfStatement (offset=-1, parent=None, leading_comments=<factory>, trailing_comments=<factory>, test=None, consequent=None, alternate=None)

JsIfStatement(offset: 'int' = -1, parent: 'Node | None' = None, leading_comments: 'list[str]' = , trailing_comments: 'list[str]' = , test: 'Expression | None' = None, consequent: 'Statement | None' = None, alternate: 'Statement | None' = None)

Expand source code Browse git
@dataclass(repr=False, eq=False)
class JsIfStatement(Statement):
    test: Expression | None = None
    consequent: Statement | None = None
    alternate: Statement | None = None

Ancestors

Instance variables

var test

The type of the None singleton.

var consequent

The type of the None singleton.

var alternate

The type of the None singleton.

Inherited members

class JsWhileStatement (offset=-1, parent=None, leading_comments=<factory>, trailing_comments=<factory>, test=None, body=None)

JsWhileStatement(offset: 'int' = -1, parent: 'Node | None' = None, leading_comments: 'list[str]' = , trailing_comments: 'list[str]' = , test: 'Expression | None' = None, body: 'Statement | None' = None)

Expand source code Browse git
@dataclass(repr=False, eq=False)
class JsWhileStatement(Statement):
    test: Expression | None = None
    body: Statement | None = None

Ancestors

Instance variables

var test

The type of the None singleton.

var body

The type of the None singleton.

Inherited members

class JsDoWhileStatement (offset=-1, parent=None, leading_comments=<factory>, trailing_comments=<factory>, test=None, body=None)

JsDoWhileStatement(offset: 'int' = -1, parent: 'Node | None' = None, leading_comments: 'list[str]' = , trailing_comments: 'list[str]' = , test: 'Expression | None' = None, body: 'Statement | None' = None)

Expand source code Browse git
@dataclass(repr=False, eq=False)
class JsDoWhileStatement(Statement):
    test: Expression | None = None
    body: Statement | None = None

Ancestors

Instance variables

var test

The type of the None singleton.

var body

The type of the None singleton.

Inherited members

class JsForStatement (offset=-1, parent=None, leading_comments=<factory>, trailing_comments=<factory>, init=None, test=None, update=None, body=None)

JsForStatement(offset: 'int' = -1, parent: 'Node | None' = None, leading_comments: 'list[str]' = , trailing_comments: 'list[str]' = , init: 'Expression | Statement | None' = None, test: 'Expression | None' = None, update: 'Expression | None' = None, body: 'Statement | None' = None)

Expand source code Browse git
@dataclass(repr=False, eq=False)
class JsForStatement(Statement):
    init: Expression | Statement | None = None
    test: Expression | None = None
    update: Expression | None = None
    body: Statement | None = None

Ancestors

Instance variables

var init

The type of the None singleton.

var test

The type of the None singleton.

var update

The type of the None singleton.

var body

The type of the None singleton.

Inherited members

class JsForInStatement (offset=-1, parent=None, leading_comments=<factory>, trailing_comments=<factory>, left=None, right=None, body=None)

JsForInStatement(offset: 'int' = -1, parent: 'Node | None' = None, leading_comments: 'list[str]' = , trailing_comments: 'list[str]' = , left: 'Expression | Statement | None' = None, right: 'Expression | None' = None, body: 'Statement | None' = None)

Expand source code Browse git
@dataclass(repr=False, eq=False)
class JsForInStatement(Statement):
    left: Expression | Statement | None = None
    right: Expression | None = None
    body: Statement | None = None

Ancestors

Instance variables

var left

The type of the None singleton.

var right

The type of the None singleton.

var body

The type of the None singleton.

Inherited members

class JsForOfStatement (offset=-1, parent=None, leading_comments=<factory>, trailing_comments=<factory>, left=None, right=None, body=None, is_await=False)

JsForOfStatement(offset: 'int' = -1, parent: 'Node | None' = None, leading_comments: 'list[str]' = , trailing_comments: 'list[str]' = , left: 'Expression | Statement | None' = None, right: 'Expression | None' = None, body: 'Statement | None' = None, is_await: 'bool' = False)

Expand source code Browse git
@dataclass(repr=False, eq=False)
class JsForOfStatement(Statement):
    left: Expression | Statement | None = None
    right: Expression | None = None
    body: Statement | None = None
    is_await: bool = False

Ancestors

Instance variables

var left

The type of the None singleton.

var right

The type of the None singleton.

var body

The type of the None singleton.

var is_await

The type of the None singleton.

Inherited members

class JsSwitchStatement (offset=-1, parent=None, leading_comments=<factory>, trailing_comments=<factory>, discriminant=None, cases=<factory>, terminated=True)

JsSwitchStatement(offset: 'int' = -1, parent: 'Node | None' = None, leading_comments: 'list[str]' = , trailing_comments: 'list[str]' = , discriminant: 'Expression | None' = None, cases: 'list[JsSwitchCase | JsErrorNode]' = , terminated: 'bool' = True)

Expand source code Browse git
@dataclass(repr=False, eq=False)
class JsSwitchStatement(Statement):
    discriminant: Expression | None = None
    cases: list[JsSwitchCase | JsErrorNode] = field(default_factory=list)
    terminated: bool = True

Ancestors

Instance variables

var cases

The type of the None singleton.

var discriminant

The type of the None singleton.

var terminated

The type of the None singleton.

Inherited members

class JsSwitchCase (offset=-1, parent=None, leading_comments=<factory>, trailing_comments=<factory>, test=None, body=<factory>)

JsSwitchCase(offset: 'int' = -1, parent: 'Node | None' = None, leading_comments: 'list[str]' = , trailing_comments: 'list[str]' = , test: 'Expression | None' = None, body: 'list[Statement]' = )

Expand source code Browse git
@dataclass(repr=False, eq=False)
class JsSwitchCase(Node):
    test: Expression | None = None
    body: list[Statement] = field(default_factory=list)

Ancestors

Instance variables

var body

The type of the None singleton.

var test

The type of the None singleton.

Inherited members

class JsTryStatement (offset=-1, parent=None, leading_comments=<factory>, trailing_comments=<factory>, block=None, handler=None, finalizer=None)

JsTryStatement(offset: 'int' = -1, parent: 'Node | None' = None, leading_comments: 'list[str]' = , trailing_comments: 'list[str]' = , block: 'JsBlockStatement | None' = None, handler: 'JsCatchClause | None' = None, finalizer: 'JsBlockStatement | None' = None)

Expand source code Browse git
@dataclass(repr=False, eq=False)
class JsTryStatement(Statement):
    block: JsBlockStatement | None = None
    handler: JsCatchClause | None = None
    finalizer: JsBlockStatement | None = None

Ancestors

Instance variables

var block

The type of the None singleton.

var handler

The type of the None singleton.

var finalizer

The type of the None singleton.

Inherited members

class JsCatchClause (offset=-1, parent=None, leading_comments=<factory>, trailing_comments=<factory>, param=None, body=None)

JsCatchClause(offset: 'int' = -1, parent: 'Node | None' = None, leading_comments: 'list[str]' = , trailing_comments: 'list[str]' = , param: 'Expression | None' = None, body: 'JsBlockStatement | None' = None)

Expand source code Browse git
@dataclass(repr=False, eq=False)
class JsCatchClause(Node):
    param: Expression | None = None
    body: JsBlockStatement | None = None

Ancestors

Instance variables

var param

The type of the None singleton.

var body

The type of the None singleton.

Inherited members

class JsThrowStatement (offset=-1, parent=None, leading_comments=<factory>, trailing_comments=<factory>, argument=None)

JsThrowStatement(offset: 'int' = -1, parent: 'Node | None' = None, leading_comments: 'list[str]' = , trailing_comments: 'list[str]' = , argument: 'Expression | None' = None)

Expand source code Browse git
@dataclass(repr=False, eq=False)
class JsThrowStatement(Statement):
    argument: Expression | None = None

    def has_spelling(self) -> bool:
        """
        A `throw` takes its expression on the same line, and nothing else: the parser refuses a
        `throw` with nothing behind it, and this is what says so if a transform assembles one.
        """
        return self.argument is not None

Ancestors

Instance variables

var argument

The type of the None singleton.

Methods

def has_spelling(self)

A throw takes its expression on the same line, and nothing else: the parser refuses a throw with nothing behind it, and this is what says so if a transform assembles one.

Expand source code Browse git
def has_spelling(self) -> bool:
    """
    A `throw` takes its expression on the same line, and nothing else: the parser refuses a
    `throw` with nothing behind it, and this is what says so if a transform assembles one.
    """
    return self.argument is not None

Inherited members

class JsReturnStatement (offset=-1, parent=None, leading_comments=<factory>, trailing_comments=<factory>, argument=None)

JsReturnStatement(offset: 'int' = -1, parent: 'Node | None' = None, leading_comments: 'list[str]' = , trailing_comments: 'list[str]' = , argument: 'Expression | None' = None)

Expand source code Browse git
@dataclass(repr=False, eq=False)
class JsReturnStatement(Statement):
    argument: Expression | None = None

Ancestors

Instance variables

var argument

The type of the None singleton.

Inherited members

class JsBreakStatement (offset=-1, parent=None, leading_comments=<factory>, trailing_comments=<factory>, label=None)

JsBreakStatement(offset: 'int' = -1, parent: 'Node | None' = None, leading_comments: 'list[str]' = , trailing_comments: 'list[str]' = , label: 'JsIdentifier | None' = None)

Expand source code Browse git
@dataclass(repr=False, eq=False)
class JsBreakStatement(Statement):
    label: JsIdentifier | None = None

Ancestors

Instance variables

var label

The type of the None singleton.

Inherited members

class JsContinueStatement (offset=-1, parent=None, leading_comments=<factory>, trailing_comments=<factory>, label=None)

JsContinueStatement(offset: 'int' = -1, parent: 'Node | None' = None, leading_comments: 'list[str]' = , trailing_comments: 'list[str]' = , label: 'JsIdentifier | None' = None)

Expand source code Browse git
@dataclass(repr=False, eq=False)
class JsContinueStatement(Statement):
    label: JsIdentifier | None = None

Ancestors

Instance variables

var label

The type of the None singleton.

Inherited members

class JsLabeledStatement (offset=-1, parent=None, leading_comments=<factory>, trailing_comments=<factory>, label=None, body=None)

JsLabeledStatement(offset: 'int' = -1, parent: 'Node | None' = None, leading_comments: 'list[str]' = , trailing_comments: 'list[str]' = , label: 'JsIdentifier | None' = None, body: 'Statement | None' = None)

Expand source code Browse git
@dataclass(repr=False, eq=False)
class JsLabeledStatement(Statement):
    label: JsIdentifier | None = None
    body: Statement | None = None

Ancestors

Instance variables

var label

The type of the None singleton.

var body

The type of the None singleton.

Inherited members

class JsWithStatement (offset=-1, parent=None, leading_comments=<factory>, trailing_comments=<factory>, object=None, body=None)

JsWithStatement(offset: 'int' = -1, parent: 'Node | None' = None, leading_comments: 'list[str]' = , trailing_comments: 'list[str]' = , object: 'Expression | None' = None, body: 'Statement | None' = None)

Expand source code Browse git
@dataclass(repr=False, eq=False)
class JsWithStatement(Statement):
    object: Expression | None = None
    body: Statement | None = None

Ancestors

Instance variables

var object

The type of the None singleton.

var body

The type of the None singleton.

Inherited members

class JsDebuggerStatement (offset=-1, parent=None, leading_comments=<factory>, trailing_comments=<factory>)

JsDebuggerStatement(offset: 'int' = -1, parent: 'Node | None' = None, leading_comments: 'list[str]' = , trailing_comments: 'list[str]' = )

Expand source code Browse git
@dataclass(repr=False, eq=False)
class JsDebuggerStatement(Statement):
    pass

Ancestors

Inherited members

class JsFunctionDeclaration (offset=-1, parent=None, leading_comments=<factory>, trailing_comments=<factory>, id=None, params=<factory>, body=None, generator=False, is_async=False, source_text=None)

JsFunctionDeclaration(offset: 'int' = -1, parent: 'Node | None' = None, leading_comments: 'list[str]' = , trailing_comments: 'list[str]' = , id: 'JsIdentifier | None' = None, params: 'list[Expression]' = , body: 'JsBlockStatement | None' = None, generator: 'bool' = False, is_async: 'bool' = False, source_text: 'str | None' = None)

Expand source code Browse git
@dataclass(repr=False, eq=False)
class JsFunctionDeclaration(Statement, spelling='source_text'):
    id: JsIdentifier | None = None
    params: list[Expression] = field(default_factory=list)
    body: JsBlockStatement | None = None
    generator: bool = False
    is_async: bool = False
    source_text: str | None = None

Ancestors

Instance variables

var params

The type of the None singleton.

var id

The type of the None singleton.

var body

The type of the None singleton.

var generator

The type of the None singleton.

var is_async

The type of the None singleton.

var source_text

The type of the None singleton.

Inherited members

class JsClassDeclaration (offset=-1, parent=None, leading_comments=<factory>, trailing_comments=<factory>, id=None, super_class=None, body=None, decorators=<factory>)

JsClassDeclaration(offset: 'int' = -1, parent: 'Node | None' = None, leading_comments: 'list[str]' = , trailing_comments: 'list[str]' = , id: 'JsIdentifier | None' = None, super_class: 'Expression | None' = None, body: 'JsClassBody | None' = None, decorators: 'list[JsDecorator]' = )

Expand source code Browse git
@dataclass(repr=False, eq=False)
class JsClassDeclaration(Statement):
    id: JsIdentifier | None = None
    super_class: Expression | None = None
    body: JsClassBody | None = None
    decorators: list[JsDecorator] = field(default_factory=list)

Ancestors

Instance variables

var decorators

The type of the None singleton.

var id

The type of the None singleton.

var super_class

The type of the None singleton.

var body

The type of the None singleton.

Inherited members

class JsImportSpecifier (offset=-1, parent=None, leading_comments=<factory>, trailing_comments=<factory>, imported=None, local=None)

JsImportSpecifier(offset: 'int' = -1, parent: 'Node | None' = None, leading_comments: 'list[str]' = , trailing_comments: 'list[str]' = , imported: 'Expression | None' = None, local: 'Expression | None' = None)

Expand source code Browse git
@dataclass(repr=False, eq=False)
class JsImportSpecifier(Node):
    imported: Expression | None = None
    local: Expression | None = None

Ancestors

Instance variables

var imported

The type of the None singleton.

var local

The type of the None singleton.

Inherited members

class JsImportDefaultSpecifier (offset=-1, parent=None, leading_comments=<factory>, trailing_comments=<factory>, local=None)

JsImportDefaultSpecifier(offset: 'int' = -1, parent: 'Node | None' = None, leading_comments: 'list[str]' = , trailing_comments: 'list[str]' = , local: 'Expression | None' = None)

Expand source code Browse git
@dataclass(repr=False, eq=False)
class JsImportDefaultSpecifier(Node):
    local: Expression | None = None

Ancestors

Instance variables

var local

The type of the None singleton.

Inherited members

class JsImportNamespaceSpecifier (offset=-1, parent=None, leading_comments=<factory>, trailing_comments=<factory>, local=None)

JsImportNamespaceSpecifier(offset: 'int' = -1, parent: 'Node | None' = None, leading_comments: 'list[str]' = , trailing_comments: 'list[str]' = , local: 'Expression | None' = None)

Expand source code Browse git
@dataclass(repr=False, eq=False)
class JsImportNamespaceSpecifier(Node):
    local: Expression | None = None

Ancestors

Instance variables

var local

The type of the None singleton.

Inherited members

class JsImportAttribute (offset=-1, parent=None, leading_comments=<factory>, trailing_comments=<factory>, key=None, value=None)

JsImportAttribute(offset: 'int' = -1, parent: 'Node | None' = None, leading_comments: 'list[str]' = , trailing_comments: 'list[str]' = , key: 'Expression | None' = None, value: 'Expression | None' = None)

Expand source code Browse git
@dataclass(repr=False, eq=False)
class JsImportAttribute(Node):
    key: Expression | None = None
    value: Expression | None = None

Ancestors

Instance variables

var key

The type of the None singleton.

var value

The type of the None singleton.

Inherited members

class JsImportDeclaration (offset=-1, parent=None, leading_comments=<factory>, trailing_comments=<factory>, specifiers=<factory>, source=None, attributes=<factory>, attributes_keyword='')

JsImportDeclaration(offset: 'int' = -1, parent: 'Node | None' = None, leading_comments: 'list[str]' = , trailing_comments: 'list[str]' = , specifiers: 'list[JsImportSpecifier | JsImportDefaultSpecifier | JsImportNamespaceSpecifier | JsErrorNode]' = , source: 'JsStringLiteral | None' = None, attributes: 'list[JsImportAttribute | JsErrorNode]' = , attributes_keyword: 'str' = '')

Expand source code Browse git
@dataclass(repr=False, eq=False)
class JsImportDeclaration(Statement):
    specifiers: list[
        JsImportSpecifier | JsImportDefaultSpecifier | JsImportNamespaceSpecifier | JsErrorNode
    ] = field(default_factory=list)
    source: JsStringLiteral | None = None
    attributes: list[JsImportAttribute | JsErrorNode] = field(default_factory=list)
    attributes_keyword: str = ''

Ancestors

Instance variables

var specifiers

The type of the None singleton.

var attributes

The type of the None singleton.

var source

The type of the None singleton.

var attributes_keyword

The type of the None singleton.

Inherited members

class JsImportExpression (offset=-1, parent=None, leading_comments=<factory>, trailing_comments=<factory>, source=None, options=None)

JsImportExpression(offset: 'int' = -1, parent: 'Node | None' = None, leading_comments: 'list[str]' = , trailing_comments: 'list[str]' = , source: 'Expression | None' = None, options: 'Expression | None' = None)

Expand source code Browse git
@dataclass(repr=False, eq=False)
class JsImportExpression(Expression):
    source: Expression | None = None
    options: Expression | None = None

Ancestors

Instance variables

var source

The type of the None singleton.

var options

The type of the None singleton.

Inherited members

class JsMetaProperty (offset=-1, parent=None, leading_comments=<factory>, trailing_comments=<factory>, meta='', property='')

JsMetaProperty(offset: 'int' = -1, parent: 'Node | None' = None, leading_comments: 'list[str]' = , trailing_comments: 'list[str]' = , meta: 'str' = '', property: 'str' = '')

Expand source code Browse git
@dataclass(repr=False, eq=False)
class JsMetaProperty(Expression):
    meta: str = ''
    property: str = ''

Ancestors

Instance variables

var meta

The type of the None singleton.

var property

The type of the None singleton.

Inherited members

class JsExportSpecifier (offset=-1, parent=None, leading_comments=<factory>, trailing_comments=<factory>, local=None, exported=None)

JsExportSpecifier(offset: 'int' = -1, parent: 'Node | None' = None, leading_comments: 'list[str]' = , trailing_comments: 'list[str]' = , local: 'Expression | None' = None, exported: 'Expression | None' = None)

Expand source code Browse git
@dataclass(repr=False, eq=False)
class JsExportSpecifier(Node):
    local: Expression | None = None
    exported: Expression | None = None

Ancestors

Instance variables

var local

The type of the None singleton.

var exported

The type of the None singleton.

Inherited members

class JsExportNamedDeclaration (offset=-1, parent=None, leading_comments=<factory>, trailing_comments=<factory>, declaration=None, specifiers=<factory>, source=None, attributes=<factory>, attributes_keyword='')

JsExportNamedDeclaration(offset: 'int' = -1, parent: 'Node | None' = None, leading_comments: 'list[str]' = , trailing_comments: 'list[str]' = , declaration: 'Statement | None' = None, specifiers: 'list[JsExportSpecifier | JsErrorNode]' = , source: 'JsStringLiteral | None' = None, attributes: 'list[JsImportAttribute | JsErrorNode]' = , attributes_keyword: 'str' = '')

Expand source code Browse git
@dataclass(repr=False, eq=False)
class JsExportNamedDeclaration(Statement):
    declaration: Statement | None = None
    specifiers: list[JsExportSpecifier | JsErrorNode] = field(default_factory=list)
    source: JsStringLiteral | None = None
    attributes: list[JsImportAttribute | JsErrorNode] = field(default_factory=list)
    attributes_keyword: str = ''

Ancestors

Instance variables

var specifiers

The type of the None singleton.

var attributes

The type of the None singleton.

var declaration

The type of the None singleton.

var source

The type of the None singleton.

var attributes_keyword

The type of the None singleton.

Inherited members

class JsExportDefaultDeclaration (offset=-1, parent=None, leading_comments=<factory>, trailing_comments=<factory>, declaration=None)

JsExportDefaultDeclaration(offset: 'int' = -1, parent: 'Node | None' = None, leading_comments: 'list[str]' = , trailing_comments: 'list[str]' = , declaration: 'Expression | Statement | None' = None)

Expand source code Browse git
@dataclass(repr=False, eq=False)
class JsExportDefaultDeclaration(Statement):
    declaration: Expression | Statement | None = None

Ancestors

Instance variables

var declaration

The type of the None singleton.

Inherited members

class JsExportAllDeclaration (offset=-1, parent=None, leading_comments=<factory>, trailing_comments=<factory>, source=None, exported=None, attributes=<factory>, attributes_keyword='')

JsExportAllDeclaration(offset: 'int' = -1, parent: 'Node | None' = None, leading_comments: 'list[str]' = , trailing_comments: 'list[str]' = , source: 'JsStringLiteral | None' = None, exported: 'Expression | None' = None, attributes: 'list[JsImportAttribute | JsErrorNode]' = , attributes_keyword: 'str' = '')

Expand source code Browse git
@dataclass(repr=False, eq=False)
class JsExportAllDeclaration(Statement):
    source: JsStringLiteral | None = None
    exported: Expression | None = None
    attributes: list[JsImportAttribute | JsErrorNode] = field(default_factory=list)
    attributes_keyword: str = ''

Ancestors

Instance variables

var attributes

The type of the None singleton.

var source

The type of the None singleton.

var exported

The type of the None singleton.

var attributes_keyword

The type of the None singleton.

Inherited members

class JsScript (offset=-1, parent=None, leading_comments=<factory>, trailing_comments=<factory>, body=<factory>, module=False, recovered=False, html_comment=None, terminated=True)

JsScript(offset: 'int' = -1, parent: 'Node | None' = None, leading_comments: 'list[str]' = , trailing_comments: 'list[str]' = , body: 'list[Statement]' = , module: 'bool' = False, recovered: 'bool' = False, html_comment: 'int | None' = None, terminated: 'bool' = True)

Expand source code Browse git
@dataclass(repr=False, eq=False)
class JsScript(Statement, spelling=('module', 'recovered', 'html_comment')):
    body: list[Statement] = field(default_factory=list)
    #: Whether the source is module code, which the host decides (§16.1) and the syntax only reports:
    #: an `import` or `export` declaration, or `import.meta`, can appear in nothing else. It is a
    #: spelling field because two scripts differing only here hold the same text — what differs is how
    #: a host loads it — and because a pass that cuts the last import out of a body does not turn a
    #: module into a script.
    module: bool = False
    #: Whether the parser had to invent a token, or step over one, in order to read this file. It is
    #: a spelling field for the reason `module` is: it records where the tree came from rather than
    #: what it spells, so two scripts differing only here are the same program. Nothing ever clears
    #: it, because no later pass can put back a token the source never held.
    recovered: bool = False
    #: The offset of the first HTML-like comment delimiter the lexer read in this file (§B.1.1), or
    #: `None` where it read none. Module code refuses both delimiters, and the comment one opens may
    #: stand where no statement carries it, so the fact is kept on the script rather than looked for
    #: among the comments the tree holds. It is a spelling field for the reason `recovered` is.
    html_comment: int | None = None
    #: Whether the file ended outside every comment. A block comment the file ends inside runs to
    #: the end of the file, is the last of the file's `trailing_comments`, and makes the file no
    #: program, which `early_errors` reports.
    terminated: bool = True

    def is_recovered(self) -> bool:
        return self.recovered

    def early_errors(self) -> list[StrictViolation]:
        """
        What the language refuses in the file under its own mode and goal: the early errors
        `refinery.lib.scripts.js.strict.collect_strict_violations` reports over the tree read as a
        script, or as a module where the file spells module syntax, and every construct the file
        ended inside — a literal, a comment, a block, a class body or a switch — which an engine
        refuses as an unexpected end of input.
        """
        from refinery.lib.scripts.js.strict import collect_strict_violations
        return collect_strict_violations(self, module=self.module)

Ancestors

Instance variables

var body

The type of the None singleton.

var module

Whether the source is module code, which the host decides (§16.1) and the syntax only reports: an import or export declaration, or import.meta, can appear in nothing else. It is a spelling field because two scripts differing only here hold the same text — what differs is how a host loads it — and because a pass that cuts the last import out of a body does not turn a module into a script.

var recovered

Whether the parser had to invent a token, or step over one, in order to read this file. It is a spelling field for the reason module is: it records where the tree came from rather than what it spells, so two scripts differing only here are the same program. Nothing ever clears it, because no later pass can put back a token the source never held.

var html_comment

The offset of the first HTML-like comment delimiter the lexer read in this file (§B.1.1), or None where it read none. Module code refuses both delimiters, and the comment one opens may stand where no statement carries it, so the fact is kept on the script rather than looked for among the comments the tree holds. It is a spelling field for the reason recovered is.

var terminated

Whether the file ended outside every comment. A block comment the file ends inside runs to the end of the file, is the last of the file's trailing_comments, and makes the file no program, which early_errors reports.

Methods

def early_errors(self)

What the language refuses in the file under its own mode and goal: the early errors collect_strict_violations() reports over the tree read as a script, or as a module where the file spells module syntax, and every construct the file ended inside — a literal, a comment, a block, a class body or a switch — which an engine refuses as an unexpected end of input.

Expand source code Browse git
def early_errors(self) -> list[StrictViolation]:
    """
    What the language refuses in the file under its own mode and goal: the early errors
    `refinery.lib.scripts.js.strict.collect_strict_violations` reports over the tree read as a
    script, or as a module where the file spells module syntax, and every construct the file
    ended inside — a literal, a comment, a block, a class body or a switch — which an engine
    refuses as an unexpected end of input.
    """
    from refinery.lib.scripts.js.strict import collect_strict_violations
    return collect_strict_violations(self, module=self.module)

Inherited members

class AwaitReading (*args, **kwds)

What the word await is where a piece of code stands. It names a binding or a reference in a script and in every function that is not async; it is the operator in an async function's parameter list and body; and it is neither in a class static block or a static field initializer, where the language refuses the word in every position.

Expand source code Browse git
class AwaitReading(enum.Enum):
    """
    What the word `await` is where a piece of code stands. It names a binding or a reference in a
    script and in every function that is not `async`; it is the operator in an `async` function's
    parameter list and body; and it is neither in a class static block or a static field
    initializer, where the language refuses the word in every position.
    """
    NAME = enum.auto()
    OPERATOR = enum.auto()
    RESERVED = enum.auto()

Ancestors

  • enum.Enum

Class variables

var NAME

The type of the None singleton.

var OPERATOR

The type of the None singleton.

var RESERVED

The type of the None singleton.

class CodeContext (await_reading, yield_is_operator, arguments_reserved, top_level)

The grammar parameters a piece of code is read under: what await is, whether yield is the operator rather than a name, whether arguments may be referred to at all, and whether the code is the top level of a file. The parser descends it and the early-error collector threads it, both through code_context_within(), so that the tree a text is read into and the errors reported over that tree agree on what every word is.

The top level is set apart because its goal symbol is not known while parsing: await is read as a name there, as a script reads it, but a for await head is read as well, since that is one way a module spells its top-level await. Below a function or a class element the goal decides nothing, and a for await head stands only where await is the operator.

Expand source code Browse git
@dataclass(frozen=True)
class CodeContext:
    """
    The grammar parameters a piece of code is read under: what `await` is, whether `yield` is the
    operator rather than a name, whether `arguments` may be referred to at all, and whether the
    code is the top level of a file. The parser descends it and the early-error collector threads
    it, both through `code_context_within`, so that the tree a text is read into and the errors
    reported over that tree agree on what every word is.

    The top level is set apart because its goal symbol is not known while parsing: `await` is read
    as a name there, as a script reads it, but a `for await` head is read as well, since that is
    one way a module spells its top-level `await`. Below a function or a class element the goal
    decides nothing, and a `for await` head stands only where `await` is the operator.
    """
    await_reading: AwaitReading
    yield_is_operator: bool
    arguments_reserved: bool
    top_level: bool

    @property
    def reads_for_await(self) -> bool:
        """
        Whether a `for await` head is read here: where `await` is the operator, and at the top
        level of a file, whose goal may make it one.
        """
        return self.await_reading is AwaitReading.OPERATOR or self.top_level

Instance variables

var await_reading

The type of the None singleton.

var yield_is_operator

The type of the None singleton.

var arguments_reserved

The type of the None singleton.

var top_level

The type of the None singleton.

var reads_for_await

Whether a for await head is read here: where await is the operator, and at the top level of a file, whose goal may make it one.

Expand source code Browse git
@property
def reads_for_await(self) -> bool:
    """
    Whether a `for await` head is read here: where `await` is the operator, and at the top
    level of a file, whose goal may make it one.
    """
    return self.await_reading is AwaitReading.OPERATOR or self.top_level