ember-vite-hmr 2.2.4 → 2.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -28,11 +28,42 @@ export default class MyComponent extends Component {
28
28
  ```
29
29
 
30
30
  This hook is called after automatic state synchronization but before the old instance is destroyed, allowing you to:
31
+
31
32
  - Perform custom state migrations
32
33
  - Clean up resources
33
34
  - Log HMR events
34
35
  - Handle complex state transitions
35
36
 
37
+ ### Private class fields
38
+
39
+ Services are hot-reloaded through a generated proxy that forwards property
40
+ access to the current delegate instance. Native `#private` class fields and
41
+ methods only work when `this` is the exact instance that declared them, so
42
+ this plugin rewrites every native `#private` member declared in your app's
43
+ own source (on the service itself and any ancestor class it extends, as
44
+ long as that ancestor is also part of your app) to a plain, uniquely-named
45
+ property. An undecorated field is also made non-enumerable (via
46
+ `Object.defineProperty`), so it's hidden from `for...in`, `Object.keys`,
47
+ `JSON.stringify`, and spread — the same as a native `#private` member. A
48
+ private method already was non-enumerable, since class method syntax is
49
+ non-enumerable by default regardless of this rewrite. A
50
+ `@tracked #field` is the one exception: the decorator turns it into an
51
+ accessor on the prototype rather than an instance data property, so it's
52
+ left exactly as the plain rewrite produces it, enumerable or not according
53
+ to whatever `@tracked` itself does with an ordinary public field. In every
54
+ case this only emulates privacy: the member is reachable and writable from
55
+ outside via its generated name (e.g. through `Object.getOwnPropertyNames`,
56
+ `Reflect.ownKeys`, or devtools) — unlike a real native `#private` field. It's
57
+ obscured, not encapsulated, so don't rely on it as a serialization or
58
+ security boundary.
59
+
60
+ The one case this can't cover: a base class living in `node_modules` (never
61
+ processed by this plugin) that declares its own native `#private` fields.
62
+ A service extending such a class would still need `this` to be the real
63
+ instance to read those fields, so proxying that service could break. Ember's
64
+ own `Service`/`EmberObject`/`CoreObject` chain declares no native `#private`
65
+ fields, so ordinary Ember apps are unaffected.
66
+
36
67
  ## Installation
37
68
 
