Skip to content

Commit e3beac8

Browse files
authored
feat(vue)!: separate include and exclude from api.options and add filter (#582)
1 parent 3c06203 commit e3beac8

File tree

3 files changed

+162
-90
lines changed

3 files changed

+162
-90
lines changed

packages/plugin-vue/package.json

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,5 +47,8 @@
4747
"source-map-js": "^1.2.1",
4848
"vite": "catalog:",
4949
"vue": "catalog:"
50+
},
51+
"dependencies": {
52+
"@rolldown/pluginutils": "1.0.0-beta.9"
5053
}
5154
}

packages/plugin-vue/src/index.ts

Lines changed: 155 additions & 90 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import type {
99
} from 'vue/compiler-sfc'
1010
import type * as _compiler from 'vue/compiler-sfc'
1111
import { computed, shallowRef } from 'vue'
12+
import { exactRegex } from '@rolldown/pluginutils'
1213
import { version } from '../package.json'
1314
import { resolveCompiler } from './compiler'
1415
import { parseVueRequest } from './utils/query'
@@ -162,7 +163,7 @@ export interface Options {
162163
customElement?: boolean | string | RegExp | (string | RegExp)[]
163164
}
164165

165-
export interface ResolvedOptions extends Options {
166+
export interface ResolvedOptions extends Omit<Options, 'include' | 'exclude'> {
166167
compiler: typeof _compiler
167168
root: string
168169
sourceMap: boolean
@@ -174,6 +175,14 @@ export interface ResolvedOptions extends Options {
174175
export interface Api {
175176
get options(): ResolvedOptions
176177
set options(value: ResolvedOptions)
178+
179+
get include(): string | RegExp | (string | RegExp)[] | undefined
180+
/** include cannot be updated after `options` hook is called */
181+
set include(value: string | RegExp | (string | RegExp)[] | undefined)
182+
get exclude(): string | RegExp | (string | RegExp)[] | undefined
183+
/** exclude cannot be updated after `options` hook is called */
184+
set exclude(value: string | RegExp | (string | RegExp)[] | undefined)
185+
177186
version: string
178187
}
179188

@@ -183,17 +192,19 @@ export default function vuePlugin(rawOptions: Options = {}): Plugin<Api> {
183192
const options = shallowRef<ResolvedOptions>({
184193
isProduction: process.env.NODE_ENV === 'production',
185194
compiler: null as any, // to be set in buildStart
186-
include: /\.vue$/,
187195
customElement: /\.ce\.vue$/,
188196
...rawOptions,
189197
root: process.cwd(),
190198
sourceMap: true,
191199
cssDevSourcemap: false,
192200
})
193-
194-
const filter = computed(() =>
195-
createFilter(options.value.include, options.value.exclude),
201+
const include = shallowRef<Exclude<Options['include'], undefined>>(
202+
rawOptions.include ?? /\.vue$/,
196203
)
204+
const exclude = shallowRef<Options['exclude']>(rawOptions.exclude)
205+
let optionsHookIsCalled = false
206+
207+
const filter = computed(() => createFilter(include.value, exclude.value))
197208
const customElementFilter = computed(() => {
198209
const customElement =
199210
options.value.features?.customElement || options.value.customElement
@@ -204,7 +215,7 @@ export default function vuePlugin(rawOptions: Options = {}): Plugin<Api> {
204215

205216
let transformCachedModule = false
206217

207-
return {
218+
const plugin: Plugin<Api> = {
208219
name: 'vite:vue',
209220

210221
api: {
@@ -214,6 +225,28 @@ export default function vuePlugin(rawOptions: Options = {}): Plugin<Api> {
214225
set options(value) {
215226
options.value = value
216227
},
228+
get include() {
229+
return include.value
230+
},
231+
set include(value) {
232+
if (optionsHookIsCalled) {
233+
throw new Error(
234+
'include cannot be updated after `options` hook is called',
235+
)
236+
}
237+
include.value = value
238+
},
239+
get exclude() {
240+
return exclude.value
241+
},
242+
set exclude(value) {
243+
if (optionsHookIsCalled) {
244+
throw new Error(
245+
'exclude cannot be updated after `options` hook is called',
246+
)
247+
}
248+
exclude.value = value
249+
},
217250
version,
218251
},
219252

@@ -317,6 +350,20 @@ export default function vuePlugin(rawOptions: Options = {}): Plugin<Api> {
317350
config.build.watch != null
318351
},
319352

353+
options() {
354+
type TransformObjectHook = Extract<
355+
typeof plugin.transform,
356+
{ filter?: unknown }
357+
>
358+
optionsHookIsCalled = true
359+
;(plugin.transform as TransformObjectHook).filter = {
360+
id: {
361+
include: [...ensureArray(include.value), /[?&]vue\b/],
362+
exclude: exclude.value,
363+
},
364+
}
365+
},
366+
320367
shouldTransformCachedModule({ id }) {
321368
if (transformCachedModule && parseVueRequest(id).query.vue) {
322369
return true
@@ -338,110 +385,128 @@ export default function vuePlugin(rawOptions: Options = {}): Plugin<Api> {
338385
}
339386
},
340387

341-
async resolveId(id) {
342-
// component export helper
343-
if (id === EXPORT_HELPER_ID) {
344-
return id
345-
}
346-
// serve sub-part requests (*?vue) as virtual modules
347-
if (parseVueRequest(id).query.vue) {
348-
return id
349-
}
388+
resolveId: {
389+
filter: {
390+
id: [exactRegex(EXPORT_HELPER_ID), /[?&]vue\b/],
391+
},
392+
handler(id) {
393+
// component export helper
394+
if (id === EXPORT_HELPER_ID) {
395+
return id
396+
}
397+
// serve sub-part requests (*?vue) as virtual modules
398+
if (parseVueRequest(id).query.vue) {
399+
return id
400+
}
401+
},
350402
},
351403

352-
load(id, opt) {
353-
if (id === EXPORT_HELPER_ID) {
354-
return helperCode
355-
}
404+
load: {
405+
filter: {
406+
id: [exactRegex(EXPORT_HELPER_ID), /[?&]vue\b/],
407+
},
408+
handler(id, opt) {
409+
if (id === EXPORT_HELPER_ID) {
410+
return helperCode
411+
}
356412

357-
const ssr = opt?.ssr === true
413+
const ssr = opt?.ssr === true
358414

359-
const { filename, query } = parseVueRequest(id)
415+
const { filename, query } = parseVueRequest(id)
360416

361-
// select corresponding block for sub-part virtual modules
362-
if (query.vue) {
363-
if (query.src) {
364-
return fs.readFileSync(filename, 'utf-8')
365-
}
366-
const descriptor = getDescriptor(filename, options.value)!
367-
let block: SFCBlock | null | undefined
368-
if (query.type === 'script') {
369-
// handle <script> + <script setup> merge via compileScript()
370-
block = resolveScript(
371-
descriptor,
372-
options.value,
373-
ssr,
374-
customElementFilter.value(filename),
375-
)
376-
} else if (query.type === 'template') {
377-
block = descriptor.template!
378-
} else if (query.type === 'style') {
379-
block = descriptor.styles[query.index!]
380-
} else if (query.index != null) {
381-
block = descriptor.customBlocks[query.index]
382-
}
383-
if (block) {
384-
return {
385-
code: block.content,
386-
map: block.map as any,
417+
// select corresponding block for sub-part virtual modules
418+
if (query.vue) {
419+
if (query.src) {
420+
return fs.readFileSync(filename, 'utf-8')
421+
}
422+
const descriptor = getDescriptor(filename, options.value)!
423+
let block: SFCBlock | null | undefined
424+
if (query.type === 'script') {
425+
// handle <script> + <script setup> merge via compileScript()
426+
block = resolveScript(
427+
descriptor,
428+
options.value,
429+
ssr,
430+
customElementFilter.value(filename),
431+
)
432+
} else if (query.type === 'template') {
433+
block = descriptor.template!
434+
} else if (query.type === 'style') {
435+
block = descriptor.styles[query.index!]
436+
} else if (query.index != null) {
437+
block = descriptor.customBlocks[query.index]
438+
}
439+
if (block) {
440+
return {
441+
code: block.content,
442+
map: block.map as any,
443+
}
387444
}
388445
}
389-
}
446+
},
390447
},
391448

392-
transform(code, id, opt) {
393-
const ssr = opt?.ssr === true
394-
const { filename, query } = parseVueRequest(id)
449+
transform: {
450+
// filter is set in options() hook
451+
handler(code, id, opt) {
452+
const ssr = opt?.ssr === true
453+
const { filename, query } = parseVueRequest(id)
395454

396-
if (query.raw || query.url) {
397-
return
398-
}
399-
400-
if (!filter.value(filename) && !query.vue) {
401-
return
402-
}
455+
if (query.raw || query.url) {
456+
return
457+
}
403458

404-
if (!query.vue) {
405-
// main request
406-
return transformMain(
407-
code,
408-
filename,
409-
options.value,
410-
this,
411-
ssr,
412-
customElementFilter.value(filename),
413-
)
414-
} else {
415-
// sub block request
416-
const descriptor: ExtendedSFCDescriptor = query.src
417-
? getSrcDescriptor(filename, query) ||
418-
getTempSrcDescriptor(filename, query)
419-
: getDescriptor(filename, options.value)!
420-
421-
if (query.src) {
422-
this.addWatchFile(filename)
459+
if (!filter.value(filename) && !query.vue) {
460+
return
423461
}
424462

425-
if (query.type === 'template') {
426-
return transformTemplateAsModule(
463+
if (!query.vue) {
464+
// main request
465+
return transformMain(
427466
code,
428-
descriptor,
467+
filename,
429468
options.value,
430469
this,
431470
ssr,
432471
customElementFilter.value(filename),
433472
)
434-
} else if (query.type === 'style') {
435-
return transformStyle(
436-
code,
437-
descriptor,
438-
Number(query.index || 0),
439-
options.value,
440-
this,
441-
filename,
442-
)
473+
} else {
474+
// sub block request
475+
const descriptor: ExtendedSFCDescriptor = query.src
476+
? getSrcDescriptor(filename, query) ||
477+
getTempSrcDescriptor(filename, query)
478+
: getDescriptor(filename, options.value)!
479+
480+
if (query.src) {
481+
this.addWatchFile(filename)
482+
}
483+
484+
if (query.type === 'template') {
485+
return transformTemplateAsModule(
486+
code,
487+
descriptor,
488+
options.value,
489+
this,
490+
ssr,
491+
customElementFilter.value(filename),
492+
)
493+
} else if (query.type === 'style') {
494+
return transformStyle(
495+
code,
496+
descriptor,
497+
Number(query.index || 0),
498+
options.value,
499+
this,
500+
filename,
501+
)
502+
}
443503
}
444-
}
504+
},
445505
},
446506
}
507+
return plugin
508+
}
509+
510+
function ensureArray<T>(value: T | T[]): T[] {
511+
return Array.isArray(value) ? value : [value]
447512
}

pnpm-lock.yaml

Lines changed: 4 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)