From ff84d7020e6356ee1ec1332361c6e67e61a05562 Mon Sep 17 00:00:00 2001 From: mehmet turac Date: Fri, 19 Jun 2026 15:50:14 +0300 Subject: [PATCH 1/2] fix: preload css for nested dynamic imports --- .../src/node/plugins/importAnalysisBuild.ts | 13 +++++++- .../__tests__/dynamic-import.spec.ts | 30 +++++++++++++++++++ playground/dynamic-import/index.html | 3 ++ playground/dynamic-import/nested/index.js | 9 ++++++ .../dynamic-import/nested/issue-22700-a.css | 3 ++ .../dynamic-import/nested/issue-22700-a.js | 3 ++ .../dynamic-import/nested/issue-22700-b.js | 1 + 7 files changed, 61 insertions(+), 1 deletion(-) create mode 100644 playground/dynamic-import/nested/issue-22700-a.css create mode 100644 playground/dynamic-import/nested/issue-22700-a.js create mode 100644 playground/dynamic-import/nested/issue-22700-b.js diff --git a/packages/vite/src/node/plugins/importAnalysisBuild.ts b/packages/vite/src/node/plugins/importAnalysisBuild.ts index e5a07630355cdb..af387737c44d90 100644 --- a/packages/vite/src/node/plugins/importAnalysisBuild.ts +++ b/packages/vite/src/node/plugins/importAnalysisBuild.ts @@ -369,7 +369,9 @@ export function buildImportAnalysisPlugin(config: ResolvedConfig): Plugin[] { } if (imports.length) { - for (let index = 0; index < imports.length; index++) { + // Nested preload wrappers emit inner markers before outer markers. + // Pair dynamic imports from the inside out so each import claims its own marker. + for (let index = imports.length - 1; index >= 0; index--) { // To handle escape sequences in specifier strings, the .n field will be provided where possible. const { n: name, @@ -439,6 +441,15 @@ export function buildImportAnalysisPlugin(config: ResolvedConfig): Plugin[] { } let markerStartPos = findPreloadMarker(code, end) + while ( + markerStartPos !== -1 && + rewroteMarkerStartPos.has(markerStartPos) + ) { + markerStartPos = findPreloadMarker( + code, + markerStartPos + preloadMarker.length, + ) + } // fix issue #3051 if (markerStartPos === -1 && imports.length === 1) { markerStartPos = findPreloadMarker(code) diff --git a/playground/dynamic-import/__tests__/dynamic-import.spec.ts b/playground/dynamic-import/__tests__/dynamic-import.spec.ts index 384f4190295f46..12206516b5677c 100644 --- a/playground/dynamic-import/__tests__/dynamic-import.spec.ts +++ b/playground/dynamic-import/__tests__/dynamic-import.spec.ts @@ -123,6 +123,36 @@ test('should load dynamic import with css in package', async () => { await expect.poll(() => getColor('.pkg-css')).toBe('blue') }) +test.runIf(isBuild)( + 'should preload css for nested dynamic imports', + async () => { + const js = findAssetFile(/index-[-\w]{8}\.js$/) + expect(js).toMatch( + /import\(`\.\/issue-22700-a-[^`]+\.js`\)\.then\((\w+)=>\w+\(\(\)=>import\(`\.\/issue-22700-b-[^`]+\.js`\)\.then\(\(\)=>\1\),\[\]\)\),__vite__mapDeps\(\[\d+,\d+\]\)/, + ) + + await page.click('.issue-22700') + await expect + .poll(() => page.textContent('.issue-22700-result')) + .toMatch('nested dynamic import with css') + await expect + .poll(() => + page.evaluate(() => + Array.from( + document.querySelectorAll( + 'link[rel="stylesheet"]', + ), + (link) => link.href, + ).some((href) => /issue-22700-a-[-\w]+\.css$/.test(href)), + ), + ) + .toBe(true) + await expect + .poll(() => getColor('.issue-22700-result')) + .toBe('rgb(12, 34, 56)') + }, +) + test('should work with load ../ and itself directory', async () => { await expect .poll(() => page.textContent('.dynamic-import-self')) diff --git a/playground/dynamic-import/index.html b/playground/dynamic-import/index.html index 1289efc3e1ac4e..06f0f1894e2b27 100644 --- a/playground/dynamic-import/index.html +++ b/playground/dynamic-import/index.html @@ -9,6 +9,7 @@ +

dynamic-import-with-vars

todo
@@ -39,6 +40,8 @@
+
+
diff --git a/playground/dynamic-import/nested/index.js b/playground/dynamic-import/nested/index.js index 6520e1f5a51bb8..49e206bc38c272 100644 --- a/playground/dynamic-import/nested/index.js +++ b/playground/dynamic-import/nested/index.js @@ -74,6 +74,15 @@ document.querySelector('.pkg-css').addEventListener('click', async () => { text('.view', 'dynamic import css in package') }) +document.querySelector('.issue-22700').addEventListener('click', async () => { + const { markerClass } = await import('./issue-22700-a.js').then((mod) => { + return import('./issue-22700-b.js').then(() => mod) + }) + const result = document.querySelector('.issue-22700-result') + result.classList.add(markerClass) + text('.issue-22700-result', 'nested dynamic import with css') +}) + function text(el, text) { document.querySelector(el).textContent = text } diff --git a/playground/dynamic-import/nested/issue-22700-a.css b/playground/dynamic-import/nested/issue-22700-a.css new file mode 100644 index 00000000000000..b6ee11707d44fb --- /dev/null +++ b/playground/dynamic-import/nested/issue-22700-a.css @@ -0,0 +1,3 @@ +.issue-22700-loaded { + color: rgb(12, 34, 56); +} diff --git a/playground/dynamic-import/nested/issue-22700-a.js b/playground/dynamic-import/nested/issue-22700-a.js new file mode 100644 index 00000000000000..766ba2100269af --- /dev/null +++ b/playground/dynamic-import/nested/issue-22700-a.js @@ -0,0 +1,3 @@ +import './issue-22700-a.css' + +export const markerClass = 'issue-22700-loaded' diff --git a/playground/dynamic-import/nested/issue-22700-b.js b/playground/dynamic-import/nested/issue-22700-b.js new file mode 100644 index 00000000000000..6bf431b8e6e1d5 --- /dev/null +++ b/playground/dynamic-import/nested/issue-22700-b.js @@ -0,0 +1 @@ +export const msg = 'nested dynamic import with css' From 72718eddec07ef3615a800d4ec1281315a45faf4 Mon Sep 17 00:00:00 2001 From: shulaoda <165626830+shulaoda@users.noreply.github.com> Date: Thu, 25 Jun 2026 07:46:31 +0800 Subject: [PATCH 2/2] test: check nested preload via the inner import's deps slot --- .../dynamic-import/__tests__/dynamic-import.spec.ts | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/playground/dynamic-import/__tests__/dynamic-import.spec.ts b/playground/dynamic-import/__tests__/dynamic-import.spec.ts index 12206516b5677c..2968409ee422da 100644 --- a/playground/dynamic-import/__tests__/dynamic-import.spec.ts +++ b/playground/dynamic-import/__tests__/dynamic-import.spec.ts @@ -126,10 +126,11 @@ test('should load dynamic import with css in package', async () => { test.runIf(isBuild)( 'should preload css for nested dynamic imports', async () => { - const js = findAssetFile(/index-[-\w]{8}\.js$/) - expect(js).toMatch( - /import\(`\.\/issue-22700-a-[^`]+\.js`\)\.then\((\w+)=>\w+\(\(\)=>import\(`\.\/issue-22700-b-[^`]+\.js`\)\.then\(\(\)=>\1\),\[\]\)\),__vite__mapDeps\(\[\d+,\d+\]\)/, - ) + const js = findAssetFile(/index-[-\w]{8}\.js$/) ?? '' + const innerPreload = js.match( + /import\([^)]*issue-22700-b-[-\w]+\.js[^)]*\)[\s\S]*?(,\[\]\)|__vite__mapDeps)/, + )?.[1] + expect(innerPreload).toBe(',[])') await page.click('.issue-22700') await expect