humanify icon indicating copy to clipboard operation
humanify copied to clipboard

Support for private class fields

Open brianjenkins94 opened this issue 1 year ago • 5 comments

It doesn't look like private class fields are renamed, can support be added for them?

brianjenkins94 avatar Oct 26 '24 20:10 brianjenkins94

Class static methods cannot be renamed either. I think these features are important

Versirity avatar Nov 26 '24 03:11 Versirity

Using https://astexplorer.net/ with @babel/parser

With this sample (Ref):

class ClassWithPrivate {
  #privateField;
  #privateFieldWithInitializer = 42;

  #privateMethod() {
    // …
  }

  static #privateStaticField;
  static #privateStaticFieldWithInitializer = 42;

  static #privateStaticMethod() {
    // …
  }
}

Gives this AST:

AST
{
  "type": "File",
  "start": 0,
  "end": 244,
  "loc": {
    "start": {
      "line": 1,
      "column": 0,
      "index": 0
    },
    "end": {
      "line": 15,
      "column": 1,
      "index": 244
    }
  },
  "errors": [],
  "program": {
    "type": "Program",
    "start": 0,
    "end": 244,
    "loc": {
      "start": {
        "line": 1,
        "column": 0,
        "index": 0
      },
      "end": {
        "line": 15,
        "column": 1,
        "index": 244
      }
    },
    "sourceType": "module",
    "interpreter": null,
    "body": [
      {
        "type": "ClassDeclaration",
        "start": 0,
        "end": 244,
        "loc": {
          "start": {
            "line": 1,
            "column": 0,
            "index": 0
          },
          "end": {
            "line": 15,
            "column": 1,
            "index": 244
          }
        },
        "id": {
          "type": "Identifier",
          "start": 6,
          "end": 22,
          "loc": {
            "start": {
              "line": 1,
              "column": 6,
              "index": 6
            },
            "end": {
              "line": 1,
              "column": 22,
              "index": 22
            },
            "identifierName": "ClassWithPrivate"
          },
          "name": "ClassWithPrivate"
        },
        "superClass": null,
        "body": {
          "type": "ClassBody",
          "start": 23,
          "end": 244,
          "loc": {
            "start": {
              "line": 1,
              "column": 23,
              "index": 23
            },
            "end": {
              "line": 15,
              "column": 1,
              "index": 244
            }
          },
          "body": [
            {
              "type": "ClassPrivateProperty",
              "start": 27,
              "end": 41,
              "loc": {
                "start": {
                  "line": 2,
                  "column": 2,
                  "index": 27
                },
                "end": {
                  "line": 2,
                  "column": 16,
                  "index": 41
                }
              },
              "static": false,
              "variance": null,
              "key": {
                "type": "PrivateName",
                "start": 27,
                "end": 40,
                "loc": {
                  "start": {
                    "line": 2,
                    "column": 2,
                    "index": 27
                  },
                  "end": {
                    "line": 2,
                    "column": 15,
                    "index": 40
                  }
                },
                "id": {
                  "type": "Identifier",
                  "start": 28,
                  "end": 40,
                  "loc": {
                    "start": {
                      "line": 2,
                      "column": 3,
                      "index": 28
                    },
                    "end": {
                      "line": 2,
                      "column": 15,
                      "index": 40
                    },
                    "identifierName": "privateField"
                  },
                  "name": "privateField"
                }
              },
              "value": null
            },
            {
              "type": "ClassPrivateProperty",
              "start": 44,
              "end": 78,
              "loc": {
                "start": {
                  "line": 3,
                  "column": 2,
                  "index": 44
                },
                "end": {
                  "line": 3,
                  "column": 36,
                  "index": 78
                }
              },
              "static": false,
              "variance": null,
              "key": {
                "type": "PrivateName",
                "start": 44,
                "end": 72,
                "loc": {
                  "start": {
                    "line": 3,
                    "column": 2,
                    "index": 44
                  },
                  "end": {
                    "line": 3,
                    "column": 30,
                    "index": 72
                  }
                },
                "id": {
                  "type": "Identifier",
                  "start": 45,
                  "end": 72,
                  "loc": {
                    "start": {
                      "line": 3,
                      "column": 3,
                      "index": 45
                    },
                    "end": {
                      "line": 3,
                      "column": 30,
                      "index": 72
                    },
                    "identifierName": "privateFieldWithInitializer"
                  },
                  "name": "privateFieldWithInitializer"
                }
              },
              "value": {
                "type": "NumericLiteral",
                "start": 75,
                "end": 77,
                "loc": {
                  "start": {
                    "line": 3,
                    "column": 33,
                    "index": 75
                  },
                  "end": {
                    "line": 3,
                    "column": 35,
                    "index": 77
                  }
                },
                "extra": {
                  "rawValue": 42,
                  "raw": "42"
                },
                "value": 42
              }
            },
            {
              "type": "ClassPrivateMethod",
              "start": 82,
              "end": 113,
              "loc": {
                "start": {
                  "line": 5,
                  "column": 2,
                  "index": 82
                },
                "end": {
                  "line": 7,
                  "column": 3,
                  "index": 113
                }
              },
              "static": false,
              "key": {
                "type": "PrivateName",
                "start": 82,
                "end": 96,
                "loc": {
                  "start": {
                    "line": 5,
                    "column": 2,
                    "index": 82
                  },
                  "end": {
                    "line": 5,
                    "column": 16,
                    "index": 96
                  }
                },
                "id": {
                  "type": "Identifier",
                  "start": 83,
                  "end": 96,
                  "loc": {
                    "start": {
                      "line": 5,
                      "column": 3,
                      "index": 83
                    },
                    "end": {
                      "line": 5,
                      "column": 16,
                      "index": 96
                    },
                    "identifierName": "privateMethod"
                  },
                  "name": "privateMethod"
                }
              },
              "kind": "method",
              "id": null,
              "generator": false,
              "async": false,
              "params": [],
              "body": {
                "type": "BlockStatement",
                "start": 99,
                "end": 113,
                "loc": {
                  "start": {
                    "line": 5,
                    "column": 19,
                    "index": 99
                  },
                  "end": {
                    "line": 7,
                    "column": 3,
                    "index": 113
                  }
                },
                "body": [],
                "directives": [],
                "innerComments": [
                  {
                    "type": "CommentLine",
                    "value": " …",
                    "start": 105,
                    "end": 109,
                    "loc": {
                      "start": {
                        "line": 6,
                        "column": 4,
                        "index": 105
                      },
                      "end": {
                        "line": 6,
                        "column": 8,
                        "index": 109
                      }
                    }
                  }
                ]
              }
            },
            {
              "type": "ClassPrivateProperty",
              "start": 117,
              "end": 144,
              "loc": {
                "start": {
                  "line": 9,
                  "column": 2,
                  "index": 117
                },
                "end": {
                  "line": 9,
                  "column": 29,
                  "index": 144
                }
              },
              "static": true,
              "variance": null,
              "key": {
                "type": "PrivateName",
                "start": 124,
                "end": 143,
                "loc": {
                  "start": {
                    "line": 9,
                    "column": 9,
                    "index": 124
                  },
                  "end": {
                    "line": 9,
                    "column": 28,
                    "index": 143
                  }
                },
                "id": {
                  "type": "Identifier",
                  "start": 125,
                  "end": 143,
                  "loc": {
                    "start": {
                      "line": 9,
                      "column": 10,
                      "index": 125
                    },
                    "end": {
                      "line": 9,
                      "column": 28,
                      "index": 143
                    },
                    "identifierName": "privateStaticField"
                  },
                  "name": "privateStaticField"
                }
              },
              "value": null
            },
            {
              "type": "ClassPrivateProperty",
              "start": 147,
              "end": 194,
              "loc": {
                "start": {
                  "line": 10,
                  "column": 2,
                  "index": 147
                },
                "end": {
                  "line": 10,
                  "column": 49,
                  "index": 194
                }
              },
              "static": true,
              "variance": null,
              "key": {
                "type": "PrivateName",
                "start": 154,
                "end": 188,
                "loc": {
                  "start": {
                    "line": 10,
                    "column": 9,
                    "index": 154
                  },
                  "end": {
                    "line": 10,
                    "column": 43,
                    "index": 188
                  }
                },
                "id": {
                  "type": "Identifier",
                  "start": 155,
                  "end": 188,
                  "loc": {
                    "start": {
                      "line": 10,
                      "column": 10,
                      "index": 155
                    },
                    "end": {
                      "line": 10,
                      "column": 43,
                      "index": 188
                    },
                    "identifierName": "privateStaticFieldWithInitializer"
                  },
                  "name": "privateStaticFieldWithInitializer"
                }
              },
              "value": {
                "type": "NumericLiteral",
                "start": 191,
                "end": 193,
                "loc": {
                  "start": {
                    "line": 10,
                    "column": 46,
                    "index": 191
                  },
                  "end": {
                    "line": 10,
                    "column": 48,
                    "index": 193
                  }
                },
                "extra": {
                  "rawValue": 42,
                  "raw": "42"
                },
                "value": 42
              }
            },
            {
              "type": "ClassPrivateMethod",
              "start": 198,
              "end": 242,
              "loc": {
                "start": {
                  "line": 12,
                  "column": 2,
                  "index": 198
                },
                "end": {
                  "line": 14,
                  "column": 3,
                  "index": 242
                }
              },
              "static": true,
              "key": {
                "type": "PrivateName",
                "start": 205,
                "end": 225,
                "loc": {
                  "start": {
                    "line": 12,
                    "column": 9,
                    "index": 205
                  },
                  "end": {
                    "line": 12,
                    "column": 29,
                    "index": 225
                  }
                },
                "id": {
                  "type": "Identifier",
                  "start": 206,
                  "end": 225,
                  "loc": {
                    "start": {
                      "line": 12,
                      "column": 10,
                      "index": 206
                    },
                    "end": {
                      "line": 12,
                      "column": 29,
                      "index": 225
                    },
                    "identifierName": "privateStaticMethod"
                  },
                  "name": "privateStaticMethod"
                }
              },
              "kind": "method",
              "id": null,
              "generator": false,
              "async": false,
              "params": [],
              "body": {
                "type": "BlockStatement",
                "start": 228,
                "end": 242,
                "loc": {
                  "start": {
                    "line": 12,
                    "column": 32,
                    "index": 228
                  },
                  "end": {
                    "line": 14,
                    "column": 3,
                    "index": 242
                  }
                },
                "body": [],
                "directives": [],
                "innerComments": [
                  {
                    "type": "CommentLine",
                    "value": " …",
                    "start": 234,
                    "end": 238,
                    "loc": {
                      "start": {
                        "line": 13,
                        "column": 4,
                        "index": 234
                      },
                      "end": {
                        "line": 13,
                        "column": 8,
                        "index": 238
                      }
                    }
                  }
                ]
              }
            }
          ]
        }
      }
    ],
    "directives": []
  },
  "comments": [
    {
      "type": "CommentLine",
      "value": " …",
      "start": 105,
      "end": 109,
      "loc": {
        "start": {
          "line": 6,
          "column": 4,
          "index": 105
        },
        "end": {
          "line": 6,
          "column": 8,
          "index": 109
        }
      }
    },
    {
      "type": "CommentLine",
      "value": " …",
      "start": 234,
      "end": 238,
      "loc": {
        "start": {
          "line": 13,
          "column": 4,
          "index": 234
        },
        "end": {
          "line": 13,
          "column": 8,
          "index": 238
        }
      }
    }
  ]
}

