Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We鈥檒l occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(html): move importmap before module scripts #9392

Merged
merged 14 commits into from Aug 21, 2022
30 changes: 30 additions & 0 deletions packages/vite/src/node/plugins/html.ts
Expand Up @@ -218,6 +218,36 @@ function handleParseError(
)
}

/**
* Move importmap to top of <head> with `transformIndexHtml`.
* Must be added after post plugins.
*/
export function htmlImportMapPlugin(): Plugin {
const importMapRE =
/[ \t]*<script[^>]*type\s*=\s*["']?importmap["']?[^>]*>.*?<\/script>/is
return {
name: 'vite:html-importmap',
transformIndexHtml(html) {
// HTML must have head for importmap
if (!headPrependInjectRE.test(html)) return

let importMap: string | undefined
html = html.replace(importMapRE, (match) => {
importMap = match
return ''
})
if (importMap) {
html = html.replace(
headPrependInjectRE,
(match) => `${match}\n${importMap}`
)
}
sapphi-red marked this conversation as resolved.
Show resolved Hide resolved

return html
}
}
}

/**
* Compiles index.html into an entry js module
*/
Expand Down
9 changes: 7 additions & 2 deletions packages/vite/src/node/plugins/index.ts
Expand Up @@ -12,7 +12,11 @@ import { importAnalysisPlugin } from './importAnalysis'
import { cssPlugin, cssPostPlugin } from './css'
import { assetPlugin } from './asset'
import { clientInjectionsPlugin } from './clientInjections'
import { buildHtmlPlugin, htmlInlineProxyPlugin } from './html'
import {
buildHtmlPlugin,
htmlImportMapPlugin,
htmlInlineProxyPlugin
} from './html'
import { wasmFallbackPlugin, wasmHelperPlugin } from './wasm'
import { modulePreloadPolyfillPlugin } from './modulePreloadPolyfill'
import { webWorkerPlugin } from './worker'
Expand Down Expand Up @@ -96,6 +100,7 @@ export async function resolvePlugins(
// internal server-only plugins are always applied after everything else
...(isBuild
? []
: [clientInjectionsPlugin(config), importAnalysisPlugin(config)])
: [clientInjectionsPlugin(config), importAnalysisPlugin(config)]),
htmlImportMapPlugin()
].filter(Boolean) as Plugin[]
}
8 changes: 7 additions & 1 deletion playground/external/__tests__/external.spec.ts
@@ -1,4 +1,10 @@
import { isBuild, page } from '~utils'
import { browserLogs, isBuild, isServe, page } from '~utils'

test.runIf(isServe)('importmap', () => {
bluwy marked this conversation as resolved.
Show resolved Hide resolved
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 () => {
Expand Down
16 changes: 15 additions & 1 deletion playground/html/__tests__/html.spec.ts
@@ -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 () => {
Expand Down Expand Up @@ -242,3 +250,9 @@ describe.runIf(isServe)('invalid', () => {
expect(content).toBeTruthy()
})
})

test.runIf(isServe)('importmap', () => {
bluwy marked this conversation as resolved.
Show resolved Hide resolved
expect(browserLogs).not.toContain(
'An import map is added after module script load was triggered.'
)
})
19 changes: 19 additions & 0 deletions playground/html/vite.config.js
Expand Up @@ -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-prepend'
}
]
}
}
]
}