38
69
  ```
@@ -11,6 +11,33 @@ function findPropertyDescriptor(component, key) {
11
11
  proto = Object.getPrototypeOf(proto);
12
12
  }
13
13
  }
14
+
15
+ // The private property key `ember-resources`' `wrapForPlainUsage()` uses on
16
+ // the plain object it wraps in a Proxy for `resource()` (which reactiveweb's
17
+ // `trackedTask` uses internally) - see
18
+ // https://github.com/NullVoxPopuli/ember-resources/blob/main/ember-resources/src/plain/index.ts.
19
+ // Carrying such a proxy's *reference* over onto a new HMR-swapped instance
20
+ // (instead of leaving the new instance's own, correctly-parented resource in
21
+ // place) is unsafe: the proxy's underlying helper cache is lazily created on
22
+ // first property read and permanently bound, at that point, to whichever
23
+ // component instance was passed as `context` when `resource()` was called -
24
+ // for a carried-over proxy that's always the *old*, already-destroyed
25
+ // instance, so the first read after the swap throws "Attempted to associate
26
+ // a destroyable child with an object that is already destroying or
27
+ // destroyed" - see issue #563.
28
+ //
29
+ // `in` is used (rather than reading a property, or `Object.keys`/
30
+ // `Object.prototype.toString.call`) because it's the one reflective
31
+ // operation `wrapForPlainUsage`'s Proxy doesn't trap (no `has` handler), so
32
+ // it forwards to a plain `[[HasProperty]]` on the underlying target without
33
+ // ever invoking the lazy getter - it can't accidentally trigger the same
34
+ // crash it's checking for. If `ember-resources` ever renames this key, this
35
+ // check simply stops matching and behavior falls back to today's (already
36
+ // broken, pre-existing) state - it does not introduce a new failure mode.
37
+ const RESOURCE_INTERMEDIATE_VALUE_KEY = '__Intermediate_Value__';
38
+ function isUnsafeToCarryOver(value) {
39
+ return (typeof value === 'object' || typeof value === 'function') && value !== null && RESOURCE_INTERMEDIATE_VALUE_KEY in value;
40
+ }
14
41
  function getState(component, skip) {
15
42
  const state = {};
16
43
  if (!component) {
@@ -30,12 +57,24 @@ function getState(component, skip) {
30
57
  if (typeof value === 'function') {
31
58
  continue;
32
59
  }
60
+
61
+ // Skip resource()-backed proxies - see isUnsafeToCarryOver above.
62
+ if (isUnsafeToCarryOver(value)) {
63
+ continue;
64
+ }
33
65
  if (entry) {
34
- if (entry.writable && !Object.prototype.toString.call(entry.value).includes('Function')) {
35
- state[key] = component[key];
66
+ // Note: don't probe `entry.value` any further here (e.g. via
67
+ // `Object.prototype.toString.call`) to decide whether it's a plain
68
+ // function - the `typeof value === 'function'` check above already
69
+ // covers that, and a stricter check on `entry.value` (which may be a
70
+ // resource proxy, e.g. reactiveweb's `trackedTask`) can trigger that
71
+ // proxy's `get` trap on first touch, lazily invoking a helper against
72
+ // this component instance - see issue #557.
73
+ if (entry.writable) {
74
+ state[key] = value;
36
75
  }
37
76
  if (entry.set) {
38
- state[key] = component[key];
77
+ state[key] = value;
39
78
  }
40
79
  }
41
80
  }
@@ -1 +1 @@
1
- {"version":3,"file":"setup-hmr-manager.js","sources":["../../src/instance-initializers/setup-hmr-manager.ts"],"sourcesContent":["import Route from '@ember/routing/route';\nimport Service from '@ember/service';\nimport Component from '@glimmer/component';\nimport { getInternalComponentManager } from '@glimmer/manager';\n\ninterface HotComponent extends Component<{ __hot__?: unknown }> {\n __get_hot_state__?: () => Record<string, unknown>;\n _hmrAccepted?: (oldInstance?: HotComponent) => void;\n [key: string]: unknown;\n}\n\ntype Mutable<T> = {\n -readonly [P in keyof T]: unknown;\n};\n\nfunction findPropertyDescriptor(\n component: HotComponent | Record<string, unknown>,\n key: string,\n) {\n let proto = component;\n while (proto) {\n const desc = Object.getOwnPropertyDescriptor(proto, key);\n if (desc) return desc;\n proto = Object.getPrototypeOf(proto);\n }\n}\n\nfunction getState(component: HotComponent, skip: string[]) {\n const state: Record<string, unknown> = {};\n if (!component) {\n return state;\n }\n for (const key in component) {\n if (skip.includes(key)) continue;\n const entry = findPropertyDescriptor(component, key);\n const value = component[key as keyof Component];\n \n // Skip Service instances - they should not be synced\n if (value instanceof Service) {\n continue;\n }\n \n // Skip Function properties - they should not be synced\n if (typeof value === 'function') {\n continue;\n }\n \n if (entry) {\n if (\n entry.writable &&\n !Object.prototype.toString.call(entry.value).includes('Function')\n ) {\n state[key] = component[key as keyof Component];\n }\n if (entry.set) {\n state[key] = component[key as keyof Component];\n }\n }\n }\n return state;\n}\n\nfunction syncState(instance: Mutable<HotComponent>) {\n const args = instance.args as { __hot__?: { getState?: () => Record<string, unknown>; oldInstance?: HotComponent } };\n if (args.__hot__) {\n const oldInstance = args.__hot__.oldInstance;\n const state =\n (instance.__get_hot_state__ as (() => Record<string, unknown>) | undefined)?.() || args.__hot__.getState?.();\n for (const k in state) {\n if (instance[k as keyof HotComponent] instanceof Service) {\n continue;\n }\n instance[k as keyof HotComponent] = state[k];\n }\n \n // Call _hmrAccepted hook if defined\n if (typeof instance._hmrAccepted === 'function') {\n instance._hmrAccepted(oldInstance);\n }\n \n args.__hot__.getState = () => getState(instance as HotComponent, ['args']);\n args.__hot__.oldInstance = instance as HotComponent;\n }\n}\n\nexport function initialize() {\n // HMR-only: in a production build `import.meta.hot` is undefined, so this\n // (and the manager/route monkey-patches below) is dead-code-eliminated.\n if (!import.meta.hot) return;\n\n const ComponentManager = getInternalComponentManager(Component);\n const proto = Object.getPrototypeOf(ComponentManager);\n const create = proto.create;\n proto.create = function (...args: unknown[]) {\n const instance = create.call(this, ...args);\n setTimeout(() => syncState(instance.component));\n return instance;\n };\n\n const setupController = Route.prototype.setupController;\n\n const StateCache: Record<string, unknown> = {};\n\n Route.prototype.setupController = function (...args: Parameters<typeof setupController>) {\n const controller = args[0] as unknown as Record<string, unknown>;\n const r = setupController.call(this, ...args);\n const fullRouteName = this.fullRouteName;\n const state = StateCache[fullRouteName] as { router?: Record<string, unknown>; controller?: Record<string, unknown> } || {};\n const skip = ['_qpDelegate', 'target', 'queryParams'];\n const routerState = getState(state.router as HotComponent, skip);\n for (const k in routerState) {\n (this as unknown as Record<string, unknown>)[k] = routerState[k];\n }\n // Only re-apply controller state onto a *new* instance (an HMR swap). On\n // ordinary navigation it is the same singleton, and re-applying a\n // non-`@tracked` query param onto itself trips Ember's mandatory setter.\n if (state.controller && state.controller !== controller) {\n const controllerState = getState(state.controller as HotComponent, skip);\n for (const k in controllerState) {\n (controller as Record<string, unknown>)[k] = controllerState[k];\n }\n }\n StateCache[fullRouteName] = {\n route: this,\n controller,\n };\n return r;\n };\n}\n\nexport default {\n initialize,\n};\n"],"names":["findPropertyDescriptor","component","key","proto","desc","Object","getOwnPropertyDescriptor","getPrototypeOf","getState","skip","state","includes","entry","value","Service","writable","prototype","toString","call","set","syncState","instance","args","__hot__","oldInstance","__get_hot_state__","k","_hmrAccepted","initialize","import","meta","hot","ComponentManager","getInternalComponentManager","Component","create","setTimeout","setupController","Route","StateCache","controller","r","fullRouteName","routerState","router","controllerState","route"],"mappings":"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"}
1
+ {"version":3,"file":"setup-hmr-manager.js","sources":["../../src/instance-initializers/setup-hmr-manager.ts"],"sourcesContent":["import Route from '@ember/routing/route';\nimport Service from '@ember/service';\nimport Component from '@glimmer/component';\nimport { getInternalComponentManager } from '@glimmer/manager';\n\ninterface HotComponent extends Component<{ __hot__?: unknown }> {\n __get_hot_state__?: () => Record<string, unknown>;\n _hmrAccepted?: (oldInstance?: HotComponent) => void;\n [key: string]: unknown;\n}\n\ntype Mutable<T> = {\n -readonly [P in keyof T]: unknown;\n};\n\nfunction findPropertyDescriptor(\n component: HotComponent | Record<string, unknown>,\n key: string,\n) {\n let proto = component;\n while (proto) {\n const desc = Object.getOwnPropertyDescriptor(proto, key);\n if (desc) return desc;\n proto = Object.getPrototypeOf(proto);\n }\n}\n\n// The private property key `ember-resources`' `wrapForPlainUsage()` uses on\n// the plain object it wraps in a Proxy for `resource()` (which reactiveweb's\n// `trackedTask` uses internally) - see\n// https://github.com/NullVoxPopuli/ember-resources/blob/main/ember-resources/src/plain/index.ts.\n// Carrying such a proxy's *reference* over onto a new HMR-swapped instance\n// (instead of leaving the new instance's own, correctly-parented resource in\n// place) is unsafe: the proxy's underlying helper cache is lazily created on\n// first property read and permanently bound, at that point, to whichever\n// component instance was passed as `context` when `resource()` was called -\n// for a carried-over proxy that's always the *old*, already-destroyed\n// instance, so the first read after the swap throws \"Attempted to associate\n// a destroyable child with an object that is already destroying or\n// destroyed\" - see issue #563.\n//\n// `in` is used (rather than reading a property, or `Object.keys`/\n// `Object.prototype.toString.call`) because it's the one reflective\n// operation `wrapForPlainUsage`'s Proxy doesn't trap (no `has` handler), so\n// it forwards to a plain `[[HasProperty]]` on the underlying target without\n// ever invoking the lazy getter - it can't accidentally trigger the same\n// crash it's checking for. If `ember-resources` ever renames this key, this\n// check simply stops matching and behavior falls back to today's (already\n// broken, pre-existing) state - it does not introduce a new failure mode.\nconst RESOURCE_INTERMEDIATE_VALUE_KEY = '__Intermediate_Value__';\n\nfunction isUnsafeToCarryOver(value: unknown): boolean {\n return (\n (typeof value === 'object' || typeof value === 'function') &&\n value !== null &&\n RESOURCE_INTERMEDIATE_VALUE_KEY in value\n );\n}\n\nfunction getState(component: HotComponent, skip: string[]) {\n const state: Record<string, unknown> = {};\n if (!component) {\n return state;\n }\n for (const key in component) {\n if (skip.includes(key)) continue;\n const entry = findPropertyDescriptor(component, key);\n const value = component[key as keyof Component];\n \n // Skip Service instances - they should not be synced\n if (value instanceof Service) {\n continue;\n }\n \n // Skip Function properties - they should not be synced\n if (typeof value === 'function') {\n continue;\n }\n\n // Skip resource()-backed proxies - see isUnsafeToCarryOver above.\n if (isUnsafeToCarryOver(value)) {\n continue;\n }\n\n if (entry) {\n // Note: don't probe `entry.value` any further here (e.g. via\n // `Object.prototype.toString.call`) to decide whether it's a plain\n // function - the `typeof value === 'function'` check above already\n // covers that, and a stricter check on `entry.value` (which may be a\n // resource proxy, e.g. reactiveweb's `trackedTask`) can trigger that\n // proxy's `get` trap on first touch, lazily invoking a helper against\n // this component instance - see issue #557.\n if (entry.writable) {\n state[key] = value;\n }\n if (entry.set) {\n state[key] = value;\n }\n }\n }\n return state;\n}\n\nfunction syncState(instance: Mutable<HotComponent>) {\n const args = instance.args as { __hot__?: { getState?: () => Record<string, unknown>; oldInstance?: HotComponent } };\n if (args.__hot__) {\n const oldInstance = args.__hot__.oldInstance;\n const state =\n (instance.__get_hot_state__ as (() => Record<string, unknown>) | undefined)?.() || args.__hot__.getState?.();\n for (const k in state) {\n if (instance[k as keyof HotComponent] instanceof Service) {\n continue;\n }\n instance[k as keyof HotComponent] = state[k];\n }\n \n // Call _hmrAccepted hook if defined\n if (typeof instance._hmrAccepted === 'function') {\n instance._hmrAccepted(oldInstance);\n }\n \n args.__hot__.getState = () => getState(instance as HotComponent, ['args']);\n args.__hot__.oldInstance = instance as HotComponent;\n }\n}\n\nexport function initialize() {\n // HMR-only: in a production build `import.meta.hot` is undefined, so this\n // (and the manager/route monkey-patches below) is dead-code-eliminated.\n if (!import.meta.hot) return;\n\n const ComponentManager = getInternalComponentManager(Component);\n const proto = Object.getPrototypeOf(ComponentManager);\n const create = proto.create;\n proto.create = function (...args: unknown[]) {\n const instance = create.call(this, ...args);\n setTimeout(() => syncState(instance.component));\n return instance;\n };\n\n const setupController = Route.prototype.setupController;\n\n const StateCache: Record<string, unknown> = {};\n\n Route.prototype.setupController = function (...args: Parameters<typeof setupController>) {\n const controller = args[0] as unknown as Record<string, unknown>;\n const r = setupController.call(this, ...args);\n const fullRouteName = this.fullRouteName;\n const state = StateCache[fullRouteName] as { router?: Record<string, unknown>; controller?: Record<string, unknown> } || {};\n const skip = ['_qpDelegate', 'target', 'queryParams'];\n const routerState = getState(state.router as HotComponent, skip);\n for (const k in routerState) {\n (this as unknown as Record<string, unknown>)[k] = routerState[k];\n }\n // Only re-apply controller state onto a *new* instance (an HMR swap). On\n // ordinary navigation it is the same singleton, and re-applying a\n // non-`@tracked` query param onto itself trips Ember's mandatory setter.\n if (state.controller && state.controller !== controller) {\n const controllerState = getState(state.controller as HotComponent, skip);\n for (const k in controllerState) {\n (controller as Record<string, unknown>)[k] = controllerState[k];\n }\n }\n StateCache[fullRouteName] = {\n route: this,\n controller,\n };\n return r;\n };\n}\n\nexport default {\n initialize,\n};\n"],"names":["findPropertyDescriptor","component","key","proto","desc","Object","getOwnPropertyDescriptor","getPrototypeOf","RESOURCE_INTERMEDIATE_VALUE_KEY","isUnsafeToCarryOver","value","getState","skip","state","includes","entry","Service","writable","set","syncState","instance","args","__hot__","oldInstance","__get_hot_state__","k","_hmrAccepted","initialize","import","meta","hot","ComponentManager","getInternalComponentManager","Component","create","call","setTimeout","setupController","Route","prototype","StateCache","controller","r","fullRouteName","routerState","router","controllerState","route"],"mappings":"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"}
@@ -0,0 +1,3 @@
1
+ import type * as BabelTypesNamespace from '@babel/types';
2
+ import type * as Babel from '@babel/core';
3
+ export declare function renamePrivateClassMembers(babel: typeof Babel, classPath: Babel.NodePath<BabelTypesNamespace.ClassDeclaration | BabelTypesNamespace.ClassExpression>, programPath: Babel.NodePath<BabelTypesNamespace.Program>): void;
@@ -0,0 +1,291 @@
1
+ function hasPrivateMembers(classBody) {
2
+ return classBody.body.some((member) => member.type === 'ClassPrivateProperty' ||
3
+ member.type === 'ClassPrivateMethod');
4
+ }
5
+ // The generated HMR proxy (see service-proxy.ts's `ctorBody`) reads and
6
+ // writes service state through a Proxy whose `get`/`set` traps forward to a
7
+ // separately constructed delegate instance. Native `#private` fields can
8
+ // only ever be accessed with `this` bound to the exact object that declared
9
+ // them, and a method called as `proxy.method()` runs with `this` set to the
10
+ // proxy, not the delegate -- so a `#private` field read from ANY function
11
+ // reached through the proxy (a prototype method or a plain own-property
12
+ // function assigned in the constructor) would throw "Cannot read private
13
+ // member ... from an object whose class did not declare it" once called
14
+ // through it. An earlier version of this proxy special-cased prototype
15
+ // methods by `.bind()`-ing them to the delegate, but that's exactly
16
+ // backwards for an own-property function value: `bind()` returns a fresh
17
+ // function object, silently dropping any metadata identity-sensitive code
18
+ // associates with the original one via a WeakMap (e.g.
19
+ // `modifier()`/`helper()`, `setComponentManager` -- see #560/#561).
20
+ //
21
+ // Rewriting `#foo` to a plain, uniquely-named, non-computed property
22
+ // sidesteps the whole problem instead: an ordinary property reads and
23
+ // writes correctly through the proxy's existing traps no matter what `this`
24
+ // is at the call site, so no per-property binding is needed at all. This
25
+ // runs generically on *every* class the app's own source defines (see the
26
+ // `Class` visitor in ../../babel-plugin.ts), not just service classes, so it
27
+ // also covers `#private` fields on an ancestor class the service extends,
28
+ // as long as that ancestor is part of the app's own source (and therefore
29
+ // passes through this same babel pass). It does NOT help with a `#private`
30
+ // field declared on an ancestor class that lives in `node_modules` -- that
31
+ // file is never passed through this transform, so its fields stay fully
32
+ // native, and a prototype method inherited from it would still crash if
33
+ // called through the proxy. Ember's own `Service`/`EmberObject`/`CoreObject`
34
+ // chain declares no native `#private` fields, so this doesn't affect normal
35
+ // Ember apps; a third-party base class that does would need to stop using
36
+ // native `#private` fields, or avoid extending it directly with an
37
+ // HMR-proxied service.
38
+ //
39
+ // A Symbol-keyed property (an earlier version of this rewrite) would keep
40
+ // the member non-enumerable, closer to real privacy, but Ember's `@tracked`
41
+ // rejects it outright: `@glimmer/tracking`'s decorator entry point
42
+ // distinguishes "I was invoked as a native decorator" from "I was invoked as
43
+ // `tracked({...})`" by checking `typeof key === 'string'` (see
44
+ // `isElementDescriptor` in `@ember/-internals`), so a `@tracked #count = 0`
45
+ // field renamed to a Symbol key silently falls through to the wrong branch
46
+ // and never tracks at all (verified empirically: reads back `undefined`,
47
+ // writes throw "Cannot assign to read only property"). A plain string key
48
+ // satisfies that check and lets `@tracked`/`decorator-transforms` treat the
49
+ // member exactly like an ordinary public tracked field.
50
+ //
51
+ // An *undecorated* private property still gets a second pass: after the
52
+ // class body is rewritten, `hideRenamedProperties` below appends
53
+ // `Object.defineProperty(this, <name>, { ..., enumerable: false })` calls to
54
+ // the constructor for every renamed instance property that has no
55
+ // decorator, so it stays invisible to `for...in`, `Object.keys`,
56
+ // `JSON.stringify` and spread -- closer to real privacy than the plain
57
+ // enumerable field this rewrite produced before. It's still just a string
58
+ // key reachable via `Object.getOwnPropertyNames`/`Reflect.ownKeys` or the
59
+ // generated name, not real encapsulation. A *decorated* private property
60
+ // (e.g. `@tracked #count`) is left exactly as the field-rewrite above
61
+ // produces it -- `decorator-transforms` turns it into an accessor pair on
62
+ // the prototype, not an instance data property, so there's no own property
63
+ // for `Object.defineProperty` to hide in the first place, and forcing one
64
+ // would fight the decorator's own storage instead of complementing it.
65
+ //
66
+ // This second pass only runs against *instance* properties: a static
67
+ // private field lives on the class itself, never reaches the HMR proxy's
68
+ // per-instance `for...in` sync loop, and isn't part of the proxy/delegate
69
+ // problem this file exists to solve, so it's left as the plain enumerable
70
+ // rewrite the first pass already produces.
71
+ //
72
+ // The one thing a generated name can't be checked against is a private
73
+ // member of the *same name* declared by an ancestor/subclass living in a
74
+ // different file (each file's uid generation only sees its own bindings) --
75
+ // deliberately not solved here, since the `hmrPriv<Name>` prefix makes a
76
+ // same-name collision across unrelated classes exceedingly unlikely in
77
+ // practice, and the alternative (whole-program cross-file analysis) is out
78
+ // of proportion to that risk.
79
+ export function renamePrivateClassMembers(babel, classPath, programPath) {
80
+ var _a;
81
+ const t = babel.types;
82
+ if (!hasPrivateMembers(classPath.node.body)) {
83
+ return;
84
+ }
85
+ // `renamedNames` tracks which private member names get rewritten so the
86
+ // reference-rewriting traversal below only touches references to *this*
87
+ // class's own renamed members (see the `classPath.traverse` call further
88
+ // down).
89
+ const renamedNames = new Set();
90
+ for (const member of classPath.node.body.body) {
91
+ if (member.type === 'ClassPrivateProperty' ||
92
+ member.type === 'ClassPrivateMethod') {
93
+ renamedNames.add(member.key.id.name);
94
+ }
95
+ }
96
+ // Existing (non-computed, identifier-keyed) member names in this class's
97
+ // own body, so a generated replacement name can never collide with an
98
+ // unrelated member already declared here.
99
+ const usedNames = new Set();
100
+ for (const member of classPath.node.body.body) {
101
+ const key = member.key;
102
+ const computed = member.computed;
103
+ if (key && !computed && t.isIdentifier(key)) {
104
+ usedNames.add(key.name);
105
+ }
106
+ }
107
+ const newNames = new Map();
108
+ const getNewName = (name) => {
109
+ let known = newNames.get(name);
110
+ if (known) {
111
+ return known;
112
+ }
113
+ const base = `hmrPriv${name.charAt(0).toUpperCase()}${name.slice(1)}`;
114
+ let candidate;
115
+ do {
116
+ candidate = programPath.scope.generateUidIdentifier(base).name;
117
+ } while (usedNames.has(candidate));
118
+ usedNames.add(candidate);
119
+ newNames.set(name, candidate);
120
+ return candidate;
121
+ };
122
+ // Names of renamed instance properties that should be hidden from
123
+ // enumeration afterward (see `hideRenamedProperties` below) -- only
124
+ // undecorated, non-static private *properties* qualify; see the comment
125
+ // above this function for why decorated and static ones are excluded.
126
+ const namesToHide = [];
127
+ // Rename the class's own private declarations directly off the body
128
+ // array -- not via `classPath.traverse` -- so a nested class's own
129
+ // (unrelated, possibly same-named) private declarations are never
130
+ // touched: they simply aren't in this array.
131
+ for (const memberPath of classPath.get('body').get('body')) {
132
+ if (memberPath.isClassPrivateProperty()) {
133
+ const node = memberPath.node;
134
+ const newName = getNewName(node.key.id.name);
135
+ memberPath.replaceWith(t.classProperty(t.identifier(newName), node.value, node.typeAnnotation, node.decorators, false, node.static));
136
+ if (!node.static && !((_a = node.decorators) === null || _a === void 0 ? void 0 : _a.length)) {
137
+ namesToHide.push(newName);
138
+ }
139
+ }
140
+ else if (memberPath.isClassPrivateMethod()) {
141
+ const node = memberPath.node;
142
+ const replacement = t.classMethod(node.kind, t.identifier(getNewName(node.key.id.name)), node.params, node.body, false, node.static, node.generator, node.async);
143
+ // `t.classMethod`'s builder has no `decorators` parameter -- has to be
144
+ // assigned after the fact or a decorated private method (e.g. `@action
145
+ // #foo() {}`) would silently lose its decorator in the rewrite.
146
+ replacement.decorators = node.decorators;
147
+ memberPath.replaceWith(replacement);
148
+ }
149
+ }
150
+ // A private name reference found inside a nested class only actually
151
+ // belongs to `classPath`'s own renamed declaration if no closer-enclosing
152
+ // class *redeclares* the same name -- private names resolve to the
153
+ // nearest enclosing class that declares them, same as any other lexical
154
+ // scoping, so a nested class legitimately shadowing `#secret` with its
155
+ // own `#secret` must keep referencing its own (native, untouched) field.
156
+ const declaresOwnPrivate = (cls, name) => cls.body.body.some((member) => (member.type === 'ClassPrivateProperty' ||
157
+ member.type === 'ClassPrivateMethod') &&
158
+ member.key.id.name === name);
159
+ const referenceBelongsToOuterClass = (path, name) => {
160
+ let enclosing = path.findParent((p) => p.isClass());
161
+ while (enclosing) {
162
+ if (enclosing.node === classPath.node) {
163
+ return true;
164
+ }
165
+ if (declaresOwnPrivate(enclosing.node, name)) {
166
+ return false;
167
+ }
168
+ enclosing = enclosing.findParent((p) => p.isClass());
169
+ }
170
+ return false;
171
+ };
172
+ // Rewrite *references* to the just-renamed names anywhere within the
173
+ // class, including inside nested classes/closures. Private names are
174
+ // lexically scoped to their enclosing class body, not to the file, so
175
+ // code legally references an outer class's private field from inside a
176
+ // nested class via a captured `this` (e.g. a factory method that builds
177
+ // and returns a class closing over `self.#secret`). Blanket-skipping
178
+ // traversal into nested classes -- as an earlier version of this
179
+ // function did -- left such references as native `#foo` PrivateNames
180
+ // while the outer declaration was renamed to a Symbol, producing invalid
181
+ // output ("Private field must be declared in an enclosing class").
182
+ classPath.traverse({
183
+ 'MemberExpression|OptionalMemberExpression'(path) {
184
+ const property = path.node.property;
185
+ if (t.isPrivateName(property) &&
186
+ renamedNames.has(property.id.name) &&
187
+ referenceBelongsToOuterClass(path, property.id.name)) {
188
+ path.node.property = t.identifier(getNewName(property.id.name));
189
+ path.node.computed = false;
190
+ }
191
+ },
192
+ BinaryExpression(path) {
193
+ const left = path.node.left;
194
+ if (path.node.operator === 'in' &&
195
+ t.isPrivateName(left) &&
196
+ renamedNames.has(left.id.name) &&
197
+ referenceBelongsToOuterClass(path, left.id.name)) {
198
+ path.node.left = t.stringLiteral(getNewName(left.id.name));
199
+ }
200
+ },
201
+ });
202
+ if (namesToHide.length > 0) {
203
+ hideRenamedProperties(babel, classPath, namesToHide);
204
+ }
205
+ }
206
+ // Inserts `Object.defineProperty(this, <name>, { value: this.<name>,
207
+ // writable: true, configurable: true, enumerable: false })` into the class's
208
+ // constructor for every name in `names`, so the renamed properties in
209
+ // `names` stop showing up in `for...in`, `Object.keys`, `JSON.stringify`,
210
+ // and spread -- the same visibility a plain class-field rewrite (which is
211
+ // always enumerable; field syntax has no way to declare otherwise) can't
212
+ // provide on its own.
213
+ //
214
+ // This inserts rather than replacing the field declaration in place so the
215
+ // field keeps initializing at its original position in declaration order --
216
+ // a later field's initializer that legally reads an earlier private field
217
+ // (`#foo = 1; bar = this.#foo + 1;`) still sees the real value, since fields
218
+ // finish initializing (in declaration order, interleaved with each other)
219
+ // before any explicit constructor statement -- including ones this function
220
+ // adds -- ever runs, regardless of where in the constructor body they're
221
+ // inserted.
222
+ //
223
+ // The statements go right after `super(...)` (derived class) or at the very
224
+ // top of the constructor (base class) rather than at the end: all fields
225
+ // are already initialized by that point, and a later `this.<name> = ...`
226
+ // assignment in the constructor only updates the property's `value` -- a
227
+ // plain assignment doesn't reapply `defineProperty`'s `enumerable: false`,
228
+ // so inserting early still reflects any later mutation correctly. Inserting
229
+ // early also means the hide calls still run even if the constructor's own
230
+ // logic contains an early `return` (e.g. a guard clause) after this point --
231
+ // appending at the end would make them dead code on that path.
232
+ //
233
+ // Reads the current value back off `this` (a plain `MemberExpression`)
234
+ // rather than a captured property descriptor: when this class is a subclass
235
+ // of a proxied HMR service, `this` after `super()` is the Proxy from
236
+ // `service-proxy.ts`'s generated constructor, and a plain read goes through
237
+ // its `get` trap, which already forwards correctly to the delegate the
238
+ // property actually lives on.
239
+ function hideRenamedProperties(babel, classPath, names) {
240
+ const t = babel.types;
241
+ const hideStatements = names.map((name) => t.expressionStatement(t.callExpression(t.memberExpression(t.identifier('Object'), t.identifier('defineProperty')), [
242
+ t.thisExpression(),
243
+ t.stringLiteral(name),
244
+ t.objectExpression([
245
+ t.objectProperty(t.identifier('value'), t.memberExpression(t.thisExpression(), t.identifier(name))),
246
+ t.objectProperty(t.identifier('writable'), t.booleanLiteral(true)),
247
+ t.objectProperty(t.identifier('configurable'), t.booleanLiteral(true)),
248
+ t.objectProperty(t.identifier('enumerable'), t.booleanLiteral(false)),
249
+ ]),
250
+ ])));
251
+ const bodyPath = classPath.get('body');
252
+ const existingCtor = bodyPath
253
+ .get('body')
254
+ .find((member) => member.isClassMethod({ kind: 'constructor' }));
255
+ if (existingCtor) {
256
+ const ctorBody = existingCtor.get('body');
257
+ if (!classPath.node.superClass) {
258
+ ctorBody.unshiftContainer('body', hideStatements);
259
+ return;
260
+ }
261
+ // Only a top-level `super(...)` statement is safe to insert after --
262
+ // one buried inside a conditional (`if (x) { super(a); } else {
263
+ // super(b); }`) isn't a single point in the body to anchor on. Fall
264
+ // back to appending at the end in that case: still not immune to an
265
+ // early `return`, but at least not a `ReferenceError` from reading
266
+ // `this` before `super()` has (unconditionally) run.
267
+ const superCallStatement = ctorBody
268
+ .get('body')
269
+ .find((statement) => statement.isExpressionStatement() &&
270
+ statement.get('expression').isCallExpression() &&
271
+ statement.get('expression.callee').isSuper());
272
+ if (superCallStatement) {
273
+ superCallStatement.insertAfter(hideStatements);
274
+ }
275
+ else {
276
+ ctorBody.pushContainer('body', hideStatements);
277
+ }
278
+ return;
279
+ }
280
+ // No constructor declared: synthesize one. A derived class's implicit
281
+ // default constructor forwards every argument to `super` before running
282
+ // anything else, so match that exactly rather than dropping constructor
283
+ // arguments the real default constructor would have passed through.
284
+ const ctorStatements = classPath.node.superClass
285
+ ? [
286
+ t.expressionStatement(t.callExpression(t.super(), [t.spreadElement(t.identifier('args'))])),
287
+ ...hideStatements,
288
+ ]
289
+ : hideStatements;
290
+ bodyPath.unshiftContainer('body', t.classMethod('constructor', t.identifier('constructor'), [t.restElement(t.identifier('args'))], t.blockStatement(ctorStatements)));
291
+ }
@@ -0,0 +1,3 @@
1
+ import type * as BabelTypesNamespace from '@babel/types';
2
+ import type * as Babel from '@babel/core';
3
+ export declare function transformServiceExport(babel: typeof Babel, path: Babel.NodePath<BabelTypesNamespace.ExportDefaultDeclaration>, state: Babel.PluginPass): void;