Skip to content

Commit f3a0bc9

Browse files
fix(plugin-legacy): remove modulepreload links for legacy-only builds (#22332)
Co-authored-by: sapphi-red <49056869+sapphi-red@users.noreply.github.com>
1 parent d9b18e0 commit f3a0bc9

3 files changed

Lines changed: 58 additions & 1 deletion

File tree

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
import { describe, expect, test } from 'vitest'
2+
import { modulePreloadLinkRE } from '../index'
3+
4+
describe('modulePreloadLinkRE', () => {
5+
const matches: Array<[string, string]> = [
6+
['rel first', '<link rel="modulepreload" crossorigin href="/assets/x.js">'],
7+
[
8+
'rel after other attributes',
9+
'<link href="/assets/x.js" rel="modulepreload">',
10+
],
11+
['rel only', '<link rel="modulepreload">'],
12+
['self-closing', '<link rel="modulepreload"/>'],
13+
['self-closing with space', '<link rel="modulepreload" />'],
14+
['single quotes', "<link rel='modulepreload' href='/assets/x.js'>"],
15+
[
16+
'attributes across multiple lines',
17+
'<link\n rel="modulepreload"\n href="/assets/x.js"\n>',
18+
],
19+
]
20+
21+
for (const [name, html] of matches) {
22+
test(`matches: ${name}`, () => {
23+
expect(html.replace(modulePreloadLinkRE, '')).toBe('')
24+
})
25+
}
26+
27+
const nonMatches: Array<[string, string]> = [
28+
['tag name with suffix', '<linkfoo rel="modulepreload">'],
29+
['custom element with hyphen', '<link-preview rel="modulepreload">'],
30+
['bare link tag', '<link>'],
31+
['stylesheet link', '<link rel="stylesheet" href="/assets/x.css">'],
32+
['preload (not modulepreload)', '<link rel="preload" href="/assets/x.js">'],
33+
[
34+
'attribute name ending in rel',
35+
'<link xrel="modulepreload" href="/assets/x.js">',
36+
],
37+
['mismatched quotes', `<link rel="modulepreload'>`],
38+
]
39+
40+
for (const [name, html] of nonMatches) {
41+
test(`does not match: ${name}`, () => {
42+
expect(html.replace(modulePreloadLinkRE, '')).toBe(html)
43+
})
44+
}
45+
})

packages/plugin-legacy/src/index.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -125,6 +125,8 @@ const _require = createRequire(import.meta.url)
125125

126126
const nonLeadingHashInFileNameRE = /[^/]+\[hash(?::\d+)?\]/
127127
const prefixedHashInFileNameRE = /\W?\[hash(?::\d+)?\]/
128+
export const modulePreloadLinkRE: RegExp =
129+
/<link(?![\w-])[^>]*?\srel=(['"])modulepreload\1[^>]*>/g
128130

129131
// browsers supporting dynamic import + import.meta.resolve + async generator
130132
const modernTargetsEsbuild = [
@@ -641,7 +643,9 @@ function viteLegacyPlugin(options: Options = {}): Plugin[] {
641643
}
642644
}
643645
if (!genModern) {
644-
html = html.replace(/<script type="module".*?<\/script>/g, '')
646+
html = html
647+
.replace(/<script type="module".*?<\/script>/g, '')
648+
.replace(modulePreloadLinkRE, '')
645649
}
646650

647651
const tags: HtmlTagDescriptor[] = []

playground/legacy/__tests__/no-polyfills/legacy-no-polyfills.spec.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,3 +14,11 @@ test.runIf(isBuild)('includes a script tag for SystemJS', async () => {
1414
.poll(() => page.getAttribute('#vite-legacy-entry', 'data-src'))
1515
.toMatch(/.\/assets\/index-legacy-(.+)\.js/)
1616
})
17+
18+
test.runIf(isBuild)(
19+
'does not emit modulepreload links for legacy-only output',
20+
async () => {
21+
await page.goto(viteTestUrl + '/no-polyfills.html')
22+
expect(await page.content()).not.toContain('modulepreload')
23+
},
24+
)

0 commit comments

Comments
 (0)