Skip to content

Commit

Permalink
feat: still working on the base init and functionalities for the cli
Browse files Browse the repository at this point in the history
  • Loading branch information
wildduck2 committed Oct 23, 2024
1 parent 9973f34 commit cf822aa
Show file tree
Hide file tree
Showing 37 changed files with 692 additions and 13 deletions.
6 changes: 6 additions & 0 deletions apps/www/ui/public/schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,12 @@
},
"ui": {
"type": "string"
},
"lib": {
"type": "string"
},
"hooks": {
"type": "string"
}
},
"required": ["utils", "components"]
Expand Down
19 changes: 19 additions & 0 deletions packages/cli-t/duck-ui.config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
export default {
// name: 'duck-ui',
// description: 'duck-ui',
// version: '1.0.0'
$schema: 'https://duckui.vercel.app/schema.json',
style: 'default',
rsc: false,
tsx: true,
tailwind: {
config: 'tailwind.config.ts',
css: './test/scss/style.scss',
baseColor: 'zinc',
cssVariables: true
},
aliases: {
components: 'test/components',
utils: 'test/utils'
}
}
7 changes: 7 additions & 0 deletions packages/cli-t/index.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
@tailwind base;
@tailwind components;
@tailwind utilities;

body {
background: red;
}
9 changes: 9 additions & 0 deletions packages/cli-t/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,18 @@
"author": "",
"license": "ISC",
"dependencies": {
"@types/fs-extra": "^11.0.1",
"@types/node": "^17.0.45",
"chalk": "5.2.0",
"commander": "^10.0.0",
"cosmiconfig": "^8.1.3",
"fast-glob": "^3.3.2",
"fs-extra": "^11.1.0",
"globals": "^15.9.0",
"kleur": "^4.1.5",
"log-symbols": "^7.0.0",
"ora": "^6.1.2",
"tsconfig-paths": "^4.2.0",
"type-fest": "^3.8.0",
"zod": "^3.23.8"
}
Expand Down
2 changes: 2 additions & 0 deletions packages/cli-t/src/commands/init/index.ts
Original file line number Diff line number Diff line change
@@ -1,2 +1,4 @@
export * from './init'
export * from './init.constants'
export * from './init.lib'
export * from './init.dto'
9 changes: 9 additions & 0 deletions packages/cli-t/src/commands/init/init.dto.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import { z } from 'zod'

export const init_options_schema = z.object({
yes: z.boolean().default(false),
defaults: z.boolean().default(false),
cwd: z.string().default(process.cwd())
})

export type InitOptions = z.infer<typeof init_options_schema>
44 changes: 44 additions & 0 deletions packages/cli-t/src/commands/init/init.lib.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
import path from 'path'
import { init_options_schema, InitOptions } from './init.dto'
import {
checkTailwindCssInstalled,
checkTypeScriptInstalled,
get_project_config
} from '@/src/utils'
import { spinner } from '@/src/utils/spinner'
import { REGISTRY_URL } from '@/src/main'

export async function init_command_action(opt: InitOptions) {
const options = init_options_schema.parse(opt)
const cwd = path.resolve(options.cwd)

await checkTailwindCssInstalled(cwd)
const config = await get_project_config(cwd)

console.log(config)
}

export async function init_command() {}
function isUrl(path: string) {
try {
new URL(path)
return true
} catch (error) {
return false
}
}

