Support for private class fields
Class static methods cannot be renamed either. I think these features are important
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
BindingIdentifierinstead ofIdentifiernow, thanks for reporting this! Closing for now, should be fixedOriginally posted by @jehna in https://github.com/jehna/humanify/issues/8#issuecomment-2423241136
-
Using
BindingIdentifierinstead ofIdentifiernowFor 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.
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
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.
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'sprivateNameVisitorFactory:
- 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