diff --git a/src/core/moduleConstants.ts b/src/core/moduleConstants.ts index 17aacae52..c7dfa03d0 100644 --- a/src/core/moduleConstants.ts +++ b/src/core/moduleConstants.ts @@ -38,3 +38,5 @@ export const routeBlockQueryRE = /\?vue&type=route/ export function asVirtualId(id: string) { return VIRTUAL_PREFIX + id } + +export const DEFINE_PAGE_QUERY_RE = /\?.*\bdefinePage\&vue\b/ diff --git a/src/data-loaders/auto-exports.ts b/src/data-loaders/auto-exports.ts index b0e8c6547..2462e3b3b 100644 --- a/src/data-loaders/auto-exports.ts +++ b/src/data-loaders/auto-exports.ts @@ -3,7 +3,7 @@ import type { Plugin } from 'vite' import MagicString from 'magic-string' import { findStaticImports, parseStaticImport } from 'mlly' import { resolve } from 'pathe' -import { type UnpluginOptions } from 'unplugin' +import { StringFilter, type UnpluginOptions } from 'unplugin' export function extractLoadersToExport( code: string, @@ -41,10 +41,9 @@ const PLUGIN_NAME = 'unplugin-vue-router:data-loaders-auto-export' */ export interface AutoExportLoadersOptions { /** - * Filter page components to apply the auto-export (defined with `createFilter()` from `unplugin-utils`) or array - * of globs. + * Filter page components to apply the auto-export. Passed to `transform.filter.id`. */ - filterPageComponents: ((id: string) => boolean) | string[] + transformFilter: StringFilter /** * Globs to match the paths of the loaders. @@ -66,28 +65,21 @@ export interface AutoExportLoadersOptions { */ export function AutoExportLoaders({ - filterPageComponents: filterPagesOrGlobs, + transformFilter, loadersPathsGlobs, root = process.cwd(), }: AutoExportLoadersOptions): Plugin { const filterPaths = createFilter(loadersPathsGlobs) - const filterPageComponents = - typeof filterPagesOrGlobs === 'function' - ? filterPagesOrGlobs - : createFilter(filterPagesOrGlobs) return { name: PLUGIN_NAME, transform: { order: 'post', - handler(code, id) { - // strip query to also match .vue?vue&lang=ts etc - const queryIndex = id.indexOf('?') - const idWithoutQuery = queryIndex >= 0 ? id.slice(0, queryIndex) : id - if (!filterPageComponents(idWithoutQuery)) { - return - } + filter: { + id: transformFilter, + }, + handler(code) { const loadersToExports = extractLoadersToExport(code, filterPaths, root) if (loadersToExports.length <= 0) return diff --git a/src/index.ts b/src/index.ts index d51df9472..623d5304b 100644 --- a/src/index.ts +++ b/src/index.ts @@ -8,6 +8,8 @@ import { routeBlockQueryRE, ROUTE_BLOCK_ID, ROUTES_LAST_LOAD_TIME, + VIRTUAL_PREFIX, + DEFINE_PAGE_QUERY_RE, } from './core/moduleConstants' import { Options, @@ -16,7 +18,6 @@ import { mergeAllExtensions, } from './options' import { createViteContext } from './core/vite' -import { createFilter } from 'unplugin-utils' import { join } from 'pathe' import { appendExtensionListToPattern } from './core/utils' import { MACRO_DEFINE_PAGE_QUERY } from './core/definePage' @@ -50,17 +51,8 @@ export default createUnplugin((opt = {}, _meta) => { mergeAllExtensions(options) ) - // this is a larger filter that includes a bit too many files - // the RouteFolderWatcher will filter it down to the actual files - const filterPageComponents = createFilter( - [ - ...options.routesFolder.flatMap((routeOption) => - pageFilePattern.map((pattern) => join(routeOption.src, pattern)) - ), - // importing the definePage block - /\?.*\bdefinePage\&vue\b/, - ], - options.exclude + const IDS_TO_INCLUDE = options.routesFolder.flatMap((routeOption) => + pageFilePattern.map((pattern) => join(routeOption.src, pattern)) ) const plugins: UnpluginOptions[] = [ @@ -68,25 +60,28 @@ export default createUnplugin((opt = {}, _meta) => { name: 'unplugin-vue-router', enforce: 'pre', - resolveId(id) { - if ( + resolveId: { + filter: { + id: { + include: [ + new RegExp(`^${MODULE_VUE_ROUTER_AUTO}$`), + new RegExp(`^${MODULE_ROUTES_PATH}$`), + routeBlockQueryRE, + ], + }, + }, + handler(id) { + // vue-router/auto // vue-router/auto-routes - id === MODULE_ROUTES_PATH || - // NOTE: it wasn't possible to override or add new exports to vue-router - // so we need to override it with a different package name - id === MODULE_VUE_ROUTER_AUTO - ) { - // virtual module - return asVirtualId(id) - } - - // this allows us to skip the route block module as a whole since we already parse it - if (routeBlockQueryRE.test(id)) { - return ROUTE_BLOCK_ID - } + if (id === MODULE_ROUTES_PATH || id === MODULE_VUE_ROUTER_AUTO) { + // must be a virtual module + return asVirtualId(id) + } - // nothing to do, just for TS - return + // otherwisse we know it matched the routeBlockQueryRE + // this allows us to skip the route block module as a whole since we already parse it + return ROUTE_BLOCK_ID + }, }, buildStart() { @@ -97,54 +92,57 @@ export default createUnplugin((opt = {}, _meta) => { ctx.stopWatcher() }, - // we only need to transform page components - transformInclude(id) { - // console.log('filtering ' + id, filterPageComponents(id) ? '✅' : '❌') - return filterPageComponents(id) - }, - - transform(code, id) { - // console.log('👋 Transforming', id) - // remove the `definePage()` from the file or isolate it - return ctx.definePageTransform(code, id) - }, - - // loadInclude is necessary for webpack - loadInclude(id) { - if (id === ROUTE_BLOCK_ID) return true - const resolvedId = getVirtualId(id) - return ( - resolvedId === MODULE_ROUTES_PATH || - resolvedId === MODULE_VUE_ROUTER_AUTO - ) + transform: { + filter: { + id: { + include: [...IDS_TO_INCLUDE, DEFINE_PAGE_QUERY_RE], + exclude: options.exclude, + }, + }, + handler(code, id) { + // remove the `definePage()` from the file or isolate it + return ctx.definePageTransform(code, id) + }, }, - load(id) { - // remove the block as it's parsed by the plugin - // stub it with an empty module - if (id === ROUTE_BLOCK_ID) { - return { - code: `export default {}`, - map: null, + load: { + filter: { + id: { + include: [ + // virtualized ids only + new RegExp(`^${ROUTE_BLOCK_ID}$`), + new RegExp(`^${VIRTUAL_PREFIX}${MODULE_VUE_ROUTER_AUTO}$`), + new RegExp(`^${VIRTUAL_PREFIX}${MODULE_ROUTES_PATH}$`), + ], + }, + }, + handler(id) { + // remove the block as it's parsed by the plugin + // stub it with an empty module + if (id === ROUTE_BLOCK_ID) { + return { + code: `export default {}`, + map: null, + } } - } - // we need to use a virtual module so that vite resolves the vue-router/auto-routes - // dependency correctly - const resolvedId = getVirtualId(id) + // we need to use a virtual module so that vite resolves the vue-router/auto-routes + // dependency correctly + const resolvedId = getVirtualId(id) - // vue-router/auto-routes - if (resolvedId === MODULE_ROUTES_PATH) { - ROUTES_LAST_LOAD_TIME.update() - return ctx.generateRoutes() - } + // vue-router/auto-routes + if (resolvedId === MODULE_ROUTES_PATH) { + ROUTES_LAST_LOAD_TIME.update() + return ctx.generateRoutes() + } - // vue-router/auto - if (resolvedId === MODULE_VUE_ROUTER_AUTO) { - return ctx.generateVueRouterProxy() - } + // vue-router/auto + if (resolvedId === MODULE_VUE_ROUTER_AUTO) { + return ctx.generateVueRouterProxy() + } - return // ok TS... + return // ok TS... + }, }, // improves DX @@ -195,7 +193,10 @@ export default createUnplugin((opt = {}, _meta) => { if (options.experimental.autoExportsDataLoaders) { plugins.push( createAutoExportPlugin({ - filterPageComponents, + transformFilter: { + include: IDS_TO_INCLUDE, + exclude: options.exclude, + }, loadersPathsGlobs: options.experimental.autoExportsDataLoaders, root: options.root, })