Skip to content

Commit

Permalink
feat: cli init
Browse files Browse the repository at this point in the history
feat: add packages detect & check

featt: command init

feat: resolve package & detect framework

feat: init

feat: prompts & components.json

chore: add test script

chore: workflow add test

fix: check

feat: init
  • Loading branch information
iamdin committed Oct 11, 2024
1 parent 0a5cfcd commit 13ad491
Show file tree
Hide file tree
Showing 49 changed files with 4,560 additions and 257 deletions.
3 changes: 1 addition & 2 deletions .github/workflows/code-check.yml
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ jobs:
- name: Run Biome
run: biome ci --diagnostic-level=warn

check-types:
types:
runs-on: ubuntu-latest
name: pnpm check-types
steps:
Expand Down Expand Up @@ -50,5 +50,4 @@ jobs:
- name: Install dependencies
run: pnpm install


- run: pnpm check-types
37 changes: 37 additions & 0 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
name: Test

on:
pull_request:
branches: ["*"]

jobs:
test:
runs-on: ubuntu-latest
name: pnpm test
steps:
- uses: actions/checkout@v4

- name: Install Node.js
uses: actions/setup-node@v3
with:
node-version: 20

- uses: pnpm/action-setup@v4
name: Install pnpm
id: pnpm-install

- name: Get pnpm store directory
id: pnpm-cache
run: |
echo "pnpm_cache_dir=$(pnpm store path)" >> $GITHUB_OUTPUT
- uses: actions/cache@v3
name: Setup pnpm cache
with:
path: ${{ steps.pnpm-cache.outputs.pnpm_cache_dir }}
key: ${{ runner.os }}-pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }}
restore-keys: |
${{ runner.os }}-pnpm-store-
- name: Install dependencies
run: pnpm install

- run: pnpm test
7 changes: 3 additions & 4 deletions biome.jsonc
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
{
"$schema": "https://biomejs.dev/schemas/1.8.3/schema.json",
"files": {
"ignore": ["**/dist/**", "**/.turbo/**", "**/.astro/**", "**/public/**"]
},
"organizeImports": {
"enabled": true
},
Expand Down Expand Up @@ -27,9 +30,5 @@
"semicolons": "asNeeded",
"trailingCommas": "es5"
}
},

"files": {
"ignore": ["dist", ".turbo", ".vercel", ".astro", "public"]
}
}
9 changes: 6 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,9 @@
"scripts": {
"bump": "taze -r -l",
"dev": "turbo dev",
"test": "turbo run test",
"build": "turbo run build",
"check": "biome check",
"check:write": "biome check --write",
"repocheck": "sherif",
"check-types": "turbo run check-types",
"clean": "rimraf -g **/node_modules **/dist **/.turbo **/.astro",
Expand All @@ -25,13 +25,16 @@
"simple-git-hooks": "^2.11.1",
"taze": "^0.17.2",
"turbo": "^2.1.3",
"typescript": "^5.6.2"
"typescript": "^5.6.2",
"vite": "^5.4.3"
},
"packageManager": "[email protected]",
"simple-git-hooks": {
"pre-commit": "pnpm lint-staged"
},
"lint-staged": {
"*": "pnpm check:write"
"*": [
"biome check --write --no-errors-on-unmatched --files-ignore-unknown=true"
]
}
}
43 changes: 43 additions & 0 deletions packages/cli/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
{
"name": "shipbase-ui",
"version": "0.0.0",
"type": "module",
"description": "Add components to your apps.",
"exports": "./dist/index.js",
"bin": "./dist/index.js",
"publishConfig": {
"access": "public"
},
"repository": {
"type": "git",
"url": "https://github.com/shipbase/ui.git",
"directory": "packages/cli"
},
"author": "@shipbase",
"license": "MIT",
"files": ["dist"],
"keywords": ["shipbase", "components", "ui", "tailwind", "ark-ui"],
"scripts": {
"dev": "tsup --watch",
"build": "tsup",
"check-types": "tsc --noEmit",
"test": "vitest"
},
"devDependencies": {
"@types/prompts": "^2.4.9",
"@ui/lib": "workspace:*",
"@ui/tsconfig": "workspace:*",
"tsup": "^8.2.4",
"vitest": "^2.0.5"
},
"dependencies": {
"cac": "^6.7.14",
"kleur": "^4.1.5",
"mlly": "^1.7.1",
"nypm": "^0.3.11",
"ora": "^8.1.0",
"prompts": "^2.4.2",
"ufo": "^1.5.4",
"zod": "^3.23.8"
}
}
178 changes: 178 additions & 0 deletions packages/cli/src/commands/init.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,178 @@
import fs from "node:fs/promises"
import path from "node:path"
import { safeResolveAndRead } from "@ui/lib/utils/mlly"
import { cyan } from "kleur/colors"
import ora from "ora"
import prompts from "prompts"
import {
type Config,
type UserConfig,
userConfigSchema,
} from "../config/schema"
import {
DEFAULT_COMPONENTS,
DEFAULT_STYLE,
DEFAULT_TAILWIND_BASE_COLOR,
DEFAULT_TAILWIND_CONFIG,
DEFAULT_UTILS,
LIBRARIES,
TAILWIND_CSS_FILE_PATH,
} from "../constants"
import { logger } from "../utils/logger"
import { prepare } from "../utils/prepare"

