mirror of
https://github.com/shadcn-ui/ui.git
synced 2026-06-26 22:26:05 +00:00
feat(cli): add support for config file (#245)
* feat(cli): add config files and examples in docs * docs(www): remove cli advanced options --------- Co-authored-by: shadcn <m@shadcn.com>
This commit is contained in:
@@ -45,6 +45,7 @@
|
||||
"dependencies": {
|
||||
"chalk": "5.2.0",
|
||||
"commander": "^10.0.0",
|
||||
"cosmiconfig": "^8.1.3",
|
||||
"execa": "^7.0.0",
|
||||
"fs-extra": "^11.1.0",
|
||||
"https-proxy-agent": "^6.2.0",
|
||||
|
||||
@@ -7,6 +7,7 @@ import ora from "ora"
|
||||
import prompts from "prompts"
|
||||
|
||||
import { Component, getAvailableComponents } from "./utils/get-components"
|
||||
import { Config, getCliConfig } from "./utils/get-config"
|
||||
import { getPackageInfo } from "./utils/get-package-info"
|
||||
import { getPackageManager } from "./utils/get-package-manager"
|
||||
import { getProjectInfo } from "./utils/get-project-info"
|
||||
@@ -27,6 +28,8 @@ const PROJECT_DEPENDENCIES = [
|
||||
async function main() {
|
||||
const packageInfo = await getPackageInfo()
|
||||
const projectInfo = await getProjectInfo()
|
||||
const cliConfig = await getCliConfig()
|
||||
|
||||
const packageManager = getPackageManager()
|
||||
|
||||
const program = new Command()
|
||||
@@ -144,11 +147,7 @@ async function main() {
|
||||
selectedComponents = await promptForComponents(availableComponents)
|
||||
}
|
||||
|
||||
const dir = await promptForDestinationDir(
|
||||
projectInfo?.srcComponentsUiDir
|
||||
? "./src/components/ui"
|
||||
: "./components/ui"
|
||||
)
|
||||
const dir = await promptForDestinationDir(cliConfig)
|
||||
|
||||
if (!selectedComponents?.length) {
|
||||
logger.warn("No components selected. Nothing to install.")
|
||||
@@ -171,10 +170,16 @@ async function main() {
|
||||
|
||||
// Write the files.
|
||||
for (const file of component.files) {
|
||||
// Replace alias with the project's alias.
|
||||
if (projectInfo?.alias) {
|
||||
file.content = file.content.replace(/@\//g, projectInfo.alias)
|
||||
}
|
||||
// because these are the predefined routes for the utils and components we can
|
||||
// use them as a replacer for the defined routes on the installed file.
|
||||
file.content = file.content.replace(
|
||||
"@/lib/utils",
|
||||
cliConfig.utilsLocation
|
||||
)
|
||||
file.content = file.content.replace(
|
||||
"@/components/ui/",
|
||||
cliConfig.componentDirAlias
|
||||
)
|
||||
|
||||
const filePath = path.resolve(dir, file.name)
|
||||
await fs.writeFile(filePath, file.content)
|
||||
@@ -210,13 +215,17 @@ async function promptForComponents(components: Component[]) {
|
||||
return selectedComponents
|
||||
}
|
||||
|
||||
async function promptForDestinationDir(installDir = "./components/ui") {
|
||||
async function promptForDestinationDir(cliConfig: Config) {
|
||||
if (!cliConfig.askForDir) {
|
||||
return cliConfig.componentsDirInstallation
|
||||
}
|
||||
|
||||
const { dir } = await prompts([
|
||||
{
|
||||
type: "text",
|
||||
name: "dir",
|
||||
message: "Where would you like to install the component(s)?",
|
||||
initial: installDir,
|
||||
initial: cliConfig.componentsDirInstallation,
|
||||
},
|
||||
])
|
||||
|
||||
|
||||
63
packages/cli/src/utils/get-config.ts
Normal file
63
packages/cli/src/utils/get-config.ts
Normal file
@@ -0,0 +1,63 @@
|
||||
import { cosmiconfig } from "cosmiconfig"
|
||||
import * as z from "zod"
|
||||
|
||||
export const COMPONENTS_DIR = "./components/ui/"
|
||||
export const UTILS_LOCATION = "@/lib/utils"
|
||||
export const COMPONENT_ALIAS = "@/components/ui/"
|
||||
|
||||
/**
|
||||
* this is the name of the key we are looking for, the following are the intended values to look for:
|
||||
* - shadcn-ui property in package.json
|
||||
* - .shadcn-uirc file in JSON or YAML format
|
||||
* - .shadcn-uirc.json, .shadcn-uirc.yaml, .shadcn-uirc.yml, .shadcn-uirc.js, or .shadcn-uirc.cjs file
|
||||
* - shadcn-uirc, shadcn-uirc.json, shadcn-uirc.yaml, shadcn-uirc.yml, shadcn-uirc.js or shadcn-uirc.cjs file inside a .config subdirectory
|
||||
* - shadcn-ui.config.js or shadcn-ui.config.cjs CommonJS module exporting an object
|
||||
*/
|
||||
const explorer = cosmiconfig("shadcn-ui")
|
||||
|
||||
const configSchema = z.object({
|
||||
componentsDirInstallation: z.string(),
|
||||
askForDir: z.boolean(),
|
||||
utilsLocation: z.string(),
|
||||
componentDirAlias: z.string(),
|
||||
})
|
||||
|
||||
export type Config = z.infer<typeof configSchema>
|
||||
|
||||
export async function getCliConfig(): Promise<Config> {
|
||||
const defaultConfig: Config = {
|
||||
componentsDirInstallation: COMPONENTS_DIR,
|
||||
askForDir: true,
|
||||
utilsLocation: UTILS_LOCATION,
|
||||
componentDirAlias: COMPONENT_ALIAS,
|
||||
}
|
||||
|
||||
const userDefinedConfig = await getConfigFromEverywhere()
|
||||
|
||||
return {
|
||||
...defaultConfig,
|
||||
...userDefinedConfig,
|
||||
askForDir: !userDefinedConfig.componentsDirInstallation,
|
||||
}
|
||||
}
|
||||
|
||||
export async function getConfigFromEverywhere() {
|
||||
try {
|
||||
const configResult = await explorer.search()
|
||||
|
||||
if (!configResult) {
|
||||
// we should always return an object so we can then merge with
|
||||
// the base config
|
||||
return {}
|
||||
}
|
||||
|
||||
const { config } = configResult
|
||||
|
||||
const parsedConfig = configSchema.partial().parse(config)
|
||||
return parsedConfig
|
||||
} catch (e) {
|
||||
// lets just show the error to the user to aware about the issue, but lets not handle it.
|
||||
console.log(e)
|
||||
return {}
|
||||
}
|
||||
}
|
||||
@@ -5,7 +5,6 @@ import fs from "fs-extra"
|
||||
export async function getProjectInfo() {
|
||||
const info = {
|
||||
tsconfig: null,
|
||||
alias: null,
|
||||
srcDir: false,
|
||||
appDir: false,
|
||||
srcComponentsUiDir: false,
|
||||
@@ -14,12 +13,9 @@ export async function getProjectInfo() {
|
||||
|
||||
try {
|
||||
const tsconfig = await getTsConfig()
|
||||
const paths = tsconfig?.compilerOptions?.paths
|
||||
const alias = paths ? Object.keys(paths)[0].replace("*", "") : null
|
||||
|
||||
return {
|
||||
tsconfig,
|
||||
alias,
|
||||
srcDir: existsSync(path.resolve("./src")),
|
||||
appDir:
|
||||
existsSync(path.resolve("./app")) ||
|
||||
|
||||
3
pnpm-lock.yaml
generated
3
pnpm-lock.yaml
generated
@@ -317,6 +317,9 @@ importers:
|
||||
commander:
|
||||
specifier: ^10.0.0
|
||||
version: 10.0.0
|
||||
cosmiconfig:
|
||||
specifier: ^8.1.3
|
||||
version: 8.1.3
|
||||
execa:
|
||||
specifier: ^7.0.0
|
||||
version: 7.0.0
|
||||
|
||||
Reference in New Issue
Block a user