Skip to content

fix(loader): prefer virtualPathResourceMap type over URL extension inference#3035

Merged
GuoLei1990 merged 11 commits into
dev/2.0from
fix/resource-manager-virtual-type
Jun 22, 2026
Merged

fix(loader): prefer virtualPathResourceMap type over URL extension inference#3035
GuoLei1990 merged 11 commits into
dev/2.0from
fix/resource-manager-virtual-type

Conversation

@cptbtptpbcptdtptp

@cptbtptpbcptdtptp cptbtptpbcptdtptp commented Jun 15, 2026

Copy link
Copy Markdown
Collaborator

Summary

  • In _assignDefaultOptions, check _virtualPathResourceMap first for the asset type before falling back to URL extension inference.
  • Ensures editor-registered assets with virtual paths (no file extension) resolve to the correct loader type without requiring the caller to explicitly specify type.

Test plan

  • pnpm run test passes
  • Load an asset by virtual path that has no file extension — should resolve via _virtualPathResourceMap type

🤖 Generated with Claude Code

Summary by CodeRabbit

  • Bug Fixes
    • Improved resource option defaulting and URL/base-URL resolution when remote configuration is present, ensuring asset types and URLs are handled more reliably.
    • Strengthened virtual-path loading so omitted types are correctly inferred.
    • Fixed query-based sub-asset loading so related ?materials[...] variants reuse a single main asset load and apply consistent fallback behavior.
  • Tests
    • Added unit coverage for virtual-path type inference and for single main-load behavior across query-based sub-asset variants.

…ference

When loading an asset by URL, prefer the type registered in
_virtualPathResourceMap over inferring from the URL file extension.
This ensures editor-registered assets with non-standard extensions
(e.g. virtual paths without extensions) resolve to the correct loader.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented Jun 15, 2026

Copy link
Copy Markdown

Review Change Stack

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review

Walkthrough

ResourceManager refactors type resolution and default-options handling to accept optional remoteConfig and prefer remoteConfig.type, moves item.url defaulting into _loadSingleItem, and simplifies sub-asset query-based loading by removing the assetBaseURL parameter. Tests verify virtual-path type inference and confirm shared main-asset loading across sub-asset query variants.

Changes

ResourceManager type resolution and sub-asset loading

Layer / File(s) Summary
Type resolution and default-options refactoring
packages/core/src/asset/ResourceManager.ts
_assignDefaultOptions now accepts optional remoteConfig and prefers remoteConfig.type when setting unset assetInfo.type. In _loadSingleItem, item.url is defaulted before _assignDefaultOptions is called; remoteConfig lookup happens after parsing the initial URL; base URL resolution and remoteAssetBaseURL derivation follow in sequence.
Sub-asset loading flow wiring
packages/core/src/asset/ResourceManager.ts
Sub-asset query-path loading is rewired to call _loadSubpackageAndMainAsset with reduced parameters, eliminating the prior assetBaseURL argument. _loadMainAsset no longer overwrites item.url; main-asset query-path failures are routed via _onSubAssetFail using remoteAssetBaseURL and queryPath.
Test coverage for type inference and virtual-path loading
tests/src/core/resource/ResourceManager.test.ts
AssetPromise import is added. New virtualPath loading test suite validates type inference from virtualPathResourceMap when loading a virtual path without specifying type, and verifies that multiple sub-asset queries with different ?materials[...] variants invoke the main-asset loader only once.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Poem

A rabbit rewired the config with grace,
Type resolution in its proper place.
No baseURL passed through the maze,
Virtual paths light up the haze,
Sub-assets share one load to chase! 🐇

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title directly and specifically describes the main change: prioritizing virtualPathResourceMap type over URL extension inference in the loader logic.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/resource-manager-virtual-type

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@codecov

codecov Bot commented Jun 15, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 79.51%. Comparing base (97d8d61) to head (e781728).
⚠️ Report is 13 commits behind head on dev/2.0.

Additional details and impacted files
@@             Coverage Diff             @@
##           dev/2.0    #3035      +/-   ##
===========================================
+ Coverage    77.31%   79.51%   +2.19%     
===========================================
  Files          914      919       +5     
  Lines       101723   102523     +800     
  Branches     10437    11371     +934     
