Skip to content

Commit

Permalink
feat: experimental auto export of data loaders
Browse files Browse the repository at this point in the history
  • Loading branch information
posva committed Aug 14, 2024
1 parent 5e78f70 commit 16af831
Show file tree
Hide file tree
Showing 10 changed files with 242 additions and 159 deletions.
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -172,6 +172,7 @@
"fast-glob": "^3.3.2",
"json5": "^2.2.3",
"local-pkg": "^0.5.0",
"magic-string": "^0.30.11",
"mlly": "^1.7.1",
"pathe": "^1.1.2",
"scule": "^1.3.0",
Expand Down
37 changes: 37 additions & 0 deletions playground/src/loaders/colada-loaders.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import { defineColadaLoader } from 'unplugin-vue-router/data-loaders/pinia-colada'
import { ref } from 'vue'

const delay = (ms: number) => new Promise((resolve) => setTimeout(resolve, ms))
export const simulateError = ref(false)

export const useUserData = defineColadaLoader('/users/colada-loader.[id]', {
async query(to, { signal }) {
console.log('[🍹] coladaLoader', to.fullPath)
// signal.addEventListener('abort', () => {
// console.log('[🍹❌] aborted', to.fullPath)
// })
// we need to read these before the delay
const id = to.params.id
// @ts-expect-error: no param "name"!
const name = to.params.name

await delay(500)
if (simulateError.value) {
throw new Error('Simulated Error')
}

const user = {
id,
name,
when: new Date().toUTCString(),
}

return user
},
key: (to) => {
// console.log('[🍹] key', to.fullPath)
return ['loader-users', to.params.id]
},
staleTime: 10000,
// lazy: (to, from) => to.name && to.name === from?.name,
})
Empty file added playground/src/loaders/todos.ts
Empty file.
44 changes: 1 addition & 43 deletions playground/src/pages/users/colada-loader.[id].vue
Original file line number Diff line number Diff line change
@@ -1,47 +1,5 @@
<script lang="ts">
import { defineColadaLoader } from 'unplugin-vue-router/data-loaders/pinia-colada'
const delay = (ms: number) => new Promise((resolve) => setTimeout(resolve, ms))
const simulateError = ref(false)
// th
export const useUserData = defineColadaLoader('/users/colada-loader.[id]', {
async query(to, { signal }) {
console.log('[🍹] coladaLoader', to.fullPath)
// signal.addEventListener('abort', () => {
// console.log('[🍹❌] aborted', to.fullPath)
// })
// we need to read these before the delay
const id = to.params.id
// @ts-expect-error: no param "name"!
const name = to.params.name
await delay(500)
if (simulateError.value) {
throw new Error('Simulated Error')
}
const user = {
id,
name,
when: new Date().toUTCString(),
}
return user
},
key: (to) => {
// console.log('[🍹] key', to.fullPath)
return ['loader-users', to.params.id]
},
staleTime: 10000,
lazy: (to, from) => to.name && to.name === from.name,
})
</script>

<script lang="ts" setup>
import { computed, ref } from 'vue'
import { simulateError, useUserData } from '@/loaders/colada-loaders'
import { serialize } from '@pinia/colada'
import { getActivePinia } from 'pinia'
Expand Down
1 change: 1 addition & 0 deletions playground/vite.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ export default defineConfig({
VueRouter({
extensions: ['.page.vue', '.vue'],
importMode: 'async',
experimental: { autoExportsDataLoaders: true },
extendRoute(route) {
route.params.forEach((param, i) => {
// transform kebab-case to camelCase
Expand Down
17 changes: 4 additions & 13 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

64 changes: 64 additions & 0 deletions src/data-loaders/auto-exports.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
import { createFilter } from '@rollup/pluginutils'
import MagicString from 'magic-string'
import { findStaticImports, parseStaticImport } from 'mlly'
import { type UnpluginOptions } from 'unplugin'

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

export function extractLoadersToExport(code: 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)
)
return []

return [
parsed.defaultImport,
...Object.values(parsed.namedImports || {}),
].filter((v): v is string => !!v && !v.startsWith('_'))
})

return importNames
}

export function createAutoExportPlugin(): UnpluginOptions {
const filterVueComponents = createFilter(
[/\.vue$/, /\.vue\?vue/, /\.vue\?v=/]
// [/[\\/]node_modules[\\/]/, /[\\/]\.git[\\/]/, /[\\/]\.nuxt[\\/]/]
)

return {
name: 'unplugin-vue-router:data-loaders-auto-export',
enforce: 'post',
vite: {
transform: {
order: 'post',
handler(code, id) {
if (!filterVueComponents(id)) {
return
}

const loadersToExports = extractLoadersToExport(code)

if (loadersToExports.length <= 0) return

const s = new MagicString(code)
s.append(
`\nexport const __loaders = [\n${loadersToExports.join(',\n')}\n];\n`
)

return {
code: s.toString(),
map: s.generateMap(),
}
},
},
},
}
}
7 changes: 7 additions & 0 deletions src/data-loaders/navigation-guard.ts
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,13 @@ export function setupLoaderGuard({
record.meta[LOADER_SET_KEY]!.add(exportValue)
}
}
if (Array.isArray(viewModule.__loaders)) {
for (const loader of viewModule.__loaders) {
if (isDataLoader(loader)) {
record.meta[LOADER_SET_KEY]!.add(loader)
}
}
}
}
)

Expand Down
Loading

0 comments on commit 16af831

Please sign in to comment.