@vxrn/vite-native-hmr 0.0.3

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.
Files changed (47) hide show
  1. package/LICENSE +21 -0
  2. package/dist/cjs/devServerClient.js +132 -0
  3. package/dist/cjs/devServerClient.js.map +6 -0
  4. package/dist/cjs/devServerClient.native.js +132 -0
  5. package/dist/cjs/devServerClient.native.js.map +6 -0
  6. package/dist/cjs/getDevServerLocation.js +49 -0
  7. package/dist/cjs/getDevServerLocation.js.map +6 -0
  8. package/dist/cjs/getDevServerLocation.native.js +50 -0
  9. package/dist/cjs/getDevServerLocation.native.js.map +6 -0
  10. package/dist/cjs/hmr-client.js +130 -0
  11. package/dist/cjs/hmr-client.js.map +6 -0
  12. package/dist/cjs/hmr-client.native.js +129 -0
  13. package/dist/cjs/hmr-client.native.js.map +6 -0
  14. package/dist/cjs/index.js +19 -0
  15. package/dist/cjs/index.js.map +6 -0
  16. package/dist/cjs/index.native.js +20 -0
  17. package/dist/cjs/index.native.js.map +6 -0
  18. package/dist/esm/devServerClient.js +101 -0
  19. package/dist/esm/devServerClient.js.map +6 -0
  20. package/dist/esm/devServerClient.native.js +100 -0
  21. package/dist/esm/devServerClient.native.js.map +6 -0
  22. package/dist/esm/getDevServerLocation.js +21 -0
  23. package/dist/esm/getDevServerLocation.js.map +6 -0
  24. package/dist/esm/getDevServerLocation.native.js +21 -0
  25. package/dist/esm/getDevServerLocation.native.js.map +6 -0
  26. package/dist/esm/hmr-client.js +100 -0
  27. package/dist/esm/hmr-client.js.map +6 -0
  28. package/dist/esm/hmr-client.native.js +98 -0
  29. package/dist/esm/hmr-client.native.js.map +6 -0
  30. package/dist/esm/index.js +2 -0
  31. package/dist/esm/index.js.map +6 -0
  32. package/dist/esm/index.native.js +2 -0
  33. package/dist/esm/index.native.js.map +6 -0
  34. package/package.json +49 -0
  35. package/src/devServerClient.ts +132 -0
  36. package/src/getDevServerLocation.ts +33 -0
  37. package/src/globals.d.ts +33 -0
  38. package/src/hmr-client.ts +206 -0
  39. package/src/index.ts +1 -0
  40. package/types/devServerClient.d.ts +6 -0
  41. package/types/devServerClient.d.ts.map +1 -0
  42. package/types/getDevServerLocation.d.ts +11 -0
  43. package/types/getDevServerLocation.d.ts.map +1 -0
  44. package/types/hmr-client.d.ts +24 -0
  45. package/types/hmr-client.d.ts.map +1 -0
  46. package/types/index.d.ts +2 -0
  47. package/types/index.d.ts.map +1 -0
