I wanted to find out how shadcn-ui CLI works. In this article, I discuss the code used to build the shadcn-ui/ui CLI.
In parts 2.0 to 2.3, we looked at different functions involved to check if there is an existing config.
Let’s move on to the next line of code
After checking if there is an existing config, in the getProjectConfig function, the next step is to find out the project type.
This project type is your project’s type in which you are trying to install shadcn-ui components via init command.
const projectType = await getProjectType(cwd)
getProjectType is imported from ui/packages/cli/src/utils/get-project-info.ts and this function has some checks to identify if your Next.js project uses app or pages router and if it has src folder.
export async function getProjectType(cwd: string): Promise<ProjectType | null> {
const files = await fg.glob("\*\*/\*", {
cwd,
deep: 3,
ignore: PROJECT\_SHARED\_IGNORE,
})
const isNextProject = files.find((file) => file.startsWith("next.config."))
if (!isNextProject) {
return null
}
const isUsingSrcDir = await fs.pathExists(path.resolve(cwd, "src"))
const isUsingAppDir = await fs.pathExists(
path.resolve(cwd, \`${isUsingSrcDir ? "src/" : ""}app\`)
)
if (isUsingAppDir) {
return isUsingSrcDir ? "next-app-src" : "next-app"
}
return isUsingSrcDir ? "next-pages-src" : "next-pages"
}
This code is pretty self-explanatory except for the fg.glob
const files = await fg.glob("\*\*/\*", {
cwd,
deep: 3,
ignore: PROJECT\_SHARED\_IGNORE,
})
Check out the fast-glob docs about the deep property.
You might be wondering what’s PROJECT_SHARED_IGNORE.
Well, PROJECT_SHARED_IGNORE is an array initiated at the top of file.
Check out the docs for ignore property.
Conclusion:
As I moved on to the next line of code in getProjectConfig in shadcn-ui CLI source code, I found a function named getProjectType. This function’s purpose is to find out if your Next.js project uses app or pages router and whether it has src folder.
This code is pretty self explanatory:
export async function getProjectType(cwd: string): Promise<ProjectType | null> {
const files = await fg.glob("\*\*/\*", {
cwd,
deep: 3,
ignore: PROJECT\_SHARED\_IGNORE,
})
const isNextProject = files.find((file) => file.startsWith("next.config."))
if (!isNextProject) {
return null
}
const isUsingSrcDir = await fs.pathExists(path.resolve(cwd, "src"))
const isUsingAppDir = await fs.pathExists(
path.resolve(cwd, \`${isUsingSrcDir ? "src/" : ""}app\`)
)
if (isUsingAppDir) {
return isUsingSrcDir ? "next-app-src" : "next-app"
}
return isUsingSrcDir ? "next-pages-src" : "next-pages"
}
Except for the way files are accessed. fg.glob is set to depth level of 3 and ignores certain folders such as node_modules, dist, build, public and .next.
From the code snippet above, there’s one of 5 values expected as a project type: null || “next-app-src” || “next-app” || “next-pages-src” || “next-pages”.
Get free courses inspired by the best practices used in open source.
About me:
Website: https://ramunarasinga.com/
Linkedin: https://www.linkedin.com/in/ramu-narasinga-189361128/
Github: https://github.com/Ramu-Narasinga
Email: ramu.narasinga@gmail.com
Learn the best practices used in open source.
Top comments (0)