Repository: facebook/react
Pull request: [react-devtools-shared] Fix URL construction when base URL is invalid

### 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


<!--
  Thanks for submitting a pull request!
  We appreciate you spending the time to work on these changes. Please provide enough information so that others can review your pull request. The three fields below are mandatory.

  Before submitting a pull request, please make sure the following is done:

  1. Fork [the repository](https://github.com/facebook/react) and create your branch from `main`.
  2. Run `yarn` in the repository root.
  3. If you've fixed a bug or added code that should be tested, add tests!
  4. Ensure the test suite passes (`yarn test`). Tip: `yarn test --watch TestName` is helpful in development.
  5. Run `yarn test --prod` to test in the production environment. It supports the same options as `yarn test`.
  6. If you need a debugger, run `yarn test --debug --watch TestName`, open `chrome://inspect`, and press "Inspect".
  7. Format your code with [prettier](https://github.com/prettier/prettier) (`yarn prettier`).
  8. Make sure your code lints (`yarn lint`). Tip: `yarn linc` to only check changed files.
  9. Run the [Flow](https://flowtype.org/) type checks (`yarn flow`).
  10. If you haven't already, complete the CLA.

  Learn more about contributing: https://reactjs.org/docs/how-to-contribute.html
-->

## Summary

<!--
 Explain the **motivation** for making this change. What existing problem does the pull request solve?
-->

## How did you test this change?

<!--
  Demonstrate the code is solid. Example: The exact commands you ran and their output, screenshots / videos if the pull request changes the user interface.
  How exactly did you verify that your PR solves the issue you wanted to solve?
  If you leave this empty, your PR will very likely be closed.
-->


Changed files:
filename: packages/react-devtools-shared/src/__tests__/utils-test.js
patch: @@ -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', () => {

filename: packages/react-devtools-shared/src/symbolicateSource.js
patch: @@ -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,
       );