From c4e2508dad24f2cd61b5f1f862b081f54ee71896 Mon Sep 17 00:00:00 2001 From: KimCookieYa Date: Sat, 6 Sep 2025 22:00:45 +0900 Subject: [PATCH] [react-devtools-shared] Fix URL construction when base URL is invalid (#34407) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ### Problem - Users encounter “Failed to construct 'URL': Invalid base URL” when clicking the “View source” action in DevTools if the underlying base URL is invalid. - This exception originates from `new URL(relative, base)` and bubbles up, interrupting the DevTools UI. - Fixes GitHub issue [#34317](https://github.com/facebook/react/issues/34317) ### Solution - Wrap URL construction to: - First try `new URL(sourceMapAt, sourceURL)`. - If that fails, try `new URL(sourceMapAt)` as an absolute URL. - If both fail, return `null` (no symbolication) rather than throwing. - This preserves normal behavior for valid bases and absolute URLs, while avoiding crashes for invalid bases. ### Implementation details - Updated `symbolicateSource` in `packages/react-devtools-shared/src/symbolicateSource.js` to handle invalid base URL scenarios without throwing. - Added/verified tests in `packages/react-devtools-shared/src/__tests__/utils-test.js`: - “should not throw for invalid base URL with relative source map” → resolves to `null`. - “should resolve absolute source map even if base URL is invalid” → still resolves correctly. ### Test plan - Lint/format: - `yarn prettier-check` - `yarn linc` - Type checking: - `yarn flow dom-node` - Unit tests: - `yarn test --watchAll=false utils-test` - Optionally: `yarn test --watchAll=false utils-test inspectedElement` - All of the above pass locally for experimental channel. ### Risks and rollout - Risk: Low. Only affects cases where the base URL is invalid. - Normal cases (valid base or absolute `sourceMappingURL`) are unchanged. - No user-facing API changes; DevTools UX becomes more resilient. ### Affected packages - `react-devtools-shared` ### Related - Fixes GitHub issue [#34317](https://github.com/facebook/react/issues/34317) ### Checklist - [x] Ran `yarn prettier-check` - [x] Ran `yarn linc` - [x] Ran `yarn flow dom-node` - [x] Relevant unit tests passing - [x] Linked issue and added a concise summary ## Summary ## How did you test this change? --- .../src/__tests__/utils-test.js | 20 +++++++++++++++++++ .../src/symbolicateSource.js | 13 +++++++++++- 2 files changed, 32 insertions(+), 1 deletion(-) diff --git a/packages/react-devtools-shared/src/__tests__/utils-test.js b/packages/react-devtools-shared/src/__tests__/utils-test.js index dcffd2a228..9b9e82102c 100644 --- a/packages/react-devtools-shared/src/__tests__/utils-test.js +++ b/packages/react-devtools-shared/src/__tests__/utils-test.js @@ -421,6 +421,26 @@ function f() { } await expect(run('http://test/c.mjs')).resolves.toStrictEqual(result); await expect(run('http://test/d.mjs')).resolves.toStrictEqual(result); }); + + it('should not throw for invalid base URL with relative source map', async () => { + const fs2 = { + 'bundle.js': `${source}bundle.js.map`, + }; + const fetch2 = async url => fs2[url] || null; + const run = url => symbolicateSource(fetch2, url, 1, 1); + await expect(run('bundle.js')).resolves.toBe(null); + }); + + it('should resolve absolute source map even if base URL is invalid', async () => { + const fs3 = { + 'invalid-base.js': `${source}http://test/a.mjs.map`, + 'http://test/a.mts': `export function f() {}`, + 'http://test/a.mjs.map': `{"version":3,"file":"a.mjs","sourceRoot":"","sources":["a.mts"],"names":[],"mappings":";;AAAA,cAAsB;AAAtB,SAAgB,CAAC,KAAI,CAAC"}`, + }; + const fetch3 = async url => fs3[url] || null; + const run = url => symbolicateSource(fetch3, url, 4, 10); + await expect(run('invalid-base.js')).resolves.toStrictEqual(result); + }); }); describe('formatConsoleArguments', () => { diff --git a/packages/react-devtools-shared/src/symbolicateSource.js b/packages/react-devtools-shared/src/symbolicateSource.js index 267e291e12..092b1f8187 100644 --- a/packages/react-devtools-shared/src/symbolicateSource.js +++ b/packages/react-devtools-shared/src/symbolicateSource.js @@ -75,7 +75,18 @@ export async function symbolicateSource( resourceLine.length, ); - const sourceMapURL = new URL(sourceMapAt, sourceURL).toString(); + // Compute the absolute source map URL. If the base URL is invalid, gracefully bail. + let sourceMapURL; + try { + sourceMapURL = new URL(sourceMapAt, sourceURL).toString(); + } catch (e) { + // Fallback: try if sourceMapAt is already an absolute URL; otherwise give up. + try { + sourceMapURL = new URL(sourceMapAt).toString(); + } catch (_e) { + return null; + } + } const sourceMap = await fetchFileWithCaching(sourceMapURL).catch( () => null, );