@@ -342,29 +342,20 @@ private static Node createDummyGoogModuleExportsTarget(AbstractCompiler compiler
342342
343343 JSDocInfo classInfo = NodeUtil .getBestJSDocInfo (classNode );
344344
345- JSDocInfo ctorInfo = null ;
346345 Node constructor = NodeUtil .getEs6ClassConstructorMemberFunctionDef (classNode );
347- if (constructor != null ) {
348- ctorInfo = NodeUtil .getBestJSDocInfo (constructor );
349- }
350346
351347 ImmutableList <MemberDefinition > properties =
352348 PolymerPassStaticUtils .extractProperties (
353349 propertiesDescriptor , DefinitionType .ES6Class , compiler , constructor );
354350
355- List <MemberDefinition > methods = new ArrayList <>();
356- for (Node keyNode = NodeUtil .getClassMembers (classNode ).getFirstChild ();
357- keyNode != null ;
358- keyNode = keyNode .getNext ()) {
359- if (!keyNode .isMemberFunctionDef ()) {
360- continue ;
361- }
362- methods .add (
363- new MemberDefinition (
364- NodeUtil .getBestJSDocInfo (keyNode ),
365- keyNode ,
366- keyNode .getFirstChild (),
367- enclosingModule ));
351+ List <MemberDefinition > methods = extractClassMethods (classNode , enclosingModule );
352+
353+ JSDocInfo ctorInfo = null ;
354+ if (constructor != null ) {
355+ ctorInfo = NodeUtil .getBestJSDocInfo (constructor );
356+
357+ // Add properties assigned to functions in the constructor (i.e. `this.prop = function`).
358+ addFunctionAssignmentsFromConstructor (constructor , enclosingModule , methods );
368359 }
369360 CompilerInput input = compiler .getInput (NodeUtil .getEnclosingScript (classNode ).getInputId ());
370361
@@ -385,6 +376,56 @@ private static Node createDummyGoogModuleExportsTarget(AbstractCompiler compiler
385376 input );
386377 }
387378
379+ /** Extracts true class methods and fields assigned to functions from an ES6 class node. */
380+ private static List <MemberDefinition > extractClassMethods (Node classNode , Node enclosingModule ) {
381+ List <MemberDefinition > methods = new ArrayList <>();
382+ for (Node keyNode = NodeUtil .getClassMembers (classNode ).getFirstChild ();
383+ keyNode != null ;
384+ keyNode = keyNode .getNext ()) {
385+ if (keyNode .isMemberFunctionDef () || isFunctionField (keyNode )) {
386+ methods .add (
387+ new MemberDefinition (
388+ NodeUtil .getBestJSDocInfo (keyNode ),
389+ keyNode ,
390+ keyNode .getFirstChild (),
391+ enclosingModule ));
392+ }
393+ }
394+ return methods ;
395+ }
396+
397+ /** Detects patterns like {@code field = function() {}} and {@code field = () => {})}. */
398+ private static boolean isFunctionField (Node field ) {
399+ if (!field .isMemberFieldDef ()) {
400+ return false ;
401+ }
402+ Node value = field .getFirstChild ();
403+ return value != null && value .isFunction ();
404+ }
405+
406+ /**
407+ * Adds properties assigned to functions in the constructor body (e.g., {@code this.prop =
408+ * function()}) to the given list of methods.
409+ */
410+ private static void addFunctionAssignmentsFromConstructor (
411+ Node constructor , Node enclosingModule , List <MemberDefinition > methods ) {
412+ Node body = NodeUtil .getFunctionBody (constructor .getFirstChild ());
413+ for (Node stmt = body .getFirstChild (); stmt != null ; stmt = stmt .getNext ()) {
414+ if (!stmt .isExprResult () || !stmt .getFirstChild ().isAssign ()) {
415+ continue ;
416+ }
417+
418+ var assignment = stmt .getFirstChild ();
419+ var propName = assignment .getFirstChild ();
420+ var propValue = assignment .getLastChild ();
421+ if (propName .isGetProp () && propName .getFirstChild ().isThis () && propValue .isFunction ()) {
422+ methods .add (
423+ new MemberDefinition (
424+ NodeUtil .getBestJSDocInfo (propName ), propName , propValue , enclosingModule ));
425+ }
426+ }
427+ }
428+
388429 /**
389430 * Appends a list of new MemberDefinitions to the end of a list and removes any previous
390431 * MemberDefinition in the list which has the same name as the new member.
0 commit comments