Skip to content

Commit 1819f04

Browse files
authored
feat(vite): add virtualModulePrefix option to support for custom virtual module prefix (#5008)
> Co-authored-by: liujiayii <[email protected]>
1 parent 3745284 commit 1819f04

File tree

11 files changed

+148
-94
lines changed

11 files changed

+148
-94
lines changed

examples/vite-vue3/uno.config.ts

Lines changed: 20 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,6 @@ import { resolve } from 'node:path'
22
import { FileSystemIconLoader } from '@iconify/utils/lib/loader/node-loaders'
33
import presetAttributify from '@unocss/preset-attributify'
44
import presetIcons from '@unocss/preset-icons'
5-
import presetWebFonts from '@unocss/preset-web-fonts'
6-
import { createLocalFontProcessor } from '@unocss/preset-web-fonts/local'
75
import presetWind3 from '@unocss/preset-wind3'
86
import { defineConfig } from '@unocss/vite'
97

@@ -25,25 +23,25 @@ export default defineConfig({
2523
custom: FileSystemIconLoader(iconDirectory),
2624
},
2725
}),
28-
presetWebFonts({
29-
provider: 'google',
30-
fonts: {
31-
sans: 'Roboto',
32-
mono: ['Fira Code', 'Fira Mono:400,700'],
33-
lobster: 'Lobster',
34-
lato: [
35-
{
36-
name: 'Lato',
37-
weights: ['400', '700'],
38-
italic: true,
39-
},
40-
{
41-
name: 'sans-serif',
42-
provider: 'none',
43-
},
44-
],
45-
},
46-
processors: createLocalFontProcessor(),
47-
}),
26+
// presetWebFonts({
27+
// provider: 'google',
28+
// fonts: {
29+
// sans: 'Roboto',
30+
// mono: ['Fira Code', 'Fira Mono:400,700'],
31+
// lobster: 'Lobster',
32+
// lato: [
33+
// {
34+
// name: 'Lato',
35+
// weights: ['400', '700'],
36+
// italic: true,
37+
// },
38+
// {
39+
// name: 'sans-serif',
40+
// provider: 'none',
41+
// },
42+
// ],
43+
// },
44+
// processors: createLocalFontProcessor(),
45+
// }),
4846
],
4947
})

examples/vite-vue3/vite.config.ts

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,15 @@
11
// import legacy from '@vitejs/plugin-legacy'
22
import Vue from '@vitejs/plugin-vue'
3-
import UnoCSS from 'unocss/vite'
43
import { defineConfig } from 'vite'
4+
import UnoCSS from '../../packages-integrations/vite/src/index'
55

66
// https://vitejs.dev/config/
77
export default defineConfig({
88
plugins: [
99
Vue(),
10-
UnoCSS(),
10+
UnoCSS({
11+
virtualModulePrefix: 'custom_vue3',
12+
}) as any,
1113
// legacy(),
1214
],
1315
})

packages-engine/core/src/types.ts

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -688,12 +688,20 @@ export interface UserOnlyOptions<Theme extends object = object> {
688688
* @default 'build'
689689
*/
690690
envMode?: 'dev' | 'build'
691+
691692
/**
692693
* legacy.renderModernChunks need to be consistent with @vitejs/plugin-legacy
693694
*/
694695
legacy?: {
695696
renderModernChunks: boolean
696697
}
698+
699+
/**
700+
* Custom prefix for virtual modules
701+
*
702+
* @default '__uno'
703+
*/
704+
virtualModulePrefix?: string
697705
}
698706

699707
/**
@@ -749,6 +757,11 @@ export interface UnocssPluginContext<Config extends UserConfig = UserConfig> {
749757
root: string
750758
updateRoot: (root: string) => Promise<LoadConfigResult<Config>>
751759
getConfigFileList: () => string[]
760+
761+
/**
762+
* Get regexes to match virtual module ids
763+
*/
764+
getVMPRegexes: () => Promise<{ prefix: string, RESOLVED_ID_WITH_QUERY_RE: RegExp, RESOLVED_ID_RE: RegExp }>
752765
}
753766

