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 6 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
5 changes: 4 additions & 1 deletion packages/playground/assets/package.json
Expand Up @@ -6,6 +6,9 @@
"dev": "vite",
"build": "vite build",
"debug": "node --inspect-brk ../../vite/bin/vite",
"preview": "vite preview"
"preview": "vite preview",
"dev:portable": "vite --config ./vite.config-portable.js dev",
"build:portable": "vite --config ./vite.config-portable.js build",
"preview:portable": "vite --config ./vite.config-portable.js preview"
}
}
14 changes: 14 additions & 0 deletions packages/playground/assets/vite.config-portable.js
@@ -0,0 +1,14 @@
/**
* @type {import('vite').UserConfig}
*/

const baseConfig = require('./vite.config.js')
module.exports = {
...baseConfig,
base: './', // relative base to make dist portable
build: {
...baseConfig.build,
outDir: 'dist',
watch: false
}
}
1 change: 1 addition & 0 deletions packages/playground/css/vite.config.js
Expand Up @@ -4,6 +4,7 @@ const path = require('path')
* @type {import('vite').UserConfig}
*/
module.exports = {
base: './',
build: {
cssTarget: 'chrome61'
},
Expand Down
1 change: 1 addition & 0 deletions packages/playground/html/vite.config.js
Expand Up @@ -4,6 +4,7 @@ const { resolve } = require('path')
* @type {import('vite').UserConfig}
*/
module.exports = {
base: './',
build: {
rollupOptions: {
input: {
Expand Down
1 change: 1 addition & 0 deletions packages/playground/legacy/vite.config.js
Expand Up @@ -3,6 +3,7 @@ const path = require('path')
const legacy = require('@vitejs/plugin-legacy').default

module.exports = {
base: './',
plugins: [
legacy({
targets: 'IE 11'
Expand Down
4 changes: 2 additions & 2 deletions packages/playground/preload/__tests__/preload.spec.ts
Expand Up @@ -16,10 +16,10 @@ if (isBuild) {
await page.goto(viteTestUrl + '/#/hello')
const html = await page.content()
expect(html).toMatch(
/link rel="modulepreload".*?href="\/assets\/Hello\.\w{8}\.js"/
/link rel="modulepreload".*?href=".*?\/assets\/Hello\.\w{8}\.js"/
)
expect(html).toMatch(
/link rel="stylesheet".*?href="\/assets\/Hello\.\w{8}\.css"/
/link rel="stylesheet".*?href=".*?\/assets\/Hello\.\w{8}\.css"/
)
})
}
17 changes: 9 additions & 8 deletions packages/plugin-legacy/index.js
Expand Up @@ -19,10 +19,11 @@ 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 detectDynamicImportVarInitCode = `var ${detectDynamicImportVarName}=false;`
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 detectModernBrowserVarInitCode = `var ${detectModernBrowserVarName}=false;`
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 @@ -440,13 +441,13 @@ function viteLegacyPlugin(options = {}) {
tags.push({
tag: 'script',
attrs: { type: 'module' },
children: detectDynamicImportVarInitCode,
children: detectModernBrowserVarInitCode,
injectTo: 'head'
})
tags.push({
tag: 'script',
attrs: { type: 'module' },
children: detectDynamicImportCode,
children: detectModernBrowserCode,
injectTo: 'head'
})
tags.push({
Expand Down Expand Up @@ -714,7 +715,7 @@ viteLegacyPlugin.default = viteLegacyPlugin
viteLegacyPlugin.cspHashes = [
createHash('sha256').update(safari10NoModuleFix).digest('base64'),
createHash('sha256').update(systemJSInlineCode).digest('base64'),
createHash('sha256').update(detectDynamicImportVarInitCode).digest('base64'),
createHash('sha256').update(detectDynamicImportCode).digest('base64'),
createHash('sha256').update(detectModernBrowserVarInitCode).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 @@ -274,11 +274,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 @@ -780,3 +780,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)
}
}
31 changes: 21 additions & 10 deletions packages/vite/src/node/plugins/asset.ts
Expand Up @@ -4,12 +4,16 @@ import fs, { promises as fsp } from 'fs'
import * as mrmime from 'mrmime'
import type { Plugin } from '../plugin'
import type { ResolvedConfig } from '../config'
import { cleanUrl } from '../utils'
import { cleanUrl, normalizePath, stringifyAsTemplateLiteral } from '../utils'
import {
assetFilenameWithBase,
isRelativeBase,
publicURLfromAsset
} from '../build'
import { FS_PREFIX } from '../constants'
import type { OutputOptions, PluginContext } from 'rollup'
import MagicString from 'magic-string'
import { createHash } from 'crypto'
import { normalizePath } from '../utils'

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

Expand Down Expand Up @@ -77,20 +81,21 @@ export function assetPlugin(config: ResolvedConfig): Plugin {

id = id.replace(urlRE, '$1').replace(/[\?&]$/, '')
const url = await fileToUrl(id, config, this)
return `export default ${JSON.stringify(url)}`
// Return a template string so we can use interpolation when replacing __VITE_ASSET__
return `export default ${stringifyAsTemplateLiteral(url)}`
bluwy marked this conversation as resolved.
Show resolved Hide resolved
},

renderChunk(code, chunk) {
let match: RegExpExecArray | null
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
patak-dev marked this conversation as resolved.
Show resolved Hide resolved

// Urls added in CSS that is imported in JS end up like
// var inlined = ".inlined{color:green;background:url(__VITE_ASSET__5aa0ddc0__)}\n";
// var inlined = `.inlined{color:green;background:url(__VITE_ASSET__5aa0ddc0__)}\n`;

// In both cases, the wrapping should already be fine
// In both cases, the wrapping allows us to use interpolation

while ((match = assetUrlRE.exec(code))) {
s = s || (s = new MagicString(code))
Expand All @@ -99,8 +104,14 @@ 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 @@ -271,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 @@ -348,7 +359,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
5 changes: 4 additions & 1 deletion packages/vite/src/node/plugins/assetImportMetaUrl.ts
Expand Up @@ -3,6 +3,8 @@ import MagicString from 'magic-string'
import path from 'path'
import { fileToUrl } from './asset'
import type { ResolvedConfig } from '../config'
import { stringifyAsTemplateLiteral } from '../utils'
import { preloadHelperId } from './importAnalysisBuild'
import { emptyString } from '../cleanString'

/**
Expand All @@ -21,6 +23,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 Expand Up @@ -75,7 +78,7 @@ export function assetImportMetaUrlPlugin(config: ResolvedConfig): Plugin {
s.overwrite(
index,
index + exp.length,
`new URL(${JSON.stringify(builtUrl)}, self.location)`,
`new URL(${stringifyAsTemplateLiteral(builtUrl)}, self.location)`,
{ contentOnly: true }
)
}
Expand Down
23 changes: 11 additions & 12 deletions packages/vite/src/node/plugins/css.ts
Expand Up @@ -12,7 +12,8 @@ import {
normalizePath,
processSrcSet,
parseRequest,
combineSourcemaps
combineSourcemaps,
stringifyAsTemplateLiteral
} from '../utils'
import type { Plugin } from '../plugin'
import type { ResolvedConfig } from '../config'
Expand Down Expand Up @@ -48,6 +49,7 @@ import { transform, formatMessages } from 'esbuild'
import { addToHTMLProxyTransformResult } from './html'
import { injectSourcesContent, getCodeWithSourcemap } from '../server/sourcemap'
import type { RawSourceMap } from '@ampproject/remapping'
import { assetFilenameWithBase, publicURLfromAsset } from '../build'

// const debug = createDebugger('vite:css')

Expand Down Expand Up @@ -180,7 +182,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 @@ -361,11 +363,12 @@ export function cssPostPlugin(config: ResolvedConfig): Plugin {
let code: string
if (usedRE.test(id)) {
if (inlined) {
code = `export default ${JSON.stringify(
code = `export default ${stringifyAsTemplateLiteral(
await minifyCSS(css, config)
)}`
} else {
code = modulesCode || `export default ${JSON.stringify(css)}`
code =
modulesCode || `export default ${stringifyAsTemplateLiteral(css)}`
}
} else {
code = `export default ''`
Expand Down Expand Up @@ -414,18 +417,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
// hoist them to the top of the CSS chunk per spec (#1845 and #6333)
Expand Down Expand Up @@ -464,7 +463,7 @@ export function cssPostPlugin(config: ResolvedConfig): Plugin {
const style = `__vite_style__`
const injectCode =
`var ${style} = document.createElement('style');` +
`${style}.innerHTML = ${JSON.stringify(chunkCSS)};` +
`${style}.innerHTML = ${stringifyAsTemplateLiteral(chunkCSS)};` +
`document.head.appendChild(${style});`
if (config.build.sourcemap) {
const s = new MagicString(code)
Expand Down