ember-vite-hmr 2.3.0 → 2.7.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 +31 -0
- package/dist/instance-initializers/setup-hmr-manager.js +106 -22
- package/dist/instance-initializers/setup-hmr-manager.js.map +1 -1
- package/dist/lib/babel-plugin/private-members.d.ts +3 -0
- package/dist/lib/babel-plugin/private-members.js +297 -0
- package/dist/lib/babel-plugin/service-proxy.d.ts +3 -0
- package/dist/lib/babel-plugin/service-proxy.js +300 -0
- package/dist/lib/babel-plugin/template-imports.d.ts +46 -0
- package/dist/lib/babel-plugin/template-imports.js +261 -0
- package/dist/lib/babel-plugin.d.ts +3 -31
- package/dist/lib/babel-plugin.js +80 -410
- package/dist/lib/hmr.d.ts +3 -1
- package/dist/lib/hmr.js +355 -413
- package/package.json +5 -4
package/dist/lib/babel-plugin.js
CHANGED
|
@@ -1,143 +1,27 @@
|
|
|
1
|
-
import
|
|
2
|
-
import {
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
var _a;
|
|
25
|
-
if (process.env.EMBER_VITE_HMR_ENABLED !== 'true') {
|
|
26
|
-
return {
|
|
27
|
-
visitor: {},
|
|
28
|
-
};
|
|
29
|
-
}
|
|
30
|
-
if ((_a = env.filename) === null || _a === void 0 ? void 0 : _a.includes('node_modules')) {
|
|
31
|
-
return {
|
|
32
|
-
visitor: {},
|
|
33
|
-
};
|
|
34
|
-
}
|
|
35
|
-
const meta = this.meta;
|
|
36
|
-
const importVar = env.meta.jsutils.bindExpression(meta.importVar || 'null', null, {
|
|
37
|
-
nameHint: 'template__imports__',
|
|
38
|
-
});
|
|
39
|
-
meta.importVar = meta.importVar || importVar;
|
|
40
|
-
return {
|
|
41
|
-
visitor: Object.assign({}, this.buildVisitor({
|
|
42
|
-
importVar,
|
|
43
|
-
importBindings: meta.importBindings,
|
|
44
|
-
babelProgram: meta.babelProgram,
|
|
45
|
-
})),
|
|
46
|
-
};
|
|
47
|
-
}
|
|
48
|
-
buildVisitor({ importVar, importBindings, babelProgram, }) {
|
|
49
|
-
const findImport = function findImport(specifier) {
|
|
50
|
-
return babelProgram.body.find((b) => b.type === 'ImportDeclaration' &&
|
|
51
|
-
b.specifiers.some((s) => s.local.name === specifier));
|
|
52
|
-
};
|
|
53
|
-
const findBlockParams = function (expression, p) {
|
|
54
|
-
if (p.node.type === 'Template') {
|
|
55
|
-
return false;
|
|
56
|
-
}
|
|
57
|
-
if (p.node &&
|
|
58
|
-
p.node.type === 'BlockStatement' &&
|
|
59
|
-
p.node.program.blockParams.includes(expression)) {
|
|
60
|
-
return true;
|
|
61
|
-
}
|
|
62
|
-
const node = p.node;
|
|
63
|
-
if (node && node.blockParams && node.blockParams.includes(expression)) {
|
|
64
|
-
return true;
|
|
65
|
-
}
|
|
66
|
-
if (!p.parent)
|
|
67
|
-
return false;
|
|
68
|
-
return findBlockParams(expression, p.parent);
|
|
69
|
-
};
|
|
70
|
-
const visitor = {
|
|
71
|
-
PathExpression: (node, p) => {
|
|
72
|
-
var _a, _b;
|
|
73
|
-
if ((((_a = p.parentNode) === null || _a === void 0 ? void 0 : _a.type) === 'SubExpression' ||
|
|
74
|
-
((_b = p.parentNode) === null || _b === void 0 ? void 0 : _b.type) === 'MustacheStatement') &&
|
|
75
|
-
p.parentNode.params.includes(node)) {
|
|
76
|
-
return;
|
|
77
|
-
}
|
|
78
|
-
const original = node.original.split('.')[0];
|
|
79
|
-
if (original === 'this')
|
|
80
|
-
return;
|
|
81
|
-
if (original.startsWith('@'))
|
|
82
|
-
return;
|
|
83
|
-
if (original === 'block')
|
|
84
|
-
return;
|
|
85
|
-
if (original.startsWith('this.'))
|
|
86
|
-
return;
|
|
87
|
-
if (findBlockParams(original, p))
|
|
88
|
-
return;
|
|
89
|
-
if (node.original === 'helper' ||
|
|
90
|
-
node.original === 'component' ||
|
|
91
|
-
node.original === 'modifier') {
|
|
92
|
-
// node.original;
|
|
93
|
-
const parent = p.parentNode;
|
|
94
|
-
if (typeof parent.params[0].original !==
|
|
95
|
-
'string') {
|
|
96
|
-
return;
|
|
97
|
-
}
|
|
98
|
-
const original = parent.params[0].original.split('.')[0];
|
|
99
|
-
if (original && findBlockParams(original, p))
|
|
100
|
-
return;
|
|
101
|
-
if (original === null || original === void 0 ? void 0 : original.includes('.'))
|
|
102
|
-
return;
|
|
103
|
-
if (!original)
|
|
104
|
-
return;
|
|
105
|
-
if (findImport(original)) {
|
|
106
|
-
const param = glimmer.builders.path(`${importVar}.${original}`);
|
|
107
|
-
parent.params.splice(0, 1, param);
|
|
108
|
-
importBindings.add(original);
|
|
109
|
-
}
|
|
110
|
-
return;
|
|
111
|
-
}
|
|
112
|
-
if (importVar) {
|
|
113
|
-
if (findImport(node.original)) {
|
|
114
|
-
node.original = `${importVar}.${node.original}`;
|
|
115
|
-
node.parts = node.original.split('.');
|
|
116
|
-
importBindings.add(original);
|
|
117
|
-
}
|
|
118
|
-
return;
|
|
119
|
-
}
|
|
120
|
-
},
|
|
121
|
-
ElementNode: (element, p) => {
|
|
122
|
-
const original = element.tag.split('.')[0];
|
|
123
|
-
if (findBlockParams(original, p))
|
|
124
|
-
return;
|
|
125
|
-
if (importVar) {
|
|
126
|
-
if (findImport(original)) {
|
|
127
|
-
element.tag = `${importVar}.${original}`;
|
|
128
|
-
p.node.tag = element.tag;
|
|
129
|
-
importBindings.add(original);
|
|
130
|
-
}
|
|
131
|
-
return;
|
|
132
|
-
}
|
|
133
|
-
},
|
|
134
|
-
};
|
|
135
|
-
return visitor;
|
|
136
|
-
}
|
|
137
|
-
}
|
|
138
|
-
export const hotAstProcessor = new HotAstProcessor();
|
|
1
|
+
import { hotAstProcessor, finalizeTemplateImports, hmrImportMetadataCache, normalizeHmrCacheFilename, } from './babel-plugin/template-imports.js';
|
|
2
|
+
import { renamePrivateClassMembers } from './babel-plugin/private-members.js';
|
|
3
|
+
import { transformServiceExport } from './babel-plugin/service-proxy.js';
|
|
4
|
+
// `ember-vite-hmr/lib/babel-plugin` is a published subpath import (see
|
|
5
|
+
// test-app/babel.config.mjs), so this file's module path and its exports
|
|
6
|
+
// (this default export, and the `hotAstProcessor` / `hmrImportMetadataCache`
|
|
7
|
+
// / `normalizeHmrCacheFilename` re-exports below) have to keep resolving
|
|
8
|
+
// exactly as-is -- the actual logic lives in ./babel-plugin/*, split by
|
|
9
|
+
// concern:
|
|
10
|
+
// - template-imports.ts: rewrites template references to imported
|
|
11
|
+
// components/helpers/modifiers through a per-module tracked class, so
|
|
12
|
+
// swapping which module a virtual HMR wrapper resolves to is visible to
|
|
13
|
+
// templates (registered separately as an ember-template-compilation
|
|
14
|
+
// transform via the `hotAstProcessor` export below), and shares the
|
|
15
|
+
// computed import metadata with lib/hmr.ts via `hmrImportMetadataCache`.
|
|
16
|
+
// - private-members.ts: rewrites every native `#private` class member the
|
|
17
|
+
// app's own source declares to a plain, uniquely-named property, so it
|
|
18
|
+
// reads/writes correctly through the service proxy below regardless of
|
|
19
|
+
// what `this` is at the call site.
|
|
20
|
+
// - service-proxy.ts: rewrites `export default class X extends Service`
|
|
21
|
+
// into a generated HMR proxy class that swaps its delegate instance on
|
|
22
|
+
// every accepted update.
|
|
23
|
+
export { hotAstProcessor, hmrImportMetadataCache, normalizeHmrCacheFilename };
|
|
139
24
|
export default function hotReplaceAst(babel) {
|
|
140
|
-
const t = babel.types;
|
|
141
25
|
return {
|
|
142
26
|
name: 'a-hot-reload-imports',
|
|
143
27
|
pre(file) {
|
|
@@ -145,267 +29,83 @@ export default function hotReplaceAst(babel) {
|
|
|
145
29
|
hotAstProcessor.meta.babelProgram = file.ast.program;
|
|
146
30
|
},
|
|
147
31
|
visitor: {
|
|
148
|
-
|
|
149
|
-
|
|
32
|
+
// Runs on *every* class the app's own source defines, independent of
|
|
33
|
+
// whether it's a service, a service's ancestor, or unrelated to the
|
|
34
|
+
// HMR proxy machinery entirely -- see `renamePrivateClassMembers`'s
|
|
35
|
+
// own doc comment for why this can't be scoped any tighter than that
|
|
36
|
+
// without whole-program analysis. This also visits the `proxyClass`
|
|
37
|
+
// (and, for an inline export, the `originalClass` copy)
|
|
38
|
+
// `transformServiceExport` generates below -- harmless today only
|
|
39
|
+
// because generated proxy classes never declare private members of
|
|
40
|
+
// their own; if that ever changes, this visitor would rename them
|
|
41
|
+
// too.
|
|
42
|
+
Class(path, state) {
|
|
43
|
+
var _a;
|
|
150
44
|
if (process.env.EMBER_VITE_HMR_ENABLED !== 'true') {
|
|
151
45
|
return;
|
|
152
46
|
}
|
|
153
47
|
if ((_a = state.filename) === null || _a === void 0 ? void 0 : _a.includes('node_modules')) {
|
|
154
48
|
return;
|
|
155
49
|
}
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
if (!(normalizedFilename === null || normalizedFilename === void 0 ? void 0 : normalizedFilename.includes('/services/'))) {
|
|
159
|
-
return;
|
|
160
|
-
}
|
|
161
|
-
const declaration = path.node.declaration;
|
|
162
|
-
// Handle both inline class declaration and identifier reference
|
|
163
|
-
let classDeclaration = null;
|
|
164
|
-
let classIdentifier = null;
|
|
165
|
-
if (declaration.type === 'ClassDeclaration' && declaration.id) {
|
|
166
|
-
// Case 1: export default class MyService extends Service { ... }
|
|
167
|
-
classDeclaration = declaration;
|
|
168
|
-
classIdentifier = declaration.id;
|
|
169
|
-
}
|
|
170
|
-
else if (declaration.type === 'Identifier') {
|
|
171
|
-
// Case 2: class MyService extends Service { ... } \n export default MyService;
|
|
172
|
-
const binding = path.scope.getBinding(declaration.name);
|
|
173
|
-
if (binding && binding.path.isClassDeclaration()) {
|
|
174
|
-
classDeclaration = binding.path
|
|
175
|
-
.node;
|
|
176
|
-
classIdentifier = classDeclaration.id;
|
|
177
|
-
}
|
|
178
|
-
}
|
|
179
|
-
if (!classDeclaration || !classIdentifier) {
|
|
50
|
+
const programPath = path.findParent((p) => p.isProgram());
|
|
51
|
+
if (!programPath) {
|
|
180
52
|
return;
|
|
181
53
|
}
|
|
182
|
-
|
|
54
|
+
renamePrivateClassMembers(babel, path, programPath);
|
|
55
|
+
},
|
|
56
|
+
ExportDefaultDeclaration(path, state) {
|
|
57
|
+
var _a, _b;
|
|
58
|
+
if (process.env.EMBER_VITE_HMR_ENABLED !== 'true') {
|
|
183
59
|
return;
|
|
184
60
|
}
|
|
185
|
-
|
|
186
|
-
const superClass = classDeclaration.superClass;
|
|
187
|
-
if (!superClass ||
|
|
188
|
-
((superClass.type !== 'Identifier' ||
|
|
189
|
-
superClass.name !== 'Service') &&
|
|
190
|
-
superClass.type !== 'MemberExpression')) {
|
|
61
|
+
if ((_a = state.filename) === null || _a === void 0 ? void 0 : _a.includes('node_modules')) {
|
|
191
62
|
return;
|
|
192
63
|
}
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
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/')) {
|
|
196
78
|
return;
|
|
197
79
|
}
|
|
198
|
-
const
|
|
199
|
-
const
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
// The live proxy instance and the latest accepted implementation must
|
|
207
|
-
// live on `import.meta.hot.data`, not in a module-scope `let`: Vite
|
|
208
|
-
// guarantees that object (and only that object) survives across a
|
|
209
|
-
// module's hot re-evaluations. A component's `@service` injection can
|
|
210
|
-
// end up constructing the proxy against a *later* re-evaluation of
|
|
211
|
-
// this module than the one whose `import.meta.hot.accept` closure
|
|
212
|
-
// captured a module-scope variable — the closure and the constructor
|
|
213
|
-
// would then silently operate on two disconnected variable bindings
|
|
214
|
-
// (a duplicate-module symptom), and the HMR swap would never reach
|
|
215
|
-
// the instance actually rendered.
|
|
216
|
-
const ctorBody = babelTemplate.statements(`
|
|
217
|
-
super(...args);
|
|
218
|
-
this._owner = args[0];
|
|
219
|
-
const Impl = (import.meta.hot && import.meta.hot.data._impl) || ${proxyClassName}.Impl;
|
|
220
|
-
this._delegate = new Impl(this._owner);
|
|
221
|
-
if (import.meta.hot) {
|
|
222
|
-
import.meta.hot.data._proxy = this;
|
|
223
|
-
}
|
|
224
|
-
// Bound methods are cached per-delegate (not per-proxy) so identity
|
|
225
|
-
// stays stable across repeated reads of the same delegate (e.g.
|
|
226
|
-
// {{on "click" this.svc.handler}} comparing args by reference), while
|
|
227
|
-
// naturally invalidating once a delegate is swapped out on HMR accept.
|
|
228
|
-
const boundMethods = new WeakMap();
|
|
229
|
-
return new Proxy(this, {
|
|
230
|
-
get(target, prop, receiver) {
|
|
231
|
-
if (prop === '_delegate') {
|
|
232
|
-
return target._delegate;
|
|
233
|
-
}
|
|
234
|
-
// A subclass of this service (see the Reflect.get fallback
|
|
235
|
-
// below) may override a method the base delegate class also
|
|
236
|
-
// defines. Since 'prop in delegate' walks the delegate's
|
|
237
|
-
// *entire* prototype chain, it would match the base
|
|
238
|
-
// implementation before the subclass's own override ever
|
|
239
|
-
// gets a chance -- so look for an own property between
|
|
240
|
-
// target's dynamic prototype and this proxy's own prototype
|
|
241
|
-
// first, and prefer it over the delegate.
|
|
242
|
-
let proto = Object.getPrototypeOf(target);
|
|
243
|
-
while (proto && proto !== ${proxyClassName}.prototype) {
|
|
244
|
-
if (Object.prototype.hasOwnProperty.call(proto, prop)) {
|
|
245
|
-
return Reflect.get(target, prop, receiver);
|
|
246
|
-
}
|
|
247
|
-
proto = Object.getPrototypeOf(proto);
|
|
248
|
-
}
|
|
249
|
-
const delegate = target._delegate;
|
|
250
|
-
// Methods invoked via normal member-call syntax (service.method())
|
|
251
|
-
// bind 'this' to whatever object the property access happened on
|
|
252
|
-
// -- the proxy, here -- not to the delegate. That breaks native
|
|
253
|
-
// private fields, whose storage lives only on the concrete
|
|
254
|
-
// delegate instance, so delegate-owned methods are explicitly
|
|
255
|
-
// bound to the delegate.
|
|
256
|
-
if (prop in delegate) {
|
|
257
|
-
const value = delegate[prop];
|
|
258
|
-
if (typeof value === 'function') {
|
|
259
|
-
let cache = boundMethods.get(delegate);
|
|
260
|
-
if (!cache) {
|
|
261
|
-
cache = new Map();
|
|
262
|
-
boundMethods.set(delegate, cache);
|
|
263
|
-
}
|
|
264
|
-
let bound = cache.get(prop);
|
|
265
|
-
if (!bound) {
|
|
266
|
-
bound = value.bind(delegate);
|
|
267
|
-
cache.set(prop, bound);
|
|
268
|
-
}
|
|
269
|
-
return bound;
|
|
270
|
-
}
|
|
271
|
-
return value;
|
|
272
|
-
}
|
|
273
|
-
// Not on the delegate: fall through to the proxy's own real
|
|
274
|
-
// prototype chain, which covers methods declared on a subclass
|
|
275
|
-
// of this service (the subclass never went through this babel
|
|
276
|
-
// transform itself, since it doesn't extend Service directly).
|
|
277
|
-
return Reflect.get(target, prop, receiver);
|
|
278
|
-
},
|
|
279
|
-
set(target, prop, value) {
|
|
280
|
-
target._delegate[prop] = value;
|
|
281
|
-
return true;
|
|
282
|
-
},
|
|
283
|
-
defineProperty(target, prop, descriptor) {
|
|
284
|
-
// A subclass's own class fields are installed via
|
|
285
|
-
// [[DefineOwnProperty]] against whatever 'this' super() returned
|
|
286
|
-
// (this proxy), bypassing the 'set' trap entirely. Route them to
|
|
287
|
-
// the delegate too, so subclass state lives alongside the rest
|
|
288
|
-
// of the service's state instead of stranded on the proxy target.
|
|
289
|
-
return Reflect.defineProperty(target._delegate, prop, descriptor);
|
|
290
|
-
},
|
|
291
|
-
});
|
|
292
|
-
`)();
|
|
293
|
-
const willDestroyBody = babelTemplate.statements(`
|
|
294
|
-
super.willDestroy();
|
|
295
|
-
this._delegate.willDestroy();
|
|
296
|
-
if (import.meta.hot && import.meta.hot.data._proxy === this) {
|
|
297
|
-
import.meta.hot.data._proxy = undefined;
|
|
298
|
-
}
|
|
299
|
-
`)();
|
|
300
|
-
// Create the HMR proxy service class
|
|
301
|
-
const proxyClass = t.classDeclaration(t.identifier(proxyClassName), Service, t.classBody([
|
|
302
|
-
// static Impl = OriginalService (reference to the original class)
|
|
303
|
-
t.classProperty(t.identifier('Impl'), t.identifier(originalClassName), null, null, false, true),
|
|
304
|
-
// @tracked _delegate
|
|
305
|
-
t.classProperty(t.identifier('_delegate'), null, null, [t.decorator(tracked)], false, false),
|
|
306
|
-
t.classMethod('constructor', t.identifier('constructor'), [t.restElement(t.identifier('args'))], t.blockStatement(ctorBody)),
|
|
307
|
-
t.classMethod('method', t.identifier('willDestroy'), [], t.blockStatement(willDestroyBody)),
|
|
308
|
-
]));
|
|
309
|
-
// For Case 1 (inline class), we need to keep the class as non-exported
|
|
310
|
-
// For Case 2 (separate declaration), the class already exists, so we don't recreate it
|
|
311
|
-
if (declaration.type === 'ClassDeclaration') {
|
|
312
|
-
// Case 1: Inline class export - create a non-exported version
|
|
313
|
-
const originalClass = t.classDeclaration(classIdentifier, classDeclaration.superClass, classDeclaration.body, classDeclaration.decorators || []);
|
|
314
|
-
// Replace the export with all the necessary declarations
|
|
315
|
-
// IMPORTANT: Class must be declared BEFORE the proxy class that references it
|
|
80
|
+
const t = babel.types;
|
|
81
|
+
const declaration = path.node.declaration;
|
|
82
|
+
// If the declaration is already a named class/function, it becomes a
|
|
83
|
+
// declaration statement in its own right — just export the identifier.
|
|
84
|
+
if ((declaration.type === 'ClassDeclaration' ||
|
|
85
|
+
declaration.type === 'FunctionDeclaration') &&
|
|
86
|
+
declaration.id) {
|
|
87
|
+
const id = declaration.id;
|
|
316
88
|
path.replaceWithMultiple([
|
|
317
|
-
|
|
318
|
-
t.
|
|
89
|
+
declaration,
|
|
90
|
+
t.variableDeclaration('const', [
|
|
91
|
+
t.variableDeclarator(t.identifier('__hmr_default__'), t.identifier(id.name)),
|
|
92
|
+
]),
|
|
93
|
+
t.exportNamedDeclaration(null, [
|
|
94
|
+
t.exportSpecifier(t.identifier('__hmr_default__'), t.identifier('default')),
|
|
95
|
+
]),
|
|
319
96
|
]);
|
|
320
97
|
}
|
|
321
98
|
else {
|
|
322
|
-
//
|
|
99
|
+
// Anonymous expression (template-only component, arrow fn, etc.)
|
|
323
100
|
path.replaceWithMultiple([
|
|
324
|
-
t.
|
|
101
|
+
t.variableDeclaration('const', [
|
|
102
|
+
t.variableDeclarator(t.identifier('__hmr_default__'), declaration),
|
|
103
|
+
]),
|
|
104
|
+
t.exportNamedDeclaration(null, [
|
|
105
|
+
t.exportSpecifier(t.identifier('__hmr_default__'), t.identifier('default')),
|
|
106
|
+
]),
|
|
325
107
|
]);
|
|
326
108
|
}
|
|
327
|
-
// Add HMR code after the export using babel template
|
|
328
|
-
if (programPath) {
|
|
329
|
-
const hmrCode = `
|
|
330
|
-
if (import.meta.hot) {
|
|
331
|
-
import.meta.hot.accept((newModule) => {
|
|
332
|
-
const NewImpl = newModule?.default?.Impl;
|
|
333
|
-
if (!NewImpl) {
|
|
334
|
-
return;
|
|
335
|
-
}
|
|
336
|
-
|
|
337
|
-
// Remember the latest accepted implementation so a proxy
|
|
338
|
-
// constructed *after* this update (its instantiation raced
|
|
339
|
-
// behind the HMR swap) still picks up the new class instead
|
|
340
|
-
// of the stale one captured at module-evaluation time.
|
|
341
|
-
import.meta.hot.data._impl = NewImpl;
|
|
342
|
-
|
|
343
|
-
const proxy = import.meta.hot.data._proxy;
|
|
344
|
-
if (!proxy) {
|
|
345
|
-
// No instance has been created yet; the next construction
|
|
346
|
-
// will read import.meta.hot.data._impl above.
|
|
347
|
-
return;
|
|
348
|
-
}
|
|
349
|
-
|
|
350
|
-
const oldDelegate = proxy._delegate;
|
|
351
|
-
const newDelegate = new NewImpl(proxy._owner);
|
|
352
|
-
proxy._delegate = newDelegate;
|
|
353
|
-
|
|
354
|
-
// Sync state from old to new while keeping new implementation defaults
|
|
355
|
-
for (const key in oldDelegate) {
|
|
356
|
-
const descriptor =
|
|
357
|
-
Object.getOwnPropertyDescriptor(oldDelegate, key) ||
|
|
358
|
-
Object.getOwnPropertyDescriptor(Object.getPrototypeOf(oldDelegate), key);
|
|
359
|
-
const hasOwnDefault = Object.prototype.hasOwnProperty.call(newDelegate, key);
|
|
360
|
-
const currentValue = newDelegate[key];
|
|
361
|
-
const previousValue = oldDelegate[key];
|
|
362
|
-
|
|
363
|
-
// Skip Service instances - they should not be synced
|
|
364
|
-
if (previousValue instanceof Service) {
|
|
365
|
-
continue;
|
|
366
|
-
}
|
|
367
|
-
|
|
368
|
-
// Skip function properties that the new implementation also
|
|
369
|
-
// declares (a fresh redefinition should win over the old
|
|
370
|
-
// one). But a function-valued property the new
|
|
371
|
-
// implementation *doesn't* declare at all isn't a stale
|
|
372
|
-
// redefinition -- it's state a subclass installed on the
|
|
373
|
-
// delegate directly (subclasses of this service never go
|
|
374
|
-
// through this transform themselves, since their
|
|
375
|
-
// superclass isn't literally named Service; their own
|
|
376
|
-
// class fields land on the delegate via the proxy's
|
|
377
|
-
// defineProperty trap instead). Dropping it here would
|
|
378
|
-
// silently break e.g. an arrow-function class field on a
|
|
379
|
-
// subclass across a base-class HMR swap.
|
|
380
|
-
if (typeof previousValue === 'function' && hasOwnDefault) {
|
|
381
|
-
continue;
|
|
382
|
-
}
|
|
383
|
-
|
|
384
|
-
const shouldSync =
|
|
385
|
-
!!descriptor &&
|
|
386
|
-
(descriptor.writable || descriptor.set || Object.prototype.hasOwnProperty.call(oldDelegate, key)) &&
|
|
387
|
-
(!hasOwnDefault || currentValue === previousValue);
|
|
388
|
-
|
|
389
|
-
if (shouldSync) {
|
|
390
|
-
try {
|
|
391
|
-
newDelegate[key] = previousValue;
|
|
392
|
-
} catch (e) {
|
|
393
|
-
// Skip non-writable properties
|
|
394
|
-
}
|
|
395
|
-
}
|
|
396
|
-
}
|
|
397
|
-
|
|
398
|
-
newDelegate._hmrAccepted?.(oldDelegate);
|
|
399
|
-
|
|
400
|
-
if (oldDelegate.willDestroy) {
|
|
401
|
-
oldDelegate.willDestroy();
|
|
402
|
-
}
|
|
403
|
-
});
|
|
404
|
-
}
|
|
405
|
-
`;
|
|
406
|
-
const template = babel.template.statements(hmrCode);
|
|
407
|
-
programPath.pushContainer('body', template());
|
|
408
|
-
}
|
|
409
109
|
},
|
|
410
110
|
Program(path, state) {
|
|
411
111
|
var _a;
|
|
@@ -415,37 +115,7 @@ export default function hotReplaceAst(babel) {
|
|
|
415
115
|
if ((_a = state.filename) === null || _a === void 0 ? void 0 : _a.includes('node_modules')) {
|
|
416
116
|
return;
|
|
417
117
|
}
|
|
418
|
-
|
|
419
|
-
hotAstProcessor.meta.importBindings.size === 0) {
|
|
420
|
-
return;
|
|
421
|
-
}
|
|
422
|
-
const util = new ImportUtil(babel, path);
|
|
423
|
-
const tracked = util.import(path, '@glimmer/tracking', 'tracked');
|
|
424
|
-
util.import(path, '@glimmer/component', 'default');
|
|
425
|
-
const klass = t.classExpression(path.scope.generateUidIdentifier('Imports'), null, t.classBody([]));
|
|
426
|
-
const bindings = [...hotAstProcessor.meta.importBindings].sort();
|
|
427
|
-
for (const local of bindings) {
|
|
428
|
-
klass.body.body.push(t.classProperty(t.identifier(local), t.identifier(local), null, [
|
|
429
|
-
t.decorator(tracked),
|
|
430
|
-
]));
|
|
431
|
-
}
|
|
432
|
-
const newExp = t.newExpression(klass, []);
|
|
433
|
-
const assign = t.assignmentExpression('=', t.identifier(hotAstProcessor.meta.importVar), newExp);
|
|
434
|
-
const varDeclaration = path.node.body.findIndex((e) => e.type === 'VariableDeclaration' &&
|
|
435
|
-
e.declarations[0].id.name ===
|
|
436
|
-
hotAstProcessor.meta.importVar) + 1;
|
|
437
|
-
const lastImportIndex = path.node.body.findLastIndex((e) => e.type === 'ImportDeclaration') + 1;
|
|
438
|
-
path.node.body.splice(Math.max(varDeclaration, lastImportIndex), 0, t.expressionStatement(assign));
|
|
439
|
-
// Export metadata about tracked imports for hmr.ts to use
|
|
440
|
-
const importMetadata = t.variableDeclaration('const', [
|
|
441
|
-
t.variableDeclarator(t.identifier('__hmr_import_metadata__'), t.objectExpression([
|
|
442
|
-
t.objectProperty(t.identifier('importVar'), t.stringLiteral(hotAstProcessor.meta.importVar)),
|
|
443
|
-
t.objectProperty(t.identifier('bindings'), t.arrayExpression(bindings.map((b) => t.stringLiteral(b)))),
|
|
444
|
-
])),
|
|
445
|
-
]);
|
|
446
|
-
const exportMetadata = t.exportNamedDeclaration(importMetadata, []);
|
|
447
|
-
path.node.body.push(exportMetadata);
|
|
448
|
-
path.scope.crawl();
|
|
118
|
+
finalizeTemplateImports(babel, path, state.filename);
|
|
449
119
|
},
|
|
450
120
|
},
|
|
451
121
|
};
|
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[];
|