Skip to content

Commit ab89513

Browse files
committed
feat: add data loaders as experimental
1 parent 83366d6 commit ab89513

18 files changed

+4664
-0
lines changed
Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,109 @@
1+
import { createFilter } from 'unplugin-utils'
2+
import type { Plugin } from 'vite'
3+
import MagicString from 'magic-string'
4+
import { findStaticImports, parseStaticImport } from 'mlly'
5+
import { resolve } from 'pathe'
6+
import { StringFilter, type UnpluginOptions } from 'unplugin'
7+
8+
export function extractLoadersToExport(
9+
code: string,
10+
filterPaths: (id: string) => boolean,
11+
root: string
12+
): string[] {
13+
const imports = findStaticImports(code)
14+
const importNames = imports.flatMap(i => {
15+
const parsed = parseStaticImport(i)
16+
17+
// since we run post-post, vite will add a leading slash to the specifier
18+
const specifier = resolve(
19+
root,
20+
parsed.specifier.startsWith('/')
21+
? parsed.specifier.slice(1)
22+
: parsed.specifier
23+
)
24+
25+
// bail out faster for anything that is not a data loader
26+
if (!filterPaths(specifier)) return []
27+
28+
return [
29+
parsed.defaultImport,
30+
...Object.values(parsed.namedImports || {}),
31+
].filter((v): v is string => !!v && !v.startsWith('_'))
32+
})
33+
34+
return importNames
35+
}
36+
37+
const PLUGIN_NAME = 'unplugin-vue-router:data-loaders-auto-export'
38+
39+
/**
40+
* {@link AutoExportLoaders} options.
41+
*/
42+
export interface AutoExportLoadersOptions {
43+
/**
44+
* Filter page components to apply the auto-export. Passed to `transform.filter.id`.
45+
*/
46+
transformFilter: StringFilter
47+
48+
/**
49+
* Globs to match the paths of the loaders.
50+
*/
51+
loadersPathsGlobs: string | string[]
52+
53+
/**
54+
* Root of the project. All paths are resolved relatively to this one.
55+
* @default `process.cwd()`
56+
*/
57+
root?: string
58+
}
59+
60+
/**
61+
* Vite Plugin to automatically export loaders from page components.
62+
*
63+
* @param options Options
64+
* @experimental - This API is experimental and can be changed in the future. It's used internally by `experimental.autoExportsDataLoaders`
65+
66+
*/
67+
export function AutoExportLoaders({
68+
transformFilter,
69+
loadersPathsGlobs,
70+
root = process.cwd(),
71+
}: AutoExportLoadersOptions): Plugin {
72+
const filterPaths = createFilter(loadersPathsGlobs)
73+
74+
return {
75+
name: PLUGIN_NAME,
76+
transform: {
77+
order: 'post',
78+
filter: {
79+
id: transformFilter,
80+
},
81+
82+
handler(code) {
83+
const loadersToExports = extractLoadersToExport(code, filterPaths, root)
84+
85+
if (loadersToExports.length <= 0) return
86+
87+
const s = new MagicString(code)
88+
s.append(
89+
`\nexport const __loaders = [\n${loadersToExports.join(',\n')}\n];\n`
90+
)
91+
92+
return {
93+
code: s.toString(),
94+
map: s.generateMap(),
95+
}
96+
},
97+
},
98+
}
99+
}
100+
101+
export function createAutoExportPlugin(
102+
options: AutoExportLoadersOptions
103+
): UnpluginOptions {
104+
return {
105+
name: PLUGIN_NAME,
106+
// Cast needed due to Vite version differences in monorepo
107+
vite: AutoExportLoaders(options),
108+
}
109+
}

0 commit comments

Comments
 (0)