ember-vite-hmr 2.4.0 → 2.7.1
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/dist/instance-initializers/setup-hmr-manager.js +64 -19
- package/dist/instance-initializers/setup-hmr-manager.js.map +1 -1
- package/dist/lib/babel-plugin/private-members.js +20 -12
- package/dist/lib/babel-plugin/service-proxy.js +2 -2
- package/dist/lib/babel-plugin/template-imports.js +16 -5
- package/dist/lib/babel-plugin.js +57 -1
- package/dist/lib/hmr.d.ts +3 -1
- package/dist/lib/hmr.js +317 -564
- package/package.json +10 -10
|
@@ -3,6 +3,13 @@ import Service from '@ember/service';
|
|
|
3
3
|
import Component from '@glimmer/component';
|
|
4
4
|
import { getInternalComponentManager } from '@glimmer/manager';
|
|
5
5
|
|
|
6
|
+
// Accessor for the HMR runtime helpers exposed on the global by
|
|
7
|
+
// virtual:ember-vite-hmr-runtime. Only set in dev/serve mode (guarded by
|
|
8
|
+
// `if (!import.meta.hot)` at the call site).
|
|
9
|
+
|
|
10
|
+
function hmrRuntime() {
|
|
11
|
+
return globalThis.__ember_vite_hmr;
|
|
12
|
+
}
|
|
6
13
|
function findPropertyDescriptor(component, key) {
|
|
7
14
|
let proto = component;
|
|
8
15
|
while (proto) {
|
|
@@ -80,24 +87,18 @@ function getState(component, skip) {
|
|
|
80
87
|
}
|
|
81
88
|
return state;
|
|
82
89
|
}
|
|
83
|
-
function syncState(instance) {
|
|
84
|
-
const args = instance.args;
|
|
85
|
-
if (args.__hot__) {
|
|
86
|
-
const oldInstance = args.__hot__.oldInstance;
|
|
87
|
-
const state = instance.__get_hot_state__?.() || args.__hot__.getState?.();
|
|
88
|
-
for (const k in state) {
|
|
89
|
-
if (instance[k] instanceof Service) {
|
|
90
|
-
continue;
|
|
91
|
-
}
|
|
92
|
-
instance[k] = state[k];
|
|
93
|
-
}
|
|
94
90
|
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
91
|
+
// Apply a stashed old-instance state to a freshly created new instance.
|
|
92
|
+
// Also calls the _hmrAccepted lifecycle hook if defined on the new instance.
|
|
93
|
+
function applySyncState(instance, state, oldInstance) {
|
|
94
|
+
for (const k in state) {
|
|
95
|
+
if (instance[k] instanceof Service) {
|
|
96
|
+
continue;
|
|
98
97
|
}
|
|
99
|
-
|
|
100
|
-
|
|
98
|
+
instance[k] = state[k];
|
|
99
|
+
}
|
|
100
|
+
if (typeof instance._hmrAccepted === 'function') {
|
|
101
|
+
instance._hmrAccepted(oldInstance);
|
|
101
102
|
}
|
|
102
103
|
}
|
|
103
104
|
function initialize() {
|
|
@@ -108,9 +109,53 @@ function initialize() {
|
|
|
108
109
|
const proto = Object.getPrototypeOf(ComponentManager);
|
|
109
110
|
const create = proto.create;
|
|
110
111
|
proto.create = function (...args) {
|
|
111
|
-
const
|
|
112
|
-
|
|
113
|
-
|
|
112
|
+
const bucket = create.call(this, ...args);
|
|
113
|
+
const component = bucket.component;
|
|
114
|
+
|
|
115
|
+
// Enqueue this instance in the runtime's per-class FIFO queue. The runtime
|
|
116
|
+
// only enqueues when isHmrClass(klass) is true (registered HMR class), and
|
|
117
|
+
// clears the queue at update() time so user-flow stale entries are never
|
|
118
|
+
// present when willDestroy fires during an HMR swap.
|
|
119
|
+
const klass = component.constructor;
|
|
120
|
+
const rt = hmrRuntime();
|
|
121
|
+
if (rt?.isHmrClass(klass)) {
|
|
122
|
+
rt.enqueueInstance(klass, component);
|
|
123
|
+
}
|
|
124
|
+
return bucket;
|
|
125
|
+
};
|
|
126
|
+
|
|
127
|
+
// Patch willDestroy on the Component prototype to detect HMR swaps and
|
|
128
|
+
// transfer state from the old instance to the freshly-created new one.
|
|
129
|
+
//
|
|
130
|
+
// Why willDestroy instead of registerDestructor: Glimmer creates the NEW
|
|
131
|
+
// component instance BEFORE calling willDestroy on the OLD one (willDestroy
|
|
132
|
+
// is scheduled in Ember's "actions" runloop queue, which runs after
|
|
133
|
+
// rendering). This ordering is exactly what we need: by willDestroy time,
|
|
134
|
+
// the new instance is already live in liveInstances.
|
|
135
|
+
const origWillDestroy = Component.prototype.willDestroy;
|
|
136
|
+
Component.prototype.willDestroy = function () {
|
|
137
|
+
const rt = hmrRuntime();
|
|
138
|
+
if (rt) {
|
|
139
|
+
const klass = this.constructor;
|
|
140
|
+
const newClass = rt.current(klass);
|
|
141
|
+
if (newClass !== klass) {
|
|
142
|
+
// The class was swapped via HMR — dequeue the next new instance for
|
|
143
|
+
// the replacement class (FIFO matches DOM/creation order) and
|
|
144
|
+
// schedule a state transfer onto it.
|
|
145
|
+
const newInstance = rt.dequeueInstance(newClass);
|
|
146
|
+
if (newInstance) {
|
|
147
|
+
const state = getState(this, ['args']);
|
|
148
|
+
const oldInstance = this;
|
|
149
|
+
setTimeout(() => applySyncState(newInstance, state, oldInstance));
|
|
150
|
+
}
|
|
151
|
+
} else if (rt.isHmrClass(klass)) {
|
|
152
|
+
// Normal destroy (navigation, timer, conditional) — remove this
|
|
153
|
+
// instance from the live queue so it doesn't accumulate as a stale
|
|
154
|
+
// entry that update() would otherwise have to clear at the next save.
|
|
155
|
+
rt.removeInstance(klass, this);
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
return origWillDestroy?.call(this);
|
|
114
159
|
};
|
|
115
160
|
const setupController = Route.prototype.setupController;
|
|
116
161
|
const StateCache = {};
|
|
@@ -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\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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"}
|
|
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 {\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\n// Accessor for the HMR runtime helpers exposed on the global by\n// virtual:ember-vite-hmr-runtime. Only set in dev/serve mode (guarded by\n// `if (!import.meta.hot)` at the call site).\ninterface HmrRuntime {\n current: (value: unknown) => unknown;\n isHmrClass: (value: unknown) => boolean;\n enqueueInstance: (klass: unknown, instance: HotComponent) => void;\n dequeueInstance: (klass: unknown) => HotComponent | null;\n removeInstance: (klass: unknown, instance: HotComponent) => void;\n}\n\nfunction hmrRuntime(): HmrRuntime | undefined {\n return (globalThis as unknown as { __ember_vite_hmr?: HmrRuntime }).__ember_vite_hmr;\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\n// Apply a stashed old-instance state to a freshly created new instance.\n// Also calls the _hmrAccepted lifecycle hook if defined on the new instance.\nfunction applySyncState(\n instance: Mutable<HotComponent>,\n state: Record<string, unknown>,\n oldInstance: HotComponent | undefined,\n) {\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 if (typeof instance._hmrAccepted === 'function') {\n instance._hmrAccepted(oldInstance);\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 bucket = create.call(this, ...args);\n const component = bucket.component as HotComponent;\n\n // Enqueue this instance in the runtime's per-class FIFO queue. The runtime\n // only enqueues when isHmrClass(klass) is true (registered HMR class), and\n // clears the queue at update() time so user-flow stale entries are never\n // present when willDestroy fires during an HMR swap.\n const klass = component.constructor as object;\n const rt = hmrRuntime();\n if (rt?.isHmrClass(klass)) {\n rt.enqueueInstance(klass, component);\n }\n\n return bucket;\n };\n\n // Patch willDestroy on the Component prototype to detect HMR swaps and\n // transfer state from the old instance to the freshly-created new one.\n //\n // Why willDestroy instead of registerDestructor: Glimmer creates the NEW\n // component instance BEFORE calling willDestroy on the OLD one (willDestroy\n // is scheduled in Ember's \"actions\" runloop queue, which runs after\n // rendering). This ordering is exactly what we need: by willDestroy time,\n // the new instance is already live in liveInstances.\n const origWillDestroy = Component.prototype.willDestroy;\n Component.prototype.willDestroy = function (this: HotComponent) {\n const rt = hmrRuntime();\n if (rt) {\n const klass = this.constructor as object;\n const newClass = rt.current(klass);\n if (newClass !== klass) {\n // The class was swapped via HMR — dequeue the next new instance for\n // the replacement class (FIFO matches DOM/creation order) and\n // schedule a state transfer onto it.\n const newInstance = rt.dequeueInstance(newClass);\n if (newInstance) {\n const state = getState(this, ['args']);\n const oldInstance = this;\n setTimeout(() => applySyncState(newInstance as Mutable<HotComponent>, state, oldInstance));\n }\n } else if (rt.isHmrClass(klass)) {\n // Normal destroy (navigation, timer, conditional) — remove this\n // instance from the live queue so it doesn't accumulate as a stale\n // entry that update() would otherwise have to clear at the next save.\n rt.removeInstance(klass, this);\n }\n }\n return origWillDestroy?.call(this);\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":["hmrRuntime","globalThis","__ember_vite_hmr","findPropertyDescriptor","component","key","proto","desc","Object","getOwnPropertyDescriptor","getPrototypeOf","RESOURCE_INTERMEDIATE_VALUE_KEY","isUnsafeToCarryOver","value","getState","skip","state","includes","entry","Service","writable","set","applySyncState","instance","oldInstance","k","_hmrAccepted","initialize","import","meta","hot","ComponentManager","getInternalComponentManager","Component","create","args","bucket","call","klass","constructor","rt","isHmrClass","enqueueInstance","origWillDestroy","prototype","willDestroy","newClass","current","newInstance","dequeueInstance","setTimeout","removeInstance","setupController","Route","StateCache","controller","r","fullRouteName","routerState","router","controllerState","route"],"mappings":"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"}
|
|
@@ -179,16 +179,18 @@ export function renamePrivateClassMembers(babel, classPath, programPath) {
|
|
|
179
179
|
// function did -- left such references as native `#foo` PrivateNames
|
|
180
180
|
// while the outer declaration was renamed to a Symbol, producing invalid
|
|
181
181
|
// output ("Private field must be declared in an enclosing class").
|
|
182
|
+
const rewriteMemberReference = (path) => {
|
|
183
|
+
const property = path.node.property;
|
|
184
|
+
if (t.isPrivateName(property) &&
|
|
185
|
+
renamedNames.has(property.id.name) &&
|
|
186
|
+
referenceBelongsToOuterClass(path, property.id.name)) {
|
|
187
|
+
path.node.property = t.identifier(getNewName(property.id.name));
|
|
188
|
+
path.node.computed = false;
|
|
189
|
+
}
|
|
190
|
+
};
|
|
182
191
|
classPath.traverse({
|
|
183
|
-
|
|
184
|
-
|
|
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
|
+
MemberExpression: rewriteMemberReference,
|
|
193
|
+
OptionalMemberExpression: rewriteMemberReference,
|
|
192
194
|
BinaryExpression(path) {
|
|
193
195
|
const left = path.node.left;
|
|
194
196
|
if (path.node.operator === 'in' &&
|
|
@@ -266,9 +268,15 @@ function hideRenamedProperties(babel, classPath, names) {
|
|
|
266
268
|
// `this` before `super()` has (unconditionally) run.
|
|
267
269
|
const superCallStatement = ctorBody
|
|
268
270
|
.get('body')
|
|
269
|
-
.find((statement) =>
|
|
270
|
-
statement.
|
|
271
|
-
|
|
271
|
+
.find((statement) => {
|
|
272
|
+
if (!statement.isExpressionStatement())
|
|
273
|
+
return false;
|
|
274
|
+
const expr = statement.get('expression');
|
|
275
|
+
if (Array.isArray(expr) || !expr.isCallExpression())
|
|
276
|
+
return false;
|
|
277
|
+
const callee = expr.get('callee');
|
|
278
|
+
return !Array.isArray(callee) && callee.isSuper();
|
|
279
|
+
});
|
|
272
280
|
if (superCallStatement) {
|
|
273
281
|
superCallStatement.insertAfter(hideStatements);
|
|
274
282
|
}
|
|
@@ -10,7 +10,7 @@ import { ImportUtil } from 'babel-import-util';
|
|
|
10
10
|
// `renamePrivateClassMembers` (see ../private-members.ts) before this runs,
|
|
11
11
|
// so this function doesn't need to special-case them at all.
|
|
12
12
|
export function transformServiceExport(babel, path, state) {
|
|
13
|
-
var _a;
|
|
13
|
+
var _a, _b;
|
|
14
14
|
const t = babel.types;
|
|
15
15
|
// Check if this is a service file (normalize path for cross-platform compatibility)
|
|
16
16
|
const normalizedFilename = (_a = state.filename) === null || _a === void 0 ? void 0 : _a.replace(/\\/g, '/');
|
|
@@ -34,7 +34,7 @@ export function transformServiceExport(babel, path, state) {
|
|
|
34
34
|
if (binding && binding.path.isClassDeclaration()) {
|
|
35
35
|
classDeclaration = binding.path
|
|
36
36
|
.node;
|
|
37
|
-
classIdentifier = classDeclaration.id;
|
|
37
|
+
classIdentifier = (_b = classDeclaration.id) !== null && _b !== void 0 ? _b : null;
|
|
38
38
|
classPath =
|
|
39
39
|
binding.path;
|
|
40
40
|
}
|
|
@@ -206,14 +206,25 @@ export function finalizeTemplateImports(babel, path, filename) {
|
|
|
206
206
|
return;
|
|
207
207
|
}
|
|
208
208
|
const util = new ImportUtil(babel, path);
|
|
209
|
-
|
|
210
|
-
|
|
209
|
+
// `current` is imported from the HMR runtime virtual module. The runtime
|
|
210
|
+
// reads `entry.current` (a @tracked cell) and returns the latest class.
|
|
211
|
+
// Using a getter here (rather than a @tracked field that gets reassigned)
|
|
212
|
+
// means Glimmer only invalidates the scope that contains the `current()`
|
|
213
|
+
// read — not the parent scope that holds the `template__imports__` object.
|
|
214
|
+
// This keeps any outer {{#let}} wrappers (e.g. for stable per-invocation
|
|
215
|
+
// ids) alive across HMR swaps of the child component.
|
|
216
|
+
const currentFn = util.import(path, 'virtual:ember-vite-hmr-runtime', 'current');
|
|
211
217
|
const klass = t.classExpression(path.scope.generateUidIdentifier('Imports'), null, t.classBody([]));
|
|
212
218
|
const bindings = [...hotAstProcessor.meta.importBindings].sort();
|
|
213
219
|
for (const local of bindings) {
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
220
|
+
// Emit: get Local() { return current(LocalBinding); }
|
|
221
|
+
// The getter closes over `LocalBinding` (the initial import value).
|
|
222
|
+
// `current()` reads `entry.current` which is @tracked in the runtime,
|
|
223
|
+
// so only the template expression that calls this getter is invalidated
|
|
224
|
+
// when the binding is updated via update(old, new).
|
|
225
|
+
klass.body.body.push(t.classMethod('get', t.identifier(local), [], t.blockStatement([
|
|
226
|
+
t.returnStatement(t.callExpression(currentFn, [t.identifier(local)])),
|
|
227
|
+
])));
|
|
217
228
|
}
|
|
218
229
|
const newExp = t.newExpression(klass, []);
|
|
219
230
|
const assign = t.assignmentExpression('=', t.identifier(hotAstProcessor.meta.importVar), newExp);
|
package/dist/lib/babel-plugin.js
CHANGED
|
@@ -54,7 +54,7 @@ export default function hotReplaceAst(babel) {
|
|
|
54
54
|
renamePrivateClassMembers(babel, path, programPath);
|
|
55
55
|
},
|
|
56
56
|
ExportDefaultDeclaration(path, state) {
|
|
57
|
-
var _a;
|
|
57
|
+
var _a, _b;
|
|
58
58
|
if (process.env.EMBER_VITE_HMR_ENABLED !== 'true') {
|
|
59
59
|
return;
|
|
60
60
|
}
|
|
@@ -62,6 +62,62 @@ export default function hotReplaceAst(babel) {
|
|
|
62
62
|
return;
|
|
63
63
|
}
|
|
64
64
|
transformServiceExport(babel, path, state);
|
|
65
|
+
// For every non-service default export, rewrite:
|
|
66
|
+
// export default <expr>
|
|
67
|
+
// into:
|
|
68
|
+
// const __hmr_default__ = <expr>;
|
|
69
|
+
// export { __hmr_default__ as default };
|
|
70
|
+
//
|
|
71
|
+
// This gives lib/hmr.ts's appended HMR code a stable local variable
|
|
72
|
+
// name to pass to register()/update()/used() regardless of whether
|
|
73
|
+
// the export is a named class, an anonymous expression, or a compiled
|
|
74
|
+
// template. Services are excluded because they are already rewritten
|
|
75
|
+
// by transformServiceExport above with their own proxy mechanism.
|
|
76
|
+
const normalizedFilename = (_b = state.filename) === null || _b === void 0 ? void 0 : _b.replace(/\\/g, '/');
|
|
77
|
+
if (normalizedFilename === null || normalizedFilename === void 0 ? void 0 : normalizedFilename.includes('/services/')) {
|
|
78
|
+
return;
|
|
79
|
+
}
|
|
80
|
+
const t = babel.types;
|
|
81
|
+
const declaration = path.node.declaration;
|
|
82
|
+
const exportDefault = t.exportNamedDeclaration(null, [
|
|
83
|
+
t.exportSpecifier(t.identifier('__hmr_default__'), t.identifier('default')),
|
|
84
|
+
]);
|
|
85
|
+
const isClassOrFunction = declaration.type === 'ClassDeclaration' ||
|
|
86
|
+
declaration.type === 'FunctionDeclaration';
|
|
87
|
+
let newPaths;
|
|
88
|
+
if (isClassOrFunction && declaration.id) {
|
|
89
|
+
// A named class/function becomes a declaration statement in its own
|
|
90
|
+
// right — just alias the identifier.
|
|
91
|
+
newPaths = path.replaceWithMultiple([
|
|
92
|
+
declaration,
|
|
93
|
+
t.variableDeclaration('const', [
|
|
94
|
+
t.variableDeclarator(t.identifier('__hmr_default__'), t.identifier(declaration.id.name)),
|
|
95
|
+
]),
|
|
96
|
+
exportDefault,
|
|
97
|
+
]);
|
|
98
|
+
}
|
|
99
|
+
else {
|
|
100
|
+
// Anonymous expression (template-only component, arrow fn, etc.).
|
|
101
|
+
// An anonymous `class extends X {}` / `function () {}` is parsed as
|
|
102
|
+
// a declaration node and must be converted to an expression to be
|
|
103
|
+
// a valid initializer.
|
|
104
|
+
const init = isClassOrFunction
|
|
105
|
+
? t.toExpression(declaration)
|
|
106
|
+
: declaration;
|
|
107
|
+
newPaths = path.replaceWithMultiple([
|
|
108
|
+
t.variableDeclaration('const', [
|
|
109
|
+
t.variableDeclarator(t.identifier('__hmr_default__'), init),
|
|
110
|
+
]),
|
|
111
|
+
exportDefault,
|
|
112
|
+
]);
|
|
113
|
+
}
|
|
114
|
+
// Register the new `__hmr_default__` binding so later plugins (e.g.
|
|
115
|
+
// @babel/plugin-transform-typescript) can resolve the export.
|
|
116
|
+
for (const newPath of newPaths) {
|
|
117
|
+
if (newPath.isVariableDeclaration()) {
|
|
118
|
+
newPath.scope.registerDeclaration(newPath);
|
|
119
|
+
}
|
|
120
|
+
}
|
|
65
121
|
},
|
|
66
122
|
Program(path, state) {
|
|
67
123
|
var _a;
|
package/dist/lib/hmr.d.ts
CHANGED
|
@@ -1,2 +1,4 @@
|
|
|
1
1
|
import { Plugin } from 'vite';
|
|
2
|
-
export declare
|
|
2
|
+
export declare const hmrRuntimeId = "virtual:ember-vite-hmr-runtime";
|
|
3
|
+
export declare function hmrRuntime(): Plugin;
|
|
4
|
+
export declare function hmr(enableViteHmrForModes?: string[]): Plugin[];
|