@lwc/hmr-client 10.2.1 → 10.3.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/client/client.d.ts +38 -0
- package/dist/client/hot-module-handler.d.ts +16 -0
- package/dist/client/index.d.ts +18 -0
- package/dist/client/index.spec.d.ts +1 -0
- package/dist/index.cjs.js +183 -283
- package/dist/index.cjs.js.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +183 -283
- package/dist/index.js.map +1 -1
- package/dist/utils/index.d.ts +1 -0
- package/dist/utils/json-web-socket.d.ts +11 -0
- package/dist/utils/typed-event-target.d.ts +14 -0
- package/dist/utils/typed-event-target.spec.d.ts +1 -0
- package/package.json +4 -2
- package/dist/connection/connection-factory.d.ts +0 -13
- package/dist/hmr-client/hmr-client.d.ts +0 -32
- package/dist/notify/consoleNotifier.d.ts +0 -4
- package/dist/notify/notify.d.ts +0 -26
- package/dist/util.d.ts +0 -2
- /package/dist/{notify/consoleLogger.d.ts → consoleLogger.d.ts} +0 -0
- /package/dist/{module-utils → utils}/module-util.d.ts +0 -0
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import JSONWebSocket from '../utils/json-web-socket';
|
|
2
|
+
import { TypedEventTarget } from '../utils/typed-event-target';
|
|
3
|
+
import { HotModuleHandler } from './hot-module-handler';
|
|
4
|
+
type HmrClientEvent = {
|
|
5
|
+
modulePath: string;
|
|
6
|
+
};
|
|
7
|
+
type HmrClientEvents = {
|
|
8
|
+
'hmr:update': HmrClientEvent;
|
|
9
|
+
'hmr:delete': HmrClientEvent;
|
|
10
|
+
'hmr:create': HmrClientEvent;
|
|
11
|
+
'hmr:hot-swapped': HmrClientEvent;
|
|
12
|
+
'hmr:close': void;
|
|
13
|
+
};
|
|
14
|
+
export declare class HMRClient {
|
|
15
|
+
moduleHandlerHooks: HotModuleHandler;
|
|
16
|
+
ws: JSONWebSocket;
|
|
17
|
+
private eventTarget;
|
|
18
|
+
constructor(ws: JSONWebSocket, moduleHandlerHooks: HotModuleHandler);
|
|
19
|
+
/**
|
|
20
|
+
* Register all modules that have been registered at this point.
|
|
21
|
+
* This function is usually called upon hmr-client initialization.
|
|
22
|
+
*/
|
|
23
|
+
registerAllModules(): void;
|
|
24
|
+
/**
|
|
25
|
+
* Register any new modules that have been registered.
|
|
26
|
+
*/
|
|
27
|
+
registerPendingModules(): void;
|
|
28
|
+
messageCallback(data: unknown): void;
|
|
29
|
+
/**
|
|
30
|
+
* Send a message to the dev server to send a hot module.
|
|
31
|
+
* The send and receive messages are asyncronous.
|
|
32
|
+
* @param modulePath the module to be fetched
|
|
33
|
+
*/
|
|
34
|
+
fetchModule(modulePath: string): void;
|
|
35
|
+
addEventListener: TypedEventTarget<HmrClientEvents>['addEventListener'];
|
|
36
|
+
removeEventListener: TypedEventTarget<HmrClientEvents>['removeEventListener'];
|
|
37
|
+
}
|
|
38
|
+
export {};
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* This interface defines capabilities that need to be implemented by the container to handle hot module
|
|
3
|
+
* updates.
|
|
4
|
+
*/
|
|
5
|
+
export interface HotModuleHandler {
|
|
6
|
+
/**
|
|
7
|
+
* Fetch all dependencies that are required for a hot module before its evaluation.
|
|
8
|
+
* @param moduleSpecifiers List of dependent modules specifiers.
|
|
9
|
+
*/
|
|
10
|
+
fetchDependencies(moduleSpecifiers: string[]): Promise<any>;
|
|
11
|
+
/**
|
|
12
|
+
* Evalaute a given Javascript source code.
|
|
13
|
+
* @param source Javascript in string format.
|
|
14
|
+
*/
|
|
15
|
+
evaluateModule(ownerModuleId: string, hotModulePath: string, source: string): Promise<unknown>;
|
|
16
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { Auth_Challenge, Auth_Response } from '@lwc/lwc-dev-server-types';
|
|
2
|
+
import { HotModuleHandler } from './hot-module-handler';
|
|
3
|
+
import { HMRClient } from './client';
|
|
4
|
+
type AuthConfig = {
|
|
5
|
+
authChallengeData: Auth_Challenge;
|
|
6
|
+
authResponseCallback: (response: Auth_Response) => boolean;
|
|
7
|
+
};
|
|
8
|
+
/**
|
|
9
|
+
* Initialize the hmr-client.
|
|
10
|
+
* @param url The lightning dev preview server to connect to. Provide the fully qualified WebSocketServer url, for example 'wss://localhost:8999'
|
|
11
|
+
* @param target The type of client being served. Currently only supports 'LEX.
|
|
12
|
+
* @param hotModuleHandler Callback hooks that the hmr-client will utilize to handle hot modules. The callbacks are implemented by the container.
|
|
13
|
+
* @param authConfig Optional authentication configuration if the hmr-client needs to establish an authenticated connection.
|
|
14
|
+
*
|
|
15
|
+
* @returns {HMRClient} the HMR Client
|
|
16
|
+
*/
|
|
17
|
+
export declare function initializeClient(url: string, _target: string, hotModuleHandler: HotModuleHandler, authConfig?: AuthConfig): Promise<HMRClient>;
|
|
18
|
+
export {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
package/dist/index.cjs.js
CHANGED
|
@@ -5,6 +5,54 @@
|
|
|
5
5
|
|
|
6
6
|
Object.defineProperty(exports, '__esModule', { value: true });
|
|
7
7
|
|
|
8
|
+
var lwcDevServerTypes = require('@lwc/lwc-dev-server-types');
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Wrapper on WebSocket to handle JSON serialization/deserialization
|
|
12
|
+
*/
|
|
13
|
+
class JSONWebSocket {
|
|
14
|
+
constructor(url, protocols) {
|
|
15
|
+
this.socket = new WebSocket(url, protocols);
|
|
16
|
+
}
|
|
17
|
+
send(data) {
|
|
18
|
+
this.socket.send(JSON.stringify(data));
|
|
19
|
+
}
|
|
20
|
+
onMessage(callback) {
|
|
21
|
+
this.socket.addEventListener('message', (event) => {
|
|
22
|
+
const jsonData = JSON.parse(event.data);
|
|
23
|
+
callback(jsonData);
|
|
24
|
+
});
|
|
25
|
+
}
|
|
26
|
+
close() {
|
|
27
|
+
this.socket.close();
|
|
28
|
+
}
|
|
29
|
+
static init(url, protocols) {
|
|
30
|
+
return new Promise((resolve, reject) => {
|
|
31
|
+
try {
|
|
32
|
+
const socket = new JSONWebSocket(url, protocols);
|
|
33
|
+
socket.socket.onopen = () => resolve(socket);
|
|
34
|
+
socket.socket.onerror = (e) => reject(e);
|
|
35
|
+
}
|
|
36
|
+
catch (e) {
|
|
37
|
+
reject(e);
|
|
38
|
+
}
|
|
39
|
+
});
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/* eslint-disable no-console */
|
|
44
|
+
/**
|
|
45
|
+
* All the console methods in one module so that these can be instrumented for JEST tests and
|
|
46
|
+
* marked for eslint ignore in one place.
|
|
47
|
+
*/
|
|
48
|
+
const LABEL = '[lightning preview]';
|
|
49
|
+
function logWarning(message) {
|
|
50
|
+
console.warn(`${LABEL} ${message}`);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
const IGNORING_PREAUTH_RESPONSES = 'Ignoring pre-authenticated responses sent from Lightning Dev Preview server';
|
|
54
|
+
const AUTH_FAILURE = 'WebSocket Authentication Failure: Failed to success fully establish an authenticated connection between Lightning Dev Preview server and client';
|
|
55
|
+
|
|
8
56
|
const hotModuleCbs = new Map();
|
|
9
57
|
/**
|
|
10
58
|
* API call to self update.
|
|
@@ -95,239 +143,6 @@ function getActiveModules() {
|
|
|
95
143
|
return Array.from(activeModules.values());
|
|
96
144
|
}
|
|
97
145
|
|
|
98
|
-
/**
|
|
99
|
-
* This module is responsible for notfying the user about module updates in the server
|
|
100
|
-
**/
|
|
101
|
-
class NotifyModuleUpdate {
|
|
102
|
-
constructor() {
|
|
103
|
-
this.observers = new Set();
|
|
104
|
-
}
|
|
105
|
-
register(observer) {
|
|
106
|
-
if (observer) {
|
|
107
|
-
this.observers.add(observer);
|
|
108
|
-
}
|
|
109
|
-
}
|
|
110
|
-
deregister(observer) {
|
|
111
|
-
if (observer) {
|
|
112
|
-
this.observers.delete(observer);
|
|
113
|
-
}
|
|
114
|
-
}
|
|
115
|
-
notify(message) {
|
|
116
|
-
this.observers.forEach((observer) => observer.notify(message));
|
|
117
|
-
}
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
/* eslint-disable no-console */
|
|
121
|
-
/**
|
|
122
|
-
* All the console methods in one module so that these can be instrumented for JEST tests and
|
|
123
|
-
* marked for eslint ignore in one place.
|
|
124
|
-
*/
|
|
125
|
-
const LABEL = '[lightning preview]';
|
|
126
|
-
function logWarning(message) {
|
|
127
|
-
console.warn(`${LABEL} ${message}`);
|
|
128
|
-
}
|
|
129
|
-
function logInfo(message) {
|
|
130
|
-
console.log(`${LABEL} ${message}`);
|
|
131
|
-
}
|
|
132
|
-
|
|
133
|
-
const SOCKET_OPEN = 'WebSocket: Connection to Lightning Dev Preview server open.';
|
|
134
|
-
const SOCKET_CONNECTION_INITIATION = 'WebSocket: Connecting to Lightning Dev Preview server:';
|
|
135
|
-
const SOCKET_SERVER_UNAVAILABLE = 'WebSocket Server Unavailable: Connection to Lightning Dev Preview server unavailable.';
|
|
136
|
-
const SOCKET_SERVER_CONNECTION_ERROR = 'WebSocket Server Connection Error: Connection to Lightning Dev Preview server failed.';
|
|
137
|
-
const IGNORING_PREAUTH_RESPONSES = 'Ignoring pre-authenticated responses sent from Lightning Dev Preview server';
|
|
138
|
-
const AUTH_FAILURE = 'WebSocket Authentication Failure: Failed to success fully establish an authenticated connection between Lightning Dev Preview server and client';
|
|
139
|
-
|
|
140
|
-
const CONNECTION_RETRY_COUNT = 5;
|
|
141
|
-
const CONNECTION_RETRY_INTERVAL_MS = 500;
|
|
142
|
-
async function getConnection(devServerUrl, target, clientId, authConfig, connectionOpenCallback, connectionCloseCallback) {
|
|
143
|
-
const socket = await initiateWSConnection(devServerUrl, CONNECTION_RETRY_COUNT);
|
|
144
|
-
connectionOpenCallback && connectionOpenCallback();
|
|
145
|
-
if (connectionCloseCallback) {
|
|
146
|
-
socket.addEventListener('close', connectionCloseCallback);
|
|
147
|
-
}
|
|
148
|
-
const connection = new Connection(socket);
|
|
149
|
-
const initData = {
|
|
150
|
-
type: 'init',
|
|
151
|
-
data: {
|
|
152
|
-
clientId,
|
|
153
|
-
url: window.location.href,
|
|
154
|
-
target,
|
|
155
|
-
},
|
|
156
|
-
};
|
|
157
|
-
// If authentication is needed, resolve promise after successful authentication
|
|
158
|
-
if (authConfig) {
|
|
159
|
-
return new Promise((resolve, reject) => {
|
|
160
|
-
// Create a listener to handle the auth response and remove it once the connection is authenticated
|
|
161
|
-
const listener = createAuthMessageHandler((response) => {
|
|
162
|
-
// Delegate auth response validation to invoker
|
|
163
|
-
const authResult = authConfig.authResponseCallback(response);
|
|
164
|
-
// If successful, resolve with a connection that is ready to be used
|
|
165
|
-
if (authResult) {
|
|
166
|
-
socket.removeEventListener('message', listener);
|
|
167
|
-
resolve(connection);
|
|
168
|
-
}
|
|
169
|
-
else {
|
|
170
|
-
reject(new Error(AUTH_FAILURE));
|
|
171
|
-
}
|
|
172
|
-
});
|
|
173
|
-
socket.addEventListener('message', listener);
|
|
174
|
-
initData.data.auth = {
|
|
175
|
-
type: 'auth-challenge',
|
|
176
|
-
data: authConfig.authChallengeData,
|
|
177
|
-
};
|
|
178
|
-
connection.send(initData);
|
|
179
|
-
});
|
|
180
|
-
}
|
|
181
|
-
else {
|
|
182
|
-
connection.send(initData);
|
|
183
|
-
return connection;
|
|
184
|
-
}
|
|
185
|
-
}
|
|
186
|
-
class Connection {
|
|
187
|
-
constructor(socket) {
|
|
188
|
-
this.socket = socket;
|
|
189
|
-
}
|
|
190
|
-
send(data) {
|
|
191
|
-
if (!this.isReady()) {
|
|
192
|
-
logWarning('Ignoring to send data before connection is ready.');
|
|
193
|
-
return false;
|
|
194
|
-
}
|
|
195
|
-
this.socket.send(JSON.stringify(data));
|
|
196
|
-
return true;
|
|
197
|
-
}
|
|
198
|
-
receive(callback) {
|
|
199
|
-
if (!this.isReady()) {
|
|
200
|
-
logWarning('Connection not ready to receive data from server.');
|
|
201
|
-
return;
|
|
202
|
-
}
|
|
203
|
-
this.socket.addEventListener('message', ({ data }) => {
|
|
204
|
-
if (data) {
|
|
205
|
-
const payload = JSON.parse(data);
|
|
206
|
-
callback(payload);
|
|
207
|
-
}
|
|
208
|
-
});
|
|
209
|
-
}
|
|
210
|
-
close() {
|
|
211
|
-
this.socket.close();
|
|
212
|
-
}
|
|
213
|
-
isReady() {
|
|
214
|
-
return this.socket.readyState === this.socket.OPEN;
|
|
215
|
-
}
|
|
216
|
-
}
|
|
217
|
-
/**
|
|
218
|
-
* A event handler to handle messages from the WebSocket.
|
|
219
|
-
* This handler is meant to only be used for authentication and then detached after successful auth.
|
|
220
|
-
*/
|
|
221
|
-
function createAuthMessageHandler(authCallback) {
|
|
222
|
-
return ({ data }) => {
|
|
223
|
-
if (data) {
|
|
224
|
-
const payload = JSON.parse(data);
|
|
225
|
-
// only accept auth-response messages from LDP Server until authenticated
|
|
226
|
-
if (payload.type !== 'auth-response') {
|
|
227
|
-
logWarning(IGNORING_PREAUTH_RESPONSES);
|
|
228
|
-
return;
|
|
229
|
-
}
|
|
230
|
-
if (payload.type === 'auth-response') {
|
|
231
|
-
const authResponse = payload;
|
|
232
|
-
authCallback(authResponse.data);
|
|
233
|
-
}
|
|
234
|
-
}
|
|
235
|
-
};
|
|
236
|
-
}
|
|
237
|
-
/**
|
|
238
|
-
* A function to establish a WebSocket connection to the provided url
|
|
239
|
-
* @param wsServerUrl The lightning dev server url to connect to
|
|
240
|
-
* @param connectionRetry number of retry attempts to connect with the server
|
|
241
|
-
* @returns
|
|
242
|
-
*/
|
|
243
|
-
async function initiateWSConnection(wsServerUrl, connectionRetry) {
|
|
244
|
-
let socket;
|
|
245
|
-
let retryCount = 0;
|
|
246
|
-
while (socket === undefined && retryCount < connectionRetry) {
|
|
247
|
-
try {
|
|
248
|
-
logInfo(`${SOCKET_CONNECTION_INITIATION} ${wsServerUrl}` + retryCount
|
|
249
|
-
? `, retry attempt: ${retryCount}`
|
|
250
|
-
: '');
|
|
251
|
-
socket = await connect(wsServerUrl);
|
|
252
|
-
}
|
|
253
|
-
catch (e) {
|
|
254
|
-
// Nothing to do, the LDP server is not available yet.
|
|
255
|
-
// Cannot tell if the server url is unavailable or invalid, regardless it will retry
|
|
256
|
-
}
|
|
257
|
-
if (socket !== undefined) {
|
|
258
|
-
break;
|
|
259
|
-
}
|
|
260
|
-
retryCount++;
|
|
261
|
-
await new Promise((resolve) => setTimeout(resolve, CONNECTION_RETRY_INTERVAL_MS));
|
|
262
|
-
}
|
|
263
|
-
if (socket === undefined) {
|
|
264
|
-
logWarning(SOCKET_SERVER_UNAVAILABLE);
|
|
265
|
-
throw new Error(SOCKET_SERVER_UNAVAILABLE);
|
|
266
|
-
}
|
|
267
|
-
return socket;
|
|
268
|
-
}
|
|
269
|
-
/**
|
|
270
|
-
* Attempt to open a WebSockect connection
|
|
271
|
-
* @param wsServerUrl WebSocketServer url to connect to
|
|
272
|
-
* @returns Resolves with a successful socket connection, that is open to communication else rejects with an error message
|
|
273
|
-
*/
|
|
274
|
-
function connect(wsServerUrl) {
|
|
275
|
-
return new Promise((resolve, reject) => {
|
|
276
|
-
try {
|
|
277
|
-
const socket = new WebSocket(wsServerUrl, 'lightning-dev-preview');
|
|
278
|
-
const errorHandler = (_e) => {
|
|
279
|
-
logWarning(`${SOCKET_SERVER_CONNECTION_ERROR}.`);
|
|
280
|
-
reject(SOCKET_SERVER_CONNECTION_ERROR);
|
|
281
|
-
};
|
|
282
|
-
socket.addEventListener('error', errorHandler);
|
|
283
|
-
// The connection is ready when the open callback is called, resolve the promise then
|
|
284
|
-
socket.addEventListener('open', () => {
|
|
285
|
-
logInfo(SOCKET_OPEN);
|
|
286
|
-
// Remove the error handler once the connection is open
|
|
287
|
-
socket.removeEventListener('error', errorHandler);
|
|
288
|
-
// The connection is ready when the open callback is called, resolve the promise then
|
|
289
|
-
resolve(socket);
|
|
290
|
-
});
|
|
291
|
-
}
|
|
292
|
-
catch (e) {
|
|
293
|
-
logWarning(`${SOCKET_SERVER_CONNECTION_ERROR}. Cause: ${JSON.stringify(e)}`);
|
|
294
|
-
reject(SOCKET_SERVER_CONNECTION_ERROR);
|
|
295
|
-
}
|
|
296
|
-
});
|
|
297
|
-
}
|
|
298
|
-
|
|
299
|
-
class ConsoleNotifier {
|
|
300
|
-
notify(message) {
|
|
301
|
-
switch (message.eventType) {
|
|
302
|
-
case 'create':
|
|
303
|
-
console.log(`A new module was added at ${message.modulePath}`);
|
|
304
|
-
break;
|
|
305
|
-
case 'delete':
|
|
306
|
-
console.log(`A module was deleted at ${message.modulePath}`);
|
|
307
|
-
break;
|
|
308
|
-
case 'update':
|
|
309
|
-
console.log(`A source change detected at ${message.modulePath}`);
|
|
310
|
-
break;
|
|
311
|
-
case 'hot-swapped':
|
|
312
|
-
console.log(`Received a hot module with path ${message.modulePath}, attempting to hot swap`);
|
|
313
|
-
break;
|
|
314
|
-
}
|
|
315
|
-
if (message.action) {
|
|
316
|
-
console.log(`Taking the following action on module update ${message.action.message}`);
|
|
317
|
-
}
|
|
318
|
-
}
|
|
319
|
-
}
|
|
320
|
-
|
|
321
|
-
function debounce(callback, delay) {
|
|
322
|
-
let timer;
|
|
323
|
-
return () => {
|
|
324
|
-
clearTimeout(timer);
|
|
325
|
-
timer = window.setTimeout(() => {
|
|
326
|
-
callback();
|
|
327
|
-
}, delay);
|
|
328
|
-
};
|
|
329
|
-
}
|
|
330
|
-
|
|
331
146
|
function extractDescriptorsFromSource(src) {
|
|
332
147
|
const amdPattern = /^define\('([^']+)',\s*\[([^\]]*)\],/;
|
|
333
148
|
const match = amdPattern.exec(src);
|
|
@@ -349,32 +164,86 @@ function adaptSourceForLex(src, hotModulePath, parentDescriptor) {
|
|
|
349
164
|
.concat('}');
|
|
350
165
|
}
|
|
351
166
|
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
167
|
+
function debounce(callback, delay) {
|
|
168
|
+
let timer;
|
|
169
|
+
return () => {
|
|
170
|
+
clearTimeout(timer);
|
|
171
|
+
timer = setTimeout(() => {
|
|
172
|
+
callback();
|
|
173
|
+
}, delay);
|
|
174
|
+
};
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
/******************************************************************************
|
|
178
|
+
Copyright (c) Microsoft Corporation.
|
|
179
|
+
|
|
180
|
+
Permission to use, copy, modify, and/or distribute this software for any
|
|
181
|
+
purpose with or without fee is hereby granted.
|
|
182
|
+
|
|
183
|
+
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
|
|
184
|
+
REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
|
|
185
|
+
AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
|
|
186
|
+
INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
|
|
187
|
+
LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
|
|
188
|
+
OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
|
189
|
+
PERFORMANCE OF THIS SOFTWARE.
|
|
190
|
+
***************************************************************************** */
|
|
191
|
+
/* global Reflect, Promise, SuppressedError, Symbol */
|
|
192
|
+
|
|
193
|
+
|
|
194
|
+
function __classPrivateFieldGet(receiver, state, kind, f) {
|
|
195
|
+
if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a getter");
|
|
196
|
+
if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot read private member from an object whose class did not declare it");
|
|
197
|
+
return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
function __classPrivateFieldSet(receiver, state, value, kind, f) {
|
|
201
|
+
if (kind === "m") throw new TypeError("Private method is not writable");
|
|
202
|
+
if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a setter");
|
|
203
|
+
if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot write private member to an object whose class did not declare it");
|
|
204
|
+
return (kind === "a" ? f.call(receiver, value) : f ? f.value = value : state.set(receiver, value)), value;
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
typeof SuppressedError === "function" ? SuppressedError : function (error, suppressed, message) {
|
|
208
|
+
var e = new Error(message);
|
|
209
|
+
return e.name = "SuppressedError", e.error = error, e.suppressed = suppressed, e;
|
|
210
|
+
};
|
|
211
|
+
|
|
212
|
+
var _TypedEventTarget_target;
|
|
213
|
+
//TODO: move to notify folder
|
|
214
|
+
class TypedEventTarget {
|
|
215
|
+
constructor() {
|
|
216
|
+
_TypedEventTarget_target.set(this, void 0);
|
|
217
|
+
__classPrivateFieldSet(this, _TypedEventTarget_target, new EventTarget(), "f");
|
|
218
|
+
}
|
|
219
|
+
addEventListener(type, listener) {
|
|
220
|
+
__classPrivateFieldGet(this, _TypedEventTarget_target, "f").addEventListener(type, listener);
|
|
221
|
+
}
|
|
222
|
+
removeEventListener(type, listener) {
|
|
223
|
+
__classPrivateFieldGet(this, _TypedEventTarget_target, "f").removeEventListener(type, listener);
|
|
224
|
+
}
|
|
225
|
+
dispatch(type, detail) {
|
|
226
|
+
return __classPrivateFieldGet(this, _TypedEventTarget_target, "f").dispatchEvent(new CustomEvent(type, { detail }));
|
|
227
|
+
}
|
|
357
228
|
}
|
|
229
|
+
_TypedEventTarget_target = new WeakMap();
|
|
230
|
+
|
|
231
|
+
const MODULE_REGISTRATION_DELAY_MS = 100;
|
|
358
232
|
let hotModuleCounter = 0;
|
|
359
233
|
class HMRClient {
|
|
360
|
-
constructor(
|
|
361
|
-
this.
|
|
234
|
+
constructor(ws, moduleHandlerHooks) {
|
|
235
|
+
this.eventTarget = new TypedEventTarget();
|
|
236
|
+
this.addEventListener = (...args) => {
|
|
237
|
+
this.eventTarget.addEventListener(...args);
|
|
238
|
+
};
|
|
239
|
+
this.removeEventListener = (...args) => {
|
|
240
|
+
this.eventTarget.removeEventListener(...args);
|
|
241
|
+
};
|
|
242
|
+
if (ws.socket.readyState !== 1) {
|
|
243
|
+
throw new Error('Socket needs to be open');
|
|
244
|
+
}
|
|
362
245
|
this.moduleHandlerHooks = moduleHandlerHooks;
|
|
363
|
-
this.
|
|
364
|
-
this.clientId = clientId;
|
|
365
|
-
// Register a console logger
|
|
366
|
-
this.notifyModuleUpdate.register(new ConsoleNotifier());
|
|
367
|
-
this.init();
|
|
368
|
-
}
|
|
369
|
-
registerObserver(observer) {
|
|
370
|
-
// Register any container specific observers
|
|
371
|
-
this.notifyModuleUpdate.register(observer);
|
|
372
|
-
}
|
|
373
|
-
/**
|
|
374
|
-
* Initialize the hmr-client.
|
|
375
|
-
*/
|
|
376
|
-
init() {
|
|
377
|
-
// Register all modules upon first connection
|
|
246
|
+
this.ws = ws;
|
|
378
247
|
this.registerAllModules();
|
|
379
248
|
// Add a debounced callback to register any new modules with server
|
|
380
249
|
const registerPendingModulesDebounced = debounce(this.registerPendingModules.bind(this), MODULE_REGISTRATION_DELAY_MS);
|
|
@@ -382,7 +251,8 @@ class HMRClient {
|
|
|
382
251
|
modulesPendingRegistration.add(modulePath);
|
|
383
252
|
registerPendingModulesDebounced();
|
|
384
253
|
});
|
|
385
|
-
this.
|
|
254
|
+
this.ws.onMessage(this.messageCallback.bind(this));
|
|
255
|
+
this.ws.socket.addEventListener('close', () => this.eventTarget.dispatch('hmr:close'));
|
|
386
256
|
}
|
|
387
257
|
/**
|
|
388
258
|
* Register all modules that have been registered at this point.
|
|
@@ -393,10 +263,9 @@ class HMRClient {
|
|
|
393
263
|
type: 'register',
|
|
394
264
|
data: {
|
|
395
265
|
modules: getActiveModules(),
|
|
396
|
-
clientId: this.clientId,
|
|
397
266
|
},
|
|
398
267
|
};
|
|
399
|
-
this.
|
|
268
|
+
this.ws.send(moduleRegistration);
|
|
400
269
|
modulesPendingRegistration.clear();
|
|
401
270
|
}
|
|
402
271
|
/**
|
|
@@ -408,15 +277,16 @@ class HMRClient {
|
|
|
408
277
|
type: 'register',
|
|
409
278
|
data: {
|
|
410
279
|
modules: Array.from(modulesPendingRegistration).map((module) => activeModules.get(module)),
|
|
411
|
-
clientId: this.clientId,
|
|
412
280
|
},
|
|
413
281
|
};
|
|
414
|
-
this.
|
|
282
|
+
this.ws.send(moduleRegistration);
|
|
415
283
|
// We assume the registration was successful
|
|
416
284
|
modulesPendingRegistration.clear();
|
|
417
285
|
}
|
|
418
286
|
}
|
|
419
287
|
messageCallback(data) {
|
|
288
|
+
if (!lwcDevServerTypes.isHmrData(data))
|
|
289
|
+
return;
|
|
420
290
|
switch (data.type) {
|
|
421
291
|
// Some paths were updated
|
|
422
292
|
case 'update': {
|
|
@@ -424,10 +294,7 @@ class HMRClient {
|
|
|
424
294
|
moduleUpdate.data.forEach(({ modulePath }) => {
|
|
425
295
|
// If there are update handlers for the modulePath, then fetch an update
|
|
426
296
|
if (hasUpdateHandlers(modulePath)) {
|
|
427
|
-
this.
|
|
428
|
-
eventType: 'update',
|
|
429
|
-
modulePath,
|
|
430
|
-
});
|
|
297
|
+
this.eventTarget.dispatch('hmr:update', { modulePath });
|
|
431
298
|
this.fetchModule(modulePath);
|
|
432
299
|
}
|
|
433
300
|
});
|
|
@@ -438,10 +305,7 @@ class HMRClient {
|
|
|
438
305
|
const hotModules = data;
|
|
439
306
|
// Process each incoming hot module
|
|
440
307
|
hotModules.data.forEach(async ({ modulePath, src }) => {
|
|
441
|
-
this.
|
|
442
|
-
eventType: 'hot-swapped',
|
|
443
|
-
modulePath,
|
|
444
|
-
});
|
|
308
|
+
this.eventTarget.dispatch('hmr:hot-swapped', { modulePath });
|
|
445
309
|
// Wrapped update handler, it will call the previous accept callbacks and remove
|
|
446
310
|
// them after they have been invoked.
|
|
447
311
|
const handler = updateHandler(modulePath);
|
|
@@ -466,10 +330,7 @@ class HMRClient {
|
|
|
466
330
|
case 'module-delete': {
|
|
467
331
|
const deletedModule = data;
|
|
468
332
|
deletedModule.data.forEach(({ modulePath }) => {
|
|
469
|
-
this.
|
|
470
|
-
eventType: 'delete',
|
|
471
|
-
modulePath,
|
|
472
|
-
});
|
|
333
|
+
this.eventTarget.dispatch('hmr:delete', { modulePath });
|
|
473
334
|
});
|
|
474
335
|
// A module was deleted
|
|
475
336
|
// reload page?
|
|
@@ -478,7 +339,7 @@ class HMRClient {
|
|
|
478
339
|
case 'error':
|
|
479
340
|
// eslint-disable-next-line no-console
|
|
480
341
|
console.log('LWC dev server encountered an error, reloading page');
|
|
481
|
-
|
|
342
|
+
location.reload();
|
|
482
343
|
break;
|
|
483
344
|
}
|
|
484
345
|
}
|
|
@@ -491,26 +352,65 @@ class HMRClient {
|
|
|
491
352
|
const fetchData = { type: 'fetch', data: { modulePath } };
|
|
492
353
|
// fetch the new module from the server
|
|
493
354
|
// eval it and return the new module
|
|
494
|
-
this.
|
|
355
|
+
this.ws.send(fetchData);
|
|
495
356
|
}
|
|
496
357
|
}
|
|
358
|
+
|
|
497
359
|
/**
|
|
498
360
|
* Initialize the hmr-client.
|
|
499
361
|
* @param url The lightning dev preview server to connect to. Provide the fully qualified WebSocketServer url, for example 'wss://localhost:8999'
|
|
500
362
|
* @param target The type of client being served. Currently only supports 'LEX.
|
|
501
363
|
* @param hotModuleHandler Callback hooks that the hmr-client will utilize to handle hot modules. The callbacks are implemented by the container.
|
|
502
364
|
* @param authConfig Optional authentication configuration if the hmr-client needs to establish an authenticated connection.
|
|
503
|
-
*
|
|
504
|
-
* @
|
|
505
|
-
* @param observer Be notified of hot module updates.
|
|
365
|
+
*
|
|
366
|
+
* @returns {HMRClient} the HMR Client
|
|
506
367
|
*/
|
|
507
|
-
async function initializeClient(url,
|
|
508
|
-
const
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
368
|
+
async function initializeClient(url, _target, hotModuleHandler, authConfig) {
|
|
369
|
+
const wsUrl = new URL(url);
|
|
370
|
+
wsUrl.searchParams.set('referer', location.href);
|
|
371
|
+
if (authConfig) {
|
|
372
|
+
wsUrl.searchParams.set('sessionTokenCipher', authConfig.authChallengeData.sessionTokenCipher);
|
|
373
|
+
}
|
|
374
|
+
const ws = await JSONWebSocket.init(wsUrl.toString(), 'lightning-dev-preview');
|
|
375
|
+
if (authConfig) {
|
|
376
|
+
try {
|
|
377
|
+
await waitForAuthResponse(ws, authConfig);
|
|
378
|
+
}
|
|
379
|
+
catch (e) {
|
|
380
|
+
ws.close();
|
|
381
|
+
throw e;
|
|
382
|
+
}
|
|
513
383
|
}
|
|
384
|
+
const client = new HMRClient(ws, hotModuleHandler);
|
|
385
|
+
listenOnConsole(client);
|
|
386
|
+
return client;
|
|
387
|
+
}
|
|
388
|
+
function waitForAuthResponse(ws, authConfig) {
|
|
389
|
+
return new Promise((resolve, reject) => {
|
|
390
|
+
const listener = ({ data }) => {
|
|
391
|
+
if (!data)
|
|
392
|
+
reject(new Error('Invalid message, missing data.')); // TODO: log the reason here.
|
|
393
|
+
const payload = JSON.parse(data);
|
|
394
|
+
if (payload.type !== 'auth-response') {
|
|
395
|
+
logWarning(IGNORING_PREAUTH_RESPONSES); // TODO: log the reason here.
|
|
396
|
+
reject(new Error('Invalid non-auth response.'));
|
|
397
|
+
}
|
|
398
|
+
if (authConfig.authResponseCallback(payload.data)) {
|
|
399
|
+
resolve();
|
|
400
|
+
}
|
|
401
|
+
else {
|
|
402
|
+
reject(new Error(AUTH_FAILURE));
|
|
403
|
+
}
|
|
404
|
+
};
|
|
405
|
+
ws.socket.addEventListener('message', listener, { once: true });
|
|
406
|
+
});
|
|
407
|
+
}
|
|
408
|
+
/* eslint-disable no-console */
|
|
409
|
+
function listenOnConsole(hmrClient) {
|
|
410
|
+
hmrClient.addEventListener('hmr:create', (event) => console.log(`A new module was added at ${event.detail.modulePath}`));
|
|
411
|
+
hmrClient.addEventListener('hmr:update', (event) => console.log(`A source change detected at ${event.detail.modulePath}`));
|
|
412
|
+
hmrClient.addEventListener('hmr:hot-swapped', (event) => console.log(`Received a hot module with path ${event.detail.modulePath}, attempting to hot swap`));
|
|
413
|
+
hmrClient.addEventListener('hmr:delete', (event) => console.log(`A module was deleted at ${event.detail.modulePath}`));
|
|
514
414
|
}
|
|
515
415
|
|
|
516
416
|
exports.accept = accept;
|