@@ -3,6 +3,40 @@ import { join } from 'node:path'
33import { addComponent , addComponentsDir , createResolver , defineNuxtModule } from '@nuxt/kit'
44import { parseSync } from 'oxc-parser'
55
6+ export interface ComponentDirConfig {
7+ path : string
8+ prefix ?: string
9+ }
10+
11+ export type ComponentDirInput = string | ComponentDirConfig
12+
13+ export type ComponentDirOption = ComponentDirInput | ComponentDirInput [ ]
14+
15+ interface NormalizedComponentDir {
16+ path : string
17+ prefix : string
18+ }
19+
20+ function isComponentDirConfig ( value : unknown ) : value is ComponentDirConfig {
21+ return typeof value === 'object' && value !== null && 'path' in value
22+ }
23+
24+ function normalizeComponentDirs ( componentDir : ComponentDirOption , fallbackPrefix : string ) : NormalizedComponentDir [ ] {
25+ const dirs = Array . isArray ( componentDir ) ? componentDir : [ componentDir ]
26+
27+ return dirs
28+ . filter ( ( dir ) : dir is ComponentDirInput => Boolean ( dir ) )
29+ . map ( ( dir ) => {
30+ if ( typeof dir === 'string' )
31+ return { path : dir , prefix : fallbackPrefix }
32+
33+ if ( isComponentDirConfig ( dir ) )
34+ return { path : dir . path , prefix : dir . prefix ?? fallbackPrefix }
35+
36+ throw new Error ( 'Invalid componentDir entry provided to shadcn module.' )
37+ } )
38+ }
39+
640// TODO: add test to make sure all registry is being parse correctly
741// Module options TypeScript interface definition
842export interface ModuleOptions {
@@ -17,7 +51,7 @@ export interface ModuleOptions {
1751 * @link https://nuxt.com/docs/api/nuxt-config#alias
1852 * @default "@/components/ui"
1953 */
20- componentDir ?: string
54+ componentDir ?: ComponentDirOption
2155}
2256
2357export default defineNuxtModule < ModuleOptions > ( {
@@ -30,63 +64,66 @@ export default defineNuxtModule<ModuleOptions>({
3064 componentDir : '@/components/ui' ,
3165 } ,
3266 async setup ( { prefix, componentDir } , nuxt ) {
33- const COMPONENT_DIR_PATH = componentDir !
3467 const ROOT_DIR_PATH = nuxt . options . rootDir
3568 const { resolve, resolvePath } = createResolver ( ROOT_DIR_PATH )
3669
37- // Components Auto Imports
38- const componentsPath = await resolvePath ( COMPONENT_DIR_PATH )
39-
40- // Early return if directory doesn't exist
41- if ( ! existsSync ( componentsPath ) ) {
42- console . warn ( `Component directory does not exist: ${ componentsPath } ` )
43- return
44- }
45-
46- // Tell Nuxt to not scan `componentsDir` for auto imports as we will do it manually
47- // See https://github.com/unovue/shadcn-vue/pull/528#discussion_r1590206268
48- addComponentsDir ( {
49- path : componentsPath ,
50- extensions : [ ] ,
51- ignore : [ '**/*' ] ,
52- } , {
53- prepend : true ,
54- } )
70+ const normalizedDirs = normalizeComponentDirs ( componentDir ?? '@/components/ui' , prefix ?? 'Ui' )
71+
72+ await Promise . all ( normalizedDirs . map ( async ( { path, prefix : dirPrefix } ) => {
73+ // Components Auto Imports
74+ const componentsPath = await resolvePath ( path )
75+
76+ // Early return if directory doesn't exist
77+ if ( ! existsSync ( componentsPath ) ) {
78+ console . warn ( `Component directory does not exist: ${ componentsPath } ` )
79+ return
80+ }
81+
82+ // Tell Nuxt to not scan `componentsDir` for auto imports as we will do it manually
83+ // See https://github.com/unovue/shadcn-vue/pull/528#discussion_r1590206268
84+ addComponentsDir ( {
85+ path : componentsPath ,
86+ extensions : [ ] ,
87+ ignore : [ '**/*' ] ,
88+ } , {
89+ prepend : true ,
90+ } )
91+
92+ // Manually scan `componentsDir` for components and register them for auto imports
93+ try {
94+ await Promise . all ( readdirSync ( componentsPath ) . map ( async ( dir ) => {
95+ try {
96+ const filePath = await resolvePath ( join ( path , dir , 'index' ) , { extensions : [ '.ts' , '.js' ] } )
97+ const content = readFileSync ( filePath , { encoding : 'utf8' } )
98+ const ast = parseSync ( filePath , content , {
99+ sourceType : 'module' ,
100+ } )
101+
102+ const exportedKeys : string [ ] = ast . program . body
103+ . filter ( node => node . type === 'ExportNamedDeclaration' )
104+ // @ts -expect-error parse return any
105+ . flatMap ( node => node . specifiers ?. map ( specifier => specifier . exported ?. name ) || [ ] )
106+ . filter ( ( key : string ) => / ^ [ A - Z ] / . test ( key ) )
55107
56- // Manually scan `componentsDir` for components and register them for auto imports
57- try {
58- await Promise . all ( readdirSync ( componentsPath ) . map ( async ( dir ) => {
59- try {
60- const filePath = await resolvePath ( join ( COMPONENT_DIR_PATH , dir , 'index' ) , { extensions : [ '.ts' , '.js' ] } )
61- const content = readFileSync ( filePath , { encoding : 'utf8' } )
62- const ast = parseSync ( filePath , content , {
63- sourceType : 'module' ,
64- } )
65-
66- const exportedKeys : string [ ] = ast . program . body
67- . filter ( node => node . type === 'ExportNamedDeclaration' )
68- // @ts -expect-error parse return any
69- . flatMap ( node => node . specifiers ?. map ( specifier => specifier . exported ?. name ) || [ ] )
70- . filter ( ( key : string ) => / ^ [ A - Z ] / . test ( key ) )
71-
72- exportedKeys . forEach ( ( key ) => {
73- addComponent ( {
74- name : `${ prefix } ${ key } ` , // name of the component to be used in vue templates
75- export : key , // (optional) if the component is a named (rather than default) export
76- filePath : resolve ( filePath ) ,
77- priority : 1 ,
108+ exportedKeys . forEach ( ( key ) => {
109+ addComponent ( {
110+ name : `${ dirPrefix } ${ key } ` , // name of the component to be used in vue templates
111+ export : key , // (optional) if the component is a named (rather than default) export
112+ filePath : resolve ( filePath ) ,
113+ priority : 1 ,
114+ } )
78115 } )
79- } )
80- }
81- catch ( err ) {
82- if ( err instanceof Error )
83- console . warn ( 'Module error: ' , err . message )
84- }
85- } ) )
86- }
87- catch ( err ) {
88- if ( err instanceof Error )
89- console . warn ( err . message )
90- }
116+ }
117+ catch ( err ) {
118+ if ( err instanceof Error )
119+ console . warn ( 'Module error: ' , err . message )
120+ }
121+ } ) )
122+ }
123+ catch ( err ) {
124+ if ( err instanceof Error )
125+ console . warn ( err . message )
126+ }
127+ } ) )
91128 } ,
92129} )
0 commit comments