export async function init({ library }: { library?: Config["library"] }) {
let userConfig: UserConfig

try {
await prepare()
userConfig = await promptConfig({ library })

const { write } = await prompts({
type: "confirm",
name: "write",
message: `Write configuration to ${cyan("components.json")}. Proceed?`,
initial: true,
})

// Write components.json.
if (write) {
const writeSpinner = ora("Writing components.json...").start()
const targetPath = path.resolve(process.cwd(), "components.json")
await fs.writeFile(
targetPath,
JSON.stringify(userConfig, null, 2),
"utf8"
)
writeSpinner.succeed()
}
} catch (error: unknown) {
if (error instanceof Error) {
logger.error(error.message)
} else {
logger.error("unknown errors")
}
process.exit(1)
}
}

async function validateFilePath(relative: string) {
const file = await safeResolveAndRead(path.resolve(process.cwd(), relative))
if (!file.success)
return "File not found. Please check the path and try again."
return true
}

async function validateTailwindGlobalCss(relative: string) {
const file = await safeResolveAndRead(path.resolve(process.cwd(), relative))
if (!file.success)
return "File not found. Please check the path and try again."
if (!file.result.includes("@tailwind base")) {
return "The file does not include '@tailwind base'. Please ensure it contains the necessary Tailwind directives."
}
return true
}

async function promptConfig(defaultConfig: Partial<UserConfig>) {
const result = await prompts(
[
{
type:
defaultConfig.library &&
LIBRARIES.findIndex((lib) => lib.value === defaultConfig.library) > -1
? null
: "select",
name: "library",
initial: defaultConfig.library,
message: "Pick your favorite library",
choices: LIBRARIES,
},
{
type: null, // TODO: Implement toggle
name: "typescript",
message: `Would you like to use ${cyan("TypeScript")} (recommended)?`,
initial: true,
active: "yes",
inactive: "no",
},
{
type: null, // TODO: Implement select
name: "style",
message: `Which ${cyan("style")} would you like to use?`,
choices: [{ title: "Default", value: "default" }],
initial: DEFAULT_STYLE,
},
{
type: null, // TODO: Implement select
name: "tailwindBaseColor",
message: `Which color would you like to use as the ${cyan("base color")}?`,
choices: [{ title: "Slate", value: "slate" }],
initial: DEFAULT_TAILWIND_BASE_COLOR,
},
{
type: "text",
name: "tailwindCss",
message: `Where is your ${cyan("global CSS")} file?`,
initial: TAILWIND_CSS_FILE_PATH.vite,
validate: validateTailwindGlobalCss,
},
{
type: "toggle",
name: "tailwindCssVariables",
message: `Would you like to use ${cyan("CSS variables")} for theming?`,
initial: true,
active: "yes",
inactive: "no",
},
{
type: null, // TODO: Implement text
name: "tailwindPrefix",
message: `Are you using a custom ${cyan(
"tailwind prefix eg. tw-"
)}? (Leave blank if not)`,
initial: "",
},
{
type: "text",
name: "tailwindConfig",
message: `Where is your ${cyan("tailwind.config.js")} located?`,
initial: DEFAULT_TAILWIND_CONFIG,
validate: validateFilePath,
},
{
type: "text",
name: "aliasComponents",
message: `Configure the import alias for ${cyan("components")}:`,
initial: DEFAULT_COMPONENTS,
},
{
type: "text",
name: "utils",
message: `Configure the import alias for ${cyan("utils")}:`,
initial: DEFAULT_UTILS,
},
],
{
onCancel: () => {
throw new Error("✖ Operation cancelled")
},
}
)

return userConfigSchema.parse({
library: defaultConfig.library ?? result.library,
style: "default",
tsx: true,
tailwind: {
config: result.tailwindConfig,
css: result.tailwindCss,
baseColor: "slate",
cssVariables: result.tailwindCssVariables,
prefix: "",
},
aliases: {
components: result.aliasComponents,
utils: result.utils,
},
})
}
19 changes: 19 additions & 0 deletions packages/cli/src/config/config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import type { Config } from "./schema"

export async function resolveConfig() {
// return {
// framework: "react",
// style: "css",
// tailwind: {
// config: "tailwind.config.js",
// css: "src/index.css",
// baseColor: "blue",
// cssVariables: true,
// prefix: "",
// },
// aliases: {
// components: "src/components",
// utils: "src /utils",
// },
// } satisfies Config
}
36 changes: 36 additions & 0 deletions packages/cli/src/config/schema.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import { z } from "zod"

export const userConfigSchema = z
.object({
// $schema: z.string().optional(),
library: z.enum(["react", "vue"]),
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(),
utils: z.string(),
}),
})
.strict()

export type UserConfig = z.infer<typeof userConfigSchema>

export const configSchema = userConfigSchema.extend({
resolvedPaths: z.object({
cwd: z.string(),
tailwindConfig: z.string(),
tailwindCss: z.string(),
components: z.string(),
utils: z.string(),
}),
})

export type Config = z.infer<typeof configSchema>
11 changes: 11 additions & 0 deletions packages/cli/src/constants/config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
export const DEFAULT_LIBRARY = "react"

export const DEFAULT_STYLE = "default"
export const DEFAULT_COMPONENTS = "@/components"
export const DEFAULT_UTILS = "@/lib/utils"
export const DEFAULT_TAILWIND_CONFIG = "tailwind.config.js"
export const DEFAULT_TAILWIND_BASE_COLOR = "slate"

export const TAILWIND_CSS_FILE_PATH = {
vite: "src/assets/base.css",
}
Loading

0 comments on commit 13ad491

Please sign in to comment.