===========================================
+ Hits         78647    81519    +2872     
+ Misses       22892    20818    -2074     
- Partials       184      186       +2     
Flag Coverage Δ
unittests 79.51% <100.00%> (+2.19%) ⬆️

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@packages/core/src/asset/ResourceManager.ts`:
- Around line 343-347: The lookup at line 343 uses the raw assetInfo.url
directly to query _virtualPathResourceMap, which fails to match entries when the
URL contains query parameters like ?q=. Normalize the URL before performing the
_virtualPathResourceMap lookup by extracting the base URL without query
parameters, then use the normalized URL as the key for the lookup while still
preserving the original assetInfo.url for any subsequent operations that need
the full URL.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: ec63d091-d98a-4f29-b6bf-49e62f8c41e9

📥 Commits

Reviewing files that changed from the base of the PR and between de75496 and 4cfbcb6.

📒 Files selected for processing (1)
  • packages/core/src/asset/ResourceManager.ts

Comment thread packages/core/src/asset/ResourceManager.ts Outdated
GuoLei1990

This comment was marked as outdated.

Strip the query (?q=) before looking up `_virtualPathResourceMap`, so a
sub-asset virtual path resolves its type the same way `_loadSingleItem`
resolves its path via `assetBaseURL`. Keep explicit `type` taking
precedence over the map (the map only overrides URL-extension inference,
matching the PR title), and guard against an undefined `url` for
urls-only loads such as TextureCube.

Add ResourceManager tests covering extensionless virtual path, sub-asset
query path, and explicit-type precedence.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (1)
tests/src/core/resource/ResourceManager.test.ts (1)

91-121: ⚡ Quick win

Add a regression test for url-less LoadItem handling.

These tests validate virtual-path precedence well, but they don’t cover the undefined-url path mentioned in the PR objective. Add one case that passes a LoadItem without url (e.g., urls-only shape) and asserts _assignDefaultOptions does not throw unexpectedly from type inference.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@tests/src/core/resource/ResourceManager.test.ts` around lines 91 - 121, Add a
regression test case within the "assignDefaultOptions virtualPath type" describe
block to cover the url-less LoadItem scenario. Create a new test that calls
_assignDefaultOptions with a LoadItem object that omits the url property (for
example, containing urls instead), then assert that the method does not throw an
error and handles type inference gracefully when url is undefined or missing.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@packages/core/src/asset/ResourceManager.ts`:
- Line 345: The call to ResourceManager._getTypeByUrl(assetInfo.url) at line 345
can execute with an undefined URL in url-list-based inputs, causing a runtime
crash when split is called on undefined before reaching the proper error
handling. Add a guard condition to only call _getTypeByUrl when assetInfo.url is
defined, allowing the assignment to evaluate to undefined and reach the intended
"asset type should be specified" validation path.

---

Nitpick comments:
In `@tests/src/core/resource/ResourceManager.test.ts`:
- Around line 91-121: Add a regression test case within the
"assignDefaultOptions virtualPath type" describe block to cover the url-less
LoadItem scenario. Create a new test that calls _assignDefaultOptions with a
LoadItem object that omits the url property (for example, containing urls
instead), then assert that the method does not throw an error and handles type
inference gracefully when url is undefined or missing.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: 587064c4-8119-4061-baef-ad75dee58b78

📥 Commits

Reviewing files that changed from the base of the PR and between 4cfbcb6 and 66d7929.

📒 Files selected for processing (2)
  • packages/core/src/asset/ResourceManager.ts
  • tests/src/core/resource/ResourceManager.test.ts

Comment thread packages/core/src/asset/ResourceManager.ts Outdated
GuoLei1990

This comment was marked as outdated.

…oading

Resolve asset type and remote path from a single _parseURL + virtualPath
lookup, and move baseUrl resolution after the lookup so virtual paths are
no longer polluted by baseUrl. _assignDefaultOptions now takes the resolved
remoteConfig instead of querying the map itself.

- Merge `urls` into `url` before _parseURL — fixes urls-only (TextureCube) throw
- Always strip the `?q=` query from item.url so the sub-asset query no longer
  leaks into the cache key / loadingPromise dedup / request url
- Move virtualPath type checks to the public load() layer (drop tests that
  poked the private _assignDefaultOptions); sync the _parseURL field rename
  in the existing queryPath tests

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (1)
tests/src/core/resource/ResourceManager.test.ts (1)

91-120: 💤 Low value

Consider cleaning up test state to improve isolation.

The tests modify shared state (_virtualPathResourceMap via initVirtualResources) and leave unresolved promises in loadingPromises. While the tests correctly validate the refactored behavior, this could cause flakiness or interference with other tests.

Consider adding cleanup in afterEach or within each test:

Suggested cleanup pattern
+    afterEach(() => {
+      // Clean up virtual path registrations
+      // `@ts-ignore`
+      engine.resourceManager._virtualPathResourceMap = Object.create(null);
+      // `@ts-ignore`  
+      engine.resourceManager._loadingPromises = {};
+    });
+
     it("infers loader type from virtualPathResourceMap when type is omitted", () => {
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@tests/src/core/resource/ResourceManager.test.ts` around lines 91 - 120, The
tests in the "virtualPath loading" describe block are not cleaning up shared
state after execution, which can cause test interference and flakiness. Add an
afterEach hook within the describe block that clears the virtual resource map
(by calling initVirtualResources with an empty array or resetting
_virtualPathResourceMap) and clears any pending loading promises from
ResourceManager to ensure proper test isolation. Alternatively, add cleanup at
the end of each test (after loaderSpy.mockRestore() calls) to reset the
resourceManager state.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@tests/src/core/resource/ResourceManager.test.ts`:
- Around line 91-120: The tests in the "virtualPath loading" describe block are
not cleaning up shared state after execution, which can cause test interference
and flakiness. Add an afterEach hook within the describe block that clears the
virtual resource map (by calling initVirtualResources with an empty array or
resetting _virtualPathResourceMap) and clears any pending loading promises from
ResourceManager to ensure proper test isolation. Alternatively, add cleanup at
the end of each test (after loaderSpy.mockRestore() calls) to reset the
resourceManager state.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: 9cf71b29-ac19-4a28-b5b1-695a6abf9f56

📥 Commits

Reviewing files that changed from the base of the PR and between 66d7929 and e78e08b.

📒 Files selected for processing (2)
  • packages/core/src/asset/ResourceManager.ts
  • tests/src/core/resource/ResourceManager.test.ts

cptbtptpbcptdtptp and others added 2 commits June 16, 2026 16:00
…eturn

- _parseURL returns { assetBaseURL } again — clearer than `url`, which
  collided with the `item.url` reassigned in _loadSingleItem
- _assignDefaultOptions returns void; it mutates in place and the return
  value was no longer used after the lookup moved to the caller

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The added test referenced a non-existent "Texture2D" loader key; the texture
type is AssetType.Texture = "Texture", so vi.spyOn got undefined and threw
"Cannot convert undefined or null to object". Use the AssetType enum for both
loader keys to avoid the typo class.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
GuoLei1990

This comment was marked as outdated.

…for type

- _assignDefaultOptions: type ??= ... (was ||=) so an explicit type is only
  defaulted when actually unset, not when it is a falsy string
- add load()-layer tests: urls merged before parse (KTXCube urls-only),
  explicit type wins over the virtualPath map type, and a virtualPath still
  resolves via the map when baseUrl is set

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
GuoLei1990

This comment was marked as outdated.

…type

A registered virtualPath records the asset's actual type; an explicit
type passed at load time should not override that fact. Reverse the
precedence to `map ?? explicit ?? extension` so a registered asset is
always loaded with its recorded type, while explicit type is still
honored for unregistered (pro-code) assets where the map has no entry.

Also drop the now-redundant explicit type in getResourceByRef, letting
the map drive type resolution as the single source of truth.
…ions

The method resolves the asset type (with validation that throws when it
cannot be determined) in addition to filling in default load options, so
"assign default options" undersold what it does. Rename to reflect that
it resolves and validates the load item, not just assigns defaults.
The variable holds an EditorResourceItem looked up from
_virtualPathResourceMap — a registered virtualPath entry carrying the
resource's real path, type and metadata. "remoteConfig" was misleading:
it is neither a config nor remote (it is an in-memory map entry that
merely contains a remote path field). Rename to reflect that it is an
entry of the virtualPath resource map.
@GuoLei1990 GuoLei1990 added the bug Something isn't working label Jun 22, 2026
@GuoLei1990 GuoLei1990 added the resource Resource-related functions label Jun 22, 2026
GuoLei1990

This comment was marked as outdated.

Loading a registered virtualPath with only a url (not via getResourceByRef)
previously dropped the params recorded in the map. Resolve params from the
map entry in _resolveLoadItemOptions, mirroring type: explicit params win,
otherwise the map params are used. getResourceByRef no longer forwards
params, since the map is now the single source of truth for both type and
params.
@GuoLei1990 GuoLei1990 merged commit 4967220 into dev/2.0 Jun 22, 2026
10 checks passed

@GuoLei1990 GuoLei1990 left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

总结

第六轮(6de6bc060 → e781728,2 个 commit,PR 已合入 dev/2.0 via 4967220a6)。本轮把 params 也纳入虚拟路径 map 解析:_resolveLoadItemOptions 新增 assetInfo.params ??= virtualResourceEntry?.params,并据此把 getResourceByRef 里冗余的 params: mapped.params 删掉(同前一轮删 type: mapped.type 同理)。逐路径核对后对 getResourceByRef 完全等价,对直接 load(虚拟路径) 是新增能力(之前 params 拿不到)。可以合(已合)。

上轮问题状态

前五轮所有问题保持关闭,本轮无变动:

  • [P2] 虚拟路径查表用未解析原始 url — ✅ 保持关闭。
  • CodeRabbit _getTypeByUrl(undefined) crash / normalize url — ✅ 保持关闭。
  • type 优先级反转 map ?? explicit ?? extension — ✅ 上轮已核安全,保持关闭。
  • getResourceByReftype: mapped.type — ✅ 上轮已核等价,保持关闭。
  • [P3] 测试回归强度不对等 / characterization — 保持关闭,不另起。
  • ||=??= / type:""params:null 不可达分支 — 已表态非问题,保持关闭。

关键核对(确认无回归)

1. params ??= virtualResourceEntry?.params_resolveLoadItemOptions :347)—— 安全且语义自洽

  • getResourceByRef 路径等价:base 是 load({url: loadUrl, params: mapped.params}),本轮删 explicit 后 params_resolveLoadItemOptions 里被 ??= mapped.params 填回(loadUrlassetBaseURL 剥 query 后回到同一虚拟路径 → virtualResourceEntry === mapped),含子资源 ?q= 路径同样剥回。数据流 1:1。
  • 直接 load(虚拟路径) 是新增能力:base 下 paramsundefined,本轮补回 map 记录的 params。
  • 唯一传 explicit params 的内部调用点 SpriteAtlasLoader:55 / GLTFTextureParser:52 都走 resolveAbsoluteUrl → 绝对 url → map 必然 miss → ??= undefined 保留 explicit,不受影响

2. params 优先级 = explicit 赢(与 type 的 map 赢相反)—— 设计上可接受

  • type 是资源身份(决定走哪个 loader),编辑器记录的即真实身份,故 map 赢、explicit 不能覆盖。
  • params 是 loader 调参(mipmap/format/keepMeshData…),map 存的是默认值,调用方传 explicit 是有意 override,故 explicit 赢。
  • 两者方向相反但各自正确:身份用 map 收口,选项允许覆盖。不是 bug。

3. 两个新测试 —— 回归强度

  • fills params from virtualPathResourceMap when params is omitted有效反向见证。base(6de6bc060_resolveLoadItemOptionsparams ??= 行 → paramsundefined → 断言 {mipmap:false} fail;本轮补回 → pass。✅
  • prefers explicit params over the virtualPath map params:断言本身正确(explicit 赢),但严格说不是反向见证(base 下直接 load 的 params 是 undefined,本轮才是 mipmap:false,两版断言 {mipmap:true} 都 pass,因为 explicit 始终在第一位)。无害,是合理的防回归基线。

问题

无 P0/P1/P2。一条 P3(不阻塞,仅记录;PR 已合,留作后续清理):

  • [P3] tests/.../ResourceManager.test.ts:146 — 注释 mirroring type precedence 说反了
    • 该测试断言 params 是 explicit 赢prefers explicit params over the virtualPath map params,:131),而 type 是 map 赢(同文件 :142 prefers the virtualPath map type over an explicit type + :178-179 注释 "an explicit type cannot override the type the editor recorded for it")。两者优先级相反,不是 "mirroring"(镜像/一致),而是倒置
    • 注释 // Explicit params overrides the map params (overwrite, not merge), mirroring type precedence 的后半句与代码事实矛盾——把有意为之的非对称说成对称,是会误导后人的 aspirational 注释。
    • 建议改成实话,并点明非对称的理由,例如:// params is an override (explicit wins) — opposite to type, which the registered map owns; type is identity, params are loader options the caller may tune

简化建议

无。本轮把 params 也收口进 map 解析、删掉 getResourceByRef 最后一处冗余显式字段,方向与前几轮"单一真相源"一致,干净。唯一遗留是上面那条说反的注释。

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working resource Resource-related functions

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants