diff --git a/lib/coffeescript/nodes.js b/lib/coffeescript/nodes.js index 11b9c0ef27..af18f89a41 100644 --- a/lib/coffeescript/nodes.js +++ b/lib/coffeescript/nodes.js @@ -4,7 +4,7 @@ // 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, Code, CodeFragment, ComputedPropertyName, DefaultLiteral, DynamicImport, DynamicImportCall, Elision, 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, 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, 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, attachCommentsToNode, compact, del, ends, extend, flatten, fragmentsToText, greater, hasLineComments, indentInitial, isAstLocGreater, isFunction, isLiteralArguments, isLiteralThis, isLocationDataEndGreater, isLocationDataStartGreater, isNumber, isPlainObject, isUnassignable, jisonLocationDataToAstLocationData, lesser, locationDataToString, makeDelimitedLiteral, merge, mergeAstLocationData, mergeLocationData, moveComments, multident, replaceUnicodeCodePointEscapes, shouldCacheOrIsAssignable, some, starts, throwSyntaxError, unfoldSoak, unshiftAfterComments, utility, zeroWidthLocationDataFromEndLocation, + var Access, Arr, Assign, AwaitReturn, Base, Block, BooleanLiteral, Call, Catch, Class, ClassProperty, ClassPrototypeProperty, Code, CodeFragment, ComputedPropertyName, DefaultLiteral, DynamicImport, DynamicImportCall, Elision, 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, 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, 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, attachCommentsToNode, compact, del, ends, extend, flatten, fragmentsToText, greater, hasLineComments, indentInitial, isAstLocGreater, isFunction, isLiteralArguments, isLiteralThis, isLocationDataEndGreater, isLocationDataStartGreater, isNumber, isPlainObject, isUnassignable, jisonLocationDataToAstLocationData, lesser, locationDataToString, makeDelimitedLiteral, merge, mergeAstLocationData, mergeLocationData, moveComments, multident, replaceUnicodeCodePointEscapes, shouldCacheOrIsAssignable, some, starts, throwSyntaxError, unfoldSoak, unshiftAfterComments, utility, zeroWidthLocationDataFromEndLocation, indexOf = [].indexOf, splice = [].splice, slice1 = [].slice; @@ -4017,6 +4017,8 @@ return this.addInitializerMethod(node); } else if (!o.compiling && this.validClassProperty(node)) { return this.addClassProperty(node); + } else if (!o.compiling && this.validClassPrototypeProperty(node)) { + return this.addClassPrototypeProperty(node); } else { return null; } @@ -4080,6 +4082,22 @@ }).withLocationDataFrom(assign); } + validClassPrototypeProperty(node) { + if (!(node instanceof Assign)) { + return false; + } + return node.context === 'object' && !node.variable.hasProperties(); + } + + addClassPrototypeProperty(assign) { + var value, variable; + ({variable, value} = assign); + return new ClassPrototypeProperty({ + name: variable.base, + value + }).withLocationDataFrom(assign); + } + makeDefaultConstructor() { var applyArgs, applyCtor, ctor; ctor = this.addInitializerMethod(new Assign(new Value(new PropertyName('constructor')), new Code())); @@ -4337,6 +4355,35 @@ }).call(this); + exports.ClassPrototypeProperty = ClassPrototypeProperty = (function() { + class ClassPrototypeProperty extends Base { + constructor({ + name: name1, + value: value1 + }) { + super(); + this.name = name1; + this.value = value1; + } + + astProperties(o) { + return { + key: this.name.ast(o, LEVEL_LIST), + value: this.value.ast(o, LEVEL_LIST), + computed: this.name instanceof ComputedPropertyName + }; + } + + }; + + ClassPrototypeProperty.prototype.children = ['name', 'value']; + + ClassPrototypeProperty.prototype.isStatement = YES; + + return ClassPrototypeProperty; + + }).call(this); + //### Import and Export exports.ModuleDeclaration = ModuleDeclaration = (function() { class ModuleDeclaration extends Base { diff --git a/src/nodes.coffee b/src/nodes.coffee index b596c024a0..c08ad4647a 100644 --- a/src/nodes.coffee +++ b/src/nodes.coffee @@ -2694,6 +2694,8 @@ exports.Class = class Class extends Base @addInitializerMethod node else if not o.compiling and @validClassProperty node @addClassProperty node + else if not o.compiling and @validClassPrototypeProperty node + @addClassPrototypeProperty node else null @@ -2736,6 +2738,17 @@ exports.Class = class Class extends Base operatorToken }).withLocationDataFrom assign + validClassPrototypeProperty: (node) -> + return no unless node instanceof Assign + node.context is 'object' and not node.variable.hasProperties() + + addClassPrototypeProperty: (assign) -> + {variable, value} = assign + new ClassPrototypeProperty({ + name: variable.base + value + }).withLocationDataFrom assign + makeDefaultConstructor: -> ctor = @addInitializerMethod new Assign (new Value new PropertyName 'constructor'), new Code @body.unshift ctor @@ -2911,6 +2924,20 @@ exports.ClassProperty = class ClassProperty extends Base operator: @operatorToken?.value ? '=' staticClassName: @staticClassName?.ast(o) ? null +exports.ClassPrototypeProperty = class ClassPrototypeProperty extends Base + constructor: ({@name, @value}) -> + super() + + children: ['name', 'value'] + + isStatement: YES + + astProperties: (o) -> + return + key: @name.ast o, LEVEL_LIST + value: @value.ast o, LEVEL_LIST + computed: @name instanceof ComputedPropertyName + #### Import and Export exports.ModuleDeclaration = class ModuleDeclaration extends Base diff --git a/test/abstract_syntax_tree.coffee b/test/abstract_syntax_tree.coffee index 1447231480..c4b8d75fbd 100644 --- a/test/abstract_syntax_tree.coffee +++ b/test/abstract_syntax_tree.coffee @@ -1475,6 +1475,28 @@ test "AST as expected for Class node", -> shorthand: no ] + testStatement ''' + class A + b: 1 + [c]: 2 + ''', + type: 'ClassDeclaration' + id: ID 'A' + superClass: null + body: + type: 'ClassBody' + body: [ + type: 'ClassPrototypeProperty' + key: ID 'b' + value: NUMBER 1 + computed: no + , + type: 'ClassPrototypeProperty' + key: ID 'c' + value: NUMBER 2 + computed: yes + ] + # test "AST as expected for ExecutableClassBody node", -> # code = """ # class Klass diff --git a/test/abstract_syntax_tree_location_data.coffee b/test/abstract_syntax_tree_location_data.coffee index 8dc29d724f..b27180f6b0 100644 --- a/test/abstract_syntax_tree_location_data.coffee +++ b/test/abstract_syntax_tree_location_data.coffee @@ -7010,3 +7010,98 @@ test "AST as expected for Class node", -> end: line: 9 column: 12 + + testAstLocationData ''' + class A + b: 1 + [c]: 2 + ''', + type: 'ClassDeclaration' + body: + body: [ + key: + start: 10 + end: 11 + range: [10, 11] + loc: + start: + line: 2 + column: 2 + end: + line: 2 + column: 3 + value: + start: 13 + end: 14 + range: [13, 14] + loc: + start: + line: 2 + column: 5 + end: + line: 2 + column: 6 + start: 10 + end: 14 + range: [10, 14] + loc: + start: + line: 2 + column: 2 + end: + line: 2 + column: 6 + , + key: + start: 18 + end: 19 + range: [18, 19] + loc: + start: + line: 3 + column: 3 + end: + line: 3 + column: 4 + value: + start: 22 + end: 23 + range: [22, 23] + loc: + start: + line: 3 + column: 7 + end: + line: 3 + column: 8 + start: 17 + end: 23 + range: [17, 23] + loc: + start: + line: 3 + column: 2 + end: + line: 3 + column: 8 + ] + start: 8 + end: 23 + range: [8, 23] + loc: + start: + line: 2 + column: 0 + end: + line: 3 + column: 8 + start: 0 + end: 23 + range: [0, 23] + loc: + start: + line: 1 + column: 0 + end: + line: 3 + column: 8