feat: use unjs modules and improve cli from main shadcn-ui source, custom ui dir (#324)
* feat: add devDeps, add nypm for installing deps
* feat: custom ui dir
* refactor: use consola instead of chalk
* test: ui alias
* refactor: import { z } from 'zod' instead of *, replace node:path with pathe
* chore: add components name to `configFile` option
* chore: update `c12` which fix json5 parse issue
and it also supports .config directory
* chore: update `https-proxy-agent`
* fix: await until dependencies are installed then run detypes process
* feat: add tailwind prefix
* test: tw-prefix snapshot
* chore: add prefix option to init
* test: apply prefix
* fix: tw-prefix parse wrongly
* chore: hide prefix temporarily
---------
Co-authored-by: zernonia <zernonia@gmail.com>
This commit is contained in:
parent
c487137ac5
commit
0e84af73de
|
|
@ -4,7 +4,6 @@ description: Powerful table and datagrids built using TanStack Table.
|
||||||
primitive: https://tanstack.com/table/v8/docs/guide/introduction
|
primitive: https://tanstack.com/table/v8/docs/guide/introduction
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|
||||||
<ComponentPreview name="DataTableDemo" />
|
<ComponentPreview name="DataTableDemo" />
|
||||||
|
|
||||||
## Introduction
|
## Introduction
|
||||||
|
|
@ -56,7 +55,6 @@ npm install @tanstack/vue-table
|
||||||
|
|
||||||
<ComponentPreview name="DataTableColumnPinningDemo" />
|
<ComponentPreview name="DataTableColumnPinningDemo" />
|
||||||
|
|
||||||
|
|
||||||
## Prerequisites
|
## Prerequisites
|
||||||
|
|
||||||
We are going to build a table to show recent payments. Here's what our data looks like:
|
We are going to build a table to show recent payments. Here's what our data looks like:
|
||||||
|
|
@ -219,7 +217,6 @@ const table = useVueTable({
|
||||||
|
|
||||||
</Callout>
|
</Callout>
|
||||||
|
|
||||||
|
|
||||||
### Render the table
|
### Render the table
|
||||||
|
|
||||||
Finally, we'll render our table in our index component.
|
Finally, we'll render our table in our index component.
|
||||||
|
|
@ -270,7 +267,6 @@ Let's format the amount cell to display the dollar amount. We'll also align the
|
||||||
|
|
||||||
Update the `header` and `cell` definitions for amount as follows:
|
Update the `header` and `cell` definitions for amount as follows:
|
||||||
|
|
||||||
|
|
||||||
```ts:line-numbers title="components/payments/columns.ts" {5-17}
|
```ts:line-numbers title="components/payments/columns.ts" {5-17}
|
||||||
import { h } from 'vue'
|
import { h } from 'vue'
|
||||||
|
|
||||||
|
|
@ -345,7 +341,6 @@ function copy(id: string) {
|
||||||
|
|
||||||
Update our columns definition to add a new `actions` column. The `actions` cell returns a `<Dropdown />` component.
|
Update our columns definition to add a new `actions` column. The `actions` cell returns a `<Dropdown />` component.
|
||||||
|
|
||||||
|
|
||||||
```ts:line-numbers showLineNumber{2,6-16}
|
```ts:line-numbers showLineNumber{2,6-16}
|
||||||
import { ColumnDef } from "@tanstack/vue-table"
|
import { ColumnDef } from "@tanstack/vue-table"
|
||||||
import DropdownAction from '@/components/DataTableDropDown.vue'
|
import DropdownAction from '@/components/DataTableDropDown.vue'
|
||||||
|
|
|
||||||
|
|
@ -42,6 +42,9 @@
|
||||||
},
|
},
|
||||||
"components": {
|
"components": {
|
||||||
"type": "string"
|
"type": "string"
|
||||||
|
},
|
||||||
|
"ui": {
|
||||||
|
"type": "string"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"required": ["utils", "components"]
|
"required": ["utils", "components"]
|
||||||
|
|
|
||||||
|
|
@ -24,6 +24,7 @@ export default antfu(
|
||||||
'no-tabs': 0,
|
'no-tabs': 0,
|
||||||
'import/first': 0,
|
'import/first': 0,
|
||||||
'node/prefer-global/process': 0,
|
'node/prefer-global/process': 0,
|
||||||
|
'style/no-tabs': 0,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -32,7 +32,7 @@
|
||||||
"dev": "tsup --watch",
|
"dev": "tsup --watch",
|
||||||
"build": "tsup",
|
"build": "tsup",
|
||||||
"typecheck": "tsc --noEmit",
|
"typecheck": "tsc --noEmit",
|
||||||
"clean": "rimraf dist && rimraf components",
|
"clean": "node ./scripts/rimraf.js",
|
||||||
"lint": "eslint .",
|
"lint": "eslint .",
|
||||||
"lint:fix": "eslint --fix .",
|
"lint:fix": "eslint --fix .",
|
||||||
"start:dev": "COMPONENTS_REGISTRY_URL=http://localhost:3001 node dist/index.js",
|
"start:dev": "COMPONENTS_REGISTRY_URL=http://localhost:3001 node dist/index.js",
|
||||||
|
|
@ -45,41 +45,39 @@
|
||||||
"test:ui": "vitest --ui"
|
"test:ui": "vitest --ui"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@antfu/ni": "^0.21.8",
|
"@babel/core": "^7.24.0",
|
||||||
"@babel/core": "^7.22.17",
|
"@babel/parser": "^7.24.0",
|
||||||
"@babel/parser": "^7.22.16",
|
|
||||||
"@babel/plugin-transform-typescript": "^7.22.15",
|
|
||||||
"@vue/compiler-sfc": "^3.4",
|
"@vue/compiler-sfc": "^3.4",
|
||||||
"chalk": "5.3.0",
|
"c12": "^1.9.0",
|
||||||
"commander": "^11.0.0",
|
"commander": "^12.0.0",
|
||||||
"cosmiconfig": "^8.3.6",
|
"consola": "^3.2.3",
|
||||||
"detype": "npm:detypes@^0.7.9",
|
"detype": "npm:detypes@^0.7.9",
|
||||||
"diff": "^5.1.0",
|
"diff": "^5.2.0",
|
||||||
"execa": "^8.0.1",
|
"fs-extra": "^11.2.0",
|
||||||
"fs-extra": "^11.1.1",
|
"https-proxy-agent": "^7.0.4",
|
||||||
"https-proxy-agent": "^7.0.2",
|
|
||||||
"lodash.template": "^4.5.0",
|
"lodash.template": "^4.5.0",
|
||||||
"magic-string": "^0.30.3",
|
"magic-string": "^0.30.8",
|
||||||
"node-fetch": "^3.3.2",
|
"nypm": "^0.3.8",
|
||||||
"ora": "^7.0.1",
|
"ofetch": "^1.3.3",
|
||||||
|
"ora": "^8.0.1",
|
||||||
|
"pathe": "^1.1.2",
|
||||||
"prompts": "^2.4.2",
|
"prompts": "^2.4.2",
|
||||||
"radix-vue": "^1.4.8",
|
"radix-vue": "^1.4.9",
|
||||||
"recast": "^0.23.4",
|
"ts-morph": "^21.0.1",
|
||||||
"rimraf": "^5.0.1",
|
|
||||||
"ts-morph": "^19.0.0",
|
|
||||||
"tsconfig-paths": "^4.2.0",
|
"tsconfig-paths": "^4.2.0",
|
||||||
"vite-tsconfig-paths": "^4.2.1",
|
"zod": "^3.22.4"
|
||||||
"zod": "^3.22.2"
|
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/babel__core": "^7.20.1",
|
"@types/babel__core": "^7.20.5",
|
||||||
"@types/diff": "^5.0.3",
|
"@types/diff": "^5.0.9",
|
||||||
"@types/fs-extra": "^11.0.1",
|
"@types/fs-extra": "^11.0.4",
|
||||||
"@types/lodash.template": "^4.5.1",
|
"@types/lodash.template": "^4.5.3",
|
||||||
"@types/prompts": "^2.4.4",
|
"@types/node": "^20.11.24",
|
||||||
|
"@types/prompts": "^2.4.9",
|
||||||
"@vitest/ui": "^0.34.4",
|
"@vitest/ui": "^0.34.4",
|
||||||
"tsup": "^7.2.0",
|
"tsup": "^8.0.2",
|
||||||
"type-fest": "^4.3.1",
|
"type-fest": "^4.10.3",
|
||||||
"typescript": "^5.2.2"
|
"typescript": "^5.3.3",
|
||||||
|
"vite-tsconfig-paths": "^4.3.1"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
10
packages/cli/scripts/rimraf.js
Normal file
10
packages/cli/scripts/rimraf.js
Normal file
|
|
@ -0,0 +1,10 @@
|
||||||
|
import fsp from 'node:fs/promises'
|
||||||
|
|
||||||
|
function rmdir(dirs) {
|
||||||
|
dirs.forEach(async (dir) => {
|
||||||
|
await fsp.unlink(dir).catch(() => {})
|
||||||
|
await fsp.rm(dir, { recursive: true, force: true }).catch(() => {})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
rmdir(['dist', 'components'])
|
||||||
|
|
@ -1,17 +1,16 @@
|
||||||
import { existsSync, promises as fs, rmSync } from 'node:fs'
|
import { existsSync, promises as fs, rmSync } from 'node:fs'
|
||||||
import path from 'node:path'
|
|
||||||
import process from 'node:process'
|
import process from 'node:process'
|
||||||
import chalk from 'chalk'
|
import path from 'pathe'
|
||||||
|
import { consola } from 'consola'
|
||||||
|
import { colors } from 'consola/utils'
|
||||||
import { Command } from 'commander'
|
import { Command } from 'commander'
|
||||||
import { execa } from 'execa'
|
|
||||||
import ora from 'ora'
|
import ora from 'ora'
|
||||||
import prompts from 'prompts'
|
import prompts from 'prompts'
|
||||||
import * as z from 'zod'
|
import { z } from 'zod'
|
||||||
|
import { addDependency, addDevDependency } from 'nypm'
|
||||||
import { transform } from '@/src/utils/transformers'
|
import { transform } from '@/src/utils/transformers'
|
||||||
import { getConfig } from '@/src/utils/get-config'
|
import { getConfig } from '@/src/utils/get-config'
|
||||||
import { getPackageManager } from '@/src/utils/get-package-manager'
|
|
||||||
import { handleError } from '@/src/utils/handle-error'
|
import { handleError } from '@/src/utils/handle-error'
|
||||||
import { logger } from '@/src/utils/logger'
|
|
||||||
import {
|
import {
|
||||||
fetchTree,
|
fetchTree,
|
||||||
getItemTargetPath,
|
getItemTargetPath,
|
||||||
|
|
@ -52,15 +51,15 @@ export const add = new Command()
|
||||||
const cwd = path.resolve(options.cwd)
|
const cwd = path.resolve(options.cwd)
|
||||||
|
|
||||||
if (!existsSync(cwd)) {
|
if (!existsSync(cwd)) {
|
||||||
logger.error(`The path ${cwd} does not exist. Please try again.`)
|
consola.error(`The path ${cwd} does not exist. Please try again.`)
|
||||||
process.exit(1)
|
process.exit(1)
|
||||||
}
|
}
|
||||||
|
|
||||||
const config = await getConfig(cwd)
|
const config = await getConfig(cwd)
|
||||||
|
|
||||||
if (!config) {
|
if (!config) {
|
||||||
logger.warn(
|
consola.warn(`Configuration is missing. Please run ${colors.green('init')} to create a components.json file.`)
|
||||||
`Configuration is missing. Please run ${chalk.green('init')} to create a components.json file.`,
|
|
||||||
)
|
|
||||||
process.exit(1)
|
process.exit(1)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -88,7 +87,7 @@ export const add = new Command()
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!selectedComponents?.length) {
|
if (!selectedComponents?.length) {
|
||||||
logger.warn('No components selected. Exiting.')
|
consola.warn('No components selected. Exiting.')
|
||||||
process.exit(0)
|
process.exit(0)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -97,7 +96,7 @@ export const add = new Command()
|
||||||
const baseColor = await getRegistryBaseColor(config.tailwind.baseColor)
|
const baseColor = await getRegistryBaseColor(config.tailwind.baseColor)
|
||||||
|
|
||||||
if (!payload.length) {
|
if (!payload.length) {
|
||||||
logger.warn('Selected components not found. Exiting.')
|
consola.warn('Selected components not found. Exiting.')
|
||||||
process.exit(0)
|
process.exit(0)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -114,7 +113,6 @@ export const add = new Command()
|
||||||
}
|
}
|
||||||
|
|
||||||
const spinner = ora('Installing components...').start()
|
const spinner = ora('Installing components...').start()
|
||||||
const skippedDeps = new Set<string>()
|
|
||||||
for (const item of payload) {
|
for (const item of payload) {
|
||||||
spinner.text = `Installing ${item.name}...`
|
spinner.text = `Installing ${item.name}...`
|
||||||
const targetDir = getItemTargetPath(
|
const targetDir = getItemTargetPath(
|
||||||
|
|
@ -144,8 +142,8 @@ export const add = new Command()
|
||||||
})
|
})
|
||||||
|
|
||||||
if (!overwrite) {
|
if (!overwrite) {
|
||||||
logger.info(
|
consola.info(
|
||||||
`Skipped ${item.name}. To overwrite, run with the ${chalk.green(
|
`Skipped ${item.name}. To overwrite, run with the ${colors.green(
|
||||||
'--overwrite',
|
'--overwrite',
|
||||||
)} flag.`,
|
)} flag.`,
|
||||||
)
|
)
|
||||||
|
|
@ -159,6 +157,20 @@ export const add = new Command()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Install dependencies.
|
||||||
|
await Promise.allSettled(
|
||||||
|
[
|
||||||
|
item.dependencies?.length && await addDependency(item.dependencies, {
|
||||||
|
cwd,
|
||||||
|
silent: true,
|
||||||
|
}),
|
||||||
|
item.devDependencies?.length && await addDevDependency(item.devDependencies, {
|
||||||
|
cwd,
|
||||||
|
silent: true,
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
)
|
||||||
|
|
||||||
const componentDir = path.resolve(targetDir, item.name)
|
const componentDir = path.resolve(targetDir, item.name)
|
||||||
if (!existsSync(componentDir))
|
if (!existsSync(componentDir))
|
||||||
await fs.mkdir(componentDir, { recursive: true })
|
await fs.mkdir(componentDir, { recursive: true })
|
||||||
|
|
@ -201,25 +213,6 @@ export const add = new Command()
|
||||||
|
|
||||||
await fs.writeFile(filePath, content)
|
await fs.writeFile(filePath, content)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Install dependencies.
|
|
||||||
if (item.dependencies?.length) {
|
|
||||||
item.dependencies.forEach(dep =>
|
|
||||||
skippedDeps.add(dep),
|
|
||||||
)
|
|
||||||
|
|
||||||
const packageManager = await getPackageManager(cwd)
|
|
||||||
await execa(
|
|
||||||
packageManager,
|
|
||||||
[
|
|
||||||
packageManager === 'npm' ? 'install' : 'add',
|
|
||||||
...item.dependencies,
|
|
||||||
],
|
|
||||||
{
|
|
||||||
cwd,
|
|
||||||
},
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
spinner.succeed('Done.')
|
spinner.succeed('Done.')
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,14 +1,14 @@
|
||||||
import { existsSync, promises as fs } from 'node:fs'
|
import { existsSync, promises as fs } from 'node:fs'
|
||||||
import path from 'node:path'
|
|
||||||
import process from 'node:process'
|
import process from 'node:process'
|
||||||
import chalk from 'chalk'
|
import path from 'pathe'
|
||||||
|
import { consola } from 'consola'
|
||||||
|
import { colors } from 'consola/utils'
|
||||||
import { Command } from 'commander'
|
import { Command } from 'commander'
|
||||||
import { type Change, diffLines } from 'diff'
|
import { type Change, diffLines } from 'diff'
|
||||||
import * as z from 'zod'
|
import { z } from 'zod'
|
||||||
import type { Config } from '@/src/utils/get-config'
|
import type { Config } from '@/src/utils/get-config'
|
||||||
import { getConfig } from '@/src/utils/get-config'
|
import { getConfig } from '@/src/utils/get-config'
|
||||||
import { handleError } from '@/src/utils/handle-error'
|
import { handleError } from '@/src/utils/handle-error'
|
||||||
import { logger } from '@/src/utils/logger'
|
|
||||||
import {
|
import {
|
||||||
fetchTree,
|
fetchTree,
|
||||||
getItemTargetPath,
|
getItemTargetPath,
|
||||||
|
|
@ -45,14 +45,14 @@ export const diff = new Command()
|
||||||
const cwd = path.resolve(options.cwd)
|
const cwd = path.resolve(options.cwd)
|
||||||
|
|
||||||
if (!existsSync(cwd)) {
|
if (!existsSync(cwd)) {
|
||||||
logger.error(`The path ${cwd} does not exist. Please try again.`)
|
consola.error(`The path ${cwd} does not exist. Please try again.`)
|
||||||
process.exit(1)
|
process.exit(1)
|
||||||
}
|
}
|
||||||
|
|
||||||
const config = await getConfig(cwd)
|
const config = await getConfig(cwd)
|
||||||
if (!config) {
|
if (!config) {
|
||||||
logger.warn(
|
consola.warn(
|
||||||
`Configuration is missing. Please run ${chalk.green(
|
`Configuration is missing. Please run ${colors.green(
|
||||||
'init',
|
'init',
|
||||||
)} to create a components.json file.`,
|
)} to create a components.json file.`,
|
||||||
)
|
)
|
||||||
|
|
@ -88,19 +88,20 @@ export const diff = new Command()
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!componentsWithUpdates.length) {
|
if (!componentsWithUpdates.length) {
|
||||||
logger.info('No updates found.')
|
consola.info('No updates found.')
|
||||||
process.exit(0)
|
process.exit(0)
|
||||||
}
|
}
|
||||||
|
|
||||||
logger.info('The following components have updates available:')
|
consola.info('The following components have updates available:')
|
||||||
for (const component of componentsWithUpdates) {
|
for (const component of componentsWithUpdates) {
|
||||||
logger.info(`- ${component.name}`)
|
consola.info(`- ${component.name}`)
|
||||||
for (const change of component.changes)
|
for (const change of component.changes)
|
||||||
logger.info(` - ${change.filePath}`)
|
consola.info(` - ${change.filePath}`)
|
||||||
}
|
}
|
||||||
logger.break()
|
|
||||||
logger.info(
|
consola.log('')
|
||||||
`Run ${chalk.green('diff <component>')} to see the changes.`,
|
consola.info(
|
||||||
|
`Run ${colors.green('diff <component>')} to see the changes.`,
|
||||||
)
|
)
|
||||||
process.exit(0)
|
process.exit(0)
|
||||||
}
|
}
|
||||||
|
|
@ -111,8 +112,8 @@ export const diff = new Command()
|
||||||
)
|
)
|
||||||
|
|
||||||
if (!component) {
|
if (!component) {
|
||||||
logger.error(
|
consola.error(
|
||||||
`The component ${chalk.green(options.component)} does not exist.`,
|
`The component ${colors.green(options.component)} does not exist.`,
|
||||||
)
|
)
|
||||||
process.exit(1)
|
process.exit(1)
|
||||||
}
|
}
|
||||||
|
|
@ -120,14 +121,14 @@ export const diff = new Command()
|
||||||
const changes = await diffComponent(component, config)
|
const changes = await diffComponent(component, config)
|
||||||
|
|
||||||
if (!changes.length) {
|
if (!changes.length) {
|
||||||
logger.info(`No updates found for ${options.component}.`)
|
consola.info(`No updates found for ${options.component}.`)
|
||||||
process.exit(0)
|
process.exit(0)
|
||||||
}
|
}
|
||||||
|
|
||||||
for (const change of changes) {
|
for (const change of changes) {
|
||||||
logger.info(`- ${change.filePath}`)
|
consola.info(`- ${change.filePath}`)
|
||||||
printDiff(change.patch)
|
printDiff(change.patch)
|
||||||
logger.info('')
|
consola.log('')
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch (error) {
|
catch (error) {
|
||||||
|
|
@ -184,10 +185,10 @@ function printDiff(diff: Change[]) {
|
||||||
diff.forEach((part) => {
|
diff.forEach((part) => {
|
||||||
if (part) {
|
if (part) {
|
||||||
if (part.added)
|
if (part.added)
|
||||||
return process.stdout.write(chalk.green(part.value))
|
return process.stdout.write(colors.green(part.value))
|
||||||
|
|
||||||
if (part.removed)
|
if (part.removed)
|
||||||
return process.stdout.write(chalk.red(part.value))
|
return process.stdout.write(colors.red(part.value))
|
||||||
|
|
||||||
return process.stdout.write(part.value)
|
return process.stdout.write(part.value)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,22 +1,21 @@
|
||||||
import { existsSync, promises as fs } from 'node:fs'
|
import { existsSync, promises as fs } from 'node:fs'
|
||||||
import path from 'node:path'
|
|
||||||
import process from 'node:process'
|
import process from 'node:process'
|
||||||
import chalk from 'chalk'
|
import path from 'pathe'
|
||||||
import { Command } from 'commander'
|
import { Command } from 'commander'
|
||||||
import { execa } from 'execa'
|
|
||||||
import template from 'lodash.template'
|
import template from 'lodash.template'
|
||||||
import ora from 'ora'
|
import ora from 'ora'
|
||||||
import prompts from 'prompts'
|
import prompts from 'prompts'
|
||||||
import * as z from 'zod'
|
import { z } from 'zod'
|
||||||
|
import { addDependency, addDevDependency } from 'nypm'
|
||||||
|
import { consola } from 'consola'
|
||||||
|
import { colors } from 'consola/utils'
|
||||||
import * as templates from '../utils/templates'
|
import * as templates from '../utils/templates'
|
||||||
import {
|
import {
|
||||||
getRegistryBaseColor,
|
getRegistryBaseColor,
|
||||||
getRegistryBaseColors,
|
getRegistryBaseColors,
|
||||||
getRegistryStyles,
|
getRegistryStyles,
|
||||||
} from '../utils/registry'
|
} from '../utils/registry'
|
||||||
import { logger } from '../utils/logger'
|
|
||||||
import { handleError } from '../utils/handle-error'
|
import { handleError } from '../utils/handle-error'
|
||||||
import { getPackageManager } from '../utils/get-package-manager'
|
|
||||||
import { transformByDetype } from '../utils/transformers/transform-sfc'
|
import { transformByDetype } from '../utils/transformers/transform-sfc'
|
||||||
import {
|
import {
|
||||||
type Config,
|
type Config,
|
||||||
|
|
@ -29,6 +28,7 @@ import {
|
||||||
resolveConfigPaths,
|
resolveConfigPaths,
|
||||||
} from '../utils/get-config'
|
} from '../utils/get-config'
|
||||||
import { transformCJSToESM } from '../utils/transformers/transform-cjs-to-esm'
|
import { transformCJSToESM } from '../utils/transformers/transform-cjs-to-esm'
|
||||||
|
import { applyPrefixesCss } from '../utils/transformers/transform-tw-prefix'
|
||||||
|
|
||||||
const PROJECT_DEPENDENCIES = {
|
const PROJECT_DEPENDENCIES = {
|
||||||
base: [
|
base: [
|
||||||
|
|
@ -64,7 +64,7 @@ export const init = new Command()
|
||||||
|
|
||||||
// Ensure target directory exists.
|
// Ensure target directory exists.
|
||||||
if (!existsSync(cwd)) {
|
if (!existsSync(cwd)) {
|
||||||
logger.error(`The path ${cwd} does not exist. Please try again.`)
|
consola.error(`The path ${cwd} does not exist. Please try again.`)
|
||||||
process.exit(1)
|
process.exit(1)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -74,11 +74,11 @@ export const init = new Command()
|
||||||
|
|
||||||
await runInit(cwd, config)
|
await runInit(cwd, config)
|
||||||
|
|
||||||
logger.info('')
|
consola.log('')
|
||||||
logger.info(
|
consola.info(
|
||||||
`${chalk.green('Success!')} Project initialization completed.`,
|
`${colors.green('Success!')} Project initialization completed.`,
|
||||||
)
|
)
|
||||||
logger.info('')
|
consola.log('')
|
||||||
}
|
}
|
||||||
catch (error) {
|
catch (error) {
|
||||||
handleError(error)
|
handleError(error)
|
||||||
|
|
@ -90,7 +90,7 @@ export async function promptForConfig(
|
||||||
defaultConfig: Config | null = null,
|
defaultConfig: Config | null = null,
|
||||||
skip = false,
|
skip = false,
|
||||||
) {
|
) {
|
||||||
const highlight = (text: string) => chalk.cyan(text)
|
const highlight = (text: string) => colors.cyan(text)
|
||||||
|
|
||||||
const styles = await getRegistryStyles()
|
const styles = await getRegistryStyles()
|
||||||
const baseColors = await getRegistryBaseColors()
|
const baseColors = await getRegistryBaseColors()
|
||||||
|
|
@ -151,6 +151,14 @@ export async function promptForConfig(
|
||||||
active: 'yes',
|
active: 'yes',
|
||||||
inactive: 'no',
|
inactive: 'no',
|
||||||
},
|
},
|
||||||
|
// {
|
||||||
|
// type: 'text',
|
||||||
|
// name: 'tailwindPrefix',
|
||||||
|
// message: `Are you using a custom ${highlight(
|
||||||
|
// 'tailwind prefix eg. tw-',
|
||||||
|
// )}? (Leave blank if not)`,
|
||||||
|
// initial: '',
|
||||||
|
// },
|
||||||
{
|
{
|
||||||
type: 'text',
|
type: 'text',
|
||||||
name: 'tailwindConfig',
|
name: 'tailwindConfig',
|
||||||
|
|
@ -187,6 +195,7 @@ export async function promptForConfig(
|
||||||
css: options.tailwindCss,
|
css: options.tailwindCss,
|
||||||
baseColor: options.tailwindBaseColor,
|
baseColor: options.tailwindBaseColor,
|
||||||
cssVariables: options.tailwindCssVariables,
|
cssVariables: options.tailwindCssVariables,
|
||||||
|
// prefix: options.tailwindPrefix,
|
||||||
},
|
},
|
||||||
aliases: {
|
aliases: {
|
||||||
utils: options.utils,
|
utils: options.utils,
|
||||||
|
|
@ -207,7 +216,7 @@ export async function promptForConfig(
|
||||||
}
|
}
|
||||||
|
|
||||||
// Write to file.
|
// Write to file.
|
||||||
logger.info('')
|
consola.log('')
|
||||||
const spinner = ora('Writing components.json...').start()
|
const spinner = ora('Writing components.json...').start()
|
||||||
const targetPath = path.resolve(cwd, 'components.json')
|
const targetPath = path.resolve(cwd, 'components.json')
|
||||||
await fs.writeFile(targetPath, JSON.stringify(config, null, 2), 'utf8')
|
await fs.writeFile(targetPath, JSON.stringify(config, null, 2), 'utf8')
|
||||||
|
|
@ -247,8 +256,8 @@ export async function runInit(cwd: string, config: Config) {
|
||||||
transformCJSToESM(
|
transformCJSToESM(
|
||||||
config.resolvedPaths.tailwindConfig,
|
config.resolvedPaths.tailwindConfig,
|
||||||
config.tailwind.cssVariables
|
config.tailwind.cssVariables
|
||||||
? template(templates.TAILWIND_CONFIG_WITH_VARIABLES)({ extension, framework: config.framework })
|
? template(templates.TAILWIND_CONFIG_WITH_VARIABLES)({ extension, framework: config.framework, prefix: config.tailwind.prefix })
|
||||||
: template(templates.TAILWIND_CONFIG)({ extension, framework: config.framework }),
|
: template(templates.TAILWIND_CONFIG)({ extension, framework: config.framework, prefix: config.tailwind.prefix }),
|
||||||
),
|
),
|
||||||
'utf8',
|
'utf8',
|
||||||
)
|
)
|
||||||
|
|
@ -259,7 +268,9 @@ export async function runInit(cwd: string, config: Config) {
|
||||||
await fs.writeFile(
|
await fs.writeFile(
|
||||||
config.resolvedPaths.tailwindCss,
|
config.resolvedPaths.tailwindCss,
|
||||||
config.tailwind.cssVariables
|
config.tailwind.cssVariables
|
||||||
? baseColor.cssVarsTemplate
|
? config.tailwind.prefix
|
||||||
|
? applyPrefixesCss(baseColor.cssVarsTemplate, config.tailwind.prefix)
|
||||||
|
: baseColor.cssVarsTemplate
|
||||||
: baseColor.inlineColorsTemplate,
|
: baseColor.inlineColorsTemplate,
|
||||||
'utf8',
|
'utf8',
|
||||||
)
|
)
|
||||||
|
|
@ -276,20 +287,29 @@ export async function runInit(cwd: string, config: Config) {
|
||||||
|
|
||||||
// Install dependencies.
|
// Install dependencies.
|
||||||
const dependenciesSpinner = ora('Installing dependencies...')?.start()
|
const dependenciesSpinner = ora('Installing dependencies...')?.start()
|
||||||
const packageManager = await getPackageManager(cwd)
|
|
||||||
|
|
||||||
const deps = PROJECT_DEPENDENCIES.base.concat(
|
const deps = PROJECT_DEPENDENCIES.base.concat(
|
||||||
config.framework === 'nuxt' ? PROJECT_DEPENDENCIES.nuxt : [],
|
|
||||||
).concat(
|
|
||||||
config.style === 'new-york' ? ['@radix-icons/vue'] : ['lucide-vue-next'],
|
config.style === 'new-york' ? ['@radix-icons/vue'] : ['lucide-vue-next'],
|
||||||
).filter(Boolean)
|
).filter(Boolean)
|
||||||
|
|
||||||
await execa(
|
async function addNuxtDevDeps() {
|
||||||
packageManager,
|
if (config.framework === 'nuxt') {
|
||||||
[packageManager === 'npm' ? 'install' : 'add', ...deps],
|
await addDevDependency(PROJECT_DEPENDENCIES.nuxt, {
|
||||||
{
|
|
||||||
cwd,
|
cwd,
|
||||||
},
|
silent: true,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
await Promise.allSettled(
|
||||||
|
[
|
||||||
|
addNuxtDevDeps(),
|
||||||
|
addDependency(deps, {
|
||||||
|
cwd,
|
||||||
|
silent: true,
|
||||||
|
}),
|
||||||
|
],
|
||||||
)
|
)
|
||||||
|
|
||||||
dependenciesSpinner?.succeed()
|
dependenciesSpinner?.succeed()
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,9 @@
|
||||||
import path from 'node:path'
|
|
||||||
import { existsSync } from 'node:fs'
|
import { existsSync } from 'node:fs'
|
||||||
import { cosmiconfig } from 'cosmiconfig'
|
import path from 'pathe'
|
||||||
|
import { loadConfig as c12LoadConfig } from 'c12'
|
||||||
import type { ConfigLoaderResult } from 'tsconfig-paths'
|
import type { ConfigLoaderResult } from 'tsconfig-paths'
|
||||||
import { loadConfig } from 'tsconfig-paths'
|
import { loadConfig } from 'tsconfig-paths'
|
||||||
import * as z from 'zod'
|
import { z } from 'zod'
|
||||||
import { resolveImport } from '@/src/utils/resolve-import'
|
import { resolveImport } from '@/src/utils/resolve-import'
|
||||||
|
|
||||||
export const DEFAULT_STYLE = 'default'
|
export const DEFAULT_STYLE = 'default'
|
||||||
|
|
@ -19,12 +19,6 @@ export const TAILWIND_CSS_PATH = {
|
||||||
astro: 'src/styles/globals.css',
|
astro: 'src/styles/globals.css',
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Figure out if we want to support all cosmiconfig formats.
|
|
||||||
// A simple components.json file would be nice.
|
|
||||||
const explorer = cosmiconfig('components', {
|
|
||||||
searchPlaces: ['components.json'],
|
|
||||||
})
|
|
||||||
|
|
||||||
export const rawConfigSchema = z
|
export const rawConfigSchema = z
|
||||||
.object({
|
.object({
|
||||||
$schema: z.string().optional(),
|
$schema: z.string().optional(),
|
||||||
|
|
@ -35,11 +29,13 @@ export const rawConfigSchema = z
|
||||||
css: z.string(),
|
css: z.string(),
|
||||||
baseColor: z.string(),
|
baseColor: z.string(),
|
||||||
cssVariables: z.boolean().default(true),
|
cssVariables: z.boolean().default(true),
|
||||||
|
prefix: z.string().optional(),
|
||||||
}),
|
}),
|
||||||
framework: z.string().default('Vite'),
|
framework: z.string().default('Vite'),
|
||||||
aliases: z.object({
|
aliases: z.object({
|
||||||
components: z.string(),
|
components: z.string(),
|
||||||
utils: z.string(),
|
utils: z.string(),
|
||||||
|
ui: z.string().default('').optional(),
|
||||||
}),
|
}),
|
||||||
})
|
})
|
||||||
.strict()
|
.strict()
|
||||||
|
|
@ -53,6 +49,7 @@ export const configSchema = rawConfigSchema
|
||||||
tailwindCss: z.string(),
|
tailwindCss: z.string(),
|
||||||
utils: z.string(),
|
utils: z.string(),
|
||||||
components: z.string(),
|
components: z.string(),
|
||||||
|
ui: z.string(),
|
||||||
}),
|
}),
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
@ -103,15 +100,22 @@ export async function resolveConfigPaths(cwd: string, config: RawConfig) {
|
||||||
tailwindCss: path.resolve(cwd, config.tailwind.css),
|
tailwindCss: path.resolve(cwd, config.tailwind.css),
|
||||||
utils: resolveImport(config.aliases.utils, tsConfig),
|
utils: resolveImport(config.aliases.utils, tsConfig),
|
||||||
components: resolveImport(config.aliases.components, tsConfig),
|
components: resolveImport(config.aliases.components, tsConfig),
|
||||||
|
ui: config.aliases.ui
|
||||||
|
? resolveImport(config.aliases.ui, tsConfig)
|
||||||
|
: resolveImport(config.aliases.components, tsConfig),
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function getRawConfig(cwd: string): Promise<RawConfig | null> {
|
export async function getRawConfig(cwd: string): Promise<RawConfig | null> {
|
||||||
try {
|
try {
|
||||||
const configResult = await explorer.search(cwd)
|
const configResult = await c12LoadConfig({
|
||||||
|
name: 'components',
|
||||||
|
configFile: 'components',
|
||||||
|
cwd,
|
||||||
|
})
|
||||||
|
|
||||||
if (!configResult)
|
if (!configResult.config || Object.keys(configResult.config).length === 0)
|
||||||
return null
|
return null
|
||||||
|
|
||||||
return rawConfigSchema.parse(configResult.config)
|
return rawConfigSchema.parse(configResult.config)
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
import path from 'node:path'
|
|
||||||
import { fileURLToPath } from 'node:url'
|
import { fileURLToPath } from 'node:url'
|
||||||
|
import path from 'pathe'
|
||||||
import fs from 'fs-extra'
|
import fs from 'fs-extra'
|
||||||
import { type PackageJson } from 'type-fest'
|
import { type PackageJson } from 'type-fest'
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,16 +0,0 @@
|
||||||
import { detect } from '@antfu/ni'
|
|
||||||
|
|
||||||
export async function getPackageManager(
|
|
||||||
targetDir: string,
|
|
||||||
): Promise<'yarn' | 'pnpm' | 'bun' | 'npm'> {
|
|
||||||
const packageManager = await detect({ programmatic: true, cwd: targetDir })
|
|
||||||
|
|
||||||
if (packageManager === 'yarn@berry')
|
|
||||||
return 'yarn'
|
|
||||||
if (packageManager === 'pnpm@6')
|
|
||||||
return 'pnpm'
|
|
||||||
if (packageManager === 'bun')
|
|
||||||
return 'bun'
|
|
||||||
|
|
||||||
return packageManager ?? 'npm'
|
|
||||||
}
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
import { existsSync } from 'node:fs'
|
import { existsSync } from 'node:fs'
|
||||||
import path from 'node:path'
|
import path from 'pathe'
|
||||||
import fs from 'fs-extra'
|
import fs from 'fs-extra'
|
||||||
|
|
||||||
export async function getProjectInfo() {
|
export async function getProjectInfo() {
|
||||||
|
|
|
||||||
|
|
@ -1,16 +1,16 @@
|
||||||
import { logger } from './logger'
|
import { consola } from 'consola'
|
||||||
|
|
||||||
export function handleError(error: unknown) {
|
export function handleError(error: unknown) {
|
||||||
if (typeof error === 'string') {
|
if (typeof error === 'string') {
|
||||||
logger.error(error)
|
consola.error(error)
|
||||||
process.exit(1)
|
process.exit(1)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (error instanceof Error) {
|
if (error instanceof Error) {
|
||||||
logger.error(error.message)
|
consola.error(error.message)
|
||||||
process.exit(1)
|
process.exit(1)
|
||||||
}
|
}
|
||||||
|
|
||||||
logger.error('Something went wrong. Please try again.')
|
consola.error('Something went wrong. Please try again.')
|
||||||
process.exit(1)
|
process.exit(1)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,19 +0,0 @@
|
||||||
import chalk from 'chalk'
|
|
||||||
|
|
||||||
export const logger = {
|
|
||||||
error(...args: unknown[]) {
|
|
||||||
console.log(chalk.red(...args))
|
|
||||||
},
|
|
||||||
warn(...args: unknown[]) {
|
|
||||||
console.log(chalk.yellow(...args))
|
|
||||||
},
|
|
||||||
info(...args: unknown[]) {
|
|
||||||
console.log(chalk.cyan(...args))
|
|
||||||
},
|
|
||||||
success(...args: unknown[]) {
|
|
||||||
console.log(chalk.green(...args))
|
|
||||||
},
|
|
||||||
break() {
|
|
||||||
console.log('')
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
@ -1,8 +1,9 @@
|
||||||
import path from 'node:path'
|
|
||||||
import process from 'node:process'
|
import process from 'node:process'
|
||||||
|
import path from 'pathe'
|
||||||
import { HttpsProxyAgent } from 'https-proxy-agent'
|
import { HttpsProxyAgent } from 'https-proxy-agent'
|
||||||
import fetch from 'node-fetch'
|
import { ofetch } from 'ofetch'
|
||||||
import type * as z from 'zod'
|
import type * as z from 'zod'
|
||||||
|
import consola from 'consola'
|
||||||
import {
|
import {
|
||||||
registryBaseColorSchema,
|
registryBaseColorSchema,
|
||||||
registryIndexSchema,
|
registryIndexSchema,
|
||||||
|
|
@ -122,9 +123,12 @@ export function getItemTargetPath(
|
||||||
override?: string,
|
override?: string,
|
||||||
) {
|
) {
|
||||||
// Allow overrides for all items but ui.
|
// Allow overrides for all items but ui.
|
||||||
if (override && item.type !== 'components:ui')
|
if (override)
|
||||||
return override
|
return override
|
||||||
|
|
||||||
|
if (item.type === 'components:ui' && config.aliases.ui)
|
||||||
|
return config.resolvedPaths.ui
|
||||||
|
|
||||||
const [parent, type] = item.type.split(':')
|
const [parent, type] = item.type.split(':')
|
||||||
if (!(parent in config.resolvedPaths))
|
if (!(parent in config.resolvedPaths))
|
||||||
return null
|
return null
|
||||||
|
|
@ -139,17 +143,18 @@ async function fetchRegistry(paths: string[]) {
|
||||||
try {
|
try {
|
||||||
const results = await Promise.all(
|
const results = await Promise.all(
|
||||||
paths.map(async (path) => {
|
paths.map(async (path) => {
|
||||||
const response = await fetch(`${baseUrl}/registry/${path}`, {
|
const response = await ofetch(`${baseUrl}/registry/${path}`, {
|
||||||
|
// @ts-expect-error agent type
|
||||||
agent,
|
agent,
|
||||||
})
|
})
|
||||||
return await response.json()
|
|
||||||
|
return response
|
||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
return results
|
return results
|
||||||
}
|
}
|
||||||
catch (error) {
|
catch (error) {
|
||||||
// eslint-disable-next-line no-console
|
consola.error(error)
|
||||||
console.log(error)
|
|
||||||
throw new Error(`Failed to fetch registry from ${baseUrl}.`)
|
throw new Error(`Failed to fetch registry from ${baseUrl}.`)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,10 @@
|
||||||
import * as z from 'zod'
|
import { z } from 'zod'
|
||||||
|
|
||||||
// TODO: Extract this to a shared package.
|
// TODO: Extract this to a shared package.
|
||||||
export const registryItemSchema = z.object({
|
export const registryItemSchema = z.object({
|
||||||
name: z.string(),
|
name: z.string(),
|
||||||
dependencies: z.array(z.string()).optional(),
|
dependencies: z.array(z.string()).optional(),
|
||||||
|
devDependencies: z.array(z.string()).optional(),
|
||||||
registryDependencies: z.array(z.string()).optional(),
|
registryDependencies: z.array(z.string()).optional(),
|
||||||
files: z.array(z.string()),
|
files: z.array(z.string()),
|
||||||
type: z.enum(['components:ui', 'components:component', 'components:example']),
|
type: z.enum(['components:ui', 'components:component', 'components:example']),
|
||||||
|
|
|
||||||
|
|
@ -17,6 +17,7 @@ module.exports = {
|
||||||
'./app/**/*.{<%- extension %>,<%- extension %>x,vue}',
|
'./app/**/*.{<%- extension %>,<%- extension %>x,vue}',
|
||||||
'./src/**/*.{<%- extension %>,<%- extension %>x,vue}',
|
'./src/**/*.{<%- extension %>,<%- extension %>x,vue}',
|
||||||
],
|
],
|
||||||
|
prefix: "<%- prefix %>",
|
||||||
theme: {
|
theme: {
|
||||||
container: {
|
container: {
|
||||||
center: true,
|
center: true,
|
||||||
|
|
@ -51,6 +52,7 @@ export const TAILWIND_CONFIG_WITH_VARIABLES = `const animate = require("tailwind
|
||||||
module.exports = {
|
module.exports = {
|
||||||
darkMode: ["class"],
|
darkMode: ["class"],
|
||||||
safelist: ["dark"],
|
safelist: ["dark"],
|
||||||
|
prefix: "<%- prefix %>",
|
||||||
<% if (framework === 'vite') { %>
|
<% if (framework === 'vite') { %>
|
||||||
content: [
|
content: [
|
||||||
'./pages/**/*.{<%- extension %>,<%- extension %>x,vue}',
|
'./pages/**/*.{<%- extension %>,<%- extension %>x,vue}',
|
||||||
|
|
|
||||||
|
|
@ -1,12 +1,13 @@
|
||||||
import { promises as fs } from 'node:fs'
|
import { promises as fs } from 'node:fs'
|
||||||
import { tmpdir } from 'node:os'
|
import { tmpdir } from 'node:os'
|
||||||
import path from 'node:path'
|
import path from 'pathe'
|
||||||
import { Project, ScriptKind, type SourceFile } from 'ts-morph'
|
import { Project, ScriptKind, type SourceFile } from 'ts-morph'
|
||||||
import type * as z from 'zod'
|
import type * as z from 'zod'
|
||||||
import type { Config } from '@/src/utils/get-config'
|
import type { Config } from '@/src/utils/get-config'
|
||||||
import type { registryBaseColorSchema } from '@/src/utils/registry/schema'
|
import type { registryBaseColorSchema } from '@/src/utils/registry/schema'
|
||||||
import { transformCssVars } from '@/src/utils/transformers/transform-css-vars'
|
import { transformCssVars } from '@/src/utils/transformers/transform-css-vars'
|
||||||
import { transformImport } from '@/src/utils/transformers/transform-import'
|
import { transformImport } from '@/src/utils/transformers/transform-import'
|
||||||
|
import { transformTwPrefixes } from '@/src/utils/transformers/transform-tw-prefix'
|
||||||
import { transformSFC } from '@/src/utils/transformers/transform-sfc'
|
import { transformSFC } from '@/src/utils/transformers/transform-sfc'
|
||||||
|
|
||||||
export interface TransformOpts {
|
export interface TransformOpts {
|
||||||
|
|
@ -25,6 +26,7 @@ export type Transformer<Output = SourceFile> = (
|
||||||
const transformers: Transformer[] = [
|
const transformers: Transformer[] = [
|
||||||
transformCssVars,
|
transformCssVars,
|
||||||
transformImport,
|
transformImport,
|
||||||
|
// transformTwPrefixes,
|
||||||
]
|
]
|
||||||
|
|
||||||
const project = new Project({
|
const project = new Project({
|
||||||
|
|
|
||||||
|
|
@ -88,29 +88,28 @@ export function applyColorMapping(
|
||||||
if (input.includes(' border '))
|
if (input.includes(' border '))
|
||||||
input = input.replace(' border ', ' border border-border ')
|
input = input.replace(' border ', ' border border-border ')
|
||||||
|
|
||||||
// Build color mappings.
|
|
||||||
const classNames = input.split(' ')
|
const classNames = input.split(' ')
|
||||||
const lightMode: string[] = []
|
const lightMode = new Set<string>()
|
||||||
const darkMode: string[] = []
|
const darkMode = new Set<string>()
|
||||||
for (const className of classNames) {
|
for (const className of classNames) {
|
||||||
const [variant, value, modifier] = splitClassName(className)
|
const [variant, value, modifier] = splitClassName(className)
|
||||||
const prefix = PREFIXES.find(prefix => value?.startsWith(prefix))
|
const prefix = PREFIXES.find(prefix => value?.startsWith(prefix))
|
||||||
if (!prefix) {
|
if (!prefix) {
|
||||||
if (!lightMode.includes(className))
|
if (!lightMode.has(className))
|
||||||
lightMode.push(className)
|
lightMode.add(className)
|
||||||
|
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
const needle = value?.replace(prefix, '')
|
const needle = value?.replace(prefix, '')
|
||||||
if (needle && needle in mapping.light) {
|
if (needle && needle in mapping.light) {
|
||||||
lightMode.push(
|
lightMode.add(
|
||||||
[variant, `${prefix}${mapping.light[needle]}`]
|
[variant, `${prefix}${mapping.light[needle]}`]
|
||||||
.filter(Boolean)
|
.filter(Boolean)
|
||||||
.join(':') + (modifier ? `/${modifier}` : ''),
|
.join(':') + (modifier ? `/${modifier}` : ''),
|
||||||
)
|
)
|
||||||
|
|
||||||
darkMode.push(
|
darkMode.add(
|
||||||
['dark', variant, `${prefix}${mapping.dark[needle]}`]
|
['dark', variant, `${prefix}${mapping.dark[needle]}`]
|
||||||
.filter(Boolean)
|
.filter(Boolean)
|
||||||
.join(':') + (modifier ? `/${modifier}` : ''),
|
.join(':') + (modifier ? `/${modifier}` : ''),
|
||||||
|
|
@ -118,9 +117,9 @@ export function applyColorMapping(
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!lightMode.includes(className))
|
if (!lightMode.has(className))
|
||||||
lightMode.push(className)
|
lightMode.add(className)
|
||||||
}
|
}
|
||||||
const combined = `${lightMode.join(' ').replace(/\'/g, '')} ${darkMode.join(' ').trim()}`.trim()
|
|
||||||
return `${combined}`
|
return [...Array.from(lightMode), ...Array.from(darkMode)].join(' ').trim()
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -8,6 +8,12 @@ export const transformImport: Transformer = async ({ sourceFile, config }) => {
|
||||||
|
|
||||||
// Replace @/lib/registry/[style] with the components alias.
|
// Replace @/lib/registry/[style] with the components alias.
|
||||||
if (moduleSpecifier.startsWith('@/lib/registry/')) {
|
if (moduleSpecifier.startsWith('@/lib/registry/')) {
|
||||||
|
if (config.aliases.ui) {
|
||||||
|
importDeclaration.setModuleSpecifier(
|
||||||
|
moduleSpecifier.replace(/^@\/lib\/registry\/[^/]+\/ui/, config.aliases.ui),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
else {
|
||||||
importDeclaration.setModuleSpecifier(
|
importDeclaration.setModuleSpecifier(
|
||||||
moduleSpecifier.replace(
|
moduleSpecifier.replace(
|
||||||
/^@\/lib\/registry\/[^/]+/,
|
/^@\/lib\/registry\/[^/]+/,
|
||||||
|
|
@ -15,6 +21,7 @@ export const transformImport: Transformer = async ({ sourceFile, config }) => {
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Replace `import { cn } from "@/lib/utils"`
|
// Replace `import { cn } from "@/lib/utils"`
|
||||||
if (moduleSpecifier === '@/lib/utils') {
|
if (moduleSpecifier === '@/lib/utils') {
|
||||||
|
|
|
||||||
80
packages/cli/src/utils/transformers/transform-tw-prefix.ts
Normal file
80
packages/cli/src/utils/transformers/transform-tw-prefix.ts
Normal file
|
|
@ -0,0 +1,80 @@
|
||||||
|
import { SyntaxKind } from 'ts-morph'
|
||||||
|
import { MagicString, parse } from '@vue/compiler-sfc'
|
||||||
|
import type { SFCTemplateBlock } from '@vue/compiler-sfc'
|
||||||
|
import { splitClassName } from './transform-css-vars'
|
||||||
|
import type { Transformer } from '@/src/utils/transformers'
|
||||||
|
|
||||||
|
export const transformTwPrefixes: Transformer = async ({
|
||||||
|
sourceFile,
|
||||||
|
config,
|
||||||
|
}) => {
|
||||||
|
const isVueFile = sourceFile.getFilePath().endsWith('vue')
|
||||||
|
if (!config.tailwind?.prefix)
|
||||||
|
return sourceFile
|
||||||
|
|
||||||
|
let template: SFCTemplateBlock | null = null
|
||||||
|
|
||||||
|
if (isVueFile) {
|
||||||
|
const parsed = parse(sourceFile.getText())
|
||||||
|
template = parsed.descriptor.template
|
||||||
|
|
||||||
|
if (!template)
|
||||||
|
return sourceFile
|
||||||
|
}
|
||||||
|
|
||||||
|
sourceFile.getDescendantsOfKind(SyntaxKind.StringLiteral).forEach((node) => {
|
||||||
|
if (template && template.loc.start.offset >= node.getPos())
|
||||||
|
return sourceFile
|
||||||
|
|
||||||
|
const attrName = sourceFile.getDescendantAtPos(node.getPos() - 2)?.getText()
|
||||||
|
if (isVueFile && attrName !== 'class')
|
||||||
|
return sourceFile
|
||||||
|
|
||||||
|
const value = node.getText()
|
||||||
|
const hasClosingDoubleQuote = value.match(/"/g)?.length === 2
|
||||||
|
if (value.search('\'') === -1 && hasClosingDoubleQuote) {
|
||||||
|
const mapped = applyPrefix(value.replace(/"/g, ''), config.tailwind.prefix)
|
||||||
|
node.replaceWithText(`"${mapped}"`)
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
const s = new MagicString(value)
|
||||||
|
s.replace(/'(.*?)'/g, (substring) => {
|
||||||
|
return `'${applyPrefix(substring.replace(/\'/g, ''), config.tailwind.prefix)}'`
|
||||||
|
})
|
||||||
|
node.replaceWithText(s.toString())
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
return sourceFile
|
||||||
|
}
|
||||||
|
|
||||||
|
export function applyPrefix(input: string, prefix: string = '') {
|
||||||
|
const classNames = input.split(' ')
|
||||||
|
const prefixed: string[] = []
|
||||||
|
for (const className of classNames) {
|
||||||
|
const [variant, value, modifier] = splitClassName(className)
|
||||||
|
if (variant) {
|
||||||
|
modifier
|
||||||
|
? prefixed.push(`${variant}:${prefix}${value}/${modifier}`)
|
||||||
|
: prefixed.push(`${variant}:${prefix}${value}`)
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
modifier
|
||||||
|
? prefixed.push(`${prefix}${value}/${modifier}`)
|
||||||
|
: prefixed.push(`${prefix}${value}`)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return prefixed.join(' ')
|
||||||
|
}
|
||||||
|
|
||||||
|
export function applyPrefixesCss(css: string, prefix: string) {
|
||||||
|
const lines = css.split('\n')
|
||||||
|
for (const line of lines) {
|
||||||
|
if (line.includes('@apply')) {
|
||||||
|
const originalTWCls = line.replace('@apply', '').trim()
|
||||||
|
const prefixedTwCls = applyPrefix(originalTWCls, prefix)
|
||||||
|
css = css.replace(originalTWCls, prefixedTwCls)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return css
|
||||||
|
}
|
||||||
|
|
@ -1,14 +1,13 @@
|
||||||
import fs from 'node:fs'
|
import fs from 'node:fs'
|
||||||
import path from 'node:path'
|
import path from 'pathe'
|
||||||
import { execa } from 'execa'
|
import { addDependency, addDevDependency } from 'nypm'
|
||||||
import { afterEach, expect, test, vi } from 'vitest'
|
import { afterEach, expect, test, vi } from 'vitest'
|
||||||
|
|
||||||
import { runInit } from '../../src/commands/init'
|
import { runInit } from '../../src/commands/init'
|
||||||
import { getConfig } from '../../src/utils/get-config'
|
import { getConfig } from '../../src/utils/get-config'
|
||||||
import * as getPackageManger from '../../src/utils/get-package-manager'
|
|
||||||
import * as registry from '../../src/utils/registry'
|
import * as registry from '../../src/utils/registry'
|
||||||
|
|
||||||
vi.mock('execa')
|
vi.mock('nypm')
|
||||||
vi.mock('fs/promises', () => ({
|
vi.mock('fs/promises', () => ({
|
||||||
writeFile: vi.fn(),
|
writeFile: vi.fn(),
|
||||||
mkdir: vi.fn(),
|
mkdir: vi.fn(),
|
||||||
|
|
@ -16,7 +15,6 @@ vi.mock('fs/promises', () => ({
|
||||||
vi.mock('ora')
|
vi.mock('ora')
|
||||||
|
|
||||||
test('init config-full', async () => {
|
test('init config-full', async () => {
|
||||||
vi.spyOn(getPackageManger, 'getPackageManager').mockResolvedValue('pnpm')
|
|
||||||
vi.spyOn(registry, 'getRegistryBaseColor').mockResolvedValue({
|
vi.spyOn(registry, 'getRegistryBaseColor').mockResolvedValue({
|
||||||
inlineColors: {},
|
inlineColors: {},
|
||||||
cssVars: {},
|
cssVars: {},
|
||||||
|
|
@ -67,10 +65,8 @@ test('init config-full', async () => {
|
||||||
expect.stringContaining("import { type ClassValue, clsx } from 'clsx'"),
|
expect.stringContaining("import { type ClassValue, clsx } from 'clsx'"),
|
||||||
'utf8',
|
'utf8',
|
||||||
)
|
)
|
||||||
expect(execa).toHaveBeenCalledWith(
|
expect(addDependency).toHaveBeenCalledWith(
|
||||||
'pnpm',
|
|
||||||
[
|
[
|
||||||
'add',
|
|
||||||
'tailwindcss-animate',
|
'tailwindcss-animate',
|
||||||
'class-variance-authority',
|
'class-variance-authority',
|
||||||
'clsx',
|
'clsx',
|
||||||
|
|
@ -80,6 +76,7 @@ test('init config-full', async () => {
|
||||||
],
|
],
|
||||||
{
|
{
|
||||||
cwd: targetDir,
|
cwd: targetDir,
|
||||||
|
silent: true,
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -88,7 +85,6 @@ test('init config-full', async () => {
|
||||||
})
|
})
|
||||||
|
|
||||||
test('init config-partial', async () => {
|
test('init config-partial', async () => {
|
||||||
vi.spyOn(getPackageManger, 'getPackageManager').mockResolvedValue('npm')
|
|
||||||
vi.spyOn(registry, 'getRegistryBaseColor').mockResolvedValue({
|
vi.spyOn(registry, 'getRegistryBaseColor').mockResolvedValue({
|
||||||
inlineColors: {},
|
inlineColors: {},
|
||||||
cssVars: {},
|
cssVars: {},
|
||||||
|
|
@ -139,10 +135,8 @@ test('init config-partial', async () => {
|
||||||
expect.stringContaining("import { type ClassValue, clsx } from 'clsx'"),
|
expect.stringContaining("import { type ClassValue, clsx } from 'clsx'"),
|
||||||
'utf8',
|
'utf8',
|
||||||
)
|
)
|
||||||
expect(execa).toHaveBeenCalledWith(
|
expect(addDependency).toHaveBeenCalledWith(
|
||||||
'npm',
|
|
||||||
[
|
[
|
||||||
'install',
|
|
||||||
'tailwindcss-animate',
|
'tailwindcss-animate',
|
||||||
'class-variance-authority',
|
'class-variance-authority',
|
||||||
'clsx',
|
'clsx',
|
||||||
|
|
@ -152,6 +146,7 @@ test('init config-partial', async () => {
|
||||||
],
|
],
|
||||||
{
|
{
|
||||||
cwd: targetDir,
|
cwd: targetDir,
|
||||||
|
silent: true,
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -4,10 +4,12 @@
|
||||||
"config": "tailwind.config.ts",
|
"config": "tailwind.config.ts",
|
||||||
"css": "src/app/globals.css",
|
"css": "src/app/globals.css",
|
||||||
"baseColor": "zinc",
|
"baseColor": "zinc",
|
||||||
"cssVariables": true
|
"cssVariables": true,
|
||||||
|
"prefix": "tw-"
|
||||||
},
|
},
|
||||||
"aliases": {
|
"aliases": {
|
||||||
"utils": "~/lib/utils",
|
"utils": "~/lib/utils",
|
||||||
"components": "~/components"
|
"components": "~/components",
|
||||||
|
"ui": "~/ui"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -12,6 +12,7 @@ export default {
|
||||||
'./app/**/*.{<%- extension %>,<%- extension %>x,vue}',
|
'./app/**/*.{<%- extension %>,<%- extension %>x,vue}',
|
||||||
'./src/**/*.{<%- extension %>,<%- extension %>x,vue}',
|
'./src/**/*.{<%- extension %>,<%- extension %>x,vue}',
|
||||||
],
|
],
|
||||||
|
prefix: \\"<%- prefix %>\\",
|
||||||
theme: {
|
theme: {
|
||||||
container: {
|
container: {
|
||||||
center: true,
|
center: true,
|
||||||
|
|
@ -48,6 +49,7 @@ exports[`handle tailwind config template correctly 2`] = `
|
||||||
export default {
|
export default {
|
||||||
darkMode: [\\"class\\"],
|
darkMode: [\\"class\\"],
|
||||||
safelist: [\\"dark\\"],
|
safelist: [\\"dark\\"],
|
||||||
|
prefix: \\"<%- prefix %>\\",
|
||||||
<% if (framework === 'vite') { %>
|
<% if (framework === 'vite') { %>
|
||||||
content: [
|
content: [
|
||||||
'./pages/**/*.{<%- extension %>,<%- extension %>x,vue}',
|
'./pages/**/*.{<%- extension %>,<%- extension %>x,vue}',
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,127 @@
|
||||||
|
// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
|
||||||
|
|
||||||
|
exports[`transform tailwind prefix 1`] = `
|
||||||
|
"const testVariants = cva(
|
||||||
|
\\"tw-bg-background hover:tw-bg-muted tw-text-primary-foreground sm:focus:tw-text-accent-foreground\\",
|
||||||
|
{
|
||||||
|
variants: {
|
||||||
|
variant: {
|
||||||
|
default:
|
||||||
|
\\"tw-bg-primary tw-text-primary-foreground hover:tw-bg-primary/90\\",
|
||||||
|
},
|
||||||
|
size: {
|
||||||
|
default: \\"tw-h-10 tw-px-4 tw-py-2\\",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
);
|
||||||
|
"
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`transform tailwind prefix 2`] = `
|
||||||
|
"<template>
|
||||||
|
<div class=\\"tw-bg-background hover:tw-bg-muted tw-text-primary-foreground sm:focus:tw-text-accent-foreground\\">
|
||||||
|
foo
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
"
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`transform tailwind prefix 3`] = `
|
||||||
|
"<template>
|
||||||
|
<div class=\\"tw-bg-white hover:tw-bg-stone-100 tw-text-stone-50 sm:focus:tw-text-stone-900 dark:tw-bg-stone-950 dark:hover:tw-bg-stone-800 dark:tw-text-stone-900 dark:sm:focus:tw-text-stone-50\\">
|
||||||
|
foo
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
"
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`transform tailwind prefix 4`] = `
|
||||||
|
"<template>
|
||||||
|
<div id=\\"testing\\" v-bind=\\"props\\" @click=\\"handleSomething\\" :data-test=\\"true\\" :class=\\"cn('tw-bg-white hover:tw-bg-stone-100 dark:tw-bg-stone-950 dark:hover:tw-bg-stone-800', true && 'tw-text-stone-50 sm:focus:tw-text-stone-900 dark:tw-text-stone-900 dark:sm:focus:tw-text-stone-50')\\">
|
||||||
|
foo
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
"
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`transform tailwind prefix 5`] = `
|
||||||
|
"@tailwind base;
|
||||||
|
@tailwind components;
|
||||||
|
@tailwind utilities;
|
||||||
|
|
||||||
|
@layer base {
|
||||||
|
:root {
|
||||||
|
--background: 0 0% 100%;
|
||||||
|
--foreground: 224 71.4% 4.1%;
|
||||||
|
|
||||||
|
--muted: 220 14.3% 95.9%;
|
||||||
|
--muted-foreground: 220 8.9% 46.1%;
|
||||||
|
|
||||||
|
--popover: 0 0% 100%;
|
||||||
|
--popover-foreground: 224 71.4% 4.1%;
|
||||||
|
|
||||||
|
--card: 0 0% 100%;
|
||||||
|
--card-foreground: 224 71.4% 4.1%;
|
||||||
|
|
||||||
|
--border: 220 13% 91%;
|
||||||
|
--input: 220 13% 91%;
|
||||||
|
|
||||||
|
--primary: 220.9 39.3% 11%;
|
||||||
|
--primary-foreground: 210 20% 98%;
|
||||||
|
|
||||||
|
--secondary: 220 14.3% 95.9%;
|
||||||
|
--secondary-foreground: 220.9 39.3% 11%;
|
||||||
|
|
||||||
|
--accent: 220 14.3% 95.9%;
|
||||||
|
--accent-foreground: 220.9 39.3% 11%;
|
||||||
|
|
||||||
|
--destructive: 0 84.2% 60.2%;
|
||||||
|
--destructive-foreground: 210 20% 98%;
|
||||||
|
|
||||||
|
--ring: 217.9 10.6% 64.9%;
|
||||||
|
|
||||||
|
--radius: 0.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dark {
|
||||||
|
--background: 224 71.4% 4.1%;
|
||||||
|
--foreground: 210 20% 98%;
|
||||||
|
|
||||||
|
--muted: 215 27.9% 16.9%;
|
||||||
|
--muted-foreground: 217.9 10.6% 64.9%;
|
||||||
|
|
||||||
|
--popover: 224 71.4% 4.1%;
|
||||||
|
--popover-foreground: 210 20% 98%;
|
||||||
|
|
||||||
|
--card: 224 71.4% 4.1%;
|
||||||
|
--card-foreground: 210 20% 98%;
|
||||||
|
|
||||||
|
--border: 215 27.9% 16.9%;
|
||||||
|
--input: 215 27.9% 16.9%;
|
||||||
|
|
||||||
|
--primary: 210 20% 98%;
|
||||||
|
--primary-foreground: 220.9 39.3% 11%;
|
||||||
|
|
||||||
|
--secondary: 215 27.9% 16.9%;
|
||||||
|
--secondary-foreground: 210 20% 98%;
|
||||||
|
|
||||||
|
--accent: 215 27.9% 16.9%;
|
||||||
|
--accent-foreground: 210 20% 98%;
|
||||||
|
|
||||||
|
--destructive: 0 62.8% 30.6%;
|
||||||
|
--destructive-foreground: 0 85.7% 97.3%;
|
||||||
|
|
||||||
|
--ring: 215 27.9% 16.9%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@layer base {
|
||||||
|
* {
|
||||||
|
@apply tw-border-border;
|
||||||
|
}
|
||||||
|
body {
|
||||||
|
@apply tw-bg-background tw-text-foreground;
|
||||||
|
}
|
||||||
|
}"
|
||||||
|
`;
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
import { describe, expect, test } from 'vitest'
|
import { describe, expect, it } from 'vitest'
|
||||||
|
|
||||||
import {
|
import {
|
||||||
applyColorMapping,
|
applyColorMapping,
|
||||||
|
|
@ -7,7 +7,7 @@ import {
|
||||||
import baseColor from '../fixtures/colors/slate.json'
|
import baseColor from '../fixtures/colors/slate.json'
|
||||||
|
|
||||||
describe('split class', () => {
|
describe('split class', () => {
|
||||||
test.each([
|
it.each([
|
||||||
{
|
{
|
||||||
input: 'bg-popover',
|
input: 'bg-popover',
|
||||||
output: [null, 'bg-popover', null],
|
output: [null, 'bg-popover', null],
|
||||||
|
|
@ -50,7 +50,7 @@ describe('split class', () => {
|
||||||
})
|
})
|
||||||
|
|
||||||
describe('apply color mapping', async () => {
|
describe('apply color mapping', async () => {
|
||||||
test.each([
|
it.each([
|
||||||
{
|
{
|
||||||
input: 'bg-background text-foreground',
|
input: 'bg-background text-foreground',
|
||||||
output: 'bg-white text-slate-950 dark:bg-slate-950 dark:text-slate-50',
|
output: 'bg-white text-slate-950 dark:bg-slate-950 dark:text-slate-50',
|
||||||
|
|
@ -64,7 +64,7 @@ describe('apply color mapping', async () => {
|
||||||
input:
|
input:
|
||||||
'text-destructive border-destructive/50 dark:border-destructive [&>svg]:text-destructive text-destructive',
|
'text-destructive border-destructive/50 dark:border-destructive [&>svg]:text-destructive text-destructive',
|
||||||
output:
|
output:
|
||||||
'text-red-500 border-red-500/50 dark:border-red-500 [&>svg]:text-red-500 text-red-500 dark:text-red-900 dark:border-red-900/50 dark:dark:border-red-900 dark:[&>svg]:text-red-900 dark:text-red-900',
|
'text-red-500 border-red-500/50 dark:border-red-500 [&>svg]:text-red-500 dark:text-red-900 dark:border-red-900/50 dark:dark:border-red-900 dark:[&>svg]:text-red-900',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
input:
|
input:
|
||||||
|
|
|
||||||
42
packages/cli/test/utils/apply-prefix.test.ts
Normal file
42
packages/cli/test/utils/apply-prefix.test.ts
Normal file
|
|
@ -0,0 +1,42 @@
|
||||||
|
import { describe, expect, it } from 'vitest'
|
||||||
|
|
||||||
|
import { applyPrefix } from '../../src/utils/transformers/transform-tw-prefix'
|
||||||
|
|
||||||
|
describe('apply tailwind prefix', () => {
|
||||||
|
it.each([
|
||||||
|
{
|
||||||
|
input: 'bg-slate-800 text-gray-500',
|
||||||
|
output: 'tw-bg-slate-800 tw-text-gray-500',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
input: 'hover:dark:bg-background dark:text-foreground',
|
||||||
|
output: 'hover:dark:tw-bg-background dark:tw-text-foreground',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
input:
|
||||||
|
'rounded-lg border border-slate-200 bg-white text-slate-950 shadow-sm dark:border-slate-800 dark:bg-slate-950 dark:text-slate-50',
|
||||||
|
output:
|
||||||
|
'tw-rounded-lg tw-border tw-border-slate-200 tw-bg-white tw-text-slate-950 tw-shadow-sm dark:tw-border-slate-800 dark:tw-bg-slate-950 dark:tw-text-slate-50',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
input:
|
||||||
|
'text-red-500 border-red-500/50 dark:border-red-500 [&>svg]:text-red-500 text-red-500 dark:text-red-900 dark:border-red-900/50 dark:dark:border-red-900 dark:[&>svg]:text-red-900 dark:text-red-900',
|
||||||
|
output:
|
||||||
|
'tw-text-red-500 tw-border-red-500/50 dark:tw-border-red-500 [&>svg]:tw-text-red-500 tw-text-red-500 dark:tw-text-red-900 dark:tw-border-red-900/50 dark:dark:tw-border-red-900 dark:[&>svg]:tw-text-red-900 dark:tw-text-red-900',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
input:
|
||||||
|
'flex h-full w-full items-center justify-center rounded-full bg-muted',
|
||||||
|
output:
|
||||||
|
'tw-flex tw-h-full tw-w-full tw-items-center tw-justify-center tw-rounded-full tw-bg-muted',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
input:
|
||||||
|
'absolute right-4 top-4 bg-primary rounded-sm opacity-70 ring-offset-background transition-opacity hover:opacity-100 focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2 disabled:pointer-events-none data-[state=open]:bg-secondary',
|
||||||
|
output:
|
||||||
|
'tw-absolute tw-right-4 tw-top-4 tw-bg-primary tw-rounded-sm tw-opacity-70 tw-ring-offset-background tw-transition-opacity hover:tw-opacity-100 focus:tw-outline-none focus:tw-ring-2 focus:tw-ring-ring focus:tw-ring-offset-2 disabled:tw-pointer-events-none data-[state=open]:tw-bg-secondary',
|
||||||
|
},
|
||||||
|
])(`applyTwPrefix($input) -> $output`, ({ input, output }) => {
|
||||||
|
expect(applyPrefix(input, 'tw-')).toBe(output)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
@ -1,9 +1,9 @@
|
||||||
import path from 'node:path'
|
import path from 'pathe'
|
||||||
import { expect, test } from 'vitest'
|
import { expect, it } from 'vitest'
|
||||||
|
|
||||||
import { getConfig, getRawConfig } from '../../src/utils/get-config'
|
import { getConfig, getRawConfig } from '../../src/utils/get-config'
|
||||||
|
|
||||||
test('get raw config', async () => {
|
it('get raw config', async () => {
|
||||||
expect(
|
expect(
|
||||||
await getRawConfig(path.resolve(__dirname, '../fixtures/config-none')),
|
await getRawConfig(path.resolve(__dirname, '../fixtures/config-none')),
|
||||||
).toEqual(null)
|
).toEqual(null)
|
||||||
|
|
@ -31,7 +31,7 @@ test('get raw config', async () => {
|
||||||
).rejects.toThrowError()
|
).rejects.toThrowError()
|
||||||
})
|
})
|
||||||
|
|
||||||
test('get config', async () => {
|
it('get config', async () => {
|
||||||
expect(
|
expect(
|
||||||
await getConfig(path.resolve(__dirname, '../fixtures/config-none')),
|
await getConfig(path.resolve(__dirname, '../fixtures/config-none')),
|
||||||
).toEqual(null)
|
).toEqual(null)
|
||||||
|
|
@ -71,6 +71,11 @@ test('get config', async () => {
|
||||||
'../fixtures/config-partial',
|
'../fixtures/config-partial',
|
||||||
'./components',
|
'./components',
|
||||||
),
|
),
|
||||||
|
ui: path.resolve(
|
||||||
|
__dirname,
|
||||||
|
'../fixtures/config-partial',
|
||||||
|
'./components',
|
||||||
|
),
|
||||||
utils: path.resolve(
|
utils: path.resolve(
|
||||||
__dirname,
|
__dirname,
|
||||||
'../fixtures/config-partial',
|
'../fixtures/config-partial',
|
||||||
|
|
@ -89,9 +94,11 @@ test('get config', async () => {
|
||||||
baseColor: 'zinc',
|
baseColor: 'zinc',
|
||||||
css: 'src/app/globals.css',
|
css: 'src/app/globals.css',
|
||||||
cssVariables: true,
|
cssVariables: true,
|
||||||
|
prefix: 'tw-',
|
||||||
},
|
},
|
||||||
aliases: {
|
aliases: {
|
||||||
components: '~/components',
|
components: '~/components',
|
||||||
|
ui: '~/ui',
|
||||||
utils: '~/lib/utils',
|
utils: '~/lib/utils',
|
||||||
},
|
},
|
||||||
framework: 'Vite',
|
framework: 'Vite',
|
||||||
|
|
@ -111,6 +118,11 @@ test('get config', async () => {
|
||||||
'../fixtures/config-full',
|
'../fixtures/config-full',
|
||||||
'./src/components',
|
'./src/components',
|
||||||
),
|
),
|
||||||
|
ui: path.resolve(
|
||||||
|
__dirname,
|
||||||
|
'../fixtures/config-full',
|
||||||
|
'./src/ui',
|
||||||
|
),
|
||||||
utils: path.resolve(
|
utils: path.resolve(
|
||||||
__dirname,
|
__dirname,
|
||||||
'../fixtures/config-full',
|
'../fixtures/config-full',
|
||||||
|
|
@ -152,6 +164,11 @@ test('get config', async () => {
|
||||||
'../fixtures/config-js',
|
'../fixtures/config-js',
|
||||||
'./components',
|
'./components',
|
||||||
),
|
),
|
||||||
|
ui: path.resolve(
|
||||||
|
__dirname,
|
||||||
|
'../fixtures/config-js',
|
||||||
|
'./components',
|
||||||
|
),
|
||||||
utils: path.resolve(__dirname, '../fixtures/config-js', './lib/utils'),
|
utils: path.resolve(__dirname, '../fixtures/config-js', './lib/utils'),
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
|
||||||
|
|
@ -1,26 +0,0 @@
|
||||||
import path from 'node:path'
|
|
||||||
import { expect, test } from 'vitest'
|
|
||||||
|
|
||||||
import { getPackageManager } from '../../src/utils/get-package-manager'
|
|
||||||
|
|
||||||
test('get package manager', async () => {
|
|
||||||
expect(
|
|
||||||
await getPackageManager(path.resolve(__dirname, '../fixtures/project-yarn')),
|
|
||||||
).toBe('yarn')
|
|
||||||
|
|
||||||
expect(
|
|
||||||
await getPackageManager(path.resolve(__dirname, '../fixtures/project-npm')),
|
|
||||||
).toBe('npm')
|
|
||||||
|
|
||||||
expect(
|
|
||||||
await getPackageManager(path.resolve(__dirname, '../fixtures/project-pnpm')),
|
|
||||||
).toBe('pnpm')
|
|
||||||
|
|
||||||
expect(
|
|
||||||
await getPackageManager(path.resolve(__dirname, '../fixtures/project-bun')),
|
|
||||||
).toBe('bun')
|
|
||||||
|
|
||||||
expect(
|
|
||||||
await getPackageManager(path.resolve(__dirname, '../fixtures/next')),
|
|
||||||
).toBe('pnpm')
|
|
||||||
})
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
import path from 'node:path'
|
import path from 'pathe'
|
||||||
import { type ConfigLoaderSuccessResult, loadConfig } from 'tsconfig-paths'
|
import { type ConfigLoaderSuccessResult, loadConfig } from 'tsconfig-paths'
|
||||||
import { expect, test } from 'vitest'
|
import { expect, test } from 'vitest'
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,9 @@
|
||||||
import { expect, test } from 'vitest'
|
import { expect, it } from 'vitest'
|
||||||
|
|
||||||
import { transform } from '../../src/utils/transformers'
|
import { transform } from '../../src/utils/transformers'
|
||||||
import stone from '../fixtures/colors/stone.json'
|
import stone from '../fixtures/colors/stone.json'
|
||||||
|
|
||||||
test('transform css vars', async () => {
|
it('transform css vars', async () => {
|
||||||
expect(
|
expect(
|
||||||
await transform({
|
await transform({
|
||||||
filename: 'app.vue',
|
filename: 'app.vue',
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
import { resolve } from 'node:path'
|
import { resolve } from 'pathe'
|
||||||
import { describe, expect, test } from 'vitest'
|
import { describe, expect, test } from 'vitest'
|
||||||
import { transform } from '../../src/utils/transformers'
|
import { transform } from '../../src/utils/transformers'
|
||||||
|
|
||||||
|
|
|
||||||
115
packages/cli/test/utils/transform-tw-prefix.test.ts
Normal file
115
packages/cli/test/utils/transform-tw-prefix.test.ts
Normal file
|
|
@ -0,0 +1,115 @@
|
||||||
|
import { expect, it } from 'vitest'
|
||||||
|
|
||||||
|
import { transform } from '../../src/utils/transformers'
|
||||||
|
import { applyPrefixesCss } from '../../src/utils/transformers/transform-tw-prefix'
|
||||||
|
import stone from '../fixtures/colors/stone.json'
|
||||||
|
|
||||||
|
it('transform tailwind prefix', async () => {
|
||||||
|
// expect(
|
||||||
|
// await transform({
|
||||||
|
// filename: 'test.ts',
|
||||||
|
// raw: `const testVariants = cva(
|
||||||
|
// 'bg-background hover:bg-muted text-primary-foreground sm:focus:text-accent-foreground',
|
||||||
|
// {
|
||||||
|
// variants: {
|
||||||
|
// variant: {
|
||||||
|
// default: 'bg-primary text-primary-foreground hover:bg-primary/90',
|
||||||
|
// },
|
||||||
|
// size: {
|
||||||
|
// default: 'h-10 px-4 py-2',
|
||||||
|
// },
|
||||||
|
// },
|
||||||
|
// },
|
||||||
|
// )`,
|
||||||
|
// config: {
|
||||||
|
// tailwind: {
|
||||||
|
// baseColor: 'stone',
|
||||||
|
// prefix: 'tw-',
|
||||||
|
// },
|
||||||
|
// aliases: {
|
||||||
|
// components: '@/components',
|
||||||
|
// utils: '@/lib/utils',
|
||||||
|
// },
|
||||||
|
// },
|
||||||
|
// baseColor: 'stone',
|
||||||
|
// }),
|
||||||
|
// ).toMatchSnapshot()
|
||||||
|
|
||||||
|
// expect(
|
||||||
|
// await transform({
|
||||||
|
// filename: 'app.vue',
|
||||||
|
// raw: `<template>
|
||||||
|
// <div class="bg-background hover:bg-muted text-primary-foreground sm:focus:text-accent-foreground">
|
||||||
|
// foo
|
||||||
|
// </div>
|
||||||
|
// </template>
|
||||||
|
// `,
|
||||||
|
// config: {
|
||||||
|
// tailwind: {
|
||||||
|
// baseColor: 'stone',
|
||||||
|
// prefix: 'tw-',
|
||||||
|
// },
|
||||||
|
// aliases: {
|
||||||
|
// components: '@/components',
|
||||||
|
// utils: '@/lib/utils',
|
||||||
|
// },
|
||||||
|
// },
|
||||||
|
// baseColor: 'stone',
|
||||||
|
// }),
|
||||||
|
// ).toMatchSnapshot()
|
||||||
|
|
||||||
|
// expect(
|
||||||
|
// await transform({
|
||||||
|
// filename: 'app.vue',
|
||||||
|
// raw: `<template>
|
||||||
|
// <div class="bg-background hover:bg-muted text-primary-foreground sm:focus:text-accent-foreground">
|
||||||
|
// foo
|
||||||
|
// </div>
|
||||||
|
// </template>
|
||||||
|
// `,
|
||||||
|
// config: {
|
||||||
|
// tailwind: {
|
||||||
|
// baseColor: 'stone',
|
||||||
|
// cssVariables: false,
|
||||||
|
// prefix: 'tw-',
|
||||||
|
// },
|
||||||
|
// aliases: {
|
||||||
|
// components: '@/components',
|
||||||
|
// utils: '@/lib/utils',
|
||||||
|
// },
|
||||||
|
// },
|
||||||
|
// baseColor: stone,
|
||||||
|
// }),
|
||||||
|
// ).toMatchSnapshot()
|
||||||
|
|
||||||
|
// expect(
|
||||||
|
// await transform({
|
||||||
|
// filename: 'app.vue',
|
||||||
|
// raw: `<template>
|
||||||
|
// <div id="testing" v-bind="props" @click="handleSomething" :data-test="true" :class="cn('bg-background hover:bg-muted', true && 'text-primary-foreground sm:focus:text-accent-foreground')">
|
||||||
|
// foo
|
||||||
|
// </div>
|
||||||
|
// </template>
|
||||||
|
// `,
|
||||||
|
// config: {
|
||||||
|
// tailwind: {
|
||||||
|
// baseColor: 'stone',
|
||||||
|
// cssVariables: false,
|
||||||
|
// prefix: 'tw-',
|
||||||
|
// },
|
||||||
|
// aliases: {
|
||||||
|
// components: '@/components',
|
||||||
|
// utils: '@/lib/utils',
|
||||||
|
// },
|
||||||
|
// },
|
||||||
|
// baseColor: stone,
|
||||||
|
// }),
|
||||||
|
// ).toMatchSnapshot()
|
||||||
|
|
||||||
|
// expect(
|
||||||
|
// applyPrefixesCss(
|
||||||
|
// '@tailwind base;\n@tailwind components;\n@tailwind utilities;\n \n@layer base {\n :root {\n --background: 0 0% 100%;\n --foreground: 224 71.4% 4.1%;\n \n --muted: 220 14.3% 95.9%;\n --muted-foreground: 220 8.9% 46.1%;\n \n --popover: 0 0% 100%;\n --popover-foreground: 224 71.4% 4.1%;\n \n --card: 0 0% 100%;\n --card-foreground: 224 71.4% 4.1%;\n \n --border: 220 13% 91%;\n --input: 220 13% 91%;\n \n --primary: 220.9 39.3% 11%;\n --primary-foreground: 210 20% 98%;\n \n --secondary: 220 14.3% 95.9%;\n --secondary-foreground: 220.9 39.3% 11%;\n \n --accent: 220 14.3% 95.9%;\n --accent-foreground: 220.9 39.3% 11%;\n \n --destructive: 0 84.2% 60.2%;\n --destructive-foreground: 210 20% 98%;\n \n --ring: 217.9 10.6% 64.9%;\n \n --radius: 0.5rem;\n }\n \n .dark {\n --background: 224 71.4% 4.1%;\n --foreground: 210 20% 98%;\n \n --muted: 215 27.9% 16.9%;\n --muted-foreground: 217.9 10.6% 64.9%;\n \n --popover: 224 71.4% 4.1%;\n --popover-foreground: 210 20% 98%;\n \n --card: 224 71.4% 4.1%;\n --card-foreground: 210 20% 98%;\n \n --border: 215 27.9% 16.9%;\n --input: 215 27.9% 16.9%;\n \n --primary: 210 20% 98%;\n --primary-foreground: 220.9 39.3% 11%;\n \n --secondary: 215 27.9% 16.9%;\n --secondary-foreground: 210 20% 98%;\n \n --accent: 215 27.9% 16.9%;\n --accent-foreground: 210 20% 98%;\n \n --destructive: 0 62.8% 30.6%;\n --destructive-foreground: 0 85.7% 97.3%;\n \n --ring: 215 27.9% 16.9%;\n }\n}\n \n@layer base {\n * {\n @apply border-border;\n }\n body {\n @apply bg-background text-foreground;\n }\n}',
|
||||||
|
// 'tw-',
|
||||||
|
// ),
|
||||||
|
// ).toMatchSnapshot()
|
||||||
|
})
|
||||||
1133
pnpm-lock.yaml
1133
pnpm-lock.yaml
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue
Block a user