@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.
- package/LICENSE +21 -0
- package/dist/cjs/devServerClient.js +132 -0
- package/dist/cjs/devServerClient.js.map +6 -0
- package/dist/cjs/devServerClient.native.js +132 -0
- package/dist/cjs/devServerClient.native.js.map +6 -0
- package/dist/cjs/getDevServerLocation.js +49 -0
- package/dist/cjs/getDevServerLocation.js.map +6 -0
- package/dist/cjs/getDevServerLocation.native.js +50 -0
- package/dist/cjs/getDevServerLocation.native.js.map +6 -0
- package/dist/cjs/hmr-client.js +130 -0
- package/dist/cjs/hmr-client.js.map +6 -0
- package/dist/cjs/hmr-client.native.js +129 -0
- package/dist/cjs/hmr-client.native.js.map +6 -0
- package/dist/cjs/index.js +19 -0
- package/dist/cjs/index.js.map +6 -0
- package/dist/cjs/index.native.js +20 -0
- package/dist/cjs/index.native.js.map +6 -0
- package/dist/esm/devServerClient.js +101 -0
- package/dist/esm/devServerClient.js.map +6 -0
- package/dist/esm/devServerClient.native.js +100 -0
- package/dist/esm/devServerClient.native.js.map +6 -0
- package/dist/esm/getDevServerLocation.js +21 -0
- package/dist/esm/getDevServerLocation.js.map +6 -0
- package/dist/esm/getDevServerLocation.native.js +21 -0
- package/dist/esm/getDevServerLocation.native.js.map +6 -0
- package/dist/esm/hmr-client.js +100 -0
- package/dist/esm/hmr-client.js.map +6 -0
- package/dist/esm/hmr-client.native.js +98 -0
- package/dist/esm/hmr-client.native.js.map +6 -0
- package/dist/esm/index.js +2 -0
- package/dist/esm/index.js.map +6 -0
- package/dist/esm/index.native.js +2 -0
- package/dist/esm/index.native.js.map +6 -0
- package/package.json +49 -0
- package/src/devServerClient.ts +132 -0
- package/src/getDevServerLocation.ts +33 -0
- package/src/globals.d.ts +33 -0
- package/src/hmr-client.ts +206 -0
- package/src/index.ts +1 -0
- package/types/devServerClient.d.ts +6 -0
- package/types/devServerClient.d.ts.map +1 -0
- package/types/getDevServerLocation.d.ts +11 -0
- package/types/getDevServerLocation.d.ts.map +1 -0
- package/types/hmr-client.d.ts +24 -0
- package/types/hmr-client.d.ts.map +1 -0
- package/types/index.d.ts +2 -0
- 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
|
+
}
|
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
|
+
}
|
package/src/globals.d.ts
ADDED
|
@@ -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"}
|
package/types/index.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,mBAAmB,CAAA"}
|