Early hints emit as=script on rel=modulepreload, producing "preloaded but not used" warnings for every route chunk
メンテナーはふだん 1 日以内に返信
まだ誰も着手していません。
評価
- 難易度
- 1/5
- 見積もり時間
- 1時間未満
- 初心者へのやさしさ
- 90/100
- issue の種類
- バグ
- 明瞭さ
- 明確に書かれている
- 活発さ
- 活発
- 技術スタック
- typescript
- 領域
- backend
調査の方向性
@tanstack/start-server-core の early-hints.ts の呼び出し元から始め、そのヒントの構築を @tanstack/router-core の getScriptPreloadAttrs と比較します。iife rel=preload; as=script の動作を維持しつつ、modulepreload のヒントでは as を省略するようにします。modulepreload の Link ヘッダーに as=script が含まれなくなり、既存の CSS および iife のパスが変更されないことが完了条件です。
索引モデルが issue の本文から書いたものです。
説明
Which project does this relate to?
Router
Describe the bug
Describe the bug
collectStaticHintsFromManifest in @tanstack/start-server-core hardcodes as: "script" on every script hint, regardless of what rel the manifest helper returned.
getScriptPreloadAttrs in @tanstack/router-core correctly returns no as for the module script format — as is only valid alongside rel=preload, which is the iife branch:
// @tanstack/router-core/dist/esm/manifest.js
function getScriptPreloadAttrs(manifest, link, assetCrossOrigin) {
const preloadLink = resolveManifestAssetLink(link);
const crossOrigin = getAssetCrossOrigin(assetCrossOrigin, "script") ?? preloadLink.crossOrigin;
return {
...getManifestScriptFormat(manifest) === "iife"
? { rel: "preload", as: "script" }
: { rel: "modulepreload" }, // ← no `as`, correctly
href: preloadLink.href,
...crossOrigin ? { crossOrigin } : {},
};
}
But the early-hints caller adds it back unconditionally:
// @tanstack/start-server-core/dist/esm/early-hints.js
const attrs = getScriptPreloadAttrs(manifest, link);
const hint = {
href: attrs.href,
rel: attrs.rel, // "modulepreload" for the module format
as: "script", // ← always added, even when rel is modulepreload
};
The emitted Link header is therefore:
</assets/index-abc123.js>; rel=modulepreload; as=script
Per the HTML spec, modulepreload does not take an as attribute — the destination is implied ("script"). Browsers register a preload that the module loader never matches, and warn a few seconds after load.
Steps to reproduce
- A TanStack Start app with the default module script format and the Link header enabled.
- Load any page and open the console.
Expected behaviour
modulepreload hints are emitted without as, matching what getScriptPreloadAttrs already returns. The hint is honoured and no warning is produced.
Actual behaviour
One warning per route chunk, on every page load. On our app that is 14 lines each load:
The resource https://example.com/assets/index-abc123.js was preloaded using link preload
but not used within a few seconds from the window's load event. Please make sure it wasn't
preloaded for nothing.
Observed in Safari 18. No functional impact — but the console becomes unusable for debugging, which is exactly where you look when something actually breaks.
Suggested fix
Carry as through from the helper instead of hardcoding it:
const attrs = getScriptPreloadAttrs(manifest, link)
const hint = {
href: attrs.href,
rel: attrs.rel,
- as: 'script',
}
+ if (attrs.as !== undefined) hint.as = attrs.as
That keeps the iife path (rel=preload; as=script) unchanged and makes the module path spec-correct. The CSS branch below it is already correct — rel=preload; as=style is valid.
Possible origin
#7327 added the Link header, and #7477 later added the iife/module split in getScriptPreloadAttrs. The as: "script" line in early-hints.ts looks like it predates the split and was not revisited when rel stopped always being preload.
Versions
@tanstack/start-server-core— reproduced in 1.167.22 (our deployed version, warnings observed in production) and the same code is present in 1.169.37@tanstack/router-core1.168.17 / 1.171.32@tanstack/react-start1.167.50@tanstack/react-router1.168.25- Deployed on Cloudflare via Nitro
Workaround
Stripping as=script from modulepreload entries in the Link response header, in our own server entry wrapper. The hint still works — it just becomes valid.
Complete minimal reproducer
see above
Steps to Reproduce the Bug
see above
Expected behavior
see above
Screenshots or Videos
see above
Platform
Not relevant
Additional context
No response
- 主要言語
- TypeScript
- スター
- 15.1k
- フォーク
- 1.9k
- 平均マージ
- 2日 35分
- マージ済み PR(30日)
- 136
環境構築
このプロジェクトの開発コンテナを、あなたの GitHub アカウントでブラウザ上に起動します。
- Dockerfile・Docker Compose ファイルなし
- プルリクエストのテンプレートあり
- コントリビューションガイドを読む
はじめの一歩
- issue を最後まで読み、次にプロジェクトのコントリビューションガイドを読みます。
- 着手することを issue にコメントします — 二人が同じ作業をするのを防げます。
- リポジトリをフォークし、ブランチを切って変更します。
- issue 番号を参照したプルリクエストを送ります。
TanStack/router のほかの issue
-
難易度 1/5 1時間未満 初心者へのやさしさ 90/100
メンテナーはふだん 1 日以内に返信
-
難易度 2/5 1〜3時間 初心者へのやさしさ 84/100
メンテナーはふだん 1 日以内に返信
-
難易度 2/5 1〜3時間 初心者へのやさしさ 75/100
メンテナーはふだん 1 日以内に返信
-
難易度 2/5 1〜3時間 初心者へのやさしさ 78/100
TanStack/router#8407 · リアクション 1 件 ·
メンテナーはふだん 1 日以内に返信
-
難易度 2/5 1〜3時間 初心者へのやさしさ 78/100
TanStack/router#8333 · リアクション 1 件 ·
メンテナーはふだん 1 日以内に返信
TanStack/router の issue をすべて見る
似ている issue
-
refactor
難易度 2/5 半日 初心者へのやさしさ 84/100
メンテナーはふだん 5 日以内に返信
-
難易度 2/5 1〜3時間 初心者へのやさしさ 72/100
OHDSI/Data2Evidence#3450 ·
メンテナーはふだん 2 日以内に返信
-
e2e-failure ready-to-code
難易度 2/5 1〜3時間 初心者へのやさしさ 90/100
redhat-developer/rhdh-plugin-export-overlays#4011 · コメント 1 件 ·
メンテナーはふだん 1 日以内に返信
-
automation missing-model model-sync provider:ofox
難易度 2/5 1〜3時間 初心者へのやさしさ 72/100
anomalyco/models.dev#8421 ·
メンテナーはふだん 1 日以内に返信
-
SlackAdapter and TelegramAdapter are not assignable to Adapter under exactOptionalPropertyTypesオープン
難易度 2/5 1〜3時間 初心者へのやさしさ 76/100
メンテナーはふだん 1 日以内に返信