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: support css?url to return asset path #11084

Closed
wants to merge 4 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions packages/vite/src/node/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,8 @@ export const OPTIMIZABLE_ENTRY_RE = /\.[cm]?[jt]s$/

export const SPECIAL_QUERY_RE = /[?&](?:worker|sharedworker|raw|url)\b/

export const URL_RE = /(\?|&)url(?:&|$)/

/**
* Prefix for resolved fs paths, since windows paths may not be valid as URLs.
*/
Expand Down
6 changes: 6 additions & 0 deletions packages/vite/src/node/plugins/asset.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import type { Plugin } from '../plugin'
import type { ResolvedConfig } from '../config'
import { cleanUrl, getHash, joinUrlSegments, normalizePath } from '../utils'
import { FS_PREFIX } from '../constants'
import { isCSSRequest } from './css'

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

Expand Down Expand Up @@ -151,6 +152,11 @@ export function assetPlugin(config: ResolvedConfig): Plugin {
return
}

if (isCSSRequest(id) && urlRE.test(id)) {
// leave css?url to css plugin
return
}

// raw requests, read from disk
if (rawRE.test(id)) {
const file = checkPublicFile(id, config) || cleanUrl(id)
Expand Down
62 changes: 60 additions & 2 deletions packages/vite/src/node/plugins/css.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ import { getCodeWithSourcemap, injectSourcesContent } from '../server/sourcemap'
import type { ModuleNode } from '../server/moduleGraph'
import type { ResolveFn, ViteDevServer } from '../'
import { toOutputFilePathInCss } from '../build'
import { CLIENT_PUBLIC_PATH, SPECIAL_QUERY_RE } from '../constants'
import { CLIENT_PUBLIC_PATH, SPECIAL_QUERY_RE, URL_RE } from '../constants'
import type { ResolvedConfig } from '../config'
import type { Plugin } from '../plugin'
import {
Expand Down Expand Up @@ -114,6 +114,9 @@ const inlineCSSRE = /(?:\?|&)inline-css\b/
const usedRE = /(?:\?|&)used\b/
const varRE = /^var\(/i

const viteCSSURLMarker = '__VITE_CSS_URL_'
const viteCSSURLMarkerRE = /__VITE_CSS_URL_([^"]+)/

const cssBundleName = 'style.css'

const enum PreprocessLang {
Expand Down Expand Up @@ -153,6 +156,21 @@ export const removedPureCssFilesCache = new WeakMap<
Map<string, RenderedChunk>
>()

export const pureCssIdMapAssetUrl = new Map<string, string>()
export const removedDynamicCssUrlsCache = new Map<string, string>()

export function getDynamicCssAssetUrl(importUrl: string): string | undefined {
const pureCssId = removedDynamicCssUrlsCache.get(importUrl)
return pureCssId ? pureCssIdMapAssetUrl.get(pureCssId) : undefined
}

/**
* remove ?url from module id
*/
export function getPureCssId(id: string, root: string): string {
return path.relative(root, id.replace(/\?.+$/, ''))
}

const postcssConfigCache: Record<
string,
WeakMap<ResolvedConfig, PostCSSConfigResult | null>
Expand Down Expand Up @@ -182,6 +200,23 @@ export function cssPlugin(config: ResolvedConfig): Plugin {
server = _server
},

load(id) {
if (isCSSRequest(id) && URL_RE.test(id)) {
const pureCssId = getPureCssId(id, config.root)

if (config.command === 'serve') {
return `export default "${config.base}${pureCssId}"`
}

this.emitFile({
type: 'chunk',
id: pureCssId
})

return `export default "${viteCSSURLMarker}${pureCssId}"`
}
},

buildStart() {
// Ensure a new cache for every build (i.e. rebuilding in watch mode)
moduleCache = new Map<string, Record<string, string>>()
Expand Down Expand Up @@ -447,6 +482,16 @@ export function cssPostPlugin(config: ResolvedConfig): Plugin {
},

async renderChunk(code, chunk, opts) {
if (
chunk.facadeModuleId &&
isCSSRequest(chunk.facadeModuleId) &&
URL_RE.test(chunk.facadeModuleId)
) {
// mark css url file
// connect fileName with pureCssId
const pureCssId = getPureCssId(chunk.facadeModuleId, config.root)
removedDynamicCssUrlsCache.set(chunk.fileName, pureCssId)
}
let chunkCSS = ''
let isPureCssChunk = true
const ids = Object.keys(chunk.modules)
Expand Down Expand Up @@ -557,7 +602,15 @@ export function cssPostPlugin(config: ResolvedConfig): Plugin {
generatedAssets
.get(config)!
.set(referenceId, { originalName, isEntry })
chunk.viteMetadata.importedCss.add(this.getFileName(referenceId))
const fileName = this.getFileName(referenceId)
if (chunk.facadeModuleId) {
// connect css id with css file name
pureCssIdMapAssetUrl.set(
getPureCssId(chunk.facadeModuleId, config.root),
config.base + fileName
)
}
chunk.viteMetadata.importedCss.add(fileName)
} else if (!config.build.ssr) {
// legacy build and inline css

Expand Down Expand Up @@ -667,6 +720,11 @@ export function cssPostPlugin(config: ResolvedConfig): Plugin {
// remove css import while preserving source map location
(m) => `/* empty css ${''.padEnd(m.length - 15)}*/`
)
chunk.code = chunk.code.replace(
viteCSSURLMarkerRE,
// replace css module id with css file name
(_, matchId) => pureCssIdMapAssetUrl.get(matchId) || matchId
)
}
}
const removedPureCssFiles = removedPureCssFilesCache.get(config)!
Expand Down
23 changes: 21 additions & 2 deletions packages/vite/src/node/plugins/importAnalysisBuild.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,12 @@ import type { ResolvedConfig } from '../config'
import { toOutputFilePathInJS } from '../build'
import { genSourceMapUrl } from '../server/sourcemap'
import { getDepsOptimizer, optimizedDepNeedsInterop } from '../optimizer'
import { isCSSRequest, removedPureCssFilesCache } from './css'
import { URL_RE } from '../constants'
import {
getDynamicCssAssetUrl,
isCSSRequest,
removedPureCssFilesCache
} from './css'
import { interopNamedImports } from './importAnalysis'

/**
Expand Down Expand Up @@ -291,7 +296,11 @@ export function buildImportAnalysisPlugin(config: ResolvedConfig): Plugin {
str().remove(end + 1, expEnd)
}

if (isDynamicImport && insertPreload) {
// css?url should not wrapped with preloadMethod
const isCssUrl =
!!specifier && isCSSRequest(specifier) && URL_RE.test(specifier)

if (isDynamicImport && insertPreload && !isCssUrl) {
needPreloadHelper = true
str().prependLeft(expStart, `${preloadMethod}(() => `)
str().appendRight(
Expand Down Expand Up @@ -468,6 +477,16 @@ export function buildImportAnalysisPlugin(config: ResolvedConfig): Plugin {
url
)

// change dynamic import to local Promise
const cssAssetUrl = getDynamicCssAssetUrl(normalizedFile)
if (cssAssetUrl) {
s.update(
expStart,
expEnd,
`Promise.resolve({default:"${cssAssetUrl}"})`
)
}

const ownerFilename = chunk.fileName
// literal import - trace direct imports and add to deps
const analyzed: Set<string> = new Set<string>()
Expand Down