Fix Invalid URL in Loader.getConsumedModules for prefix-form module IDs#4243
Fix Invalid URL in Loader.getConsumedModules for prefix-form module IDs#4243
Conversation
Same root cause as the relativizeResource fix: after the import maps change, module identifiers can be in registered prefix form (e.g. @cardstack/catalog/...). Loader.getConsumedModules passed these directly to new URL() which throws TypeError: Invalid URL. Use resolveCardReference() to resolve the prefix to a real URL first. This fixes the client-side error seen when prerendering catalog cards: "Failed to render live search result: TypeError: Failed to construct 'URL': Invalid URL at Loader.getConsumedModules" Fixes CS-10498 Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…dules
Verifies that getConsumedModules works when called with a registered
prefix-form module identifier (e.g. @cardstack/catalog/...). Without
the fix, new URL('@test-loader/f') throws TypeError: Invalid URL.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
|
You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard. |
Preview deployments |
|
Do you think this would supersede #4242? |
There was a problem hiding this comment.
Pull request overview
This PR fixes a client-side TypeError: Invalid URL that occurs when Loader.getConsumedModules is called with a prefix-form module identifier (e.g. @cardstack/catalog/...) by resolving the prefix to a real URL before constructing a URL.
Changes:
- Update
Loader.getConsumedModulesto resolve prefix-form module identifiers viaresolveCardReference()before callingnew URL(). - Add a regression test ensuring
getConsumedModules('@test-loader/f')works once a prefix mapping is registered.
Reviewed changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated 1 comment.
| File | Description |
|---|---|
| packages/runtime-common/loader.ts | Resolves prefix-form module IDs before URL parsing inside getConsumedModules. |
| packages/host/tests/unit/loader-test.ts | Adds regression coverage for getConsumedModules with a registered prefix-form module identifier. |
Comments suppressed due to low confidence (1)
packages/runtime-common/loader.ts:180
- After attempting
await this.import(...)insidegetConsumedModules, the localmodulevariable is not refreshed fromthis.modules. Whenmodulewas initially missing (or infetching), this can cause the method to return[]even though the import succeeded, becausemodule?.stateis still based on the pre-import value. Re-read the module from the cache (using the resolved href) after the import attempt before checking its state and walkingconsumedModules.
if (!module || module.state === 'fetching') {
// we haven't yet tried importing the module or we are still in the process of importing the module
try {
await this.import<Record<string, any>>(moduleIdentifier);
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
packages/runtime-common/loader.ts
Outdated
| } | ||
|
|
||
| let resolvedModuleIdentifier = new URL(moduleIdentifier); | ||
| let resolvedModuleIdentifier = new URL( | ||
| resolveCardReference(moduleIdentifier, undefined), | ||
| ); |
There was a problem hiding this comment.
getConsumedModules tracks visited modules and self-exclusion using the raw moduleIdentifier string, but the module lookup now normalizes via resolveCardReference(). If the initial identifier is a registered prefix (e.g. @cardstack/...) and a dependency cycle leads back to the same module via its resolved URL, moduleIdentifier !== initialIdentifier will be true and the function can incorrectly include the starting module in the returned consumed list (and also miss de-duping between prefix vs URL forms). Consider normalizing both moduleIdentifier and initialIdentifier to the same resolved/canonical href before doing consumed.includes(...) and the self-exclusion check, and store only that normalized form in consumed.
There was a problem hiding this comment.
Good catch. Fixed in 099cf4d — both moduleIdentifier and initialIdentifier are now normalized to their resolved URL href before cycle detection and self-exclusion, so prefix-form and URL-form of the same module are treated consistently.
…le detection Resolve both moduleIdentifier and initialIdentifier to their canonical URL href before cycle detection and self-exclusion checks. This ensures prefix-form identifiers (e.g. @cardstack/catalog/...) and their resolved URL equivalents are treated as the same module, preventing incorrect self-inclusion or missed de-duplication in dependency cycles. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Host Test Results 1 files ± 0 1 suites ±0 4h 12m 20s ⏱️ + 2h 3m 57s For more details on these errors, see this check. Results for commit 099cf4d. ± Comparison against base commit 7ce5f7b. ♻️ This comment has been updated with latest results. |
backspace
left a comment
There was a problem hiding this comment.
The host failures look unrelated to me
Summary
Loader.getConsumedModulesinloader.ts@cardstack/catalog/...).getConsumedModulespassed these directly tonew URL()which throwsTypeError: Invalid URLresolveCardReference()to resolve the prefix to a real URL firstTest plan
loader-test.tsthat callsgetConsumedModuleswith a prefix-form identifier — without the fixnew URL('@test-loader/f')throwsTypeError: Invalid URL, with the fix it resolves correctlyFixes CS-10498
🤖 Generated with Claude Code