From that, we can see that:

  • #privateField: ClassPrivateProperty -> PrivateName -> Identifier
  • #privateFieldWithInitializer: ClassPrivateProperty -> PrivateName -> Identifier
  • #privateMethod: ClassPrivateMethod -> PrivateName -> Identifier
  • #privateStaticField: ClassPrivateProperty -> PrivateName -> Identifier
  • #privateStaticFieldWithInitializer: ClassPrivateProperty -> PrivateName -> Identifier
  • #privateStaticMethod: ClassPrivateMethod -> PrivateName -> Identifier

Given each of these end up as Identifier, I wonder if this change was relevant:

  • https://github.com/jehna/humanify/issues/8
    • Minifiers only rename variable/function/parameter bindings and not any other identifier like properties because it's almost impossible to guarantee the program still works then.

      ..snip..

      I think thats the only change necessary but it needs some testing:

      https://github.com/jehna/humanify/blob/4dfe05e20f530a8919e6d1e9f89564be5bb874e0/src/openai/rename-variables-and-functions.ts#L16

      - Identifier: (path) => { 
      + BindingIdentifier: (path) => {
      

      Originally posted by @j4k0xb in https://github.com/jehna/humanify/issues/8#issue-2038232442

    • Using BindingIdentifier instead of Identifier now, thanks for reporting this! Closing for now, should be fixed

      Originally posted by @jehna in https://github.com/jehna/humanify/issues/8#issuecomment-2423241136

    • Using BindingIdentifier instead of Identifier now

      For continuity, I believe this was implemented in: https://github.com/jehna/humanify/commit/1fffcde1d1bed609fb3e1b72171ea983e0c1d2bd

      https://github.com/jehna/humanify/blob/311758f42f1c1b4ce599b2e84116454bbfc000b7/src/plugins/local-llm-rename/visit-all-identifiers.ts#L68-L77

      Originally posted by @0xdevalias in https://github.com/jehna/humanify/issues/8#issuecomment-2665002520

I don't know for sure if this worked before that change, but from a relatively naive understanding of the code, I suspect it probably wouldn't work after it since Identifier was changed to BindingIdentifier; unless there is some other area of the code that handles this case more specifically.

I find the current implementation of visitAllIdentifiers a little confusing to reason about (see https://github.com/jehna/humanify/issues/330), so not sure exactly what the best way of implementing a fix here would be.

0xdevalias avatar Feb 18 '25 09:02 0xdevalias

Given each of these end up as Identifier, I wonder if this change was relevant

A bit relevant but going directly back to replacing identifiers would lead to the same problems as before. And @babel/traverse has no built-in way to track the scope and references of private class fields/methods. Would probably have to do it similarly to this plugin/function: https://github.com/babel/babel/blob/ce0e87b12cf50a88bd06d4e39bf4ff205da862a2/packages/babel-helper-create-class-features-plugin/src/fields.ts#L203

j4k0xb avatar Feb 18 '25 15:02 j4k0xb

A bit relevant but going directly back to replacing identifiers would lead to the same problems as before.

@j4k0xb Yeah, I definitely wasn't suggestion a naive switch from BindingIdentifier to Identifier; more just wanted to highlight when/where it may have been introduced.

And @babel/traverse has no built-in way to track the scope and references of private class fields/methods.

Oh? True.. that's annoying.

0xdevalias avatar Feb 21 '25 04:02 0xdevalias

Copying the following here as the more relevant place for it:


The current implementation doesn't rename class or static fields so I thought leveraging the TypeScript API could yield better, more contextually-aware results.

Originally posted by @brianjenkins94 in https://github.com/jehna/humanify/issues/330#issuecomment-2928543830


The current implementation doesn't rename class or static fields

@brianjenkins94 Ah, ok. This would seem to be the more relevant issue for that, so your comment on this one was somewhat disconnected from that context:

  • https://github.com/jehna/humanify/issues/183

While you may have already seen it, my original exploration into the AST was here:

  • https://github.com/jehna/humanify/issues/183#issuecomment-2665020171

And this comment seemed to suggest that Babel's default scope tracking doesn't cover private class fields/methods, but linked to one potential solution using Babel's own babel-helper-create-class-features-plugin's privateNameVisitorFactory:

  • https://github.com/jehna/humanify/issues/183#issuecomment-2666094327

I haven't deeply explored that option, nor whether TypeScript's LSP/etc would do better in this case.

But in any case, if that were to be explored deeper, the other issue would be a more appropriate place for that to be done/discussed than here.

Originally posted by @0xdevalias in https://github.com/jehna/humanify/issues/330#issuecomment-2928553627

0xdevalias avatar Jun 02 '25 03:06 0xdevalias