shadcn-ui/ui codebase analysis: How does shadcn-ui CLI work? — Part 2.2
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 part 1.0 and part 1.1, I discussed the code written in packages/cli/src/index.ts. In part 2.0, I talked about how the commander.js is used along with zod to parse the CLI argument passed. In Part 2.1, looked at a function named preFlight and a package named fast-glob. In part 2.2, we will look at few more lines of code.
getConfig is imported from a different file named get-config. Reason behind this could be that context matters when it comes where you place your function. For example, logically, a function named getConfig can never be placed in a file named get-project-info.
// https://github.com/shadcn-ui/ui/blob/main/packages/cli/src/utils/get-config.ts#L16// TODO: Figure out if we want to support all cosmiconfig formats.// A simple components.json file would be nice.const explorer = cosmiconfig("components", { searchPlaces: ["components.json"],})
explorer.search Searches for a configuration file. Returns a Promise that resolves with a result or with null, if no configuration file is found.
You can do the same thing synchronously with explorerSync.search().
Let’s say your module name is goldengrahams so you initialized with const explorer = cosmiconfig('goldengrahams');. Here's how your default search() will work:
Starting from process.cwd() (or some other directory defined by the searchFrom argument to search()), look for configuration objects in the following places:
A goldengrahams property in a package.json file.
A .goldengrahamsrc file with JSON or YAML syntax.
A .goldengrahamsrc.json, .goldengrahamsrc.yaml, .goldengrahamsrc.yml, .goldengrahamsrc.js, .goldengrahamsrc.ts, .goldengrahamsrc.mjs, or .goldengrahamsrc.cjs file. (To learn more about how JS files are loaded, see "Loading JS modules".)
A goldengrahamsrc, goldengrahamsrc.json, goldengrahamsrc.yaml, goldengrahamsrc.yml, goldengrahamsrc.js, goldengrahamsrc.ts, goldengrahamsrc.mjs, or goldengrahamsrc.cjs file in the .config subdirectory.
A goldengrahams.config.js, goldengrahams.config.ts, goldengrahams.config.mjs, or goldengrahams.config.cjs file. (To learn more about how JS files are loaded, see "Loading JS modules".)
Turns out, explorer.search(cwd) is searching for components.json. Hang on, how exactly search function knows the module name?
const explorer = cosmiconfig("components", { searchPlaces: ["components.json"],})
When we set the cosmicconfig with “components”, we are setting the moduleName to “components” which means explorer.search looks for a file named components.json in a given directory. Brilliant!
return rawConfigSchema.parse(configResult.config) } catch (error) { throw new Error(`Invalid configuration found in ${cwd}/components.json.`) }
configResult from cosmic search is parsed against rawConfigSchema.
In this article, I was following along the call stack when the function getProjectConfig is called as this function has a bunch of calls to other functions that are placed logically in files (contextually). What I found inspiring was the usage of cosmicconfig, I have never come across this package but, boi does it have 54M downloads per week. It now makes sense how shadcn-ui/ui gets the config information from components.json (you will know this if you have used shadcn-ui/ui CLI before).
const explorer = cosmiconfig("components", { searchPlaces: ["components.json"],})// somewhere in getRawConfig file in utils/get-config.tsconst configResult = await explorer.search(cwd)
cosmicconfig searches for specific config in a given directory. In this case, it searches for “components.json”. There was other package named fast-glob that I discussed in part 2.1, fast-glob is a package that provides methods for traversing the file system and returning pathnames that matched a defined set of a specified pattern but this only returns pathnames.
Right, so if you want to get pathnames based on a certain pattern from a file system, use fast-glob. If you want to access certain config in a dir use CosmicConfig because Cosmiconfig will check the current directory for the following:
a package.json property
a JSON or YAML, extensionless “rc file”
an “rc file” with the extensions .json, .yaml, .yml, .js, .ts, .mjs, or .cjs
any of the above two inside a .config subdirectory
a .config.js, .config.ts, .config.mjs, or .config.cjs file
Want to learn how to build shadcn-ui/ui from scratch? Check outbuild-from-scratch
Hey, my name is Ramu Narasinga. I study large open-source projects and create content about their codebase architecture and best practices, sharing it through articles, videos.