Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit80780f6

Browse files
feat(module): support multiple component directories (#1597)
Co-authored-by: Sadegh Barati <sadeghbaratiwork@gmail.com>
1 parent9d37f88 commit80780f6

File tree

11 files changed

+177
-54
lines changed

11 files changed

+177
-54
lines changed

‎packages/module/README.md‎

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,28 @@ export default defineNuxtConfig({
6464

6565
That's it! You can now use Shadcn Nuxt in your Nuxt app ✨
6666

67+
###Multiple component directories
68+
69+
Projects that split their base UI components from custom extensions can configure multiple directories, each with its own optional prefix:
70+
71+
```ts
72+
exportdefaultdefineNuxtConfig({
73+
modules: ['shadcn-nuxt'],
74+
shadcn: {
75+
prefix:'Ui',
76+
componentDir: [
77+
'@/components/ui',
78+
{
79+
path:'@/components/ai',
80+
prefix:'Ai',
81+
},
82+
],
83+
},
84+
})
85+
```
86+
87+
Each directory is ignored by Nuxt's default auto-import scanning and re-registered through the module, ensuring clean separation without console warnings. See[issue#1593](https://github.com/unovue/shadcn-vue/issues/1593) for the background.
88+
6789
##Development
6890

6991
```bash

‎packages/module/src/module.ts‎

Lines changed: 91 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,40 @@ import { join } from 'node:path'
33
import{addComponent,addComponentsDir,createResolver,defineNuxtModule}from'@nuxt/kit'
44
import{parseSync}from'oxc-parser'
55

6+
exportinterfaceComponentDirConfig{
7+
path:string
8+
prefix?:string
9+
}
10+
11+
exporttypeComponentDirInput=string|ComponentDirConfig
12+
13+
exporttypeComponentDirOption=ComponentDirInput|ComponentDirInput[]
14+
15+
interfaceNormalizedComponentDir{
16+
path:string
17+
prefix:string
18+
}
19+
20+
functionisComponentDirConfig(value:unknown):value isComponentDirConfig{
21+
returntypeofvalue==='object'&&value!==null&&'path'invalue
22+
}
23+
24+
functionnormalizeComponentDirs(componentDir:ComponentDirOption,fallbackPrefix:string):NormalizedComponentDir[]{
25+
constdirs=Array.isArray(componentDir) ?componentDir :[componentDir]
26+
27+
returndirs
28+
.filter((dir):dir isComponentDirInput=>Boolean(dir))
29+
.map((dir)=>{
30+
if(typeofdir==='string')
31+
return{path:dir,prefix:fallbackPrefix}
32+
33+
if(isComponentDirConfig(dir))
34+
return{path:dir.path,prefix:dir.prefix??fallbackPrefix}
35+
36+
thrownewError('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
842
exportinterfaceModuleOptions{
@@ -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

2357
exportdefaultdefineNuxtModule<ModuleOptions>({
@@ -30,63 +64,66 @@ export default defineNuxtModule<ModuleOptions>({
3064
componentDir:'@/components/ui',
3165
},
3266
asyncsetup({ prefix, componentDir},nuxt){
33-
constCOMPONENT_DIR_PATH=componentDir!
3467
constROOT_DIR_PATH=nuxt.options.rootDir
3568
const{ resolve, resolvePath}=createResolver(ROOT_DIR_PATH)
3669

37-
// Components Auto Imports
38-
constcomponentsPath=awaitresolvePath(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+
constnormalizedDirs=normalizeComponentDirs(componentDir??'@/components/ui',prefix??'Ui')
71+
72+
awaitPromise.all(normalizedDirs.map(async({ path,prefix:dirPrefix})=>{
73+
// Components Auto Imports
74+
constcomponentsPath=awaitresolvePath(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+
awaitPromise.all(readdirSync(componentsPath).map(async(dir)=>{
95+
try{
96+
constfilePath=awaitresolvePath(join(path,dir,'index'),{extensions:['.ts','.js']})
97+
constcontent=readFileSync(filePath,{encoding:'utf8'})
98+
constast=parseSync(filePath,content,{
99+
sourceType:'module',
100+
})
101+
102+
constexportedKeys: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-
awaitPromise.all(readdirSync(componentsPath).map(async(dir)=>{
59-
try{
60-
constfilePath=awaitresolvePath(join(COMPONENT_DIR_PATH,dir,'index'),{extensions:['.ts','.js']})
61-
constcontent=readFileSync(filePath,{encoding:'utf8'})
62-
constast=parseSync(filePath,content,{
63-
sourceType:'module',
64-
})
65-
66-
constexportedKeys: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(errinstanceofError)
83-
console.warn('Module error: ',err.message)
84-
}
85-
}))
86-
}
87-
catch(err){
88-
if(errinstanceofError)
89-
console.warn(err.message)
90-
}
116+
}
117+
catch(err){
118+
if(errinstanceofError)
119+
console.warn('Module error: ',err.message)
120+
}
121+
}))
122+
}
123+
catch(err){
124+
if(errinstanceofError)
125+
console.warn(err.message)
126+
}
127+
}))
91128
},
92129
})
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
<script setup>
2+
</script>
3+
4+
<template>
5+
<div>
6+
<UiButton />
7+
<AiChatPanel />
8+
</div>
9+
</template>
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
<template>
2+
<section>
3+
AI Chat Panel
4+
</section>
5+
</template>
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export{defaultasChatPanel}from'./ChatPanel.vue'
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
<template>
2+
<button>
3+
Base Button
4+
</button>
5+
</template>
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export{defaultasButton}from'./Button.vue'
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
importMyModulefrom'../../../src/module'
2+
3+
exportdefaultdefineNuxtConfig({
4+
modules:[
5+
MyModule,
6+
],
7+
shadcn:{
8+
prefix:'Ui',
9+
componentDir:[
10+
'@/components/ui',
11+
{
12+
path:'@/components/ai',
13+
prefix:'Ai',
14+
},
15+
],
16+
},
17+
})
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
{
2+
"name":"multi-dir",
3+
"type":"module",
4+
"private":true
5+
}
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
declare module'*.vue'{
2+
importtype{DefineComponent}from'vue'
3+
4+
constcomponent:DefineComponent<Record<string,never>,Record<string,never>,unknown>
5+
exportdefaultcomponent
6+
}

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp