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’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat!: relative base #7644

Merged
merged 39 commits into from May 18, 2022
Merged
Show file tree
Hide file tree
Changes from 17 commits
Commits
Show all changes
39 commits
Select commit Hold shift + click to select a range
cb42862
feat: relative base
patak-dev Apr 7, 2022
bdacc9f
fix: don't use template string in dev
patak-dev Apr 7, 2022
7e4484a
fix: windows
patak-dev Apr 8, 2022
7744ca3
chore: stringifyAsTemplateLiteral util
patak-dev Apr 8, 2022
3bd450d
chore: typo
patak-dev Apr 13, 2022
2db74e8
chore: merge main
patak-dev Apr 13, 2022
1491bb9
chore: remove unused regex
patak-dev Apr 14, 2022
77a05e3
fix: resolve worker TODO
patak-dev Apr 15, 2022
69d872f
fix: apply fix by @locriacyber
patak-dev Apr 29, 2022
41d9884
chore: typo
patak-dev Apr 29, 2022
d69f214
chore: merge main
patak-dev May 9, 2022
1d955db
chore: merge main
patak-dev May 9, 2022
80dacfb
chore: update
patak-dev May 9, 2022
e1f49c7
refactor: back to JSON.stringify
patak-dev May 12, 2022
a0b232c
chore: merge main
patak-dev May 12, 2022
2cbd9ef
chore: update
patak-dev May 12, 2022
a4cef7e
chore: comment
patak-dev May 12, 2022
51c863c
chore: merge main and refactor worker with relative base
patak-dev May 13, 2022
26eb46d
chore: update
patak-dev May 13, 2022
638945c
chore: update
patak-dev May 13, 2022
9681aca
chore: allow custom assetFileNames
patak-dev May 13, 2022
a354401
feat: support custom file names in module preload
patak-dev May 14, 2022
b2a9967
chore: remove unneded worker options formatting
patak-dev May 14, 2022
cba14d8
fix: rework assets and worker assets handling
patak-dev May 14, 2022
f61c30e
fix: import.meta.url param position
patak-dev May 15, 2022
7f7fbf9
chore: use absolute URL for assets in CSS
patak-dev May 15, 2022
c3d0821
fix: worker hash to url cache
patak-dev May 17, 2022
77e1f39
feat: absolute public urls in JS, asset paths in CSS
patak-dev May 17, 2022
7ee53f6
test: add relative base asset test cases
patak-dev May 17, 2022
96bc8c5
test: fix serve
patak-dev May 17, 2022
8afe78e
test: bump assetsInlineLimit to 8kb
patak-dev May 17, 2022
3a09a2c
chore: improve asset paths in CSS handling
patak-dev May 17, 2022
79240a7
fix: relative CSS path
patak-dev May 17, 2022
46c15af
feat: support assetFileNames functional form
patak-dev May 17, 2022
7629352
chore: naming
patak-dev May 17, 2022
f785bed
test: worker with relative base
patak-dev May 17, 2022
b5b0a84
chore: merge main
patak-dev May 17, 2022
7f7d763
test: skip invalid serve tests for relative base
patak-dev May 17, 2022
277f967
fix: urls in windows
patak-dev May 17, 2022
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
10 changes: 5 additions & 5 deletions packages/plugin-legacy/index.js
Expand Up @@ -19,9 +19,9 @@ const legacyPolyfillId = 'vite-legacy-polyfill'
const legacyEntryId = 'vite-legacy-entry'
const systemJSInlineCode = `System.import(document.getElementById('${legacyEntryId}').getAttribute('data-src'))`

const detectDynamicImportVarName = '__vite_is_dynamic_import_support'
const detectDynamicImportCode = `try{import("_").catch(()=>1);}catch(e){}window.${detectDynamicImportVarName}=true;`
const dynamicFallbackInlineCode = `!function(){if(window.${detectDynamicImportVarName})return;console.warn("vite: loading legacy build because dynamic import is unsupported, syntax error above should be ignored");var e=document.getElementById("${legacyPolyfillId}"),n=document.createElement("script");n.src=e.src,n.onload=function(){${systemJSInlineCode}},document.body.appendChild(n)}();`
const detectModernBrowserVarName = '__vite_is_modern_browser'
const detectModernBrowserCode = `try{import(new URL(import.meta.url).href).catch(()=>1);}catch(e){}window.${detectModernBrowserVarName}=true;`
const dynamicFallbackInlineCode = `!function(){if(window.${detectModernBrowserVarName})return;console.warn("vite: loading legacy build because dynamic import or import.meta.url is unsupported, syntax error above should be ignored");var e=document.getElementById("${legacyPolyfillId}"),n=document.createElement("script");n.src=e.src,n.onload=function(){${systemJSInlineCode}},document.body.appendChild(n)}();`

const forceDynamicImportUsage = `export function __vite_legacy_guard(){import('data:text/javascript,')};`

Expand Down Expand Up @@ -439,7 +439,7 @@ function viteLegacyPlugin(options = {}) {
tags.push({
tag: 'script',
attrs: { type: 'module' },
children: detectDynamicImportCode,
children: detectModernBrowserCode,
injectTo: 'head'
})
tags.push({
Expand Down Expand Up @@ -707,6 +707,6 @@ viteLegacyPlugin.default = viteLegacyPlugin
viteLegacyPlugin.cspHashes = [
createHash('sha256').update(safari10NoModuleFix).digest('base64'),
createHash('sha256').update(systemJSInlineCode).digest('base64'),
createHash('sha256').update(detectDynamicImportCode).digest('base64'),
createHash('sha256').update(detectModernBrowserCode).digest('base64'),
createHash('sha256').update(dynamicFallbackInlineCode).digest('base64')
]
29 changes: 27 additions & 2 deletions packages/vite/src/node/build.ts
Expand Up @@ -245,11 +245,11 @@ export function resolveBuildOptions(raw?: BuildOptions): ResolvedBuildOptions {
// Support browserslist
// "defaults and supports es6-module and supports es6-module-dynamic-import",
resolved.target = [
'es2019',
'es2020', // support import.meta.url
'edge88',
'firefox78',
'chrome87',
'safari13.1'
'safari13' // transpile nullish coalescing
]
} else if (resolved.target === 'esnext' && resolved.minify === 'terser') {
// esnext + terser: limit to es2019 so it can be minified by terser
Expand Down Expand Up @@ -753,3 +753,28 @@ function injectSsrFlag<T extends Record<string, any>>(
): T & { ssr: boolean } {
return { ...(options ?? {}), ssr: true } as T & { ssr: boolean }
}

export function isRelativeBase(base: string): boolean {
return base === '' || base.startsWith('.')
}

export function assetFilenameWithBase(filename: string, base: string): string {
if (isRelativeBase(base)) {
// relative base - asset file will be in the same dir
return `./${path.posix.basename(filename)}`
} else {
return base + filename
}
}

export function publicURLfromAsset(
url: string,
config: ResolvedConfig
): string {
if (isRelativeBase(config.base)) {
// Assume flat assets structure
return path.posix.relative(config.build.assetsDir, '') + url
} else {
return config.base + url.slice(1)
}
}
26 changes: 19 additions & 7 deletions packages/vite/src/node/plugins/asset.ts
Expand Up @@ -6,9 +6,13 @@ import type { OutputOptions, PluginContext } from 'rollup'
import MagicString from 'magic-string'
import type { Plugin } from '../plugin'
import type { ResolvedConfig } from '../config'
import { cleanUrl } from '../utils'
import { cleanUrl, getHash, normalizePath } from '../utils'
import {
assetFilenameWithBase,
isRelativeBase,
publicURLfromAsset
} from '../build'
import { FS_PREFIX } from '../constants'
import { getHash, normalizePath } from '../utils'

export const assetUrlRE = /__VITE_ASSET__([a-z\d]{8})__(?:\$_(.*?)__)?/g

Expand Down Expand Up @@ -84,7 +88,7 @@ export function assetPlugin(config: ResolvedConfig): Plugin {
let s: MagicString | undefined

// Urls added with JS using e.g.
// imgElement.src = "my/file.png" are using quotes
// imgElement.src = "__VITE_ASSET__5aa0ddc0__" are using quotes

// Urls added in CSS that is imported in JS end up like
// var inlined = ".inlined{color:green;background:url(__VITE_ASSET__5aa0ddc0__)}\n";
Expand All @@ -98,8 +102,16 @@ export function assetPlugin(config: ResolvedConfig): Plugin {
// fallback to this.getFileName for that.
const file = getAssetFilename(hash, config) || this.getFileName(hash)
chunk.viteMetadata.importedAssets.add(cleanUrl(file))
const outputFilepath = config.base + file + postfix
s.overwrite(match.index, match.index + full.length, outputFilepath, {
const outputFilepath = assetFilenameWithBase(
file + postfix,
config.base
)
const replacement = isRelativeBase(config.base)
? `" + new URL(${JSON.stringify(
outputFilepath
)}, import.meta.url) + "`
: outputFilepath
s.overwrite(match.index, match.index + full.length, replacement, {
contentOnly: true
})
}
Expand Down Expand Up @@ -270,7 +282,7 @@ async function fileToBuiltUrl(
skipPublicCheck = false
): Promise<string> {
if (!skipPublicCheck && checkPublicFile(id, config)) {
return config.base + id.slice(1)
return publicURLfromAsset(id, config)
}

const cache = assetCache.get(config)!
Expand Down Expand Up @@ -343,7 +355,7 @@ export async function urlToBuiltUrl(
pluginContext: PluginContext
): Promise<string> {
if (checkPublicFile(url, config)) {
return config.base + url.slice(1)
return publicURLfromAsset(url, config)
}
const file = url.startsWith('/')
? path.join(config.root, url)
Expand Down
2 changes: 2 additions & 0 deletions packages/vite/src/node/plugins/assetImportMetaUrl.ts
Expand Up @@ -4,6 +4,7 @@ import { stripLiteral } from 'strip-literal'
import type { Plugin } from '../plugin'
import type { ResolvedConfig } from '../config'
import { fileToUrl } from './asset'
import { preloadHelperId } from './importAnalysisBuild'

/**
* Convert `new URL('./foo.png', import.meta.url)` to its resolved built URL
Expand All @@ -21,6 +22,7 @@ export function assetImportMetaUrlPlugin(config: ResolvedConfig): Plugin {
async transform(code, id, options) {
if (
!options?.ssr &&
id !== preloadHelperId &&
code.includes('new URL') &&
code.includes(`import.meta.url`)
) {
Expand Down
13 changes: 5 additions & 8 deletions packages/vite/src/node/plugins/css.ts
Expand Up @@ -42,6 +42,7 @@ import {
processSrcSet
} from '../utils'
import { emptyCssComments } from '../utils'
import { assetFilenameWithBase, publicURLfromAsset } from '../build'
import { addToHTMLProxyTransformResult } from './html'
import {
assetUrlRE,
Expand Down Expand Up @@ -182,7 +183,7 @@ export function cssPlugin(config: ResolvedConfig): Plugin {

const urlReplacer: CssUrlReplacer = async (url, importer) => {
if (checkPublicFile(url, config)) {
return config.base + url.slice(1)
return publicURLfromAsset(url, config)
}
const resolved = await resolveUrl(url, importer)
if (resolved) {
Expand Down Expand Up @@ -427,18 +428,14 @@ export function cssPostPlugin(config: ResolvedConfig): Plugin {
}
) => {
// replace asset url references with resolved url.
const isRelativeBase = config.base === '' || config.base.startsWith('.')
css = css.replace(assetUrlRE, (_, fileHash, postfix = '') => {
const filename = getAssetFilename(fileHash, config) + postfix
chunk.viteMetadata.importedAssets.add(cleanUrl(filename))
if (!isRelativeBase || inlined) {
// absolute base or relative base but inlined (injected as style tag into
// index.html) use the base as-is
if (inlined) {
// inlined (injected as style tag into index.html) use the base as-is
return config.base + filename
} else {
// relative base + extracted CSS - asset file will be in the same dir
return `./${path.posix.basename(filename)}`
}
return assetFilenameWithBase(filename, config.base)
})
// only external @imports and @charset should exist at this point
css = await finalizeCss(css, minify, config)
Expand Down
61 changes: 44 additions & 17 deletions packages/vite/src/node/plugins/html.ts
Expand Up @@ -28,6 +28,7 @@ import {
processSrcSet,
slash
} from '../utils'
import { isRelativeBase } from '../build'
import type { ResolvedConfig } from '../config'
import {
assetUrlRE,
Expand Down Expand Up @@ -236,7 +237,10 @@ export function buildHtmlPlugin(config: ResolvedConfig): Plugin {

async transform(html, id) {
if (id.endsWith('.html')) {
const publicPath = `/${slash(path.relative(config.root, id))}`
const relativeUrlPath = slash(path.relative(config.root, id))
const publicPath = `/${relativeUrlPath}`
const publicBase = getPublicBase(relativeUrlPath, config)

// pre-transform
html = await applyHtmlTransforms(html, preHooks, {
path: publicPath,
Expand Down Expand Up @@ -272,7 +276,7 @@ export function buildHtmlPlugin(config: ResolvedConfig): Plugin {
s.overwrite(
src!.value!.loc.start.offset,
src!.value!.loc.end.offset,
`"${config.base + url.slice(1)}"`,
`"${normalizePublicPath(url, publicBase)}"`,
{ contentOnly: true }
)
}
Expand Down Expand Up @@ -354,7 +358,7 @@ export function buildHtmlPlugin(config: ResolvedConfig): Plugin {
s.overwrite(
p.value.loc.start.offset,
p.value.loc.end.offset,
`"${config.base + url.slice(1)}"`,
`"${normalizePublicPath(url, publicBase)}"`,
{ contentOnly: true }
)
}
Expand Down Expand Up @@ -470,7 +474,7 @@ export function buildHtmlPlugin(config: ResolvedConfig): Plugin {
{ contentOnly: true }
)
} else if (checkPublicFile(url, config)) {
s.overwrite(start, end, config.base + url.slice(1), {
s.overwrite(start, end, normalizePublicPath(url, publicBase), {
contentOnly: true
})
}
Expand Down Expand Up @@ -531,28 +535,33 @@ export function buildHtmlPlugin(config: ResolvedConfig): Plugin {

const toScriptTag = (
chunk: OutputChunk,
publicBase: string,
isAsync: boolean
): HtmlTagDescriptor => ({
tag: 'script',
attrs: {
...(isAsync ? { async: true } : {}),
type: 'module',
crossorigin: true,
src: toPublicPath(chunk.fileName, config)
src: toPublicPath(chunk.fileName, publicBase)
}
})

const toPreloadTag = (chunk: OutputChunk): HtmlTagDescriptor => ({
const toPreloadTag = (
chunk: OutputChunk,
publicBase: string
): HtmlTagDescriptor => ({
tag: 'link',
attrs: {
rel: 'modulepreload',
crossorigin: true,
href: toPublicPath(chunk.fileName, config)
href: toPublicPath(chunk.fileName, publicBase)
}
})

const getCssTagsForChunk = (
chunk: OutputChunk,
publicBase: string,
seen: Set<string> = new Set()
): HtmlTagDescriptor[] => {
const tags: HtmlTagDescriptor[] = []
Expand All @@ -561,7 +570,7 @@ export function buildHtmlPlugin(config: ResolvedConfig): Plugin {
chunk.imports.forEach((file) => {
const importee = bundle[file]
if (importee?.type === 'chunk') {
tags.push(...getCssTagsForChunk(importee, seen))
tags.push(...getCssTagsForChunk(importee, publicBase, seen))
}
})
}
Expand All @@ -573,7 +582,7 @@ export function buildHtmlPlugin(config: ResolvedConfig): Plugin {
tag: 'link',
attrs: {
rel: 'stylesheet',
href: toPublicPath(file, config)
href: toPublicPath(file, publicBase)
}
})
}
Expand All @@ -583,6 +592,9 @@ export function buildHtmlPlugin(config: ResolvedConfig): Plugin {
}

for (const [id, html] of processedHtml) {
const relativeUrlPath = path.posix.relative(config.root, id)
const publicBase = getPublicBase(relativeUrlPath, config)

const isAsync = isAsyncScriptMap.get(config)!.get(id)!

let result = html
Expand Down Expand Up @@ -610,10 +622,13 @@ export function buildHtmlPlugin(config: ResolvedConfig): Plugin {
// when inlined, discard entry chunk and inject <script> for everything in post-order
const imports = getImportedChunks(chunk)
const assetTags = canInlineEntry
? imports.map((chunk) => toScriptTag(chunk, isAsync))
: [toScriptTag(chunk, isAsync), ...imports.map(toPreloadTag)]
? imports.map((chunk) => toScriptTag(chunk, publicBase, isAsync))
: [
toScriptTag(chunk, publicBase, isAsync),
...imports.map((i) => toPreloadTag(i, publicBase))
]

assetTags.push(...getCssTagsForChunk(chunk))
assetTags.push(...getCssTagsForChunk(chunk, publicBase))

result = injectToHead(result, assetTags)
}
Expand All @@ -629,7 +644,7 @@ export function buildHtmlPlugin(config: ResolvedConfig): Plugin {
tag: 'link',
attrs: {
rel: 'stylesheet',
href: toPublicPath(cssChunk.fileName, config)
href: toPublicPath(cssChunk.fileName, publicBase)
}
}
])
Expand All @@ -653,7 +668,6 @@ export function buildHtmlPlugin(config: ResolvedConfig): Plugin {
if (s) {
result = s.toString()
}
const relativeUrlPath = path.posix.relative(config.root, id)
result = await applyHtmlTransforms(result, postHooks, {
path: '/' + relativeUrlPath,
filename: id,
Expand All @@ -662,7 +676,7 @@ export function buildHtmlPlugin(config: ResolvedConfig): Plugin {
})
// resolve asset url references
result = result.replace(assetUrlRE, (_, fileHash, postfix = '') => {
return config.base + getAssetFilename(fileHash, config) + postfix
return publicBase + getAssetFilename(fileHash, config) + postfix
})

if (chunk && canInlineEntry) {
Expand Down Expand Up @@ -812,8 +826,21 @@ function isEntirelyImport(code: string) {
return !code.replace(importRE, '').replace(commentRE, '').trim().length
}

function toPublicPath(filename: string, config: ResolvedConfig) {
return isExternalUrl(filename) ? filename : config.base + filename
function getPublicBase(urlRelativePath: string, config: ResolvedConfig) {
return isRelativeBase(config.base)
? path.posix.join(
path.posix.relative(urlRelativePath, '').slice(0, -2),
'./'
)
: config.base
}

function toPublicPath(filename: string, publicBase: string) {
return isExternalUrl(filename) ? filename : publicBase + filename
}

function normalizePublicPath(publicPath: string, publicBase: string) {
return publicBase + publicPath.slice(1)
}

const headInjectRE = /([ \t]*)<\/head>/i
Expand Down