754767
export interface SourceMap {

packages-integrations/astro/src/index.ts

Lines changed: 17 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,9 @@
1-
import type { UserConfigDefaults } from '@unocss/core'
2-
import type { VitePluginConfig } from '@unocss/vite'
1+
import type { UnocssPluginContext, UserConfigDefaults } from '@unocss/core'
2+
import type { UnocssVitePluginAPI, VitePluginConfig } from '@unocss/vite'
33
import type { AstroIntegration } from 'astro'
44
import type { Plugin } from 'vite'
55
import { join, resolve } from 'node:path'
66
import { fileURLToPath } from 'node:url'
7-
import { RESOLVED_ID_RE } from '#integration/constants'
87
import VitePlugin from '@unocss/vite'
98
import { normalizePath } from 'vite'
109

@@ -17,14 +16,23 @@ interface AstroVitePluginOptions {
1716
function AstroVitePlugin(options: AstroVitePluginOptions): Plugin {
1817
const { injects } = options
1918
let root: string
19+
let ctx: UnocssPluginContext<VitePluginConfig>
2020

2121
return {
2222
name: 'unocss:astro',
2323
enforce: 'pre',
2424
configResolved(config) {
2525
root = config.root
26+
27+
const api = config.plugins.find(i => i.name === 'unocss:api')?.api as UnocssVitePluginAPI | undefined
28+
if (!api) {
29+
throw new Error('[@unocss/astro] Unable to find UnoCSS Vite plugin API.')
30+
}
31+
ctx = api.getContext()
2632
},
2733
async resolveId(id, importer) {
34+
const { RESOLVED_ID_RE } = await ctx.getVMPRegexes()
35+
2836
if (RESOLVED_ID_RE.test(id)) {
2937
// https://github.com/withastro/astro/blob/087270c61fd5c91ddd37db5c8fd93a8a0ef41f94/packages/astro/src/core/util.ts#L91-L93
3038
// Align data-astro-dev-id with data-vite-dev-id to fix https://github.com/unocss/unocss/issues/2513
@@ -99,9 +107,12 @@ export default function UnoCSSAstroIntegration<Theme extends object>(
99107

100108
updateConfig({
101109
vite: {
102-
plugins: [AstroVitePlugin({
103-
injects,
104-
}), ...VitePlugin(options, defaults)],
110+
plugins: [
111+
AstroVitePlugin({
112+
injects,
113+
}) as any,
114+
...VitePlugin(options, defaults),
115+
],
105116
},
106117
})
107118

packages-integrations/vite/src/modes/global/build.ts

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import type { GenerateResult, UnocssPluginContext } from '@unocss/core'
22
import type { Plugin, ResolvedConfig, Rollup } from 'vite'
33
import type { VitePluginConfig } from '../../types'
44
import { isAbsolute, resolve } from 'node:path'
5-
import { LAYER_MARK_ALL, RESOLVED_ID_RE } from '#integration/constants'
5+
import { LAYER_MARK_ALL } from '#integration/constants'
66
import { setupContentExtractor } from '#integration/content'
77
import { getLayerPlaceholder, resolveId, resolveLayer } from '#integration/layers'
88
import { applyTransformers } from '#integration/transformers'
@@ -87,10 +87,10 @@ export function GlobalModeBuildPlugin(ctx: UnocssPluginContext<VitePluginConfig>
8787
tasks.push(extract(code, filename))
8888
},
8989
},
90-
resolveId(id, importer) {
91-
const entry = resolveId(id, importer)
90+
async resolveId(id, importer) {
91+
const entry = await resolveId(ctx, id, importer)
9292
if (entry) {
93-
const layer = resolveLayer(entry)
93+
const layer = await resolveLayer(ctx, entry)
9494
if (layer) {
9595
if (importer)
9696
unocssImporters.add(importer)
@@ -104,8 +104,8 @@ export function GlobalModeBuildPlugin(ctx: UnocssPluginContext<VitePluginConfig>
104104
return entry
105105
}
106106
},
107-
load(id) {
108-
const layer = resolveLayer(getPath(id))
107+
async load(id) {
108+
const layer = await resolveLayer(ctx, getPath(id))
109109
if (layer) {
110110
if (!vfsLayers.has(layer)) {
111111
this.error(`[unocss] layer ${JSON.stringify(id)} is imported but not being resolved before, it might be an internal bug of UnoCSS`)
@@ -171,7 +171,8 @@ export function GlobalModeBuildPlugin(ctx: UnocssPluginContext<VitePluginConfig>
171171
{
172172
name: 'unocss:global:build:generate',
173173
apply: 'build',
174-
async renderChunk(code, chunk, options) {
174+
async renderChunk(_, chunk, options) {
175+
const { RESOLVED_ID_RE } = await ctx.getVMPRegexes()
175176
const entryModules = Object.keys(chunk.modules).filter(id => RESOLVED_ID_RE.test(id))
176177
if (!entryModules.length)
177178
return null

packages-integrations/vite/src/modes/global/dev.ts

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -40,8 +40,8 @@ export function GlobalModeDevPlugin(ctx: UnocssPluginContext): Plugin[] {
4040
} while (true)
4141

4242
const css = layer === LAYER_MARK_ALL
43-
? result.getLayers(undefined, Array.from(entries)
44-
.map(i => resolveLayer(i)).filter((i): i is string => !!i))
43+
? result.getLayers(undefined, await Promise.all(Array.from(entries)
44+
.map(i => resolveLayer(ctx, i))).then(layers => layers.filter((i): i is string => !!i)))
4545
: result.getLayer(layer)
4646
const hash = getHash(css || '', HASH_LENGTH)
4747
lastServedHash.set(layer, hash)
@@ -157,8 +157,8 @@ export function GlobalModeDevPlugin(ctx: UnocssPluginContext): Plugin[] {
157157
tasks.push(extract(code, filename))
158158
},
159159
},
160-
resolveId(id) {
161-
const entry = resolveId(id)
160+
async resolveId(id) {
161+
const entry = await resolveId(ctx, id)
162162
if (entry) {
163163
resolved = true
164164
clearWarnTimer()
@@ -167,7 +167,7 @@ export function GlobalModeDevPlugin(ctx: UnocssPluginContext): Plugin[] {
167167
}
168168
},
169169
async load(id) {
170-
const layer = resolveLayer(getPath(id))
170+
const layer = await resolveLayer(ctx, getPath(id))
171171
if (!layer)
172172
return null
173173

@@ -189,7 +189,7 @@ export function GlobalModeDevPlugin(ctx: UnocssPluginContext): Plugin[] {
189189
},
190190
enforce: 'post',
191191
async transform(code, id) {
192-
const layer = resolveLayer(getPath(id))
192+
const layer = await resolveLayer(ctx, getPath(id))
193193

194194
// inject css modules to send callback on css load
195195
if (layer && code.includes('import.meta.hot')) {

packages-integrations/vite/src/modes/per-module.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -35,13 +35,13 @@ export function PerModuleModePlugin(ctx: UnocssPluginContext): Plugin[] {
3535
{
3636
name: 'unocss:module-scope:pre',
3737
enforce: 'pre',
38-
resolveId(id) {
39-
const entry = resolveId(id)
38+
async resolveId(id) {
39+
const entry = await resolveId(ctx, id)
4040
if (entry)
4141
return entry
4242
},
4343
async load(id) {
44-
const layer = resolveLayer(getPath(id))
44+
const layer = await resolveLayer(ctx, getPath(id))
4545
if (!layer)
4646
return null
4747

packages-integrations/webpack/src/unplugin.ts

Lines changed: 41 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import type { ResolvedUnpluginOptions, UnpluginOptions } from 'unplugin'
33
import type { WebpackPluginOptions } from '.'
44
import { isAbsolute, normalize } from 'node:path'
55
import process from 'node:process'
6-
import { LAYER_MARK_ALL, RESOLVED_ID_RE } from '#integration/constants'
6+
import { LAYER_MARK_ALL } from '#integration/constants'
77
import { setupContentExtractor } from '#integration/content'
88
import { createContext } from '#integration/context'
99
import { getHash } from '#integration/hash'
@@ -47,10 +47,11 @@ export function unplugin<Theme extends object>(configOrPath?: WebpackPluginOptio
4747
const plugin = {
4848
name: 'unocss:webpack',
4949
enforce: 'pre',
50-
transformInclude(id) {
51-
return filter('', id) && !id.endsWith('.html') && !RESOLVED_ID_RE.test(id)
52-
},
5350
async transform(code, id) {
51+
const { RESOLVED_ID_RE } = await ctx.getVMPRegexes()
52+
if (RESOLVED_ID_RE.test(id) || !filter('', id) || id.endsWith('.html'))
53+
return
54+
5455
const result = await applyTransformers(ctx, code, id, 'pre')
5556
if (isCssId(id))
5657
return result
@@ -60,8 +61,8 @@ export function unplugin<Theme extends object>(configOrPath?: WebpackPluginOptio
6061
tasks.push(extract(result.code, id))
6162
return result
6263
},
63-
resolveId(id) {
64-
const entry = resolveId(id)
64+
async resolveId(id) {
65+
const entry = await resolveId(ctx, id)
6566
if (entry === id)
6667
return
6768
if (entry) {
@@ -74,16 +75,14 @@ export function unplugin<Theme extends object>(configOrPath?: WebpackPluginOptio
7475
return entry + query
7576
}
7677
},
77-
loadInclude(id) {
78-
const layer = getLayer(id)
79-
return !!layer
80-
},
8178
// serve the placeholders in virtual module
82-
load(id) {
83-
const layer = getLayer(id)
79+
async load(id) {
80+
const layer = await getLayer(ctx, id)
81+
if (!layer)
82+
return
83+
8484
const hash = hashes.get(id)
85-
if (layer)
86-
return (hash ? getHashPlaceholder(hash) : '') + getLayerPlaceholder(layer)
85+
return (hash ? getHashPlaceholder(hash) : '') + getLayerPlaceholder(layer)
8786
},
8887
webpack(compiler) {
8988
compiler.hooks.beforeCompile.tapPromise(PLUGIN_NAME, async () => {
@@ -110,6 +109,9 @@ export function unplugin<Theme extends object>(configOrPath?: WebpackPluginOptio
110109

111110
await flushTasks()
112111
const result = await ctx.uno.generate(tokens, { minify: true })
112+
const resolvedLayers = (await Promise.all(Array.from(entries)
113+
.map(i => resolveLayer(ctx, i))))
114+
.filter((i): i is string => !!i)
113115

114116
for (const file of files) {
115117
// https://github.com/unocss/unocss/pull/1428
@@ -123,8 +125,7 @@ export function unplugin<Theme extends object>(configOrPath?: WebpackPluginOptio
123125
code = code.replace(LAYER_PLACEHOLDER_RE, (_, layer, escapeView) => {
124126
replaced = true
125127
const css = layer.trim() === LAYER_MARK_ALL
126-
? result.getLayers(undefined, Array.from(entries)
127-
.map(i => resolveLayer(i)).filter((i): i is string => !!i))
128+
? result.getLayers(undefined, resolvedLayers)
128129
: (result.getLayer(layer) || '')
129130

130131
escapeCss = escapeCss ?? getCssEscaperForJsContent(escapeView.trim())
@@ -161,36 +162,38 @@ export function unplugin<Theme extends object>(configOrPath?: WebpackPluginOptio
161162
virtualModules = Array.from(plugin.__vfsModules)
162163
}
163164

164-
virtualModules
165-
.forEach((id) => {
166-
let path = decodeURIComponent(id.startsWith(plugin.__virtualModulePrefix) ? id.slice(plugin.__virtualModulePrefix.length) : id)
167-
// unplugin changes the id in the `load` hook, follow it
168-
// https://github.com/unjs/unplugin/pull/145/files#diff-2b106437404a793ee5b8f3823344656ce880f698d3d8cb6a7cf785e36fb4bf5cR27
169-
path = normalizeAbsolutePath(path)
170-
const layer = resolveLayer(path)
171-
if (!layer)
172-
return
173-
const code = layer === LAYER_MARK_ALL
174-
? result.getLayers(undefined, Array.from(entries)
175-
.map(i => resolveLayer(i)).filter((i): i is string => !!i))
176-
: (result.getLayer(layer) || '')
177-
178-
const hash = getHash(code)
179-
hashes.set(path, hash)
180-
plugin.__vfs.writeModule(id, code)
181-
})
165+
const resolvedLayers = (await Promise.all(Array.from(entries)
166+
.map(i => resolveLayer(ctx, i))))
167+
.filter((i): i is string => !!i)
168+
169+
for (const id of virtualModules) {
170+
let path = decodeURIComponent(id.startsWith(plugin.__virtualModulePrefix) ? id.slice(plugin.__virtualModulePrefix.length) : id)
171+
// unplugin changes the id in the `load` hook, follow it
172+
// https://github.com/unjs/unplugin/pull/145/files#diff-2b106437404a793ee5b8f3823344656ce880f698d3d8cb6a7cf785e36fb4bf5cR27
173+
path = normalizeAbsolutePath(path)
174+
const layer = await resolveLayer(ctx, path)
175+
if (!layer)
176+
continue
177+
const code = layer === LAYER_MARK_ALL
178+
? result.getLayers(undefined, resolvedLayers)
179+
: (result.getLayer(layer) || '')
180+
181+
const hash = getHash(code)
182+
hashes.set(path, hash)
183+
plugin.__vfs.writeModule(id, code)
184+
}
182185
}
183186

184187
return plugin
185188
})
186189
}
187190

188-
function getLayer(id: string) {
189-
let layer = resolveLayer(getPath(id))
191+
async function getLayer(ctx: any, id: string) {
192+
let layer = await resolveLayer(ctx, getPath(id))
190193
if (!layer) {
191-
const entry = resolveId(id)
194+
const entry = await resolveId(ctx, id)
192195
if (entry)
193-
layer = resolveLayer(entry)
196+
layer = await resolveLayer(ctx, entry)
194197
}
195198
return layer
196199
}

0 commit comments

Comments
 (0)