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/hmr.js
CHANGED
|
@@ -9,235 +9,198 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
|
|
|
9
9
|
};
|
|
10
10
|
import path from 'path';
|
|
11
11
|
import { fileURLToPath } from 'url';
|
|
12
|
-
import { parseSync } from '@babel/core';
|
|
13
|
-
import traverseModule from '@babel/traverse';
|
|
14
|
-
const traverse = traverseModule.default || traverseModule;
|
|
15
12
|
import { readFile } from 'fs/promises';
|
|
13
|
+
import { hmrImportMetadataCache } from './babel-plugin.js';
|
|
16
14
|
const __filename = fileURLToPath(import.meta.url);
|
|
17
15
|
const __dirname = path.dirname(__filename);
|
|
18
|
-
//
|
|
19
|
-
//
|
|
20
|
-
// (
|
|
21
|
-
const
|
|
22
|
-
|
|
23
|
-
|
|
16
|
+
// Embroider's resolver registry. It's the binding the app entry's HMR hook
|
|
17
|
+
// mutates, so we use it to locate that entry. Embroider keeps this as an
|
|
18
|
+
// internal literal (not a public export), so we mirror the string here.
|
|
19
|
+
const compatModulesSpecifier = '@embroider/virtual/compat-modules';
|
|
20
|
+
export const hmrRuntimeId = 'virtual:ember-vite-hmr-runtime';
|
|
21
|
+
const resolvedRuntimeId = '\0' + hmrRuntimeId;
|
|
22
|
+
// This runs in the browser.
|
|
23
|
+
// It has no imports on purpose: a virtual module has no location on disk,
|
|
24
|
+
// so Embroider's resolver has nothing to resolve `@glimmer/tracking` against.
|
|
25
|
+
// The app modules that call `define` can import `tracked` normally and pass
|
|
26
|
+
// it in.
|
|
27
|
+
//
|
|
28
|
+
// `byValue` lets an old reference to a component class find its current
|
|
29
|
+
// registry entry, so modules holding stale bindings still serve the newest
|
|
30
|
+
// version.
|
|
31
|
+
//
|
|
32
|
+
// `consumed` lets a self-accepting module that nobody rendered through
|
|
33
|
+
// fall back to `invalidate()` rather than silently doing nothing.
|
|
34
|
+
//
|
|
35
|
+
// `tracked` is called as a function (not a decorator syntax), so the runtime
|
|
36
|
+
// doesn't depend on the app's Babel decorator config.
|
|
37
|
+
const runtimeSource = `
|
|
38
|
+
const entries = new WeakMap();
|
|
39
|
+
|
|
40
|
+
// Per-class FIFO queues of live component instances, used to pair old instances
|
|
41
|
+
// (being destroyed by an HMR swap) with the new instances that replace them.
|
|
42
|
+
// Keyed by the NEW class (the replacement). Cleared at update() time so that
|
|
43
|
+
// instances created by normal user-flow navigation before the swap are never
|
|
44
|
+
// in the queue — only instances created after the swap callback fires.
|
|
45
|
+
const liveInstanceQueues = new WeakMap();
|
|
46
|
+
|
|
47
|
+
function isRef(value) {
|
|
48
|
+
return value !== null && (typeof value === 'object' || typeof value === 'function');
|
|
24
49
|
}
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
50
|
+
|
|
51
|
+
function makeEntry(tracked, value) {
|
|
52
|
+
class Entry {}
|
|
53
|
+
const desc = tracked(Entry.prototype, 'current', { configurable: true, enumerable: true, writable: true, initializer: null });
|
|
54
|
+
Object.defineProperty(Entry.prototype, 'current', desc);
|
|
55
|
+
const entry = new Entry();
|
|
56
|
+
entry.current = value;
|
|
57
|
+
entry.consumed = false;
|
|
58
|
+
return entry;
|
|
30
59
|
}
|
|
31
|
-
|
|
32
|
-
//
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
// build one fully static invocation per combination of blocks the caller
|
|
39
|
-
// actually passed, so blocks not passed are truly absent rather than merely
|
|
40
|
-
// empty (see #531).
|
|
41
|
-
function generateInvocations(remaining, selected) {
|
|
42
|
-
if (!remaining.length) {
|
|
43
|
-
return invocation(selected);
|
|
44
|
-
}
|
|
45
|
-
const [y, ...rest] = remaining;
|
|
46
|
-
return `{{#if (has-block '${y}')}}${generateInvocations(rest, [...selected, y])}{{else}}${generateInvocations(rest, selected)}{{/if}}`;
|
|
60
|
+
|
|
61
|
+
// Register value for the first time. No-op if already registered.
|
|
62
|
+
export function register(value, tracked) {
|
|
63
|
+
if (!isRef(value)) return;
|
|
64
|
+
if (!entries.has(value)) {
|
|
65
|
+
entries.set(value, makeEntry(tracked, value));
|
|
66
|
+
}
|
|
47
67
|
}
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
68
|
+
|
|
69
|
+
// Re-point an existing entry from oldValue to newValue.
|
|
70
|
+
// Also registers newValue under the same entry so future current() calls
|
|
71
|
+
// on newValue resolve to whatever is current at that point.
|
|
72
|
+
//
|
|
73
|
+
// Clearing liveInstanceQueues[newValue] here is the key to correctness for
|
|
74
|
+
// syncState across user-flow create/destroy cycles. Any instances of newValue
|
|
75
|
+
// created and destroyed by normal navigation *before* this swap happened are
|
|
76
|
+
// stale. By resetting the queue at swap time, only instances created *after*
|
|
77
|
+
// this update() call (the replacement instances Glimmer is about to create)
|
|
78
|
+
// are eligible to receive state from the old instances being torn down.
|
|
79
|
+
export function update(oldValue, newValue) {
|
|
80
|
+
if (!isRef(oldValue)) return;
|
|
81
|
+
const entry = entries.get(oldValue);
|
|
82
|
+
if (entry) {
|
|
83
|
+
entry.current = newValue;
|
|
84
|
+
if (isRef(newValue)) {
|
|
85
|
+
entries.set(newValue, entry);
|
|
86
|
+
// Clear the queue for newValue so only instances created after this
|
|
87
|
+
// swap are paired with old instances being torn down.
|
|
88
|
+
liveInstanceQueues.delete(newValue);
|
|
61
89
|
}
|
|
62
|
-
|
|
90
|
+
// oldValue is being retired — it will never be the target of a future
|
|
91
|
+
// dequeueInstance() call, so any queued instances for it are now stale.
|
|
92
|
+
liveInstanceQueues.delete(oldValue);
|
|
93
|
+
}
|
|
63
94
|
}
|
|
64
|
-
const getHotComponent = (imp, specifier, yields) => `
|
|
65
|
-
import { ${specifier} as TargetComponent } from "${imp}";
|
|
66
|
-
import Component from "@glimmer/component";
|
|
67
|
-
import { tracked } from "@glimmer/tracking";
|
|
68
|
-
import { createComputeRef } from "@glimmer/reference";
|
|
69
|
-
import { curry } from '@glimmer/runtime';
|
|
70
|
-
import { registerDestructor } from '@ember/destroyable';
|
|
71
|
-
import { getInternalComponentManager, setInternalComponentManager } from '@glimmer/manager';
|
|
72
95
|
|
|
73
|
-
function
|
|
74
|
-
|
|
96
|
+
export function current(value) {
|
|
97
|
+
if (!isRef(value)) return value;
|
|
98
|
+
const entry = entries.get(value);
|
|
99
|
+
if (!entry) return value;
|
|
100
|
+
entry.consumed = true;
|
|
101
|
+
return entry.current;
|
|
75
102
|
}
|
|
76
103
|
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
import.meta.hot.accept('${imp}', (module) => {
|
|
81
|
-
for (const callback of hotCallbacks) {
|
|
82
|
-
callback(module);
|
|
83
|
-
}
|
|
84
|
-
});
|
|
104
|
+
export function used(value) {
|
|
105
|
+
if (!isRef(value)) return false;
|
|
106
|
+
return Boolean(entries.get(value)?.consumed);
|
|
85
107
|
}
|
|
86
108
|
|
|
87
|
-
//
|
|
88
|
-
//
|
|
89
|
-
//
|
|
90
|
-
|
|
91
|
-
|
|
109
|
+
// Returns true if value has been registered with the HMR runtime.
|
|
110
|
+
// Used by setup-hmr-manager.ts to skip non-HMR components when building
|
|
111
|
+
// the liveInstances queue so stale entries don't corrupt later HMR swaps.
|
|
112
|
+
export function isHmrClass(value) {
|
|
113
|
+
return isRef(value) && entries.has(value);
|
|
114
|
+
}
|
|
92
115
|
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
// Forward the caller's original, updatable reference (e.g. a path like
|
|
103
|
-
// "this.value") instead of wrapping the reified value in a fresh
|
|
104
|
-
// read-only compute ref. A read-only ref fails Ember's "You can only
|
|
105
|
-
// pass a path to mut" check, breaking {{mut @arg}} in wrapped components.
|
|
106
|
-
named[name] = capturedArgs?.named[name] ?? createComputeRef(() => args[name]);
|
|
107
|
-
}
|
|
108
|
-
const CurriedComponent = 0;
|
|
109
|
-
this.curried = curry(CurriedComponent, TargetComponent, owner, { positional, named});
|
|
110
|
-
if (import.meta.hot) {
|
|
111
|
-
const callback = (module) => {
|
|
112
|
-
this.curried = curry(CurriedComponent, module.default, owner, { positional, named});
|
|
113
|
-
};
|
|
114
|
-
hotCallbacks.add(callback);
|
|
115
|
-
registerDestructor(this, () => {
|
|
116
|
-
hotCallbacks.delete(callback);
|
|
117
|
-
});
|
|
118
|
-
}
|
|
116
|
+
// Called from setup-hmr-manager.ts's create() hook for each new HMR-registered
|
|
117
|
+
// component instance. Appends to the per-class queue in DOM (creation) order.
|
|
118
|
+
export function enqueueInstance(klass, instance) {
|
|
119
|
+
if (!isRef(klass)) return;
|
|
120
|
+
const q = liveInstanceQueues.get(klass);
|
|
121
|
+
if (q) {
|
|
122
|
+
q.push(instance);
|
|
123
|
+
} else {
|
|
124
|
+
liveInstanceQueues.set(klass, [instance]);
|
|
119
125
|
}
|
|
126
|
+
}
|
|
120
127
|
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
128
|
+
// Called from setup-hmr-manager.ts's willDestroy hook. Returns and removes the
|
|
129
|
+
// oldest queued instance for klass (FIFO = DOM order), or null if none.
|
|
130
|
+
export function dequeueInstance(klass) {
|
|
131
|
+
if (!isRef(klass)) return null;
|
|
132
|
+
const q = liveInstanceQueues.get(klass);
|
|
133
|
+
if (!q || q.length === 0) return null;
|
|
134
|
+
const instance = q.shift();
|
|
135
|
+
if (q.length === 0) liveInstanceQueues.delete(klass);
|
|
136
|
+
return instance ?? null;
|
|
124
137
|
}
|
|
125
138
|
|
|
126
|
-
//
|
|
127
|
-
//
|
|
128
|
-
//
|
|
129
|
-
//
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
const
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
}
|
|
143
|
-
};
|
|
144
|
-
// Glimmer's debug render tree normally adds one 'component' node per
|
|
145
|
-
// invoked component instance (see VM_GET_COMPONENT_SELF_OP). Returning no
|
|
146
|
-
// nodes here suppresses that node for HotComponent itself, so only the
|
|
147
|
-
// real, curried target component (invoked in HotComponent's own template)
|
|
148
|
-
// shows up in ember-inspector's component tree - this is the same hook
|
|
149
|
-
// Ember core uses internally to keep its own {{outlet}}/{{mount}} wrapper
|
|
150
|
-
// machinery out of (or relabeled in) that tree (see OutletComponentManager
|
|
151
|
-
// and MountManager in ember-source).
|
|
152
|
-
shadowManager.getDebugCustomRenderTree = function () {
|
|
153
|
-
return [];
|
|
154
|
-
};
|
|
155
|
-
setInternalComponentManager(shadowManager, HotComponent);
|
|
139
|
+
// Called from setup-hmr-manager.ts's willDestroy hook when no HMR swap is in
|
|
140
|
+
// progress (normal navigation destroy). Removes this specific instance from the
|
|
141
|
+
// queue so destroyed instances don't accumulate indefinitely when a timer
|
|
142
|
+
// mounts/unmounts the same component repeatedly between saves.
|
|
143
|
+
export function removeInstance(klass, instance) {
|
|
144
|
+
if (!isRef(klass)) return;
|
|
145
|
+
const q = liveInstanceQueues.get(klass);
|
|
146
|
+
if (!q) return;
|
|
147
|
+
const idx = q.indexOf(instance);
|
|
148
|
+
if (idx !== -1) q.splice(idx, 1);
|
|
149
|
+
if (q.length === 0) liveInstanceQueues.delete(klass);
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
// Expose helpers on the global so setup-hmr-manager.ts's synchronous
|
|
153
|
+
// initialize() can call them without a dynamic import.
|
|
154
|
+
globalThis.__ember_vite_hmr = { current, isHmrClass, enqueueInstance, dequeueInstance, removeInstance };
|
|
156
155
|
`;
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
}
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
156
|
+
// `enforce: 'pre'` makes this run before Embroider's resolver, which would
|
|
157
|
+
// otherwise try to resolve the virtual id as a package and fail.
|
|
158
|
+
// `apply: 'serve'` keeps it out of production builds entirely.
|
|
159
|
+
export function hmrRuntime() {
|
|
160
|
+
return {
|
|
161
|
+
name: 'ember-vite-hmr-runtime',
|
|
162
|
+
enforce: 'pre',
|
|
163
|
+
apply: 'serve',
|
|
164
|
+
resolveId(source) {
|
|
165
|
+
if (source === hmrRuntimeId) {
|
|
166
|
+
return resolvedRuntimeId;
|
|
167
|
+
}
|
|
168
|
+
},
|
|
169
|
+
load(id) {
|
|
170
|
+
if (id === resolvedRuntimeId) {
|
|
171
|
+
return runtimeSource;
|
|
172
|
+
}
|
|
173
|
+
},
|
|
174
174
|
};
|
|
175
|
-
return cachedYields[filename];
|
|
176
|
-
}
|
|
177
|
-
function difference(a, b) {
|
|
178
|
-
const diff = [];
|
|
179
|
-
for (const bElement of b) {
|
|
180
|
-
if (!a.has(bElement)) {
|
|
181
|
-
diff.push(bElement);
|
|
182
|
-
}
|
|
183
|
-
}
|
|
184
|
-
return diff;
|
|
185
175
|
}
|
|
186
176
|
// Helper function to normalize paths consistently across platforms
|
|
187
177
|
function normalizePath(inputPath) {
|
|
188
|
-
// Always convert backslashes to forward slashes
|
|
189
178
|
return inputPath.replace(/\\/g, '/');
|
|
190
179
|
}
|
|
191
|
-
const virtualPrefix = '/ember-vite-hmr/virtual/component:';
|
|
192
|
-
// Embroider's resolver registry. It's the binding the app entry's HMR hook
|
|
193
|
-
// mutates, so we use it to locate that entry. Embroider keeps this as an
|
|
194
|
-
// internal literal (not a public export), so we mirror the string here.
|
|
195
|
-
const compatModulesSpecifier = '@embroider/virtual/compat-modules';
|
|
196
180
|
export function hmr(enableViteHmrForModes = ['development']) {
|
|
197
181
|
let base = '/';
|
|
182
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
198
183
|
let server;
|
|
199
|
-
|
|
184
|
+
const mainPlugin = {
|
|
200
185
|
name: 'hmr-plugin',
|
|
201
186
|
enforce: 'post',
|
|
202
187
|
config(config, env) {
|
|
203
188
|
var _a;
|
|
204
|
-
// The per-component hot wrapper (see getHotComponent) imports the first
|
|
205
|
-
// four of these, but it is generated/served on demand, so it is never
|
|
206
|
-
// part of the static module graph Vite's dependency scanner crawls on
|
|
207
|
-
// the first pass. `@ember/component/template-only` has the same
|
|
208
|
-
// problem for a different reason: it's injected by the template
|
|
209
|
-
// compiler into a template-only component's compiled output, which the
|
|
210
|
-
// scanner (source-only) never sees either. Without pre-declaring them,
|
|
211
|
-
// the browser requests them on boot, Vite discovers "new" deps and
|
|
212
|
-
// triggers a re-optimize + full page reload.
|
|
213
|
-
//
|
|
214
|
-
// We must use the Embroider-rewritten `ember-source/...` subpaths: the
|
|
215
|
-
// bare `@glimmer/reference` etc. specifiers the wrapper writes cannot be
|
|
216
|
-
// resolved by optimizeDeps.include. (`@glimmer/component` and
|
|
217
|
-
// `@glimmer/tracking` are omitted on purpose — normal app code already
|
|
218
|
-
// pulls them into the scan.)
|
|
219
189
|
if (!enableViteHmrForModes.includes(env.mode)) {
|
|
220
190
|
return;
|
|
221
191
|
}
|
|
222
192
|
// With `optimizeDeps.noDiscovery: true`, Vite never scans the app's own
|
|
223
|
-
// source, so nothing else pulls these
|
|
224
|
-
//
|
|
225
|
-
//
|
|
226
|
-
//
|
|
227
|
-
//
|
|
228
|
-
// There's nothing useful this hook can pre-declare in that mode, so
|
|
229
|
-
// skip it entirely rather than fight the user's own dep-optimization
|
|
230
|
-
// config.
|
|
193
|
+
// source, so nothing else pulls these glimmer subpaths into the optimizer.
|
|
194
|
+
// Forcing them into `include` then creates a second, separately pre-bundled
|
|
195
|
+
// copy of the glimmer VM alongside the unoptimized one the rest of the
|
|
196
|
+
// (unscanned) app actually uses, leading to "The global context for Glimmer
|
|
197
|
+
// VM was not set" (#554). Skip entirely in that mode.
|
|
231
198
|
if ((_a = config.optimizeDeps) === null || _a === void 0 ? void 0 : _a.noDiscovery) {
|
|
232
199
|
return;
|
|
233
200
|
}
|
|
234
201
|
return {
|
|
235
202
|
optimizeDeps: {
|
|
236
203
|
include: [
|
|
237
|
-
'ember-source/@glimmer/reference/index.js',
|
|
238
|
-
'ember-source/@glimmer/runtime/index.js',
|
|
239
|
-
'ember-source/@ember/destroyable/index.js',
|
|
240
|
-
'ember-source/@glimmer/manager/index.js',
|
|
241
204
|
'ember-source/@ember/component/template-only.js',
|
|
242
205
|
],
|
|
243
206
|
},
|
|
@@ -259,22 +222,6 @@ export function hmr(enableViteHmrForModes = ['development']) {
|
|
|
259
222
|
if (id.includes('@ember-vite-hmr/setup-ember-hmr.js')) {
|
|
260
223
|
return id;
|
|
261
224
|
}
|
|
262
|
-
if (importer === null || importer === void 0 ? void 0 : importer.startsWith(virtualPrefix)) {
|
|
263
|
-
const newImporter = path.join(process.cwd(), 'package.json');
|
|
264
|
-
importer = newImporter;
|
|
265
|
-
return this.resolve(id, importer, meta);
|
|
266
|
-
}
|
|
267
|
-
if (id.startsWith(virtualPrefix)) {
|
|
268
|
-
let [imp, specifier] = id
|
|
269
|
-
.split('?')[0]
|
|
270
|
-
.slice(virtualPrefix.length)
|
|
271
|
-
.split('::');
|
|
272
|
-
if (imp === null || imp === void 0 ? void 0 : imp.startsWith('.')) {
|
|
273
|
-
const r = yield this.resolve(imp, importer);
|
|
274
|
-
return id.replace(`${imp}::${specifier}`, `${r.id}::${specifier}`);
|
|
275
|
-
}
|
|
276
|
-
return id;
|
|
277
|
-
}
|
|
278
225
|
if (id === '/ember-vite-hmr/services/vite-hot-reload') {
|
|
279
226
|
return this.resolve('ember-vite-hmr/services/vite-hot-reload', path.resolve(process.cwd(), 'package.json'), meta);
|
|
280
227
|
}
|
|
@@ -285,56 +232,6 @@ export function hmr(enableViteHmrForModes = ['development']) {
|
|
|
285
232
|
if (id.includes('@ember-vite-hmr/setup-ember-hmr.js')) {
|
|
286
233
|
return yield readFile(path.resolve(__dirname, '..', 'setup-ember-hmr.js'), 'utf8');
|
|
287
234
|
}
|
|
288
|
-
if (id.startsWith(virtualPrefix)) {
|
|
289
|
-
if (!server) {
|
|
290
|
-
// During build, server is not available
|
|
291
|
-
return null;
|
|
292
|
-
}
|
|
293
|
-
let [imp, specifier] = id
|
|
294
|
-
.split('?')[0]
|
|
295
|
-
.slice(virtualPrefix.length, -'.gts'.length)
|
|
296
|
-
.split('::');
|
|
297
|
-
imp = imp.replace('embroider_virtual', '@embroider/virtual');
|
|
298
|
-
let filename = imp;
|
|
299
|
-
if (filename.includes('__vpc__')) {
|
|
300
|
-
filename = filename.split('__vpc__')[0];
|
|
301
|
-
}
|
|
302
|
-
const res = yield server.transformRequest(filename);
|
|
303
|
-
const content = res === null || res === void 0 ? void 0 : res.code;
|
|
304
|
-
// For a classic component with a separately resolved template (a colocated
|
|
305
|
-
// `.hbs` file next to a backing class, or a template-only component), the
|
|
306
|
-
// compiled backing module only *imports* its template (e.g. `import TEMPLATE
|
|
307
|
-
// from "./foo.hbs?import"`); it doesn't inline it. Any {{yield ... to="..."}}
|
|
308
|
-
// usage lives in that template file, so it has to be fetched and scanned
|
|
309
|
-
// separately, otherwise named blocks other than "default" are never detected
|
|
310
|
-
// and get silently dropped by the generated hot-reload wrapper.
|
|
311
|
-
let yieldSourceFilename = filename;
|
|
312
|
-
let yieldSourceContent = content;
|
|
313
|
-
const templateImportMatch = content === null || content === void 0 ? void 0 : content.match(/\bfrom\s*['"]([^'"]+\.hbs(?:\?[^'"]*)?)['"]/);
|
|
314
|
-
if (templateImportMatch) {
|
|
315
|
-
// The compiled backing class embeds the template import as a
|
|
316
|
-
// browser-facing URL (i.e. prefixed with the configured vite
|
|
317
|
-
// `base`), since that's what gets sent to the client. But
|
|
318
|
-
// `server.transformRequest` is an internal API keyed by root-
|
|
319
|
-
// relative ids, so the base has to be stripped back off before
|
|
320
|
-
// reusing that specifier here, or this 404s under a non-root base.
|
|
321
|
-
let templateSpecifier = templateImportMatch[1];
|
|
322
|
-
if (base !== '/' && templateSpecifier.startsWith(base)) {
|
|
323
|
-
templateSpecifier = `/${templateSpecifier.slice(base.length)}`;
|
|
324
|
-
}
|
|
325
|
-
const templateRes = yield server.transformRequest(templateSpecifier);
|
|
326
|
-
yieldSourceFilename = templateSpecifier;
|
|
327
|
-
yieldSourceContent = templateRes === null || templateRes === void 0 ? void 0 : templateRes.code;
|
|
328
|
-
}
|
|
329
|
-
const resId = yield this.resolve(yieldSourceFilename, path.resolve(process.cwd(), 'package.json'));
|
|
330
|
-
// Strip any query string so the cache key matches the plain file path that
|
|
331
|
-
// `hotUpdate` below receives when that template file is edited.
|
|
332
|
-
const cacheKey = resId.id.split('?')[0];
|
|
333
|
-
const cached = getYieldsFromFile(cacheKey, yieldSourceContent !== null && yieldSourceContent !== void 0 ? yieldSourceContent : '');
|
|
334
|
-
const yields = cached.yields;
|
|
335
|
-
cached.modules.push(id);
|
|
336
|
-
return getHotComponent(imp, specifier, [...yields].filter((y) => !!y));
|
|
337
|
-
}
|
|
338
235
|
});
|
|
339
236
|
},
|
|
340
237
|
transformIndexHtml(html) {
|
|
@@ -353,49 +250,69 @@ export function hmr(enableViteHmrForModes = ['development']) {
|
|
|
353
250
|
];
|
|
354
251
|
},
|
|
355
252
|
handleHotUpdate(ctx) {
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
}
|
|
253
|
+
var _a, _b;
|
|
254
|
+
const file = ctx.file.split('?')[0];
|
|
359
255
|
const otherModules = [];
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
if (
|
|
368
|
-
|
|
256
|
+
// ── .hbs pair-component wiring (unchanged) ───────────────────────────
|
|
257
|
+
if (file.endsWith('.hbs')) {
|
|
258
|
+
const pairedModule = ctx.modules.find((m) => [...m.importers].find((i) => i.id.startsWith('embroider_virtual') &&
|
|
259
|
+
i.id.endsWith('-embroider-pair-component')));
|
|
260
|
+
if (pairedModule) {
|
|
261
|
+
const pairComponent = [...pairedModule.importers].find((i) => i.id.startsWith('embroider_virtual') &&
|
|
262
|
+
i.id.endsWith('-embroider-pair-component'));
|
|
263
|
+
if (pairComponent) {
|
|
264
|
+
const componentModule = [...pairComponent.clientImportedModules].find((cim) => cim.id.split('?')[0].match(/\/component\.(js|ts|gjs|gts)/));
|
|
265
|
+
if (componentModule) {
|
|
266
|
+
otherModules.push(componentModule);
|
|
267
|
+
}
|
|
369
268
|
}
|
|
370
269
|
}
|
|
270
|
+
return [...ctx.modules, ...otherModules];
|
|
371
271
|
}
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
272
|
+
// ── subclass propagation ──────────────────────────────────────────────
|
|
273
|
+
// When a component file (e.g. block-base.gts) self-accepts, Vite stops
|
|
274
|
+
// propagation before any subclass module (e.g. block-child.ts extends
|
|
275
|
+
// BlockBase) can be notified. The subclass's importer (e.g. equipment.gts)
|
|
276
|
+
// therefore never fires its accept(block-child, cb) callback, so
|
|
277
|
+
// template__imports__.BlockChild keeps pointing at the old class with
|
|
278
|
+
// the old template.
|
|
279
|
+
//
|
|
280
|
+
// Fix: for every component file that changed, walk its direct importers
|
|
281
|
+
// in Vite's module graph. Any importer that is itself a component file
|
|
282
|
+
// under /components/ is a potential subclass — add it to the update set
|
|
283
|
+
// so Vite re-evaluates it too. That re-evaluation runs its own
|
|
284
|
+
// self-accept callback (update(OldChild, NewChild)) and causes the
|
|
285
|
+
// parent template's accept(block-child, cb) to fire, swapping in the new
|
|
286
|
+
// subclass and its newly-inherited template.
|
|
287
|
+
// Subclass propagation only applies to files under /components/ — route
|
|
288
|
+
// templates (.gts under /templates/) import components but are not
|
|
289
|
+
// subclasses of them. Including templates would force Vite to re-evaluate
|
|
290
|
+
// equipment.gts when resource-holder.gts changes, breaking the existing
|
|
291
|
+
// accept(dep, cb) wiring that already handles that case correctly.
|
|
292
|
+
const isComponentFile = (p) => !p.includes('node_modules') &&
|
|
293
|
+
!p.includes('/-components/') &&
|
|
294
|
+
p.includes('/components/') &&
|
|
295
|
+
(p.endsWith('.gjs') ||
|
|
296
|
+
p.endsWith('.gts') ||
|
|
297
|
+
p.endsWith('.ts') ||
|
|
298
|
+
p.endsWith('.js'));
|
|
299
|
+
if (isComponentFile(file)) {
|
|
300
|
+
for (const mod of ctx.modules) {
|
|
301
|
+
for (const importer of mod.importers) {
|
|
302
|
+
const importerId = normalizePath((_b = (_a = importer.id) === null || _a === void 0 ? void 0 : _a.split('?')[0]) !== null && _b !== void 0 ? _b : '');
|
|
303
|
+
if (isComponentFile(importerId) &&
|
|
304
|
+
!ctx.modules.some((m) => m.id === importer.id) &&
|
|
305
|
+
!otherModules.includes(importer)) {
|
|
306
|
+
otherModules.push(importer);
|
|
392
307
|
}
|
|
393
308
|
}
|
|
394
309
|
}
|
|
395
|
-
}
|
|
310
|
+
}
|
|
311
|
+
return [...ctx.modules, ...otherModules];
|
|
396
312
|
},
|
|
397
313
|
transform(source, id) {
|
|
398
314
|
return __awaiter(this, void 0, void 0, function* () {
|
|
315
|
+
var _a, _b;
|
|
399
316
|
if (process.env.EMBER_VITE_HMR_ENABLED !== 'true') {
|
|
400
317
|
return source;
|
|
401
318
|
}
|
|
@@ -424,142 +341,116 @@ export function hmr(enableViteHmrForModes = ['development']) {
|
|
|
424
341
|
});
|
|
425
342
|
}`;
|
|
426
343
|
}
|
|
427
|
-
if (resourcePath.includes('ember-vite-hmr/virtual/components')) {
|
|
428
|
-
return source;
|
|
429
|
-
}
|
|
430
344
|
if (resourcePath.includes('node_modules')) {
|
|
431
345
|
return source;
|
|
432
346
|
}
|
|
433
|
-
// Add hot reload statements for tracked imports
|
|
434
|
-
//
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
bindings = prop.value.elements
|
|
472
|
-
.filter((el) => (el === null || el === void 0 ? void 0 : el.type) === 'StringLiteral')
|
|
473
|
-
.map((el) => el.value);
|
|
474
|
-
}
|
|
475
|
-
}
|
|
476
|
-
}
|
|
477
|
-
}
|
|
478
|
-
}
|
|
479
|
-
},
|
|
480
|
-
});
|
|
481
|
-
// Second pass: Find matching imports (only if we have bindings to match)
|
|
347
|
+
// Add hot reload statements for tracked imports.
|
|
348
|
+
//
|
|
349
|
+
// lib/babel-plugin.ts's hotReplaceAst already computes this exact
|
|
350
|
+
// metadata (importVar/bindings/importStatements) while babel processes
|
|
351
|
+
// this same file earlier in the pipeline, and shares it via
|
|
352
|
+
// hmrImportMetadataCache keyed by filename -- so the common case skips
|
|
353
|
+
// re-parsing and re-traversing this file from scratch. Only fall back
|
|
354
|
+
// to recovering it the slow way (parsing the already-babel-transformed
|
|
355
|
+
// source and pulling the __hmr_import_metadata__ export back out of
|
|
356
|
+
// it) when there's no cache entry, e.g. because this file's babel pass
|
|
357
|
+
// didn't go through hotReplaceAst in this process, or a concurrent
|
|
358
|
+
// babel pass for another file raced the cache write out (see that
|
|
359
|
+
// cache's own doc comment).
|
|
360
|
+
let importVar = null;
|
|
361
|
+
let bindings = [];
|
|
362
|
+
let importStatements = [];
|
|
363
|
+
const cached = hmrImportMetadataCache.get(resourcePath);
|
|
364
|
+
if (cached) {
|
|
365
|
+
importVar = cached.importVar;
|
|
366
|
+
bindings = cached.bindings;
|
|
367
|
+
importStatements = cached.importStatements;
|
|
368
|
+
}
|
|
369
|
+
else {
|
|
370
|
+
// Fall back to extracting metadata from the __hmr_import_metadata__
|
|
371
|
+
// export the babel plugin emits into the source.
|
|
372
|
+
const metaMatch = source.match(/export const __hmr_import_metadata__ = (\{[\s\S]*?\});/);
|
|
373
|
+
if (metaMatch) {
|
|
374
|
+
try {
|
|
375
|
+
// eslint-disable-next-line no-new-func
|
|
376
|
+
const meta = new Function(`return ${metaMatch[1]}`)();
|
|
377
|
+
importVar = meta.importVar;
|
|
378
|
+
bindings = (_a = meta.bindings) !== null && _a !== void 0 ? _a : [];
|
|
379
|
+
}
|
|
380
|
+
catch (_c) {
|
|
381
|
+
// unparseable metadata — skip
|
|
382
|
+
}
|
|
383
|
+
}
|
|
384
|
+
// Recover importStatements by scanning the source for each binding
|
|
482
385
|
if (importVar && bindings.length > 0) {
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
}
|
|
493
|
-
else if (specifier.type === 'ImportSpecifier') {
|
|
494
|
-
// For named imports, use the imported name
|
|
495
|
-
// Handle both Identifier and StringLiteral types
|
|
496
|
-
const imported = specifier.imported;
|
|
497
|
-
specifierName =
|
|
498
|
-
imported.type === 'Identifier'
|
|
499
|
-
? imported.name
|
|
500
|
-
: imported.value;
|
|
501
|
-
}
|
|
502
|
-
else if (specifier.type === 'ImportNamespaceSpecifier') {
|
|
503
|
-
specifierName = '*';
|
|
504
|
-
}
|
|
505
|
-
importStatements.push({
|
|
506
|
-
local,
|
|
507
|
-
source: importSource,
|
|
508
|
-
specifier: specifierName,
|
|
509
|
-
});
|
|
510
|
-
}
|
|
386
|
+
for (const binding of bindings) {
|
|
387
|
+
const importMatch = source.match(new RegExp(`import\\s+(?:(${binding})|(\\{[^}]*\\b${binding}\\b[^}]*\\})|\\*\\s+as\\s+(${binding}))\\s+from\\s+['"]([^'"]+)['"]`));
|
|
388
|
+
if (importMatch) {
|
|
389
|
+
const src = importMatch[4];
|
|
390
|
+
let specifier = 'default';
|
|
391
|
+
if (importMatch[2]) {
|
|
392
|
+
// named import — find alias or name
|
|
393
|
+
const namedMatch = importMatch[2].match(new RegExp(`(\\w+)\\s+as\\s+${binding}|${binding}`));
|
|
394
|
+
specifier = (_b = namedMatch === null || namedMatch === void 0 ? void 0 : namedMatch[1]) !== null && _b !== void 0 ? _b : binding;
|
|
511
395
|
}
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
if (importVar) {
|
|
517
|
-
// Generate hot reload code for each import (only if we have bindings)
|
|
518
|
-
const hotReloadStatements = [];
|
|
519
|
-
for (const imp of importStatements) {
|
|
520
|
-
// Resolve the import to check if it's from node_modules
|
|
521
|
-
const resolved = yield this.resolve(imp.source, resourcePath, {});
|
|
522
|
-
if ((resolved === null || resolved === void 0 ? void 0 : resolved.id) &&
|
|
523
|
-
normalizePath(resolved.id).includes('node_modules')) {
|
|
524
|
-
// Skip node_modules imports
|
|
525
|
-
continue;
|
|
396
|
+
else if (importMatch[3]) {
|
|
397
|
+
specifier = '*';
|
|
398
|
+
}
|
|
399
|
+
importStatements.push({ local: binding, source: src, specifier });
|
|
526
400
|
}
|
|
527
|
-
const sourceId = imp.source.replace(/@embroider\/virtual/g, 'embroider_virtual');
|
|
528
|
-
const virtualPath = `/ember-vite-hmr/virtual/component:${sourceId}::${imp.specifier}.gjs`;
|
|
529
|
-
hotReloadStatements.push(`
|
|
530
|
-
(async () => {
|
|
531
|
-
const GlimmerComponent = (await import('@glimmer/component')).default;
|
|
532
|
-
const { hasInternalComponentManager } = await import('@glimmer/manager');
|
|
533
|
-
// Class-based Glimmer components are functions, detected the cheap way
|
|
534
|
-
// via prototype chain. Template-only components (no backing class, e.g.
|
|
535
|
-
// a bare \`<template>\` export) are plain objects instead, so they have to
|
|
536
|
-
// be recognized by checking for a registered component manager - that
|
|
537
|
-
// also keeps helpers/modifiers (which use separate manager registries)
|
|
538
|
-
// out of this branch.
|
|
539
|
-
const isComponent = typeof ${imp.local} === 'function'
|
|
540
|
-
? ${imp.local}.prototype instanceof GlimmerComponent
|
|
541
|
-
: typeof ${imp.local} === 'object' && ${imp.local} !== null && hasInternalComponentManager(${imp.local});
|
|
542
|
-
if (isComponent) {
|
|
543
|
-
const c = await import('${virtualPath}');
|
|
544
|
-
${importVar}.${imp.local} = c.default;
|
|
545
|
-
import.meta.hot.accept('${virtualPath}', (c) => {
|
|
546
|
-
${importVar}.${imp.local} = c['${imp.specifier}'];
|
|
547
|
-
});
|
|
548
|
-
import.meta.hot.accept('${imp.source}');
|
|
549
|
-
}
|
|
550
|
-
})();`);
|
|
551
401
|
}
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
}
|
|
560
|
-
|
|
402
|
+
}
|
|
403
|
+
}
|
|
404
|
+
// Process metadata if we found importVar (even with empty bindings)
|
|
405
|
+
if (importVar) {
|
|
406
|
+
const hotReloadStatements = [];
|
|
407
|
+
for (const imp of importStatements) {
|
|
408
|
+
// Resolve the import to check if it's from node_modules
|
|
409
|
+
const resolved = yield this.resolve(imp.source, resourcePath, {});
|
|
410
|
+
if ((resolved === null || resolved === void 0 ? void 0 : resolved.id) &&
|
|
411
|
+
normalizePath(resolved.id).includes('node_modules')) {
|
|
412
|
+
continue;
|
|
561
413
|
}
|
|
414
|
+
// Each imported binding is already a local variable in scope thanks
|
|
415
|
+
// to its import statement. Register it by value on first load so
|
|
416
|
+
// `current(binding)` (called by the template__imports__ getter) has
|
|
417
|
+
// an entry to read from. When the dep module reloads, update() sets
|
|
418
|
+
// entry.current = newVal — the getter re-reads it, invalidating only
|
|
419
|
+
// the scope that contains the getter call, not the parent scope.
|
|
420
|
+
// No assignment to template__imports__.X needed: the getter handles it.
|
|
421
|
+
hotReloadStatements.push(`
|
|
422
|
+
(async () => {
|
|
423
|
+
const { register: ember_vite_hmr_register, update: ember_vite_hmr_update, used: ember_vite_hmr_used } = await import(${JSON.stringify(hmrRuntimeId)});
|
|
424
|
+
const { tracked: ember_vite_hmr_tracked } = await import('@glimmer/tracking');
|
|
425
|
+
ember_vite_hmr_register(${imp.local}, ember_vite_hmr_tracked);
|
|
426
|
+
import.meta.hot.accept(${JSON.stringify(imp.source)}, (m) => {
|
|
427
|
+
if (m) {
|
|
428
|
+
const newVal = m[${JSON.stringify(imp.specifier === 'default' ? 'default' : imp.specifier)}];
|
|
429
|
+
ember_vite_hmr_update(${imp.local}, newVal);
|
|
430
|
+
} else if (!ember_vite_hmr_used(${imp.local})) {
|
|
431
|
+
import.meta.hot.invalidate('nothing rendered ${imp.local} through the HMR runtime');
|
|
432
|
+
}
|
|
433
|
+
});
|
|
434
|
+
})();`);
|
|
562
435
|
}
|
|
436
|
+
// Always remove the metadata export
|
|
437
|
+
source = source.replace(/export const __hmr_import_metadata__[^;]+;/, '');
|
|
438
|
+
// Do NOT add a self-accept in the importVar block.
|
|
439
|
+
//
|
|
440
|
+
// When module A imports module B, and B's template__imports__ class is in
|
|
441
|
+
// A's scope, A must be notified when B changes (so A's accept callback can
|
|
442
|
+
// swap in the new B class via template__imports__.B = newB). If B were to
|
|
443
|
+
// self-accept, Vite would stop propagation before A's callback fires — A's
|
|
444
|
+
// template__imports__ would never be updated, and Glimmer would never
|
|
445
|
+
// re-render. The same applies to route templates: they rely on Ember's
|
|
446
|
+
// canAcceptNew mechanism (added below), which self-accept would bypass.
|
|
447
|
+
//
|
|
448
|
+
// Components without importVar (no template imports) get a self-accept in
|
|
449
|
+
// the section below, after the supportedPaths check.
|
|
450
|
+
const hotReloadCode = hotReloadStatements.length > 0
|
|
451
|
+
? `\nif (import.meta.hot) {\n${hotReloadStatements.join('\n')}\n}`
|
|
452
|
+
: '';
|
|
453
|
+
source = source + hotReloadCode;
|
|
563
454
|
}
|
|
564
455
|
const supportedPaths = ['routers', 'controllers', 'routes', 'templates'];
|
|
565
456
|
const supportedFileNames = [
|
|
@@ -575,6 +466,56 @@ ${hotReloadStatements.join('\n')}
|
|
|
575
466
|
}
|
|
576
467
|
if (!supportedPaths.some((s) => resourcePath.includes(`/${s}/`)) &&
|
|
577
468
|
!supportedFileNames.some((s) => resourcePath.endsWith(s))) {
|
|
469
|
+
// Component files (including template-less .ts/.js ones that just extend
|
|
470
|
+
// another component) that have no cross-component imports need a
|
|
471
|
+
// self-accept boundary so edits to them don't propagate up to a full
|
|
472
|
+
// page reload. Restrict plain .ts/.js to paths under /components/ to
|
|
473
|
+
// avoid accidentally self-accepting services, utilities, adapters, etc.
|
|
474
|
+
const isComponentFile = resourcePath.endsWith('.gjs') ||
|
|
475
|
+
resourcePath.endsWith('.gts') ||
|
|
476
|
+
((resourcePath.endsWith('.ts') || resourcePath.endsWith('.js')) &&
|
|
477
|
+
resourcePath.includes('/components/'));
|
|
478
|
+
if (isComponentFile && !importVar) {
|
|
479
|
+
// Self-accept with a callback that updates the tracked-cell registry
|
|
480
|
+
// so Glimmer re-renders and swaps in the new class. This handles
|
|
481
|
+
// edits to the component's own class (new method, changed @tracked
|
|
482
|
+
// property, etc.) — without it, a bare accept() would silently
|
|
483
|
+
// discard the new class and leave the running instance on the old one.
|
|
484
|
+
//
|
|
485
|
+
// Only do this when there is no importVar (no template imports of
|
|
486
|
+
// other components). When importVar is present, the component's own
|
|
487
|
+
// importer already has an accept(dep, cb) that fires when this file
|
|
488
|
+
// changes — adding a self-accept here would stop Vite's propagation
|
|
489
|
+
// before that callback can fire, breaking the re-render. The same
|
|
490
|
+
// applies to re-export barrels: a self-accept on my-button.ts stops
|
|
491
|
+
// Vite walking up to index.ts's importer, silencing the accept
|
|
492
|
+
// callback that updates template__imports__.
|
|
493
|
+
//
|
|
494
|
+
// The babel plugin rewrites `export default <expr>` into
|
|
495
|
+
// `const __hmr_default__ = <expr>; export { __hmr_default__ as default }`
|
|
496
|
+
// giving us a stable local variable to register by value.
|
|
497
|
+
// hot.data.default carries the old value across self-accept
|
|
498
|
+
// re-evaluations so update() can find the existing entry.
|
|
499
|
+
return `${source}
|
|
500
|
+
if (import.meta.hot) {
|
|
501
|
+
(async () => {
|
|
502
|
+
const { register: ember_vite_hmr_register, update: ember_vite_hmr_update, used: ember_vite_hmr_used } = await import(${JSON.stringify(hmrRuntimeId)});
|
|
503
|
+
const { tracked: ember_vite_hmr_tracked } = await import('@glimmer/tracking');
|
|
504
|
+
const _hmr_prev = import.meta.hot.data.default;
|
|
505
|
+
import.meta.hot.data.default = __hmr_default__;
|
|
506
|
+
if (_hmr_prev) {
|
|
507
|
+
ember_vite_hmr_update(_hmr_prev, __hmr_default__);
|
|
508
|
+
}
|
|
509
|
+
ember_vite_hmr_register(__hmr_default__, ember_vite_hmr_tracked);
|
|
510
|
+
import.meta.hot.accept((m) => {
|
|
511
|
+
if (m && !ember_vite_hmr_used(import.meta.hot.data.default)) {
|
|
512
|
+
import.meta.hot.invalidate('nothing rendered this component through the HMR runtime');
|
|
513
|
+
}
|
|
514
|
+
});
|
|
515
|
+
})();
|
|
516
|
+
}
|
|
517
|
+
`;
|
|
518
|
+
}
|
|
578
519
|
return source;
|
|
579
520
|
}
|
|
580
521
|
if (supportedPaths.includes('templates') &&
|
|
@@ -596,4 +537,5 @@ ${hotReloadStatements.join('\n')}
|
|
|
596
537
|
});
|
|
597
538
|
},
|
|
598
539
|
};
|
|
540
|
+
return [hmrRuntime(), mainPlugin];
|
|
599
541
|
}
|