shadcn-ui/ui codebase analysis: How does shadcn-ui CLI work? — Part 2.3
Tags: javascript, opensource, shadcnui, nextjs
Published: 2024-07-01 21:22:59

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 2.2](https://medium.com/@ramu.narasinga_61050/shadcn-ui-ui-codebase-analysis-how-does-shadcn-ui-cli-work-part-2-2-73cff5651b06), I followed along the call stack when the function getProjectConfig is called and discussed functions named [getConfig](https://github.com/shadcn-ui/ui/blob/main/packages/cli/src/utils/get-config.ts#L55), [getRawConfig](https://github.com/shadcn-ui/ui/blob/main/packages/cli/src/utils/get-config.ts#L91). A detailed explanation is provided about getRawConfig that is called from getConfig. In this article, we will analyse few more lines of code from getConfig function.

![](https://media.licdn.com/dms/image/D4E12AQFI9vQMv__mbg/article-inline_image-shrink_1000_1488/0/1719868556454?e=1725494400&v=beta&t=8DBsSv5N2c6wJjt5dY_8Zk1Rzj11lBFiyGrqVCqpryw)

resolveConfigPaths
------------------

```js
export async function resolveConfigPaths(cwd: string, config: RawConfig) {
  // Read tsconfig.json.
  const tsConfig = await loadConfig(cwd)

  if (tsConfig.resultType === "failed") {
    throw new Error(
      \`Failed to load ${config.tsx ? "tsconfig" : "jsconfig"}.json. ${
        tsConfig.message ?? ""
      }\`.trim()
    )
  }

  return configSchema.parse({
    ...config,
    resolvedPaths: {
      tailwindConfig: path.resolve(cwd, config.tailwind.config),
      tailwindCss: path.resolve(cwd, config.tailwind.css),
      utils: await resolveImport(config.aliases\["utils"\], tsConfig),
      components: await resolveImport(config.aliases\["components"\], tsConfig),
      ui: config.aliases\["ui"\]
        ? await resolveImport(config.aliases\["ui"\], tsConfig)
        : await resolveImport(config.aliases\["components"\], tsConfig),
    },
  })
}
```

Let’s break this function down.

### loadConfig

```js
// Read tsconfig.json.
const tsConfig = await loadConfig(cwd)

loadConfig is imported from [tsconfig-paths](https://www.npmjs.com/package/tsconfig-paths).

This function loads the tsconfig.json or jsconfig.json. It will start searching from the specified cwd directory.

if (tsConfig.resultType === "failed") {
    throw new Error(
      \`Failed to load ${config.tsx ? "tsconfig" : "jsconfig"}.json. ${
        tsConfig.message ?? ""
      }\`.trim()
    )
  }
```

This is an error check that throws an error when tsConfig or jsConfig fails to load.

```js
return configSchema.parse({
    ...config,
    resolvedPaths: {
      tailwindConfig: path.resolve(cwd, config.tailwind.config),
      tailwindCss: path.resolve(cwd, config.tailwind.css),
      utils: await resolveImport(config.aliases\["utils"\], tsConfig),
      components: await resolveImport(config.aliases\["components"\], tsConfig),
      ui: config.aliases\["ui"\]
        ? await resolveImport(config.aliases\["ui"\], tsConfig)
        : await resolveImport(config.aliases\["components"\], tsConfig),
    },
  })
```

[configSchema](https://github.com/shadcn-ui/ui/blob/main/packages/cli/src/utils/get-config.ts#L43) is returned by the [resolveConfigPaths](https://github.com/shadcn-ui/ui/blob/main/packages/cli/src/utils/get-config.ts#L65C23-L65C41). This code snippet uses [path.resolve](https://nodejs.org/api/path.html#pathresolvepaths) and [resolveImport](https://github.com/shadcn-ui/ui/blob/main/packages/cli/src/utils/resolve-import.ts#L3).

### resolveImport

```js
import { createMatchPath, type ConfigLoaderSuccessResult } from "tsconfig-paths"

export async function resolveImport(
  importPath: string,
  config: Pick<ConfigLoaderSuccessResult, "absoluteBaseUrl" | "paths">
) {
  return createMatchPath(config.absoluteBaseUrl, config.paths)(
    importPath,
    undefined,
    () => true,
    \[".ts", ".tsx"\]
  )
}
```

You can read more about [createMatchPath](https://www.npmjs.com/package/tsconfig-paths#creatematchpath).

Conclusion:
-----------

I updated the [commanderjs-usage-in-shadcnui](https://github.com/Ramu-Narasinga/commanderjs-usage-in-shadcn-ui) with shadcn-ui CLI package code to understand the getConfig call in [getProjectConfig](https://github.com/shadcn-ui/ui/blob/main/packages/cli/src/utils/get-project-info.ts#L73). Turns out, this little detour to understand the series of function calls following getConfig are to check if there is existing component config.

```js
// Check for existing component config.
const existingConfig = await getConfig(cwd)
if (existingConfig) {
  return existingConfig
}
```

Just to recap, the call stack is like this: getConfig calls getRawConfig, getRawConfig uses [explorer.search](http://explorer.search) (from cosmicconfig) and then if there is an existing component config, resolveConfigPaths is returned that uses some helpers function such as createMatchPath provided by tsconfig-paths package. All this trouble just to check if there’s an existingConfig. Why tho?

The answer lies in the different schema that you get when there’s an existing component config available. ([configSchema](https://github.com/shadcn-ui/ui/blob/main/packages/cli/src/utils/get-config.ts#L43) and [rawConfigSchema](https://github.com/shadcn-ui/ui/blob/main/packages/cli/src/utils/get-config.ts#L20)). There’s something unique about the way these functions are organised!

> _Want to learn how to build shadcn-ui/ui from scratch? Check out_ [_build-from-scratch_](https://tthroo.com/)

About me:
---------

Website: [https://ramunarasinga.com/](https://ramunarasinga.com/)

Linkedin: [https://www.linkedin.com/in/ramu-narasinga-189361128/](https://www.linkedin.com/in/ramu-narasinga-189361128/)

Github: [https://github.com/Ramu-Narasinga](https://github.com/Ramu-Narasinga)

Email: [ramu.narasinga@gmail.com](mailto:ramu.narasinga@gmail.com)

[Build shadcn-ui/ui from scratch](https://tthroo.com/)

References:
-----------

1.  [https://github.com/shadcn-ui/ui/blob/main/packages/cli/src/utils/get-config.ts#L55](https://github.com/shadcn-ui/ui/blob/main/packages/cli/src/utils/get-config.ts#L55)
2.  [https://github.com/shadcn-ui/ui/blob/main/packages/cli/src/utils/get-config.ts#L4](https://github.com/shadcn-ui/ui/blob/main/packages/cli/src/utils/get-config.ts#L4)
3.  [https://github.com/shadcn-ui/ui/blob/main/packages/cli/src/utils/get-config.ts#L2](https://github.com/shadcn-ui/ui/blob/main/packages/cli/src/utils/get-config.ts#L2)
4.  [https://www.npmjs.com/package/tsconfig-paths](https://www.npmjs.com/package/tsconfig-paths)
5.  [https://github.com/shadcn-ui/ui/blob/main/packages/cli/src/utils/resolve-import.ts#L3](https://github.com/shadcn-ui/ui/blob/main/packages/cli/src/utils/resolve-import.ts#L3)
6.  [https://www.npmjs.com/package/tsconfig-paths#creatematchpath](https://www.npmjs.com/package/tsconfig-paths#creatematchpath)