@@ -0,0 +1,98 @@
1
+ import { getDevServerLocation } from "./getDevServerLocation";
2
+ class HMRClient {
3
+ constructor(app) {
4
+ this.app = app;
5
+ this.lastHash = "";
6
+ this.url = `ws://${getDevServerLocation().hostname}:${process.env.REACT_NATIVE_SERVER_PUBLIC_PORT}/__hmr?platform=${process.env.REACT_NATIVE_PLATFORM || "ios"}`, this.socket = new WebSocket(this.url), console.log("[HMRClient] Connecting..."), this.socket.onopen = () => {
7
+ console.log("[HMRClient] Connected");
8
+ }, this.socket.onclose = () => {
9
+ console.log(`[HMRClient] Disconnected ${this.url}`);
10
+ }, this.socket.onerror = (event) => {
11
+ console.log("[HMRClient] Error", event);
12
+ }, this.socket.onmessage = (event) => {
13
+ try {
14
+ const data = JSON.parse(event.data.toString());
15
+ this.processMessage(data);
16
+ } catch (error) {
17
+ console.warn("[HMRClient] Invalid HMR message", error);
18
+ }
19
+ };
20
+ }
21
+ upToDate(hash) {
22
+ return hash && (this.lastHash = hash), this.lastHash === __webpack_hash__;
23
+ }
24
+ processMessage(message) {
25
+ switch (message.action) {
26
+ case "building":
27
+ this.app.LoadingView.showMessage("Rebuilding...", "refresh"), console.log("[HMRClient] Bundle rebuilding", {
28
+ name: message.body?.name
29
+ });
30
+ break;
31
+ case "built":
32
+ console.log("[HMRClient] Bundle rebuilt", {
33
+ name: message.body?.name,
34
+ time: message.body?.time
35
+ });
36
+ case "sync":
37
+ if (!message.body) {
38
+ console.warn("[HMRClient] HMR message body is empty");
39
+ return;
40
+ }
41
+ if (message.body.errors?.length) {
42
+ message.body.errors.forEach((error) => {
43
+ console.error("Cannot apply update due to error:", error);
44
+ }), this.app.LoadingView.hide();
45
+ return;
46
+ }
47
+ message.body.warnings?.length && message.body.warnings.forEach((warning) => {
48
+ console.warn("[HMRClient] Bundle contains warnings:", warning);
49
+ }), this.applyUpdate(message.body);
50
+ }
51
+ }
52
+ applyUpdate(update) {
53
+ if (!module.hot)
54
+ throw new Error("[HMRClient] Hot Module Replacement is disabled.");
55
+ !this.upToDate(update.hash) && module.hot.status() === "idle" && (console.log("[HMRClient] Checking for updates on the server..."), this.checkUpdates(update));
56
+ }
57
+ async checkUpdates(update) {
58
+ try {
59
+ this.app.LoadingView.showMessage("Refreshing...", "refresh");
60
+ const updatedModules = await module.hot?.check(!1);
61
+ if (!updatedModules) {
62
+ console.warn("[HMRClient] Cannot find update - full reload needed"), this.app.reload();
63
+ return;
64
+ }
65
+ const renewedModules = await module.hot?.apply({
66
+ ignoreDeclined: !0,
67
+ ignoreUnaccepted: !1,
68
+ ignoreErrored: !1,
69
+ onDeclined: (data) => {
70
+ console.warn("[HMRClient] Ignored an update due to declined module", {
71
+ chain: data.chain
72
+ });
73
+ }
74
+ });
75
+ this.upToDate() || this.checkUpdates(update);
76
+ const unacceptedModules = updatedModules.filter((moduleId) => renewedModules && renewedModules.indexOf(moduleId) < 0);
77
+ unacceptedModules.length ? (console.warn("[HMRClient] Not every module was accepted - full reload needed", {
78
+ unacceptedModules
79
+ }), this.app.reload()) : (console.log("[HMRClient] Renewed modules - app is up to date", {
80
+ renewedModules
81
+ }), this.app.dismissErrors());
82
+ } catch (error) {
83
+ module.hot?.status() === "fail" || module.hot?.status() === "abort" ? (console.warn("[HMRClient] Cannot check for update - full reload needed"), console.warn("[HMRClient]", error), this.app.reload()) : console.warn("[HMRClient] Update check failed", { error });
84
+ } finally {
85
+ this.app.LoadingView.hide();
86
+ }
87
+ }
88
+ }
89
+ const loadHMRClient = () => {
90
+ const { DevSettings, Platform } = require("react-native"), LoadingView = require("react-native/Libraries/Utilities/LoadingView"), reload = () => DevSettings.reload(), dismissErrors = () => {
91
+ Platform.OS === "ios" ? require("react-native/Libraries/NativeModules/specs/NativeRedBox").default?.dismiss?.() : require("react-native/Libraries/Core/NativeExceptionsManager").default?.dismissRedbox(), require("react-native/Libraries/LogBox/Data/LogBoxData").clear();
92
+ };
93
+ new HMRClient({ reload, dismissErrors, LoadingView });
94
+ };
95
+ export {
96
+ loadHMRClient
97
+ };
98
+ //# sourceMappingURL=hmr-client.js.map
@@ -0,0 +1,6 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../src/hmr-client.ts"],
4
+ "mappings": "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",
5
+ "names": []
6
+ }
@@ -0,0 +1,2 @@
1
+ export * from "./devServerClient";
2
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1,6 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../src/index.ts"],
4
+ "mappings": "AAAA,cAAc;",
5
+ "names": []
6
+ }
@@ -0,0 +1,2 @@
1
+ export * from "./devServerClient";
2
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1,6 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../src/index.ts"],
4
+ "mappings": "AAAA,cAAc;",
5
+ "names": []
6
+ }
package/package.json ADDED
@@ -0,0 +1,49 @@
1
+ {
2
+ "name": "@vxrn/vite-native-hmr",
3
+ "version": "0.0.3",
4
+ "types": "./types/index.d.ts",
5
+ "main": "dist/cjs",
6
+ "module": "dist/esm",
7
+ "files": [
8
+ "src",
9
+ "types",
10
+ "dist"
11
+ ],
12
+ "scripts": {
13
+ "build": "tamagui-build",
14
+ "watch": "tamagui-build --watch",
15
+ "clean": "tamagui-build clean",
16
+ "clean:build": "tamagui-build clean:build"
17
+ },
18
+ "exports": {
19
+ "./package.json": "./package.json",
20
+ ".": {
21
+ "types": "./types/index.d.ts",
22
+ "import": "./dist/esm/index.js",
23
+ "require": "./dist/cjs/index.js"
24
+ },
25
+ "./hmr-client": {
26
+ "types": "./types/hmr-client.d.ts",
27
+ "import": "./dist/esm/hmr-client.js",
28
+ "require": "./dist/cjs/hmr-client.js"
29
+ },
30
+ "./hmr-vite": {
31
+ "types": "./types/client.d.ts",
32
+ "import": "./dist/esm/client.js",
33
+ "require": "./dist/cjs/client.js"
34
+ }
35
+ },
36
+ "dependencies": {
37
+ "pretty-format": "^28.1.0"
38
+ },
39
+ "devDependencies": {
40
+ "@tamagui/build": "latest",
41
+ "react-native": "^0.72.1"
42
+ },
43
+ "peerDependencies": {
44
+ "react-native": "*"
45
+ },
46
+ "publishConfig": {
47
+ "access": "public"
48
+ }
49
+ }
@@ -0,0 +1,132 @@
1
+ import prettyFormat from 'pretty-format'
2
+
3
+ import { getDevServerLocation } from './getDevServerLocation'
4
+ import { loadHMRClient } from './hmr-client'
5
+
6
+ // force import hmr client hacky
7
+ loadHMRClient()
8
+
9
+ /**
10
+ * With Webpack we don't use built-in metro-specific HMR client,
11
+ * so the module `react-native/Libraries/Utilities/HMRClient.js` should be replaced with this one.
12
+ *
13
+ * Most of the code is noop apart from the `log` function which handles sending logs from client
14
+ * application to the dev server.
15
+ *
16
+ * The console gets "polyfilled" here:
17
+ * https://github.com/facebook/react-native/blob/v0.63.4/Libraries/Core/setUpDeveloperTools.js#L51-L69
18
+ */
19
+
20
+ class DevServerClient {
21
+ socket?: WebSocket
22
+ buffer: Array<{ level: string; data: any[] }> = []
23
+
24
+ constructor() {
25
+ const initSocket = () => {
26
+ const address = `ws://${getDevServerLocation().host}/__client`
27
+ this.socket = new WebSocket(address)
28
+
29
+ const onClose = (event: Event) => {
30
+ console.warn(
31
+ 'Disconnected from the Dev Server:',
32
+ (event as unknown as { message: string | null }).message
33
+ )
34
+ this.socket = undefined
35
+ }
36
+
37
+ this.socket.onclose = onClose
38
+ this.socket.onerror = onClose
39
+ this.socket.onopen = () => {
40
+ this.flushBuffer()
41
+ }
42
+ }
43
+
44
+ if (process.env.NODE_ENV === 'development') {
45
+ initSocket()
46
+ }
47
+ }
48
+
49
+ send(level: string, data: any[]) {
50
+ try {
51
+ this.socket?.send(
52
+ JSON.stringify({
53
+ type: 'client-log',
54
+ level,
55
+ data: data.map((item: any) =>
56
+ typeof item === 'string'
57
+ ? item
58
+ : prettyFormat(item, {
59
+ escapeString: true,
60
+ highlight: true,
61
+ maxDepth: 3,
62
+ min: true,
63
+ plugins: [
64
+ // @ts-expect-error
65
+ prettyFormat.plugins.ReactElement,
66
+ ],
67
+ })
68
+ ),
69
+ })
70
+ )
71
+ } catch {
72
+ try {
73
+ this.socket?.send(
74
+ JSON.stringify({
75
+ type: 'client-log',
76
+ level,
77
+ data: data.map((item: any) =>
78
+ typeof item === 'string' ? item : JSON.stringify(item)
79
+ ),
80
+ })
81
+ )
82
+ } catch (err) {
83
+ try {
84
+ this.socket?.send(
85
+ JSON.stringify({
86
+ type: 'client-log',
87
+ level: 'error',
88
+ data: ['error sending client log: ' + err],
89
+ })
90
+ )
91
+ } catch {
92
+ // final err
93
+ }
94
+ }
95
+ // Ignore error
96
+ }
97
+ }
98
+
99
+ flushBuffer() {
100
+ if (console['_tmpLogs']) {
101
+ console['_tmpLogs'].forEach(({ level, data }) => {
102
+ this.buffer.push({ level, data })
103
+ })
104
+ delete console['_tmpLogs']
105
+ }
106
+
107
+ for (const { level, data } of this.buffer) {
108
+ this.send(level, data)
109
+ }
110
+ this.buffer = []
111
+ }
112
+
113
+ log(level: string, data: any[]) {
114
+ if (this.socket && this.socket.readyState === WebSocket.OPEN) {
115
+ this.flushBuffer()
116
+ this.send(level, data)
117
+ } else {
118
+ if (console['_tmpLogs']) return
119
+ this.buffer.push({ level, data })
120
+ }
121
+ }
122
+ }
123
+
124
+ const client = new DevServerClient()
125
+
126
+ export const setup = () => {}
127
+ export const enable = () => {}
128
+ export const disable = () => {}
129
+ export const registerBundle = () => {}
130
+ export const log = (level: string, data: any[]) => {
131
+ client.log(level, data)
132
+ }
@@ -0,0 +1,33 @@
1
+ // @ts-ignore
2
+ import getDevServer from 'react-native/Libraries/Core/Devtools/getDevServer'
3
+
4
+ export interface DevServerLocation {
5
+ host: string
6
+ hostname: string
7
+ href: string
8
+ origin: string
9
+ pathname: string
10
+ port?: string
11
+ protocol?: string
12
+ }
13
+
14
+ let location: DevServerLocation | undefined
15
+
16
+ export function getDevServerLocation(): DevServerLocation {
17
+ if (!location) {
18
+ const { url } = getDevServer() as { url: string }
19
+ const origin = url.replace(/\/$/, '')
20
+ const host = origin.replace(/https?:\/\//, '')
21
+ location = {
22
+ host,
23
+ hostname: host.split(':')[0],
24
+ href: url,
25
+ origin,
26
+ pathname: url.split(host)[1],
27
+ port: host.split(':')[1],
28
+ protocol: (url.match(/^([a-z])+:\/\//) || [undefined, undefined])[1],
29
+ }
30
+ }
31
+
32
+ return location
33
+ }
@@ -0,0 +1,33 @@
1
+ interface HMRInfo {
2
+ type: string
3
+ chain: Array<string | number>
4
+ error?: Error
5
+ moduleId: string | number
6
+ }
7
+
8
+ interface HotApi {
9
+ status():
10
+ | 'idle'
11
+ | 'check'
12
+ | 'prepare'
13
+ | 'ready'
14
+ | 'dispose'
15
+ | 'apply'
16
+ | 'abort'
17
+ | 'fail'
18
+ check(autoPlay: boolean): Promise<Array<string | number>>
19
+ apply(options: {
20
+ ignoreUnaccepted?: boolean
21
+ ignoreDeclined?: boolean
22
+ ignoreErrored?: boolean
23
+ onDeclined?: (info: HMRInfo) => void
24
+ onUnaccepted?: (info: HMRInfo) => void
25
+ onAccepted?: (info: HMRInfo) => void
26
+ onDisposed?: (info: HMRInfo) => void
27
+ onErrored?: (info: HMRInfo) => void
28
+ }): Promise<Array<string | number>>
29
+ }
30
+
31
+ interface NodeModule {
32
+ hot?: HotApi
33
+ }
@@ -0,0 +1,206 @@
1
+ import { getDevServerLocation } from './getDevServerLocation'
2
+
3
+ /**
4
+ * Represent Hot Module Replacement Update body.
5
+ *
6
+ * @internal
7
+ */
8
+ export interface HMRMessageBody {
9
+ name: string
10
+ time: number
11
+ hash: string
12
+ warnings: any[]
13
+ errors: any[]
14
+ modules: Record<string, string>
15
+ }
16
+
17
+ /**
18
+ * Represent Hot Module Replacement Update message.
19
+ *
20
+ * @internal
21
+ */
22
+ export interface HMRMessage {
23
+ action: 'building' | 'built' | 'sync'
24
+ body: HMRMessageBody | null
25
+ }
26
+
27
+ class HMRClient {
28
+ url: string
29
+ socket: WebSocket
30
+ lastHash = ''
31
+
32
+ constructor(
33
+ private app: {
34
+ reload: () => void
35
+ dismissErrors: () => void
36
+ LoadingView: {
37
+ showMessage(text: string, type: 'load' | 'refresh'): void
38
+ hide(): void
39
+ }
40
+ }
41
+ ) {
42
+ this.url = `ws://${getDevServerLocation().hostname}:${
43
+ process.env.REACT_NATIVE_SERVER_PUBLIC_PORT
44
+ }/__hmr?platform=${process.env.REACT_NATIVE_PLATFORM || 'ios'}`
45
+
46
+ this.socket = new WebSocket(this.url)
47
+
48
+ console.log('[HMRClient] Connecting...')
49
+
50
+ this.socket.onopen = () => {
51
+ console.log('[HMRClient] Connected')
52
+ }
53
+
54
+ this.socket.onclose = () => {
55
+ console.log(`[HMRClient] Disconnected ${this.url}`)
56
+ }
57
+
58
+ this.socket.onerror = (event) => {
59
+ console.log('[HMRClient] Error', event)
60
+ }
61
+
62
+ this.socket.onmessage = (event) => {
63
+ try {
64
+ const data = JSON.parse(event.data.toString())
65
+ this.processMessage(data)
66
+ } catch (error) {
67
+ console.warn('[HMRClient] Invalid HMR message', error)
68
+ }
69
+ }
70
+ }
71
+
72
+ upToDate(hash?: string) {
73
+ if (hash) {
74
+ this.lastHash = hash
75
+ }
76
+ // @ts-expect-error will deal with this when we get to it
77
+ return this.lastHash === __webpack_hash__
78
+ }
79
+
80
+ processMessage(message: HMRMessage) {
81
+ switch (message.action) {
82
+ case 'building':
83
+ this.app.LoadingView.showMessage('Rebuilding...', 'refresh')
84
+ console.log('[HMRClient] Bundle rebuilding', {
85
+ name: message.body?.name,
86
+ })
87
+ break
88
+ case 'built':
89
+ console.log('[HMRClient] Bundle rebuilt', {
90
+ name: message.body?.name,
91
+ time: message.body?.time,
92
+ })
93
+ // Fall through
94
+ case 'sync':
95
+ if (!message.body) {
96
+ console.warn('[HMRClient] HMR message body is empty')
97
+ return
98
+ }
99
+
100
+ if (message.body.errors?.length) {
101
+ message.body.errors.forEach((error) => {
102
+ console.error('Cannot apply update due to error:', error)
103
+ })
104
+ this.app.LoadingView.hide()
105
+ return
106
+ }
107
+
108
+ if (message.body.warnings?.length) {
109
+ message.body.warnings.forEach((warning) => {
110
+ console.warn('[HMRClient] Bundle contains warnings:', warning)
111
+ })
112
+ }
113
+
114
+ this.applyUpdate(message.body)
115
+ }
116
+ }
117
+
118
+ applyUpdate(update: HMRMessageBody) {
119
+ if (!module.hot) {
120
+ throw new Error('[HMRClient] Hot Module Replacement is disabled.')
121
+ }
122
+
123
+ if (!this.upToDate(update.hash) && module.hot.status() === 'idle') {
124
+ console.log('[HMRClient] Checking for updates on the server...')
125
+ this.checkUpdates(update)
126
+ }
127
+ }
128
+
129
+ async checkUpdates(update: HMRMessageBody) {
130
+ try {
131
+ this.app.LoadingView.showMessage('Refreshing...', 'refresh')
132
+ const updatedModules = await module.hot?.check(false)
133
+ if (!updatedModules) {
134
+ console.warn('[HMRClient] Cannot find update - full reload needed')
135
+ this.app.reload()
136
+ return
137
+ }
138
+
139
+ const renewedModules = await module.hot?.apply({
140
+ ignoreDeclined: true,
141
+ ignoreUnaccepted: false,
142
+ ignoreErrored: false,
143
+ onDeclined: (data) => {
144
+ // This module declined update, no need to do anything
145
+ console.warn('[HMRClient] Ignored an update due to declined module', {
146
+ chain: data.chain,
147
+ })
148
+ },
149
+ })
150
+
151
+ if (!this.upToDate()) {
152
+ this.checkUpdates(update)
153
+ }
154
+
155
+ // Double check to make sure all updated modules were accepted (renewed)
156
+ const unacceptedModules = updatedModules.filter((moduleId) => {
157
+ return renewedModules && renewedModules.indexOf(moduleId) < 0
158
+ })
159
+
160
+ if (unacceptedModules.length) {
161
+ console.warn('[HMRClient] Not every module was accepted - full reload needed', {
162
+ unacceptedModules,
163
+ })
164
+ this.app.reload()
165
+ } else {
166
+ console.log('[HMRClient] Renewed modules - app is up to date', {
167
+ renewedModules,
168
+ })
169
+ this.app.dismissErrors()
170
+ }
171
+ } catch (error) {
172
+ if (module.hot?.status() === 'fail' || module.hot?.status() === 'abort') {
173
+ console.warn('[HMRClient] Cannot check for update - full reload needed')
174
+ console.warn('[HMRClient]', error)
175
+ this.app.reload()
176
+ } else {
177
+ console.warn('[HMRClient] Update check failed', { error })
178
+ }
179
+ } finally {
180
+ this.app.LoadingView.hide()
181
+ }
182
+ }
183
+ }
184
+
185
+ export const loadHMRClient = () => {
186
+ const { DevSettings, Platform } = require('react-native')
187
+ const LoadingView = require('react-native/Libraries/Utilities/LoadingView')
188
+
189
+ const reload = () => DevSettings.reload()
190
+ const dismissErrors = () => {
191
+ if (Platform.OS === 'ios') {
192
+ const NativeRedBox =
193
+ require('react-native/Libraries/NativeModules/specs/NativeRedBox').default
194
+ NativeRedBox?.dismiss?.()
195
+ } else {
196
+ const NativeExceptionsManager =
197
+ require('react-native/Libraries/Core/NativeExceptionsManager').default
198
+ NativeExceptionsManager?.dismissRedbox()
199
+ }
200
+
201
+ const LogBoxData = require('react-native/Libraries/LogBox/Data/LogBoxData')
202
+ LogBoxData.clear()
203
+ }
204
+
205
+ new HMRClient({ reload, dismissErrors, LoadingView })
206
+ }
package/src/index.ts ADDED
@@ -0,0 +1 @@
1
+ export * from './devServerClient'
@@ -0,0 +1,6 @@
1
+ export declare const setup: () => void;
2
+ export declare const enable: () => void;
3
+ export declare const disable: () => void;
4
+ export declare const registerBundle: () => void;
5
+ export declare const log: (level: string, data: any[]) => void;
6
+ //# sourceMappingURL=devServerClient.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"devServerClient.d.ts","sourceRoot":"","sources":["../src/devServerClient.ts"],"names":[],"mappings":"AA6HA,eAAO,MAAM,KAAK,YAAW,CAAA;AAC7B,eAAO,MAAM,MAAM,YAAW,CAAA;AAC9B,eAAO,MAAM,OAAO,YAAW,CAAA;AAC/B,eAAO,MAAM,cAAc,YAAW,CAAA;AACtC,eAAO,MAAM,GAAG,UAAW,MAAM,QAAQ,GAAG,EAAE,SAE7C,CAAA"}
@@ -0,0 +1,11 @@
1
+ export interface DevServerLocation {
2
+ host: string;
3
+ hostname: string;
4
+ href: string;
5
+ origin: string;
6
+ pathname: string;
7
+ port?: string;
8
+ protocol?: string;
9
+ }
10
+ export declare function getDevServerLocation(): DevServerLocation;
11
+ //# sourceMappingURL=getDevServerLocation.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"getDevServerLocation.d.ts","sourceRoot":"","sources":["../src/getDevServerLocation.ts"],"names":[],"mappings":"AAGA,MAAM,WAAW,iBAAiB;IAChC,IAAI,EAAE,MAAM,CAAA;IACZ,QAAQ,EAAE,MAAM,CAAA;IAChB,IAAI,EAAE,MAAM,CAAA;IACZ,MAAM,EAAE,MAAM,CAAA;IACd,QAAQ,EAAE,MAAM,CAAA;IAChB,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,QAAQ,CAAC,EAAE,MAAM,CAAA;CAClB;AAID,wBAAgB,oBAAoB,IAAI,iBAAiB,CAiBxD"}
@@ -0,0 +1,24 @@
1
+ /**
2
+ * Represent Hot Module Replacement Update body.
3
+ *
4
+ * @internal
5
+ */
6
+ export interface HMRMessageBody {
7
+ name: string;
8
+ time: number;
9
+ hash: string;
10
+ warnings: any[];
11
+ errors: any[];
12
+ modules: Record<string, string>;
13
+ }
14
+ /**
15
+ * Represent Hot Module Replacement Update message.
16
+ *
17
+ * @internal
18
+ */
19
+ export interface HMRMessage {
20
+ action: 'building' | 'built' | 'sync';
21
+ body: HMRMessageBody | null;
22
+ }
23
+ export declare const loadHMRClient: () => void;
24
+ //# sourceMappingURL=hmr-client.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"hmr-client.d.ts","sourceRoot":"","sources":["../src/hmr-client.ts"],"names":[],"mappings":"AAEA;;;;GAIG;AACH,MAAM,WAAW,cAAc;IAC7B,IAAI,EAAE,MAAM,CAAA;IACZ,IAAI,EAAE,MAAM,CAAA;IACZ,IAAI,EAAE,MAAM,CAAA;IACZ,QAAQ,EAAE,GAAG,EAAE,CAAA;IACf,MAAM,EAAE,GAAG,EAAE,CAAA;IACb,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;CAChC;AAED;;;;GAIG;AACH,MAAM,WAAW,UAAU;IACzB,MAAM,EAAE,UAAU,GAAG,OAAO,GAAG,MAAM,CAAA;IACrC,IAAI,EAAE,cAAc,GAAG,IAAI,CAAA;CAC5B;AAgKD,eAAO,MAAM,aAAa,YAqBzB,CAAA"}
@@ -0,0 +1,2 @@
1
+ export * from './devServerClient';
2
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,mBAAmB,CAAA"}