From 4beeb2da60a92bd8678da821bbdf46190497bf5f Mon Sep 17 00:00:00 2001 From: Nick Morgan Date: Thu, 2 May 2024 10:48:15 -0400 Subject: [PATCH 1/8] Use URL to parse URLs --- .../src/reactotron-react-native.ts | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/lib/reactotron-react-native/src/reactotron-react-native.ts b/lib/reactotron-react-native/src/reactotron-react-native.ts index 5e333c848..619ec6e0b 100644 --- a/lib/reactotron-react-native/src/reactotron-react-native.ts +++ b/lib/reactotron-react-native/src/reactotron-react-native.ts @@ -33,13 +33,14 @@ let tempClientId: string | null = null * * On an Android emulator, if you want to connect any servers of local, you will need run adb reverse on your terminal. This function gets the localhost IP of host machine directly to bypass this. */ -const getHost = (defaultHost = "localhost") => - typeof NativeModules?.SourceCode?.getConstants().scriptURL === "string" // type guard in case this ever breaks https://github.com/facebook/react-native/blob/main/packages/react-native/Libraries/NativeModules/specs/NativeSourceCode.js#L15-L21 - ? NativeModules.SourceCode.scriptURL // Example: 'http://192.168.0.100:8081/index.bundle?platform=ios&dev=true&minify=false&modulesOnly=false&runModule=true&app=com.helloworld' - .split("://")[1] // Remove the scheme: '192.168.0.100:8081/index.bundle?platform=ios&dev=true&minify=false&modulesOnly=false&runModule=true&app=com.helloworld' - .split("/")[0] // Remove the path: '192.168.0.100:8081' - .split(":")[0] // Remove the port: '192.168.0.100' - : defaultHost +const getHost = (defaultHost = "localhost") => { + try { + return new URL(NativeModules?.SourceCode?.getConstants().scriptURL).hostname + } catch (error) { + console.warn(`getHost: "${error.message}" for scriptURL - Falling back to ${defaultHost}`) + return defaultHost + } +} const DEFAULTS: ClientOptions = { createSocket: (path: string) => new WebSocket(path), // eslint-disable-line From a4619bd71b7b310efae3aba851be713d42eefb8d Mon Sep 17 00:00:00 2001 From: Nick Morgan Date: Thu, 2 May 2024 10:57:48 -0400 Subject: [PATCH 2/8] Adding NativeModule ref link --- lib/reactotron-react-native/src/reactotron-react-native.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/reactotron-react-native/src/reactotron-react-native.ts b/lib/reactotron-react-native/src/reactotron-react-native.ts index 619ec6e0b..d8108cf3a 100644 --- a/lib/reactotron-react-native/src/reactotron-react-native.ts +++ b/lib/reactotron-react-native/src/reactotron-react-native.ts @@ -35,6 +35,7 @@ let tempClientId: string | null = null */ const getHost = (defaultHost = "localhost") => { try { + // RN Reference: https://github.com/facebook/react-native/blob/main/packages/react-native/src/private/specs/modules/NativeSourceCode.js return new URL(NativeModules?.SourceCode?.getConstants().scriptURL).hostname } catch (error) { console.warn(`getHost: "${error.message}" for scriptURL - Falling back to ${defaultHost}`) From ff96fe2011987f7b82f1bdd414fe4fc7ca0baa56 Mon Sep 17 00:00:00 2001 From: Nick Morgan Date: Fri, 3 May 2024 14:21:39 -0400 Subject: [PATCH 3/8] Make sure we also reset Nx cache --- scripts/reset.sh | 3 +++ 1 file changed, 3 insertions(+) diff --git a/scripts/reset.sh b/scripts/reset.sh index 796369505..be19fb3cc 100644 --- a/scripts/reset.sh +++ b/scripts/reset.sh @@ -1,5 +1,8 @@ #!/bin/bash +echo "Nx Reset - Clears all the cached Nx artifacts and metadata about the workspace and shuts down the Nx Daemon." +yarn nx reset + sh scripts/clean.sh echo "Removing all node_modules folders from the project" From b63fe4f80d6ee6823fcf462ef104d4fb6454e3aa Mon Sep 17 00:00:00 2001 From: Nick Morgan Date: Fri, 3 May 2024 14:56:52 -0400 Subject: [PATCH 4/8] Using regex for host and falling back to defaultHost --- .../src/reactotron-react-native.ts | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/lib/reactotron-react-native/src/reactotron-react-native.ts b/lib/reactotron-react-native/src/reactotron-react-native.ts index d8108cf3a..ddb68149b 100644 --- a/lib/reactotron-react-native/src/reactotron-react-native.ts +++ b/lib/reactotron-react-native/src/reactotron-react-native.ts @@ -36,7 +36,15 @@ let tempClientId: string | null = null const getHost = (defaultHost = "localhost") => { try { // RN Reference: https://github.com/facebook/react-native/blob/main/packages/react-native/src/private/specs/modules/NativeSourceCode.js - return new URL(NativeModules?.SourceCode?.getConstants().scriptURL).hostname + const scriptURL = NativeModules?.SourceCode?.getConstants().scriptURL + if (typeof scriptURL !== "string") throw new Error("Invalid non-string URL") + + // Using a capture group to extract the hostname from a URL + const host = scriptURL.match(/^(?:https?:\/\/)?([^/:\s]+)(?::\d+)?(?:[/?#]|$)/)?.[1] + + if (typeof host !== "string") throw new Error("Invalid URL - host not found") + + return host } catch (error) { console.warn(`getHost: "${error.message}" for scriptURL - Falling back to ${defaultHost}`) return defaultHost From 00571e107ea767af04ff100efb6845b87e926f0c Mon Sep 17 00:00:00 2001 From: Nick Morgan Date: Thu, 9 May 2024 17:13:12 -0400 Subject: [PATCH 5/8] Adding tests for getHostFromUrl functionality --- .../src/helpers/parseURL.test.ts | 59 +++++++++++++++++++ .../src/helpers/parseURL.ts | 19 ++++++ .../src/reactotron-react-native.ts | 8 +-- 3 files changed, 80 insertions(+), 6 deletions(-) create mode 100644 lib/reactotron-react-native/src/helpers/parseURL.test.ts create mode 100644 lib/reactotron-react-native/src/helpers/parseURL.ts diff --git a/lib/reactotron-react-native/src/helpers/parseURL.test.ts b/lib/reactotron-react-native/src/helpers/parseURL.test.ts new file mode 100644 index 000000000..437cc7b78 --- /dev/null +++ b/lib/reactotron-react-native/src/helpers/parseURL.test.ts @@ -0,0 +1,59 @@ +import { getHostFromUrl } from "./parseURL" + +describe("getHostFromUrl", () => { + it("should throw when no host is found", () => { + expect(() => { + getHostFromUrl("") + }).toThrow() + }) + + it("should get host from URL without scheme", () => { + Object.entries({ + localhost: "localhost", + "127.0.0.1": "127.0.0.1", + }).forEach(([host, url]) => { + expect(getHostFromUrl(url)).toEqual(host) + }) + expect(getHostFromUrl("localhost")).toEqual("localhost") + expect(getHostFromUrl("127.0.0.1")).toEqual("127.0.0.1") + }) + + it("should get the host from URL with http scheme", () => { + Object.entries({ + localhost: "http://localhost", + "example.com": "http://example.com", + }).forEach(([host, url]) => { + expect(getHostFromUrl(url)).toEqual(host) + }) + }) + + it("should get the host from URL with https scheme", () => { + Object.entries({ + localhost: "https://localhost", + "example.com": "https://example.com", + }).forEach(([host, url]) => { + expect(getHostFromUrl(url)).toEqual(host) + }) + }) + + it("should get the host from URL and ignore path, port, and query params", () => { + Object.entries({ + localhost: + "http://localhost:8081/.expo/.virtual-metro-entry.bundle?platform=ios&dev=true&lazy=true&minify=false&inlineSourceMap=false&modulesOnly=false&runModule=true&app=com.reactotronapp", + "192.168.1.141": + "https://192.168.1.141:8081/.expo/.virtual-metro-entry.bundle?platform=ios&dev=true&lazy=true&minify=false&inlineSourceMap=false&modulesOnly=false&runModule=true&app=com.reactotronapp", + }).forEach(([host, url]) => { + expect(getHostFromUrl(url)).toEqual(host) + }) + }) + + it("should get the host from URL with hyphens", () => { + expect(getHostFromUrl("https://example-app.com")).toEqual("example-app.com") + }) + + it("should throw when the URL is an unsupported scheme", () => { + expect(() => { + getHostFromUrl("file:///Users/tron") + }).toThrow() + }) +}) diff --git a/lib/reactotron-react-native/src/helpers/parseURL.ts b/lib/reactotron-react-native/src/helpers/parseURL.ts new file mode 100644 index 000000000..219cb7802 --- /dev/null +++ b/lib/reactotron-react-native/src/helpers/parseURL.ts @@ -0,0 +1,19 @@ +/** + * Given a valid http(s) URL, the host for the given URL + * is returned. + * + * @param url {string} URL to extract the host from + * @returns {string} host of given URL or throws + */ +// Using a capture group to extract the hostname from a URL +export function getHostFromUrl(url: string) { + // Group 1: http(s):// + // Group 2: host + // Group 3: port + // Group 4: rest + const host = url.match(/^(?:https?:\/\/)?([^/:\s]+)(?::\d+)?(?:[/?#]|$)/)?.[1] + + if (typeof host !== "string") throw new Error("Invalid URL - host not found") + + return host +} diff --git a/lib/reactotron-react-native/src/reactotron-react-native.ts b/lib/reactotron-react-native/src/reactotron-react-native.ts index ddb68149b..bf80b8119 100644 --- a/lib/reactotron-react-native/src/reactotron-react-native.ts +++ b/lib/reactotron-react-native/src/reactotron-react-native.ts @@ -19,6 +19,7 @@ import networking, { NetworkingOptions } from "./plugins/networking" import storybook from "./plugins/storybook" import devTools from "./plugins/devTools" import trackGlobalLogs from "./plugins/trackGlobalLogs" +import { getHostFromUrl } from "./helpers/parseURL" const constants = NativeModules.PlatformConstants || {} @@ -39,12 +40,7 @@ const getHost = (defaultHost = "localhost") => { const scriptURL = NativeModules?.SourceCode?.getConstants().scriptURL if (typeof scriptURL !== "string") throw new Error("Invalid non-string URL") - // Using a capture group to extract the hostname from a URL - const host = scriptURL.match(/^(?:https?:\/\/)?([^/:\s]+)(?::\d+)?(?:[/?#]|$)/)?.[1] - - if (typeof host !== "string") throw new Error("Invalid URL - host not found") - - return host + return getHostFromUrl(scriptURL) } catch (error) { console.warn(`getHost: "${error.message}" for scriptURL - Falling back to ${defaultHost}`) return defaultHost From 1fa045ee447b84dad67f93f2c6b0ebc50f16fb86 Mon Sep 17 00:00:00 2001 From: Nick Morgan Date: Mon, 13 May 2024 15:20:28 -0400 Subject: [PATCH 6/8] Add expo go URL scheme --- lib/reactotron-react-native/src/helpers/parseURL.test.ts | 9 +++++++++ lib/reactotron-react-native/src/helpers/parseURL.ts | 2 +- 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/lib/reactotron-react-native/src/helpers/parseURL.test.ts b/lib/reactotron-react-native/src/helpers/parseURL.test.ts index 437cc7b78..774f0c7a6 100644 --- a/lib/reactotron-react-native/src/helpers/parseURL.test.ts +++ b/lib/reactotron-react-native/src/helpers/parseURL.test.ts @@ -36,6 +36,15 @@ describe("getHostFromUrl", () => { }) }) + it("should get the host from URL with expo scheme for Expo Go", () => { + Object.entries({ + localhost: "exp://localhost", + "example.com": "exp://example.com", + }).forEach(([host, url]) => { + expect(getHostFromUrl(url)).toEqual(host) + }) + }) + it("should get the host from URL and ignore path, port, and query params", () => { Object.entries({ localhost: diff --git a/lib/reactotron-react-native/src/helpers/parseURL.ts b/lib/reactotron-react-native/src/helpers/parseURL.ts index 219cb7802..5a51a8345 100644 --- a/lib/reactotron-react-native/src/helpers/parseURL.ts +++ b/lib/reactotron-react-native/src/helpers/parseURL.ts @@ -11,7 +11,7 @@ export function getHostFromUrl(url: string) { // Group 2: host // Group 3: port // Group 4: rest - const host = url.match(/^(?:https?:\/\/)?([^/:\s]+)(?::\d+)?(?:[/?#]|$)/)?.[1] + const host = url.match(/^(?:(?:https?|exp):\/\/)?([^/:\s]+)(?::\d+)?(?:[/?#]|$)/)?.[1] if (typeof host !== "string") throw new Error("Invalid URL - host not found") From e74153f20f578a00a2163ccd3a48a465de8f697e Mon Sep 17 00:00:00 2001 From: Nick Morgan Date: Mon, 13 May 2024 15:29:43 -0400 Subject: [PATCH 7/8] Adding support for IPv6 --- .../src/helpers/parseURL.test.ts | 12 ++++++++++++ lib/reactotron-react-native/src/helpers/parseURL.ts | 2 +- 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/lib/reactotron-react-native/src/helpers/parseURL.test.ts b/lib/reactotron-react-native/src/helpers/parseURL.test.ts index 774f0c7a6..519ac83da 100644 --- a/lib/reactotron-react-native/src/helpers/parseURL.test.ts +++ b/lib/reactotron-react-native/src/helpers/parseURL.test.ts @@ -11,6 +11,7 @@ describe("getHostFromUrl", () => { Object.entries({ localhost: "localhost", "127.0.0.1": "127.0.0.1", + "[::1]": "[::1]", }).forEach(([host, url]) => { expect(getHostFromUrl(url)).toEqual(host) }) @@ -56,6 +57,17 @@ describe("getHostFromUrl", () => { }) }) + it("should get the host from an IPv6 URL and ignore path, port, and query params", () => { + Object.entries({ + "[::1]": + "http://[::1]:8081/.expo/.virtual-metro-entry.bundle?platform=ios&dev=true&lazy=true&minify=false&inlineSourceMap=false&modulesOnly=false&runModule=true&app=com.reactotronapp", + "[2001:0db8:85a3:0000:0000:8a2e:0370:7334]": + "https://[2001:0db8:85a3:0000:0000:8a2e:0370:7334]:8081/.expo/.virtual-metro-entry.bundle?platform=ios&dev=true&lazy=true&minify=false&inlineSourceMap=false&modulesOnly=false&runModule=true&app=com.reactotronapp", + }).forEach(([host, url]) => { + expect(getHostFromUrl(url)).toEqual(host) + }) + }) + it("should get the host from URL with hyphens", () => { expect(getHostFromUrl("https://example-app.com")).toEqual("example-app.com") }) diff --git a/lib/reactotron-react-native/src/helpers/parseURL.ts b/lib/reactotron-react-native/src/helpers/parseURL.ts index 5a51a8345..a92297cea 100644 --- a/lib/reactotron-react-native/src/helpers/parseURL.ts +++ b/lib/reactotron-react-native/src/helpers/parseURL.ts @@ -11,7 +11,7 @@ export function getHostFromUrl(url: string) { // Group 2: host // Group 3: port // Group 4: rest - const host = url.match(/^(?:(?:https?|exp):\/\/)?([^/:\s]+)(?::\d+)?(?:[/?#]|$)/)?.[1] + const host = url.match(/^(?:(?:https?|exp):\/\/)?(\[[^\]]+\]|[^/:\s]+)(?::\d+)?(?:[/?#]|$)/)?.[1] if (typeof host !== "string") throw new Error("Invalid URL - host not found") From c3940d42b66be448b1fc19670e2d443a9637e1b9 Mon Sep 17 00:00:00 2001 From: Nick Morgan Date: Mon, 13 May 2024 16:23:20 -0400 Subject: [PATCH 8/8] Removing `exp` scheme support - not needed --- lib/reactotron-react-native/src/helpers/parseURL.test.ts | 9 --------- lib/reactotron-react-native/src/helpers/parseURL.ts | 2 +- 2 files changed, 1 insertion(+), 10 deletions(-) diff --git a/lib/reactotron-react-native/src/helpers/parseURL.test.ts b/lib/reactotron-react-native/src/helpers/parseURL.test.ts index 519ac83da..0afa09357 100644 --- a/lib/reactotron-react-native/src/helpers/parseURL.test.ts +++ b/lib/reactotron-react-native/src/helpers/parseURL.test.ts @@ -37,15 +37,6 @@ describe("getHostFromUrl", () => { }) }) - it("should get the host from URL with expo scheme for Expo Go", () => { - Object.entries({ - localhost: "exp://localhost", - "example.com": "exp://example.com", - }).forEach(([host, url]) => { - expect(getHostFromUrl(url)).toEqual(host) - }) - }) - it("should get the host from URL and ignore path, port, and query params", () => { Object.entries({ localhost: diff --git a/lib/reactotron-react-native/src/helpers/parseURL.ts b/lib/reactotron-react-native/src/helpers/parseURL.ts index a92297cea..863adc1e4 100644 --- a/lib/reactotron-react-native/src/helpers/parseURL.ts +++ b/lib/reactotron-react-native/src/helpers/parseURL.ts @@ -11,7 +11,7 @@ export function getHostFromUrl(url: string) { // Group 2: host // Group 3: port // Group 4: rest - const host = url.match(/^(?:(?:https?|exp):\/\/)?(\[[^\]]+\]|[^/:\s]+)(?::\d+)?(?:[/?#]|$)/)?.[1] + const host = url.match(/^(?:https?:\/\/)?(\[[^\]]+\]|[^/:\s]+)(?::\d+)?(?:[/?#]|$)/)?.[1] if (typeof host !== "string") throw new Error("Invalid URL - host not found")