mirror of
https://github.com/vitejs/vite.git
synced 2024-11-21 22:59:10 +00:00
fix(html): move importmap before module scripts (#9392)
Co-authored-by: 翠 / green <green@sapphi.red>
This commit is contained in:
parent
31c2926c68
commit
b386fba49e
@ -7,6 +7,7 @@ import type {
|
||||
SourceMapInput
|
||||
} from 'rollup'
|
||||
import MagicString from 'magic-string'
|
||||
import colors from 'picocolors'
|
||||
import type {
|
||||
AttributeNode,
|
||||
CompilerError,
|
||||
@ -54,6 +55,10 @@ const inlineImportRE =
|
||||
/(?<!(?<!\.\.)\.)\bimport\s*\(("([^"]|(?<=\\)")*"|'([^']|(?<=\\)')*')\)/g
|
||||
const htmlLangRE = /\.(html|htm)$/
|
||||
|
||||
const importMapRE =
|
||||
/[ \t]*<script[^>]*type\s*=\s*["']?importmap["']?[^>]*>.*?<\/script>/is
|
||||
const moduleScriptRE = /[ \t]*<script[^>]*type\s*=\s*["']?module["']?[^>]*>/is
|
||||
|
||||
export const isHTMLProxy = (id: string): boolean => htmlProxyRE.test(id)
|
||||
|
||||
export const isHTMLRequest = (request: string): boolean =>
|
||||
@ -225,6 +230,8 @@ function handleParseError(
|
||||
*/
|
||||
export function buildHtmlPlugin(config: ResolvedConfig): Plugin {
|
||||
const [preHooks, postHooks] = resolveHtmlTransforms(config.plugins)
|
||||
preHooks.unshift(preImportMapHook(config))
|
||||
postHooks.push(postImportMapHook())
|
||||
const processedHtml = new Map<string, string>()
|
||||
const isExcludedUrl = (url: string) =>
|
||||
url.startsWith('#') ||
|
||||
@ -796,6 +803,51 @@ export type IndexHtmlTransform =
|
||||
transform: IndexHtmlTransformHook
|
||||
}
|
||||
|
||||
export function preImportMapHook(
|
||||
config: ResolvedConfig
|
||||
): IndexHtmlTransformHook {
|
||||
return (html, ctx) => {
|
||||
const importMapIndex = html.match(importMapRE)?.index
|
||||
if (importMapIndex === undefined) return
|
||||
|
||||
const moduleScriptIndex = html.match(moduleScriptRE)?.index
|
||||
if (moduleScriptIndex === undefined) return
|
||||
|
||||
if (moduleScriptIndex < importMapIndex) {
|
||||
const relativeHtml = normalizePath(
|
||||
path.relative(config.root, ctx.filename)
|
||||
)
|
||||
config.logger.warnOnce(
|
||||
colors.yellow(
|
||||
colors.bold(
|
||||
`(!) <script type="importmap"> should come before <script type="module"> in /${relativeHtml}`
|
||||
)
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Move importmap before the first module script
|
||||
*/
|
||||
export function postImportMapHook(): IndexHtmlTransformHook {
|
||||
return (html) => {
|
||||
if (!moduleScriptRE.test(html)) return
|
||||
|
||||
let importMap: string | undefined
|
||||
html = html.replace(importMapRE, (match) => {
|
||||
importMap = match
|
||||
return ''
|
||||
})
|
||||
if (importMap) {
|
||||
html = html.replace(moduleScriptRE, (match) => `${importMap}\n${match}`)
|
||||
}
|
||||
|
||||
return html
|
||||
}
|
||||
}
|
||||
|
||||
export function resolveHtmlTransforms(
|
||||
plugins: readonly Plugin[]
|
||||
): [IndexHtmlTransformHook[], IndexHtmlTransformHook[]] {
|
||||
|
@ -11,6 +11,8 @@ import {
|
||||
applyHtmlTransforms,
|
||||
assetAttrsConfig,
|
||||
getScriptInfo,
|
||||
postImportMapHook,
|
||||
preImportMapHook,
|
||||
resolveHtmlTransforms,
|
||||
traverseHtml
|
||||
} from '../../plugins/html'
|
||||
@ -43,12 +45,22 @@ export function createDevHtmlTransformFn(
|
||||
): (url: string, html: string, originalUrl: string) => Promise<string> {
|
||||
const [preHooks, postHooks] = resolveHtmlTransforms(server.config.plugins)
|
||||
return (url: string, html: string, originalUrl: string): Promise<string> => {
|
||||
return applyHtmlTransforms(html, [...preHooks, devHtmlHook, ...postHooks], {
|
||||
path: url,
|
||||
filename: getHtmlFilename(url, server),
|
||||
server,
|
||||
originalUrl
|
||||
})
|
||||
return applyHtmlTransforms(
|
||||
html,
|
||||
[
|
||||
preImportMapHook(server.config),
|
||||
...preHooks,
|
||||
devHtmlHook,
|
||||
...postHooks,
|
||||
postImportMapHook()
|
||||
],
|
||||
{
|
||||
path: url,
|
||||
filename: getHtmlFilename(url, server),
|
||||
server,
|
||||
originalUrl
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,5 +1,11 @@
|
||||
import { describe, expect, test } from 'vitest'
|
||||
import { isBuild, page } from '~utils'
|
||||
import { browserLogs, isBuild, page } from '~utils'
|
||||
|
||||
test('importmap', () => {
|
||||
expect(browserLogs).not.toContain(
|
||||
'An import map is added after module script load was triggered.'
|
||||
)
|
||||
})
|
||||
|
||||
describe.runIf(isBuild)('build', () => {
|
||||
test('should externalize imported packages', async () => {
|
||||
|
@ -1,5 +1,13 @@
|
||||
import { beforeAll, describe, expect, test } from 'vitest'
|
||||
import { editFile, getColor, isBuild, isServe, page, viteTestUrl } from '~utils'
|
||||
import {
|
||||
browserLogs,
|
||||
editFile,
|
||||
getColor,
|
||||
isBuild,
|
||||
isServe,
|
||||
page,
|
||||
viteTestUrl
|
||||
} from '~utils'
|
||||
|
||||
function testPage(isNested: boolean) {
|
||||
test('pre transform', async () => {
|
||||
@ -242,3 +250,9 @@ describe.runIf(isServe)('invalid', () => {
|
||||
expect(content).toBeTruthy()
|
||||
})
|
||||
})
|
||||
|
||||
test('importmap', () => {
|
||||
expect(browserLogs).not.toContain(
|
||||
'An import map is added after module script load was triggered.'
|
||||
)
|
||||
})
|
||||
|
@ -160,6 +160,25 @@ ${
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
name: 'head-prepend-importmap',
|
||||
transformIndexHtml() {
|
||||
return [
|
||||
{
|
||||
tag: 'script',
|
||||
attrs: { type: 'importmap' },
|
||||
children: `
|
||||
{
|
||||
"imports": {
|
||||
"vue": "https://unpkg.com/vue@3.2.0/dist/vue.runtime.esm-browser.js"
|
||||
}
|
||||
}
|
||||
`,
|
||||
injectTo: 'head'
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user