function getRegistryUrl(path: string) {
if (isUrl(path)) {
// If the url contains /chat/b/, we assume it's the v0 registry.
//NOTE: We need to add the /json suffix if it's missing.
const url = new URL(path)
if (url.pathname.match(/\/chat\/b\//) && !url.pathname.endsWith('/json')) {
url.pathname = `${url.pathname}/json`
}

return url.toString()
}

return `${REGISTRY_URL}/${path}`
}
5 changes: 2 additions & 3 deletions packages/cli-t/src/commands/init/init.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { Command } from 'commander'
import { init_command_config } from './init.constants'
import { init_command_action } from './init.lib'

const { name, description, option_1, option_2, option_3 } = init_command_config

Expand All @@ -11,9 +12,7 @@ export function init_command(): Command {
.option(option_1.flags, option_1.description, option_1.defaultValue)
.option(option_2.flags, option_2.description, option_2.defaultValue)
.option(option_3.flags, option_3.description, option_3.defaultValue)
.action((opt) => {
console.log('init', opt)
})
.action(init_command_action)

return init_command
}
4 changes: 4 additions & 0 deletions packages/cli-t/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,5 +4,9 @@
import { init } from './main'

// INIT START

process.on('SIGINT', () => process.exit(0))
process.on('SIGTERM', () => process.exit(0))

init()
// INIT END
2 changes: 2 additions & 0 deletions packages/cli-t/src/main/main.constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,5 @@ export const config = {
'This is the main file of the duck-ui CLI application written with TypeScript',
version: '1.0.0'
}

export const REGISTRY_URL = 'https://duckui.vercel.app/registry'
2 changes: 1 addition & 1 deletion packages/cli-t/src/main/main.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { Command } from 'commander'
import { getPackageJson } from '../utils/get-package-json'
import { config } from './main.constants'
import { init_command } from '../commands/init'
import { getPackageJson } from '../utils'

export function init() {
const duck_ui = new Command()
Expand Down
45 changes: 45 additions & 0 deletions packages/cli-t/src/utils/checkers/checkers.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
import {
IGNORED_DIRECTORIES,
tailwindCssInstallationGuide
} from '../get-project-info/get-project-info.constants'
import { logger } from '../logger'
import fs from 'fs-extra'
import path from 'path'
import fg from 'fast-glob'

// Check if TypeScript is installed
export async function checkTypeScriptInstalled(cwd: string) {
return fs.pathExists(path.resolve(cwd, 'tsconfig.json'))
}

// Check if TailwindCss is installed
export async function checkTailwindCssInstalled(cwd: string) {
const tailwindcss = fg.globSync('tailwind.config.*', {
cwd,
deep: 3,
ignore: IGNORED_DIRECTORIES
})

if (!tailwindcss.length) {
logger.error(`TailwindCss is not configured in this directory.`).break()
logger.info(...Object.values(tailwindCssInstallationGuide)).break()
}

return true
}

// Check if the working directory exists
export function checkDirectoryExist(cwd: string): typeof logger | undefined {
if (!fs.lstatSync(cwd).isDirectory()) {
return logger.error(`The working directory ${cwd} does not exist.`)
}
}

// Check if the project is valid
export function checkProjectIsValid(cwd: string): void {
// Check if the cwd exists && it's a directory
checkDirectoryExist(cwd)

// Check TailwindCss is configured
checkTailwindCssInstalled(cwd)
}
1 change: 1 addition & 0 deletions packages/cli-t/src/utils/checkers/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from './checkers'
9 changes: 0 additions & 9 deletions packages/cli-t/src/utils/get-package-json.ts

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import { cosmiconfig } from 'cosmiconfig'

export const explorer = cosmiconfig('duck-ui', {
searchPlaces: ['duck-ui.config.js', 'duck-ui.config.ts']
})
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import { z } from 'zod'

export const raw_config_schema = z
.object({
$schema: z.string().optional(),
style: z.string(),
rsc: z.coerce.boolean().default(false),
tsx: z.coerce.boolean().default(true),
tailwind: z.object({
config: z.string(),
css: z.string(),
baseColor: z.string(),
cssVariables: z.boolean().default(true),
prefix: z.string().default('').optional()
}),
aliases: z.object({
components: z.string(),
hooks: z.string().optional(),
pages: z.string().optional(),
utils: z.string(),
lib: z.string().optional(),
ui: z.string().optional()
})
})
.strict()

export type RawConfigType = z.infer<typeof raw_config_schema>

export const config_cchema = raw_config_schema.extend({
resolvedPaths: z.object({
tailwindConfig: z.string(),
tailwindCss: z.string(),
utils: z.string(),
components: z.string(),
ui: z.string()
})
})

export type ConfigType = z.infer<typeof configSchema>
104 changes: 104 additions & 0 deletions packages/cli-t/src/utils/get-project-config/get-project-config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
import { loadConfig } from 'tsconfig-paths'
import { logger } from '../logger'
import { explorer } from './get-project-config.constants'
import { resolve_import } from '../resolve-import'
import {
config_cchema,
raw_config_schema,
RawConfigType
} from './get-project-config.dto'
import path from 'path'
import {
get_tailwindcss_file,
get_ts_config_alias_prefix
} from '../get-project-info'
import { checkTypeScriptInstalled } from '../checkers'
import { get_project_type } from '../get-project-type'

export async function get_raw_config(
cwd: string
): Promise<RawConfigType | null> {
try {
const rawConfig = await explorer.search(cwd)
if (!rawConfig) {
return null
}

return raw_config_schema.parse(rawConfig.config)
} catch (error) {
logger.error(`Invalid configuration found in ${cwd}/components.json.`)
process.exit(1)
}
}

export async function get_config(cwd: string) {
const config = await get_raw_config(cwd)

if (!config) {
return null
}

return await resolve_config_paths(cwd, config)
}

// Resolve Config Paths
export async function resolve_config_paths(cwd: string, config: RawConfigType) {
const ts_config = loadConfig(cwd)

if (ts_config.resultType === 'failed') {
return logger.error(
`Failed to leaod ${config.tsx ? 'tsconfig' : 'jsconfig'}.json. ${ts_config.message ?? ''}`.trim()
)
}

return config_cchema.parse({
...config,
resolvedPaths: {
tailwindConfig: path.resolve(cwd, config.tailwind.config),
tailwindCss: path.resolve(cwd, config.tailwind.css),
utils: await resolve_import(config.aliases.utils, ts_config),
components: await resolve_import(config.aliases.components, ts_config),
ui: config.aliases.ui
? await resolve_import(config.aliases.ui, ts_config)
: await resolve_import(config.aliases.components, ts_config)
}
})
}

export async function get_project_config(cwd: string) {
const project_config = get_config(cwd)

if (project_config) {
return project_config
}

const project_type = await get_project_type(cwd)
const tailwindcss_file = await get_tailwindcss_file(cwd)
const ts_config_alias_prefix = await get_ts_config_alias_prefix(cwd)

if (!project_type || !tailwindcss_file || !ts_config_alias_prefix) {
return null
}

const is_tsx = await checkTypeScriptInstalled(cwd)

const config: RawConfigType = {
$schema: 'https://duckui.vercel.app/schema.json',
rsc: ['next-app', 'next-app-src'].includes(project_type),
tsx: is_tsx,
style: 'default',
tailwind: {
config: is_tsx ? 'tailwind.config.ts' : 'tailwind.config.js',
baseColor: 'zinc',
css: tailwindcss_file,
cssVariables: true,
prefix: ''
},
aliases: {
utils: `${ts_config_alias_prefix}/lib/utils`,
components: `${ts_config_alias_prefix}/components`
}
}

return resolve_config_paths(cwd, config)
}
1 change: 1 addition & 0 deletions packages/cli-t/src/utils/get-project-config/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from './get-project-config'
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
export const IGNORED_DIRECTORIES = [
'**/node_modules',
'**/.git',
'**/dist',
'**/.next',
'**/build',
'**/coverage',
'**/public'
]

export const tailwindCssInstallationGuide = {
info: 'please install TailwindCss: https://github.com/tailwindlabs/tailwindcss'
}
Loading

0 comments on commit cf822aa

Please sign in to comment.