UNPKG

coffeescript

Version:
1,232 lines (1,122 loc) 327 kB
// Generated by CoffeeScript 2.7.0 (function() { // `nodes.coffee` contains all of the node classes for the syntax tree. Most // nodes are created as the result of actions in the [grammar](grammar.html), // but some are created by other nodes as a method of code generation. To convert // the syntax tree into a string of JavaScript code, call `compile()` on the root. var Access, Arr, Assign, AwaitReturn, Base, Block, BooleanLiteral, Call, Catch, Class, ClassProperty, ClassPrototypeProperty, Code, CodeFragment, ComputedPropertyName, DefaultLiteral, Directive, DynamicImport, DynamicImportCall, Elision, EmptyInterpolation, ExecutableClassBody, Existence, Expansion, ExportAllDeclaration, ExportDeclaration, ExportDefaultDeclaration, ExportNamedDeclaration, ExportSpecifier, ExportSpecifierList, Extends, For, FuncDirectiveReturn, FuncGlyph, HEREGEX_OMIT, HereComment, HoistTarget, IdentifierLiteral, If, ImportClause, ImportDeclaration, ImportDefaultSpecifier, ImportNamespaceSpecifier, ImportSpecifier, ImportSpecifierList, In, Index, InfinityLiteral, Interpolation, JSXAttribute, JSXAttributes, JSXElement, JSXEmptyExpression, JSXExpressionContainer, JSXIdentifier, JSXNamespacedName, JSXTag, JSXText, JS_FORBIDDEN, LEADING_BLANK_LINE, LEVEL_ACCESS, LEVEL_COND, LEVEL_LIST, LEVEL_OP, LEVEL_PAREN, LEVEL_TOP, LineComment, Literal, MetaProperty, ModuleDeclaration, ModuleSpecifier, ModuleSpecifierList, NEGATE, NO, NaNLiteral, NullLiteral, NumberLiteral, Obj, ObjectProperty, Op, Param, Parens, PassthroughLiteral, PropertyName, Range, RegexLiteral, RegexWithInterpolations, Return, Root, SIMPLENUM, SIMPLE_STRING_OMIT, STRING_OMIT, Scope, Sequence, Slice, Splat, StatementLiteral, StringLiteral, StringWithInterpolations, Super, SuperCall, Switch, SwitchCase, SwitchWhen, TAB, THIS, TRAILING_BLANK_LINE, TaggedTemplateCall, TemplateElement, ThisLiteral, Throw, Try, UTILITIES, UndefinedLiteral, Value, While, YES, YieldReturn, addDataToNode, astAsBlockIfNeeded, attachCommentsToNode, compact, del, emptyExpressionLocationData, ends, extend, extractSameLineLocationDataFirst, extractSameLineLocationDataLast, flatten, fragmentsToText, greater, hasLineComments, indentInitial, isAstLocGreater, isFunction, isLiteralArguments, isLiteralThis, isLocationDataEndGreater, isLocationDataStartGreater, isNumber, isPlainObject, isUnassignable, jisonLocationDataToAstLocationData, lesser, locationDataToString, makeDelimitedLiteral, merge, mergeAstLocationData, mergeLocationData, moveComments, multident, parseNumber, replaceUnicodeCodePointEscapes, shouldCacheOrIsAssignable, sniffDirectives, some, starts, throwSyntaxError, unfoldSoak, unshiftAfterComments, utility, zeroWidthLocationDataFromEndLocation, indexOf = [].indexOf, splice = [].splice, slice1 = [].slice; Error.stackTraceLimit = 2e308; ({Scope} = require('./scope')); ({isUnassignable, JS_FORBIDDEN} = require('./lexer')); // Import the helpers we plan to use. ({compact, flatten, extend, merge, del, starts, ends, some, addDataToNode, attachCommentsToNode, locationDataToString, throwSyntaxError, replaceUnicodeCodePointEscapes, isFunction, isPlainObject, isNumber, parseNumber} = require('./helpers')); // Functions required by parser. exports.extend = extend; exports.addDataToNode = addDataToNode; // Constant functions for nodes that don’t need customization. YES = function() { return true; }; NO = function() { return false; }; THIS = function() { return this; }; NEGATE = function() { this.negated = !this.negated; return this; }; //### CodeFragment // The various nodes defined below all compile to a collection of **CodeFragment** objects. // A CodeFragments is a block of generated code, and the location in the source file where the code // came from. CodeFragments can be assembled together into working code just by catting together // all the CodeFragments' `code` snippets, in order. exports.CodeFragment = CodeFragment = class CodeFragment { constructor(parent, code) { var ref1; this.code = `${code}`; this.type = (parent != null ? (ref1 = parent.constructor) != null ? ref1.name : void 0 : void 0) || 'unknown'; this.locationData = parent != null ? parent.locationData : void 0; this.comments = parent != null ? parent.comments : void 0; } toString() { // This is only intended for debugging. return `${this.code}${this.locationData ? ": " + locationDataToString(this.locationData) : ''}`; } }; // Convert an array of CodeFragments into a string. fragmentsToText = function(fragments) { var fragment; return ((function() { var j, len1, results1; results1 = []; for (j = 0, len1 = fragments.length; j < len1; j++) { fragment = fragments[j]; results1.push(fragment.code); } return results1; })()).join(''); }; //### Base // The **Base** is the abstract base class for all nodes in the syntax tree. // Each subclass implements the `compileNode` method, which performs the // code generation for that node. To compile a node to JavaScript, // call `compile` on it, which wraps `compileNode` in some generic extra smarts, // to know when the generated code needs to be wrapped up in a closure. // An options hash is passed and cloned throughout, containing information about // the environment from higher in the tree (such as if a returned value is // being requested by the surrounding function), information about the current // scope, and indentation level. exports.Base = Base = (function() { class Base { compile(o, lvl) { return fragmentsToText(this.compileToFragments(o, lvl)); } // Occasionally a node is compiled multiple times, for example to get the name // of a variable to add to scope tracking. When we know that a “premature” // compilation won’t result in comments being output, set those comments aside // so that they’re preserved for a later `compile` call that will result in // the comments being included in the output. compileWithoutComments(o, lvl, method = 'compile') { var fragments, unwrapped; if (this.comments) { this.ignoreTheseCommentsTemporarily = this.comments; delete this.comments; } unwrapped = this.unwrapAll(); if (unwrapped.comments) { unwrapped.ignoreTheseCommentsTemporarily = unwrapped.comments; delete unwrapped.comments; } fragments = this[method](o, lvl); if (this.ignoreTheseCommentsTemporarily) { this.comments = this.ignoreTheseCommentsTemporarily; delete this.ignoreTheseCommentsTemporarily; } if (unwrapped.ignoreTheseCommentsTemporarily) { unwrapped.comments = unwrapped.ignoreTheseCommentsTemporarily; delete unwrapped.ignoreTheseCommentsTemporarily; } return fragments; } compileNodeWithoutComments(o, lvl) { return this.compileWithoutComments(o, lvl, 'compileNode'); } // Common logic for determining whether to wrap this node in a closure before // compiling it, or to compile directly. We need to wrap if this node is a // *statement*, and it's not a *pureStatement*, and we're not at // the top level of a block (which would be unnecessary), and we haven't // already been asked to return the result (because statements know how to // return results). compileToFragments(o, lvl) { var fragments, node; o = extend({}, o); if (lvl) { o.level = lvl; } node = this.unfoldSoak(o) || this; node.tab = o.indent; fragments = o.level === LEVEL_TOP || !node.isStatement(o) ? node.compileNode(o) : node.compileClosure(o); this.compileCommentFragments(o, node, fragments); return fragments; } compileToFragmentsWithoutComments(o, lvl) { return this.compileWithoutComments(o, lvl, 'compileToFragments'); } // Statements converted into expressions via closure-wrapping share a scope // object with their parent closure, to preserve the expected lexical scope. compileClosure(o) { var args, argumentsNode, func, meth, parts, ref1, ref2; this.checkForPureStatementInExpression(); o.sharedScope = true; func = new Code([], Block.wrap([this])); args = []; if (this.contains((function(node) { return node instanceof SuperCall; }))) { func.bound = true; } else if ((argumentsNode = this.contains(isLiteralArguments)) || this.contains(isLiteralThis)) { args = [new ThisLiteral()]; if (argumentsNode) { meth = 'apply'; args.push(new IdentifierLiteral('arguments')); } else { meth = 'call'; } func = new Value(func, [new Access(new PropertyName(meth))]); } parts = (new Call(func, args)).compileNode(o); switch (false) { case !(func.isGenerator || ((ref1 = func.base) != null ? ref1.isGenerator : void 0)): parts.unshift(this.makeCode("(yield* ")); parts.push(this.makeCode(")")); break; case !(func.isAsync || ((ref2 = func.base) != null ? ref2.isAsync : void 0)): parts.unshift(this.makeCode("(await ")); parts.push(this.makeCode(")")); } return parts; } compileCommentFragments(o, node, fragments) { var base1, base2, comment, commentFragment, j, len1, ref1, unshiftCommentFragment; if (!node.comments) { return fragments; } // This is where comments, that are attached to nodes as a `comments` // property, become `CodeFragment`s. “Inline block comments,” e.g. // `/* */`-delimited comments that are interspersed within code on a line, // are added to the current `fragments` stream. All other fragments are // attached as properties to the nearest preceding or following fragment, // to remain stowaways until they get properly output in `compileComments` // later on. unshiftCommentFragment = function(commentFragment) { var precedingFragment; if (commentFragment.unshift) { // Find the first non-comment fragment and insert `commentFragment` // before it. return unshiftAfterComments(fragments, commentFragment); } else { if (fragments.length !== 0) { precedingFragment = fragments[fragments.length - 1]; if (commentFragment.newLine && precedingFragment.code !== '' && !/\n\s*$/.test(precedingFragment.code)) { commentFragment.code = `\n${commentFragment.code}`; } } return fragments.push(commentFragment); } }; ref1 = node.comments; for (j = 0, len1 = ref1.length; j < len1; j++) { comment = ref1[j]; if (!(indexOf.call(this.compiledComments, comment) < 0)) { continue; } this.compiledComments.push(comment); // Don’t output this comment twice. // For block/here comments, denoted by `###`, that are inline comments // like `1 + ### comment ### 2`, create fragments and insert them into // the fragments array. // Otherwise attach comment fragments to their closest fragment for now, // so they can be inserted into the output later after all the newlines // have been added. if (comment.here) { // Block comment, delimited by `###`. commentFragment = new HereComment(comment).compileNode(o); // Line comment, delimited by `#`. } else { commentFragment = new LineComment(comment).compileNode(o); } if ((commentFragment.isHereComment && !commentFragment.newLine) || node.includeCommentFragments()) { // Inline block comments, like `1 + /* comment */ 2`, or a node whose // `compileToFragments` method has logic for outputting comments. unshiftCommentFragment(commentFragment); } else { if (fragments.length === 0) { fragments.push(this.makeCode('')); } if (commentFragment.unshift) { if ((base1 = fragments[0]).precedingComments == null) { base1.precedingComments = []; } fragments[0].precedingComments.push(commentFragment); } else { if ((base2 = fragments[fragments.length - 1]).followingComments == null) { base2.followingComments = []; } fragments[fragments.length - 1].followingComments.push(commentFragment); } } } return fragments; } // If the code generation wishes to use the result of a complex expression // in multiple places, ensure that the expression is only ever evaluated once, // by assigning it to a temporary variable. Pass a level to precompile. // If `level` is passed, then returns `[val, ref]`, where `val` is the compiled value, and `ref` // is the compiled reference. If `level` is not passed, this returns `[val, ref]` where // the two values are raw nodes which have not been compiled. cache(o, level, shouldCache) { var complex, ref, sub; complex = shouldCache != null ? shouldCache(this) : this.shouldCache(); if (complex) { ref = new IdentifierLiteral(o.scope.freeVariable('ref')); sub = new Assign(ref, this); if (level) { return [sub.compileToFragments(o, level), [this.makeCode(ref.value)]]; } else { return [sub, ref]; } } else { ref = level ? this.compileToFragments(o, level) : this; return [ref, ref]; } } // Occasionally it may be useful to make an expression behave as if it was 'hoisted', whereby the // result of the expression is available before its location in the source, but the expression's // variable scope corresponds to the source position. This is used extensively to deal with executable // class bodies in classes. // Calling this method mutates the node, proxying the `compileNode` and `compileToFragments` // methods to store their result for later replacing the `target` node, which is returned by the // call. hoist() { var compileNode, compileToFragments, target; this.hoisted = true; target = new HoistTarget(this); compileNode = this.compileNode; compileToFragments = this.compileToFragments; this.compileNode = function(o) { return target.update(compileNode, o); }; this.compileToFragments = function(o) { return target.update(compileToFragments, o); }; return target; } cacheToCodeFragments(cacheValues) { return [fragmentsToText(cacheValues[0]), fragmentsToText(cacheValues[1])]; } // Construct a node that returns the current node’s result. // Note that this is overridden for smarter behavior for // many statement nodes (e.g. `If`, `For`). makeReturn(results, mark) { var node; if (mark) { // Mark this node as implicitly returned, so that it can be part of the // node metadata returned in the AST. this.canBeReturned = true; return; } node = this.unwrapAll(); if (results) { return new Call(new Literal(`${results}.push`), [node]); } else { return new Return(node); } } // Does this node, or any of its children, contain a node of a certain kind? // Recursively traverses down the *children* nodes and returns the first one // that verifies `pred`. Otherwise return undefined. `contains` does not cross // scope boundaries. contains(pred) { var node; node = void 0; this.traverseChildren(false, function(n) { if (pred(n)) { node = n; return false; } }); return node; } // Pull out the last node of a node list. lastNode(list) { if (list.length === 0) { return null; } else { return list[list.length - 1]; } } // Debugging representation of the node, for inspecting the parse tree. // This is what `coffee --nodes` prints out. toString(idt = '', name = this.constructor.name) { var tree; tree = '\n' + idt + name; if (this.soak) { tree += '?'; } this.eachChild(function(node) { return tree += node.toString(idt + TAB); }); return tree; } checkForPureStatementInExpression() { var jumpNode; if (jumpNode = this.jumps()) { return jumpNode.error('cannot use a pure statement in an expression'); } } // Plain JavaScript object representation of the node, that can be serialized // as JSON. This is what the `ast` option in the Node API returns. // We try to follow the [Babel AST spec](https://github.com/babel/babel/blob/master/packages/babel-parser/ast/spec.md) // as closely as possible, for improved interoperability with other tools. // **WARNING: DO NOT OVERRIDE THIS METHOD IN CHILD CLASSES.** // Only override the component `ast*` methods as needed. ast(o, level) { var astNode; // Merge `level` into `o` and perform other universal checks. o = this.astInitialize(o, level); // Create serializable representation of this node. astNode = this.astNode(o); // Mark AST nodes that correspond to expressions that (implicitly) return. // We can’t do this as part of `astNode` because we need to assemble child // nodes first before marking the parent being returned. if ((this.astNode != null) && this.canBeReturned) { Object.assign(astNode, { returns: true }); } return astNode; } astInitialize(o, level) { o = Object.assign({}, o); if (level != null) { o.level = level; } if (o.level > LEVEL_TOP) { this.checkForPureStatementInExpression(); } if (this.isStatement(o) && o.level !== LEVEL_TOP && (o.scope != null)) { // `@makeReturn` must be called before `astProperties`, because the latter may call // `.ast()` for child nodes and those nodes would need the return logic from `makeReturn` // already executed by then. this.makeReturn(null, true); } return o; } astNode(o) { // Every abstract syntax tree node object has four categories of properties: // - type, stored in the `type` field and a string like `NumberLiteral`. // - location data, stored in the `loc`, `start`, `end` and `range` fields. // - properties specific to this node, like `parsedValue`. // - properties that are themselves child nodes, like `body`. // These fields are all intermixed in the Babel spec; `type` and `start` and // `parsedValue` are all top level fields in the AST node object. We have // separate methods for returning each category, that we merge together here. return Object.assign({}, { type: this.astType(o) }, this.astProperties(o), this.astLocationData()); } // By default, a node class has no specific properties. astProperties() { return {}; } // By default, a node class’s AST `type` is its class name. astType() { return this.constructor.name; } // The AST location data is a rearranged version of our Jison location data, // mutated into the structure that the Babel spec uses. astLocationData() { return jisonLocationDataToAstLocationData(this.locationData); } // Determines whether an AST node needs an `ExpressionStatement` wrapper. // Typically matches our `isStatement()` logic but this allows overriding. isStatementAst(o) { return this.isStatement(o); } // Passes each child to a function, breaking when the function returns `false`. eachChild(func) { var attr, child, j, k, len1, len2, ref1, ref2; if (!this.children) { return this; } ref1 = this.children; for (j = 0, len1 = ref1.length; j < len1; j++) { attr = ref1[j]; if (this[attr]) { ref2 = flatten([this[attr]]); for (k = 0, len2 = ref2.length; k < len2; k++) { child = ref2[k]; if (func(child) === false) { return this; } } } } return this; } traverseChildren(crossScope, func) { return this.eachChild(function(child) { var recur; recur = func(child); if (recur !== false) { return child.traverseChildren(crossScope, func); } }); } // `replaceInContext` will traverse children looking for a node for which `match` returns // true. Once found, the matching node will be replaced by the result of calling `replacement`. replaceInContext(match, replacement) { var attr, child, children, i, j, k, len1, len2, ref1, ref2; if (!this.children) { return false; } ref1 = this.children; for (j = 0, len1 = ref1.length; j < len1; j++) { attr = ref1[j]; if (children = this[attr]) { if (Array.isArray(children)) { for (i = k = 0, len2 = children.length; k < len2; i = ++k) { child = children[i]; if (match(child)) { splice.apply(children, [i, i - i + 1].concat(ref2 = replacement(child, this))), ref2; return true; } else { if (child.replaceInContext(match, replacement)) { return true; } } } } else if (match(children)) { this[attr] = replacement(children, this); return true; } else { if (children.replaceInContext(match, replacement)) { return true; } } } } } invert() { return new Op('!', this); } unwrapAll() { var node; node = this; while (node !== (node = node.unwrap())) { continue; } return node; } // For this node and all descendents, set the location data to `locationData` // if the location data is not already set. updateLocationDataIfMissing(locationData, force) { if (force) { this.forceUpdateLocation = true; } if (this.locationData && !this.forceUpdateLocation) { return this; } delete this.forceUpdateLocation; this.locationData = locationData; return this.eachChild(function(child) { return child.updateLocationDataIfMissing(locationData); }); } // Add location data from another node withLocationDataFrom({locationData}) { return this.updateLocationDataIfMissing(locationData); } // Add location data and comments from another node withLocationDataAndCommentsFrom(node) { var comments; this.withLocationDataFrom(node); ({comments} = node); if (comments != null ? comments.length : void 0) { this.comments = comments; } return this; } // Throw a SyntaxError associated with this node’s location. error(message) { return throwSyntaxError(message, this.locationData); } makeCode(code) { return new CodeFragment(this, code); } wrapInParentheses(fragments) { return [this.makeCode('('), ...fragments, this.makeCode(')')]; } wrapInBraces(fragments) { return [this.makeCode('{'), ...fragments, this.makeCode('}')]; } // `fragmentsList` is an array of arrays of fragments. Each array in fragmentsList will be // concatenated together, with `joinStr` added in between each, to produce a final flat array // of fragments. joinFragmentArrays(fragmentsList, joinStr) { var answer, fragments, i, j, len1; answer = []; for (i = j = 0, len1 = fragmentsList.length; j < len1; i = ++j) { fragments = fragmentsList[i]; if (i) { answer.push(this.makeCode(joinStr)); } answer = answer.concat(fragments); } return answer; } }; // Default implementations of the common node properties and methods. Nodes // will override these with custom logic, if needed. // `children` are the properties to recurse into when tree walking. The // `children` list *is* the structure of the AST. The `parent` pointer, and // the pointer to the `children` are how you can traverse the tree. Base.prototype.children = []; // `isStatement` has to do with “everything is an expression”. A few things // can’t be expressions, such as `break`. Things that `isStatement` returns // `true` for are things that can’t be used as expressions. There are some // error messages that come from `nodes.coffee` due to statements ending up // in expression position. Base.prototype.isStatement = NO; // Track comments that have been compiled into fragments, to avoid outputting // them twice. Base.prototype.compiledComments = []; // `includeCommentFragments` lets `compileCommentFragments` know whether this node // has special awareness of how to handle comments within its output. Base.prototype.includeCommentFragments = NO; // `jumps` tells you if an expression, or an internal part of an expression, // has a flow control construct (like `break`, `continue`, or `return`) // that jumps out of the normal flow of control and can’t be used as a value. // (Note that `throw` is not considered a flow control construct.) // This is important because flow control in the middle of an expression // makes no sense; we have to disallow it. Base.prototype.jumps = NO; // If `node.shouldCache() is false`, it is safe to use `node` more than once. // Otherwise you need to store the value of `node` in a variable and output // that variable several times instead. Kind of like this: `5` need not be // cached. `returnFive()`, however, could have side effects as a result of // evaluating it more than once, and therefore we need to cache it. The // parameter is named `shouldCache` rather than `mustCache` because there are // also cases where we might not need to cache but where we want to, for // example a long expression that may well be idempotent but we want to cache // for brevity. Base.prototype.shouldCache = YES; Base.prototype.isChainable = NO; Base.prototype.isAssignable = NO; Base.prototype.isNumber = NO; Base.prototype.unwrap = THIS; Base.prototype.unfoldSoak = NO; // Is this node used to assign a certain variable? Base.prototype.assigns = NO; return Base; }).call(this); //### HoistTarget // A **HoistTargetNode** represents the output location in the node tree for a hoisted node. // See Base#hoist. exports.HoistTarget = HoistTarget = class HoistTarget extends Base { // Expands hoisted fragments in the given array static expand(fragments) { var fragment, i, j, ref1; for (i = j = fragments.length - 1; j >= 0; i = j += -1) { fragment = fragments[i]; if (fragment.fragments) { splice.apply(fragments, [i, i - i + 1].concat(ref1 = this.expand(fragment.fragments))), ref1; } } return fragments; } constructor(source1) { super(); this.source = source1; // Holds presentational options to apply when the source node is compiled. this.options = {}; // Placeholder fragments to be replaced by the source node’s compilation. this.targetFragments = { fragments: [] }; } isStatement(o) { return this.source.isStatement(o); } // Update the target fragments with the result of compiling the source. // Calls the given compile function with the node and options (overriden with the target // presentational options). update(compile, o) { return this.targetFragments.fragments = compile.call(this.source, merge(o, this.options)); } // Copies the target indent and level, and returns the placeholder fragments compileToFragments(o, level) { this.options.indent = o.indent; this.options.level = level != null ? level : o.level; return [this.targetFragments]; } compileNode(o) { return this.compileToFragments(o); } compileClosure(o) { return this.compileToFragments(o); } }; //### Root // The root node of the node tree exports.Root = Root = (function() { class Root extends Base { constructor(body1) { super(); this.body = body1; this.isAsync = (new Code([], this.body)).isAsync; } // Wrap everything in a safety closure, unless requested not to. It would be // better not to generate them in the first place, but for now, clean up // obvious double-parentheses. compileNode(o) { var fragments, functionKeyword; o.indent = o.bare ? '' : TAB; o.level = LEVEL_TOP; o.compiling = true; this.initializeScope(o); fragments = this.body.compileRoot(o); if (o.bare) { return fragments; } functionKeyword = `${this.isAsync ? 'async ' : ''}function`; return [].concat(this.makeCode(`(${functionKeyword}() {\n`), fragments, this.makeCode("\n}).call(this);\n")); } initializeScope(o) { var j, len1, name, ref1, ref2, results1; o.scope = new Scope(null, this.body, null, (ref1 = o.referencedVars) != null ? ref1 : []); ref2 = o.locals || []; results1 = []; for (j = 0, len1 = ref2.length; j < len1; j++) { name = ref2[j]; // Mark given local variables in the root scope as parameters so they don’t // end up being declared on the root block. results1.push(o.scope.parameter(name)); } return results1; } commentsAst() { var comment, commentToken, j, len1, ref1, results1; if (this.allComments == null) { this.allComments = (function() { var j, len1, ref1, ref2, results1; ref2 = (ref1 = this.allCommentTokens) != null ? ref1 : []; results1 = []; for (j = 0, len1 = ref2.length; j < len1; j++) { commentToken = ref2[j]; if (!commentToken.heregex) { if (commentToken.here) { results1.push(new HereComment(commentToken)); } else { results1.push(new LineComment(commentToken)); } } } return results1; }).call(this); } ref1 = this.allComments; results1 = []; for (j = 0, len1 = ref1.length; j < len1; j++) { comment = ref1[j]; results1.push(comment.ast()); } return results1; } astNode(o) { o.level = LEVEL_TOP; this.initializeScope(o); return super.astNode(o); } astType() { return 'File'; } astProperties(o) { this.body.isRootBlock = true; return { program: Object.assign(this.body.ast(o), this.astLocationData()), comments: this.commentsAst() }; } }; Root.prototype.children = ['body']; return Root; }).call(this); //### Block // The block is the list of expressions that forms the body of an // indented block of code -- the implementation of a function, a clause in an // `if`, `switch`, or `try`, and so on... exports.Block = Block = (function() { class Block extends Base { constructor(nodes) { super(); this.expressions = compact(flatten(nodes || [])); } // Tack an expression on to the end of this expression list. push(node) { this.expressions.push(node); return this; } // Remove and return the last expression of this expression list. pop() { return this.expressions.pop(); } // Add an expression at the beginning of this expression list. unshift(node) { this.expressions.unshift(node); return this; } // If this Block consists of just a single node, unwrap it by pulling // it back out. unwrap() { if (this.expressions.length === 1) { return this.expressions[0]; } else { return this; } } // Is this an empty block of code? isEmpty() { return !this.expressions.length; } isStatement(o) { var exp, j, len1, ref1; ref1 = this.expressions; for (j = 0, len1 = ref1.length; j < len1; j++) { exp = ref1[j]; if (exp.isStatement(o)) { return true; } } return false; } jumps(o) { var exp, j, jumpNode, len1, ref1; ref1 = this.expressions; for (j = 0, len1 = ref1.length; j < len1; j++) { exp = ref1[j]; if (jumpNode = exp.jumps(o)) { return jumpNode; } } } // A Block node does not return its entire body, rather it // ensures that the final expression is returned. makeReturn(results, mark) { var expr, expressions, last, lastExp, len, penult, ref1, ref2; len = this.expressions.length; ref1 = this.expressions, [lastExp] = slice1.call(ref1, -1); lastExp = (lastExp != null ? lastExp.unwrap() : void 0) || false; // We also need to check that we’re not returning a JSX tag if there’s an // adjacent one at the same level; JSX doesn’t allow that. if (lastExp && lastExp instanceof Parens && lastExp.body.expressions.length > 1) { ({ body: {expressions} } = lastExp); [penult, last] = slice1.call(expressions, -2); penult = penult.unwrap(); last = last.unwrap(); if (penult instanceof JSXElement && last instanceof JSXElement) { expressions[expressions.length - 1].error('Adjacent JSX elements must be wrapped in an enclosing tag'); } } if (mark) { if ((ref2 = this.expressions[len - 1]) != null) { ref2.makeReturn(results, mark); } return; } while (len--) { expr = this.expressions[len]; this.expressions[len] = expr.makeReturn(results); if (expr instanceof Return && !expr.expression) { this.expressions.splice(len, 1); } break; } return this; } compile(o, lvl) { if (!o.scope) { return new Root(this).withLocationDataFrom(this).compile(o, lvl); } return super.compile(o, lvl); } // Compile all expressions within the **Block** body. If we need to return // the result, and it’s an expression, simply return it. If it’s a statement, // ask the statement to do so. compileNode(o) { var answer, compiledNodes, fragments, index, j, lastFragment, len1, node, ref1, top; this.tab = o.indent; top = o.level === LEVEL_TOP; compiledNodes = []; ref1 = this.expressions; for (index = j = 0, len1 = ref1.length; j < len1; index = ++j) { node = ref1[index]; if (node.hoisted) { // This is a hoisted expression. // We want to compile this and ignore the result. node.compileToFragments(o); continue; } node = node.unfoldSoak(o) || node; if (node instanceof Block) { // This is a nested block. We don’t do anything special here like // enclose it in a new scope; we just compile the statements in this // block along with our own. compiledNodes.push(node.compileNode(o)); } else if (top) { node.front = true; fragments = node.compileToFragments(o); if (!node.isStatement(o)) { fragments = indentInitial(fragments, this); [lastFragment] = slice1.call(fragments, -1); if (!(lastFragment.code === '' || lastFragment.isComment)) { fragments.push(this.makeCode(';')); } } compiledNodes.push(fragments); } else { compiledNodes.push(node.compileToFragments(o, LEVEL_LIST)); } } if (top) { if (this.spaced) { return [].concat(this.joinFragmentArrays(compiledNodes, '\n\n'), this.makeCode('\n')); } else { return this.joinFragmentArrays(compiledNodes, '\n'); } } if (compiledNodes.length) { answer = this.joinFragmentArrays(compiledNodes, ', '); } else { answer = [this.makeCode('void 0')]; } if (compiledNodes.length > 1 && o.level >= LEVEL_LIST) { return this.wrapInParentheses(answer); } else { return answer; } } compileRoot(o) { var fragments; this.spaced = true; fragments = this.compileWithDeclarations(o); HoistTarget.expand(fragments); return this.compileComments(fragments); } // Compile the expressions body for the contents of a function, with // declarations of all inner variables pushed up to the top. compileWithDeclarations(o) { var assigns, declaredVariable, declaredVariables, declaredVariablesIndex, declars, exp, fragments, i, j, k, len1, len2, post, ref1, rest, scope, spaced; fragments = []; post = []; ref1 = this.expressions; for (i = j = 0, len1 = ref1.length; j < len1; i = ++j) { exp = ref1[i]; exp = exp.unwrap(); if (!(exp instanceof Literal)) { break; } } o = merge(o, { level: LEVEL_TOP }); if (i) { rest = this.expressions.splice(i, 9e9); [spaced, this.spaced] = [this.spaced, false]; [fragments, this.spaced] = [this.compileNode(o), spaced]; this.expressions = rest; } post = this.compileNode(o); ({scope} = o); if (scope.expressions === this) { declars = o.scope.hasDeclarations(); assigns = scope.hasAssignments; if (declars || assigns) { if (i) { fragments.push(this.makeCode('\n')); } fragments.push(this.makeCode(`${this.tab}var `)); if (declars) { declaredVariables = scope.declaredVariables(); for (declaredVariablesIndex = k = 0, len2 = declaredVariables.length; k < len2; declaredVariablesIndex = ++k) { declaredVariable = declaredVariables[declaredVariablesIndex]; fragments.push(this.makeCode(declaredVariable)); if (Object.prototype.hasOwnProperty.call(o.scope.comments, declaredVariable)) { fragments.push(...o.scope.comments[declaredVariable]); } if (declaredVariablesIndex !== declaredVariables.length - 1) { fragments.push(this.makeCode(', ')); } } } if (assigns) { if (declars) { fragments.push(this.makeCode(`,\n${this.tab + TAB}`)); } fragments.push(this.makeCode(scope.assignedVariables().join(`,\n${this.tab + TAB}`))); } fragments.push(this.makeCode(`;\n${this.spaced ? '\n' : ''}`)); } else if (fragments.length && post.length) { fragments.push(this.makeCode("\n")); } } return fragments.concat(post); } compileComments(fragments) { var code, commentFragment, fragment, fragmentIndent, fragmentIndex, indent, j, k, l, len1, len2, len3, newLineIndex, onNextLine, p, pastFragment, pastFragmentIndex, q, ref1, ref2, ref3, ref4, trail, upcomingFragment, upcomingFragmentIndex; for (fragmentIndex = j = 0, len1 = fragments.length; j < len1; fragmentIndex = ++j) { fragment = fragments[fragmentIndex]; // Insert comments into the output at the next or previous newline. // If there are no newlines at which to place comments, create them. if (fragment.precedingComments) { // Determine the indentation level of the fragment that we are about // to insert comments before, and use that indentation level for our // inserted comments. At this point, the fragments’ `code` property // is the generated output JavaScript, and CoffeeScript always // generates output indented by two spaces; so all we need to do is // search for a `code` property that begins with at least two spaces. fragmentIndent = ''; ref1 = fragments.slice(0, (fragmentIndex + 1)); for (k = ref1.length - 1; k >= 0; k += -1) { pastFragment = ref1[k]; indent = /^ {2,}/m.exec(pastFragment.code); if (indent) { fragmentIndent = indent[0]; break; } else if (indexOf.call(pastFragment.code, '\n') >= 0) { break; } } code = `\n${fragmentIndent}` + ((function() { var l, len2, ref2, results1; ref2 = fragment.precedingComments; results1 = []; for (l = 0, len2 = ref2.length; l < len2; l++) { commentFragment = ref2[l]; if (commentFragment.isHereComment && commentFragment.multiline) { results1.push(multident(commentFragment.code, fragmentIndent, false)); } else { results1.push(commentFragment.code); } } return results1; })()).join(`\n${fragmentIndent}`).replace(/^(\s*)$/gm, ''); ref2 = fragments.slice(0, (fragmentIndex + 1)); for (pastFragmentIndex = l = ref2.length - 1; l >= 0; pastFragmentIndex = l += -1) { pastFragment = ref2[pastFragmentIndex]; newLineIndex = pastFragment.code.lastIndexOf('\n'); if (newLineIndex === -1) { // Keep searching previous fragments until we can’t go back any // further, either because there are no fragments left or we’ve // discovered that we’re in a code block that is interpolated // inside a string. if (pastFragmentIndex === 0) { pastFragment.code = '\n' + pastFragment.code; newLineIndex = 0; } else if (pastFragment.isStringWithInterpolations && pastFragment.code === '{') { code = code.slice(1) + '\n'; // Move newline to end. newLineIndex = 1; } else { continue; } } delete fragment.precedingComments; pastFragment.code = pastFragment.code.slice(0, newLineIndex) + code + pastFragment.code.slice(newLineIndex); break; } } // Yes, this is awfully similar to the previous `if` block, but if you // look closely you’ll find lots of tiny differences that make this // confusing if it were abstracted into a function that both blocks share. if (fragment.followingComments) { // Does the first trailing comment follow at the end of a line of code, // like `; // Comment`, or does it start a new line after a line of code? trail = fragment.followingComments[0].trail; fragmentIndent = ''; // Find the indent of the next line of code, if we have any non-trailing // comments to output. We need to first find the next newline, as these // comments will be output after that; and then the indent of the line // that follows the next newline. if (!(trail && fragment.followingComments.length === 1)) { onNextLine = false; ref3 = fragments.slice(fragmentIndex); for (p = 0, len2 = ref3.length; p < len2; p++) { upcomingFragment = ref3[p]; if (!onNextLine) { if (indexOf.call(upcomingFragment.code, '\n') >= 0) { onNextLine = true; } else { continue; } } else { indent = /^ {2,}/m.exec(upcomingFragment.code); if (indent) { fragmentIndent = indent[0]; break; } else if (indexOf.call(upcomingFragment.code, '\n') >= 0) { break; } } } } // Is this comment following the indent inserted by bare mode? // If so, there’s no need to indent this further. code = fragmentIndex === 1 && /^\s+$/.test(fragments[0].code) ? '' : trail ? ' ' : `\n${fragmentIndent}`; // Assemble properly indented comments. code += ((function() { var len3, q, ref4, results1; ref4 = fragment.followingComments; results1 = []; for (q = 0, len3 = ref4.length; q < len3; q++) { commentFragment = ref4[q]; if (commentFragment.isHereComment && commentFragment.multiline) { results1.push(multident(commentFragment.code, fragmentIndent, false)); } else { results1.push(commentFragment.code); } } return results1; })()).join(`\n${fragmentIndent}`).replace(/^(\s*)$/gm, ''); ref4 = fragments.slice(fragmentIndex); for (upcomingFragmentIndex = q = 0, len3 = ref4.length; q < len3; upcomingFragmentIndex = ++q) { upcomingFragment = ref4[upcomingFragmentIndex]; newLineIndex = upcomingFragment.code.indexOf('\n'); if (newLineIndex === -1) { // Keep searching upcoming fragments until we can’t go any // further, either because there are no fragments left or we’ve // discovered that we’re in a code block that is interpolated // inside a string. if (upcomingFragmentIndex === fragments.length - 1) { upcomingFragment.code = upcomingFragment.code + '\n'; newLineIndex = upcomingFragment.code.length; } else if (upcomingFragment.isStringWithInterpolations && upcomingFragment.code === '}') { code = `${code}\n`; newLineIndex = 0; } else { continue; } } delete fragment.followingComments; if (upcomingFragment.code === '\n') { // Avoid inserting extra blank lines. code = code.replace(/^\n/, ''); } upcomingFragment.code = upcomingFragment.code.slice(0, newLineIndex) + code + upcomingFragment.code.slice(newLineIndex); break; } } } return fragments; } // Wrap up the given nodes as a **Block**, unless it already happens // to be one. static wrap(nodes) { if (nodes.length === 1 && nodes[0] instanceof Block) { return nodes[0]; } return new Block(nodes); } astNode(o) { if (((o.level != null) && o.level !== LEVEL_TOP) && this.expressions.length) { return (new Sequence(this.expressions).withLocationDataFrom(this)).ast(o); } return super.astNode(o); } astType() { if (this.isRootBlock) { return 'Program'; } else if (this.isClassBody) { return 'ClassBody'; } else { return 'BlockStatement'; } } astProperties(o) { var body, checkForDirectives, directives, expression, expressionAst, j, len1, ref1; checkForDirectives = del(o, 'checkForDirectives'); if (this.isRootBlock || checkForDirectives) { sniffDirectives(this.expressions, { notFinalExpression: checkForDirectives }); } directives = []; body = []; ref1 = this.expressions; for (j = 0, len1 = ref1.length; j < len1; j++) { expression = ref1[j]; expressionAst = expression.ast(o); // Ignore generated PassthroughLiteral