Skip to content

Commit

Permalink
feat(loaders): change experimental option to match only specific folders
Browse files Browse the repository at this point in the history
  • Loading branch information
posva committed Aug 15, 2024
1 parent 952561a commit f01cddd
Show file tree
Hide file tree
Showing 4 changed files with 61 additions and 21 deletions.
4 changes: 3 additions & 1 deletion playground/vite.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,9 @@ export default defineConfig({
VueRouter({
extensions: ['.page.vue', '.vue'],
importMode: 'async',
experimental: { autoExportsDataLoaders: true },
experimental: {
autoExportsDataLoaders: ['src/loaders/**/*', '@/loaders/**/*'],
},
extendRoute(route) {
route.params.forEach((param, i) => {
// transform kebab-case to camelCase
Expand Down
52 changes: 36 additions & 16 deletions src/data-loaders/auto-exports.ts
Original file line number Diff line number Diff line change
@@ -1,22 +1,29 @@
import { createFilter } from '@rollup/pluginutils'
import MagicString from 'magic-string'
import { findStaticImports, parseStaticImport } from 'mlly'
import { join, resolve } from 'pathe'

Check failure on line 4 in src/data-loaders/auto-exports.ts

View workflow job for this annotation

GitHub Actions / test (lts/*, ubuntu-latest)

Unhandled error

TypeCheckError: 'join' is declared but its value is never read. ❯ src/data-loaders/auto-exports.ts:4:10
import { type UnpluginOptions } from 'unplugin'

const ignoredSpecifiers = ['vue', 'vue-router', 'pinia']

export function extractLoadersToExport(code: string): string[] {
export function extractLoadersToExport(
code: string,
filterPaths: (id: string) => boolean,
root: string
): string[] {
const imports = findStaticImports(code)
const importNames = imports.flatMap((i) => {
const parsed = parseStaticImport(i)

// bail out faster for anything that is not a data loader
if (
// NOTE: move out to a regexp if the option is exposed
parsed.specifier.startsWith('@vueuse/') &&
ignoredSpecifiers.includes(parsed.specifier)
// since we run post-post, vite will add a leading slash to the specifier
const specifier = resolve(
root,
parsed.specifier.startsWith('/')
? parsed.specifier.slice(1)
: parsed.specifier
)
return []
console.log('🔍', specifier)

// bail out faster for anything that is not a data loader
if (!filterPaths(specifier)) return []

return [
parsed.defaultImport,
Expand All @@ -27,11 +34,17 @@ export function extractLoadersToExport(code: string): string[] {
return importNames
}

export function createAutoExportPlugin(): UnpluginOptions {
const filterVueComponents = createFilter(
[/\.vue$/, /\.vue\?vue/, /\.vue\?v=/]
// [/[\\/]node_modules[\\/]/, /[\\/]\.git[\\/]/, /[\\/]\.nuxt[\\/]/]
)
export function createAutoExportPlugin({
filterPageComponents,
loadersPathsGlobs,
root,
}: {
filterPageComponents: (id: string) => boolean
loadersPathsGlobs: string | string[]
root: string
}): UnpluginOptions {
console.log('Creating auto-export plugin', loadersPathsGlobs)
const filterPaths = createFilter(loadersPathsGlobs)

return {
name: 'unplugin-vue-router:data-loaders-auto-export',
Expand All @@ -40,11 +53,18 @@ export function createAutoExportPlugin(): UnpluginOptions {
transform: {
order: 'post',
handler(code, id) {
if (!filterVueComponents(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
}

const loadersToExports = extractLoadersToExport(code)
const loadersToExports = extractLoadersToExport(
code,
filterPaths,
root
)

if (loadersToExports.length <= 0) return

Expand Down
9 changes: 8 additions & 1 deletion src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -188,8 +188,15 @@ export default createUnplugin<Options | undefined>((opt = {}, _meta) => {
},
]

// Experimental options
if (options.experimental.autoExportsDataLoaders) {
plugins.push(createAutoExportPlugin())
plugins.push(
createAutoExportPlugin({
filterPageComponents,
loadersPathsGlobs: options.experimental.autoExportsDataLoaders,
root: options.root,
})
)
}

return plugins
Expand Down
17 changes: 14 additions & 3 deletions src/options.ts
Original file line number Diff line number Diff line change
Expand Up @@ -216,9 +216,12 @@ export interface Options {
*/
experimental?: {
/**
* Automatically export data loaders in vue components. This allows you to not
* (Vite only). File paths or globs where loaders are exported. This will be used to filter out imported loaders and
* automatically re export them in page components. You can for example set this to `'src/loaders/**\/*'` (without
* the backslash) to automatically re export any imported variable from files in the `src/loaders` folder within a
* page component.
*/
autoExportsDataLoaders?: boolean
autoExportsDataLoaders?: string | string[]
}
}

Expand All @@ -239,7 +242,7 @@ export const DEFAULT_OPTIONS = {
},
watch: !process.env.CI,
experimental: {
autoExportsDataLoaders: false,
autoExportsDataLoaders: 'src/loaders/**/*',
},
} satisfies Options

Expand Down Expand Up @@ -299,6 +302,14 @@ export function resolveOptions(options: Options) {
src: resolve(root, routeOption.src),
}))

if (options.experimental?.autoExportsDataLoaders) {
options.experimental.autoExportsDataLoaders = (
Array.isArray(options.experimental.autoExportsDataLoaders)
? options.experimental.autoExportsDataLoaders
: [options.experimental.autoExportsDataLoaders]
).map((path) => resolve(root, path))
}

if (options.extensions) {
options.extensions = options.extensions
// ensure that extensions start with a dot or warn the user
Expand Down

0 comments on commit f01cddd

Please sign in to comment.