shadcn-ui/ui codebase analysis: How does shadcn-ui CLI work? — Part 2.12
Tags: shadcnui, nextjs, opensource, javascript
Published: 2024-07-12 21:49:42

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.11, we looked at runInit function and how shadcn-ui/ui ensures directories provided in resolvedPaths in config exist.

The following operations are performed in runInit function:

1.  Ensure all resolved paths directories exist.
2.  Write tailwind config.
3.  Write css file.
4.  Write cn file.
5.  Install dependencies.

Let’s understand how shadcn-ui/ui CLI writes to tailwind config in runInit function.

Write tailwind config
---------------------

After checking the directories exist, there are few more operations performed before writing tailwind config as shown in the below code.

This code is picked from [cli/src/commands/init.ts](https://github.com/shadcn-ui/ui/blob/main/packages/cli/src/commands/init.ts#L331C3-L356C4).

```js
const extension = config.tsx ? "ts" : "js"

const tailwindConfigExtension = path.extname(
  config.resolvedPaths.tailwindConfig
)

let tailwindConfigTemplate: string
if (tailwindConfigExtension === ".ts") {
  tailwindConfigTemplate = config.tailwind.cssVariables
    ? templates.TAILWIND\_CONFIG\_TS\_WITH\_VARIABLES
    : templates.TAILWIND\_CONFIG\_TS
} else {
  tailwindConfigTemplate = config.tailwind.cssVariables
    ? templates.TAILWIND\_CONFIG\_WITH\_VARIABLES
    : templates.TAILWIND\_CONFIG
}

// Write tailwind config.
await fs.writeFile(
  config.resolvedPaths.tailwindConfig,
  template(tailwindConfigTemplate)({
    extension,
    prefix: config.tailwind.prefix,
  }),
  "utf8"
)
```

Let’s understand this code snippet

### Extension

```js
const extension = config.tsx ? "ts" : "js"
```

This extension is used in later parts of code that deal with writing cn file.

### tailwindConfigExtension

```js
const tailwindConfigExtension = path.extname(
  config.resolvedPaths.tailwindConfig
)
```

The path.extname() method returns the extension of the path, from the last occurrence of the . (period) character to end of string in the last portion of the path. If there is no . in the last portion of the path, or if there are no . characters other than the first character of the basename of path (see path.basename()) , an empty string is returned.

```js
let tailwindConfigTemplate: string
if (tailwindConfigExtension === ".ts") {
  tailwindConfigTemplate = config.tailwind.cssVariables
    ? templates.TAILWIND\_CONFIG\_TS\_WITH\_VARIABLES
    : templates.TAILWIND\_CONFIG\_TS
} else {
  tailwindConfigTemplate = config.tailwind.cssVariables
    ? templates.TAILWIND\_CONFIG\_WITH\_VARIABLES
    : templates.TAILWIND\_CONFIG
}
```

Depending on the tailwindConfigExtension and config.tailwind.cssVariables, tailwindConfigTemplate is set to a value using [templates](https://github.com/shadcn-ui/ui/blob/main/packages/cli/src/utils/templates.ts). Templates is imported from [utils/templates](https://github.com/shadcn-ui/ui/blob/main/packages/cli/src/utils/templates.ts) and contains some variables initialised with values related to tailwind config

![](https://media.licdn.com/dms/image/D4E12AQGyhhK0CoJKzg/article-inline_image-shrink_1000_1488/0/1720820632890?e=1726099200&v=beta&t=jQwU0AuFGV5wr0BymlnCvWNa5UtYOUG-mOgyDalbdq0)

### Write to tailwind config file

```js
await fs.writeFile(
  config.resolvedPaths.tailwindConfig,
  template(tailwindConfigTemplate)({
    extension,
    prefix: config.tailwind.prefix,
  }),
  "utf8"
)
```

[template](https://lodash.com/docs#template) used here in the above snippet is different from utils/templates. Different how? this template is imported from [lodash.template](https://lodash.com/docs#template)

![](https://media.licdn.com/dms/image/D4E12AQEjUUALWk7D3g/article-inline_image-shrink_1000_1488/0/1720820633086?e=1726099200&v=beta&t=FxAaPPCc5JJegC4Y93H074bLb-etIjTFxDOnKNtcsrE)

Read more about [lodash.template](https://lodash.com/docs#template)

An example using lodash.template:

```js
// Use the "interpolate" delimiter to create a compiled template.
var compiled = \_.template('hello <%= user %>!');
compiled({ 'user': 'fred' });
// => 'hello fred!'
```

This explains why there is %extension% in utils/templates.ts

![](https://media.licdn.com/dms/image/D4E12AQGqgyoiZXTIbA/article-inline_image-shrink_1000_1488/0/1720820633152?e=1726099200&v=beta&t=HcZ_u4P-kfjzwMiGH2ronN8qY8sSmpfdQy5ZNInGJmI)

It gets replaced with w/e is passed. Interesting…

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

After checking the directories exist (explained in the part 2.11), there are few more operations performed before writing tailwind config as shown in the below code.

```js
const extension = config.tsx ? "ts" : "js"

const tailwindConfigExtension = path.extname(
  config.resolvedPaths.tailwindConfig
)

let tailwindConfigTemplate: string
if (tailwindConfigExtension === ".ts") {
  tailwindConfigTemplate = config.tailwind.cssVariables
    ? templates.TAILWIND\_CONFIG\_TS\_WITH\_VARIABLES
    : templates.TAILWIND\_CONFIG\_TS
} else {
  tailwindConfigTemplate = config.tailwind.cssVariables
    ? templates.TAILWIND\_CONFIG\_WITH\_VARIABLES
    : templates.TAILWIND\_CONFIG
}

// Write tailwind config.
await fs.writeFile(
  config.resolvedPaths.tailwindConfig,
  template(tailwindConfigTemplate)({
    extension,
    prefix: config.tailwind.prefix,
  }),
  "utf8"
)
```

extension — This extension is used in later parts of code that deal with writing cn file.

tailwindConfigExtension — The path.extname() method returns the extension of the path, from the last occurrence of the . (period) character to end of string in the last portion of the path.

Depending on the tailwindConfigExtension and config.tailwind.cssVariables, tailwindConfigTemplate is set to a value using [templates](https://github.com/shadcn-ui/ui/blob/main/packages/cli/src/utils/templates.ts). Templates is imported from [utils/templates](https://github.com/shadcn-ui/ui/blob/main/packages/cli/src/utils/templates.ts) and contains some variables initialised with values related to tailwind config

Write to tailwind config file — using fs.writeFile, tailwindConfig is updated but there is a catch. template from lodash.template is used to perform some replacements before writing to the file.

An example usage of lodash.template from the docs:

```js
// Use the "interpolate" delimiter to create a compiled template.
var compiled = \_.template('hello <%= user %>!');
compiled({ 'user': 'fred' });
// => 'hello fred!'
```

> _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/commands/init.ts#L331C3-L356C4](https://github.com/shadcn-ui/ui/blob/main/packages/cli/src/commands/init.ts#L331C3-L356C4)
2.  [https://github.com/shadcn-ui/ui/blob/main/packages/cli/src/utils/get-config.ts#L53](https://github.com/shadcn-ui/ui/blob/main/packages/cli/src/utils/get-config.ts#L53)
3.  [https://github.com/shadcn-ui/ui/blob/main/packages/cli/src/utils/get-config.ts#L43](https://github.com/shadcn-ui/ui/blob/main/packages/cli/src/utils/get-config.ts#L43)
4.  [https://github.com/shadcn-ui/ui/blob/main/packages/cli/src/utils/get-config.ts#L20](https://github.com/shadcn-ui/ui/blob/main/packages/cli/src/utils/get-config.ts#L20)
5.  [https://github.com/shadcn-ui/ui/blob/main/packages/cli/src/utils/templates.ts](https://github.com/shadcn-ui/ui/blob/main/packages/cli/src/utils/templates.ts)
6.  [https://lodash.com/docs#template](https://lodash.com/docs#template)