purvex-ui 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +47 -0
- package/src/commands/add.js +80 -0
- package/src/commands/init.js +147 -0
- package/src/commands/install.js +30 -0
- package/src/commands/setup.js +482 -0
- package/src/commands/uninstall.js +130 -0
- package/src/index.js +126 -0
package/package.json
ADDED
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "purvex-ui",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "React component generator with Tailwind CSS v4 & Purple Elegant theme - Built by Al Zaki Ibra Ramadani",
|
|
5
|
+
"main": "src/index.js",
|
|
6
|
+
"bin": {
|
|
7
|
+
"purvex-ui": "src/index.js"
|
|
8
|
+
},
|
|
9
|
+
"files": [
|
|
10
|
+
"src/",
|
|
11
|
+
"templates/",
|
|
12
|
+
"registry/"
|
|
13
|
+
],
|
|
14
|
+
"scripts": {
|
|
15
|
+
"test": "echo \"No tests yet\"",
|
|
16
|
+
"prepare": "node -e \"require('fs').chmodSync('src/index.js', '755')\""
|
|
17
|
+
},
|
|
18
|
+
"keywords": [
|
|
19
|
+
"react",
|
|
20
|
+
"ui",
|
|
21
|
+
"components",
|
|
22
|
+
"tailwindcss",
|
|
23
|
+
"vite",
|
|
24
|
+
"cli",
|
|
25
|
+
"generator",
|
|
26
|
+
"purple",
|
|
27
|
+
"theme"
|
|
28
|
+
],
|
|
29
|
+
"author": "Al Zaki Ibra Ramadani",
|
|
30
|
+
"license": "MIT",
|
|
31
|
+
"repository": {
|
|
32
|
+
"type": "git",
|
|
33
|
+
"url": "git+https://github.com/VortechLabs/purvex-ui.git"
|
|
34
|
+
},
|
|
35
|
+
"bugs": {
|
|
36
|
+
"url": "https://github.com/VortechLabs/purvex-ui/issues"
|
|
37
|
+
},
|
|
38
|
+
"homepage": "https://purvex-ui.vercel.app",
|
|
39
|
+
"dependencies": {
|
|
40
|
+
"chalk": "^4.1.2",
|
|
41
|
+
"commander": "^11.1.0",
|
|
42
|
+
"fs-extra": "^11.2.0"
|
|
43
|
+
},
|
|
44
|
+
"engines": {
|
|
45
|
+
"node": ">=14.0.0"
|
|
46
|
+
}
|
|
47
|
+
}
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
const fs = require('fs');
|
|
2
|
+
const path = require('path');
|
|
3
|
+
const chalk = require('chalk');
|
|
4
|
+
|
|
5
|
+
async function addComponent(componentName, options) {
|
|
6
|
+
try {
|
|
7
|
+
console.log(chalk.blue.bold(`\nš Adding ${componentName}...\n`));
|
|
8
|
+
|
|
9
|
+
// 1. Load registry
|
|
10
|
+
const cliDir = path.dirname(path.dirname(__dirname));
|
|
11
|
+
const rootDir = path.dirname(path.dirname(cliDir));
|
|
12
|
+
const registryPath = path.join(rootDir, 'registry', 'components.json');
|
|
13
|
+
|
|
14
|
+
if (!fs.existsSync(registryPath)) {
|
|
15
|
+
console.log(chalk.red('ā Registry not found'));
|
|
16
|
+
return;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
const registryContent = fs.readFileSync(registryPath, 'utf-8');
|
|
20
|
+
const registry = JSON.parse(registryContent);
|
|
21
|
+
|
|
22
|
+
// 2. Find component
|
|
23
|
+
const component = registry.components.find(c => c.name === componentName);
|
|
24
|
+
|
|
25
|
+
if (!component) {
|
|
26
|
+
console.log(chalk.red(`ā Component "${componentName}" not found`));
|
|
27
|
+
console.log(chalk.yellow('\nAvailable components:'));
|
|
28
|
+
registry.components.forEach(c => {
|
|
29
|
+
console.log(chalk.cyan(` - ${c.name}`));
|
|
30
|
+
});
|
|
31
|
+
return;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
// 3. Setup target directory INSIDE src
|
|
35
|
+
const userProjectRoot = process.cwd();
|
|
36
|
+
const targetDir = path.join(userProjectRoot, 'src', 'components', 'ui');
|
|
37
|
+
|
|
38
|
+
// Auto create directory jika belum ada
|
|
39
|
+
if (!fs.existsSync(targetDir)) {
|
|
40
|
+
fs.mkdirSync(targetDir, { recursive: true });
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
// 4. Copy template files
|
|
44
|
+
for (const file of component.files) {
|
|
45
|
+
const sourcePath = path.join(rootDir, 'templates', file);
|
|
46
|
+
const targetPath = path.join(targetDir, path.basename(file));
|
|
47
|
+
|
|
48
|
+
if (!fs.existsSync(sourcePath)) {
|
|
49
|
+
console.log(chalk.yellow(`ā ļø Template ${file} not found, skipping...`));
|
|
50
|
+
continue;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
// Read template
|
|
54
|
+
let content = fs.readFileSync(sourcePath, 'utf-8');
|
|
55
|
+
|
|
56
|
+
// Update import path untuk src/
|
|
57
|
+
content = content.replace(/@\/lib\/utils/g, '@/lib/utils');
|
|
58
|
+
content = content.replace(/@\/components\/ui/g, '@/components/ui');
|
|
59
|
+
|
|
60
|
+
// Write to target
|
|
61
|
+
fs.writeFileSync(targetPath, content);
|
|
62
|
+
|
|
63
|
+
const relativePath = path.relative(userProjectRoot, targetPath);
|
|
64
|
+
console.log(chalk.green('ā
Created: ') + relativePath);
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
// 5. Success message
|
|
68
|
+
console.log(chalk.green.bold(`\nš Component ${componentName} added successfully!`));
|
|
69
|
+
console.log(chalk.white('\nUsage:'));
|
|
70
|
+
console.log(chalk.cyan(` import { ${componentName.charAt(0).toUpperCase() + componentName.slice(1)} } from "@/components/ui/${componentName}";`));
|
|
71
|
+
|
|
72
|
+
console.log(chalk.white('\nStart your dev server:'));
|
|
73
|
+
console.log(chalk.cyan(' npm run dev'));
|
|
74
|
+
|
|
75
|
+
} catch (error) {
|
|
76
|
+
console.error(chalk.red('ā Error adding component:'), error.message);
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
module.exports = { addComponent };
|
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
const fs = require('fs');
|
|
2
|
+
const path = require('path');
|
|
3
|
+
const chalk = require('chalk');
|
|
4
|
+
const { execSync } = require('child_process');
|
|
5
|
+
|
|
6
|
+
async function initProject() {
|
|
7
|
+
try {
|
|
8
|
+
const userProjectRoot = process.cwd();
|
|
9
|
+
|
|
10
|
+
console.log(chalk.blue.bold('š Initializing Purvex UI...\n'));
|
|
11
|
+
|
|
12
|
+
// 1. Check package.json
|
|
13
|
+
const packageJsonPath = path.join(userProjectRoot, 'package.json');
|
|
14
|
+
if (!fs.existsSync(packageJsonPath)) {
|
|
15
|
+
console.log(chalk.red('ā Tidak ada package.json ditemukan'));
|
|
16
|
+
console.log(chalk.yellow(' Buat project React dulu: npx create-vite@latest . --template react'));
|
|
17
|
+
return;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
// 2. Auto install dependencies jika belum ada
|
|
21
|
+
console.log(chalk.blue('š¦ Checking dependencies...'));
|
|
22
|
+
|
|
23
|
+
const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, 'utf-8'));
|
|
24
|
+
const deps = packageJson.dependencies || {};
|
|
25
|
+
const devDeps = packageJson.devDependencies || {};
|
|
26
|
+
|
|
27
|
+
const missingDeps = [];
|
|
28
|
+
|
|
29
|
+
if (!deps['clsx']) missingDeps.push('clsx');
|
|
30
|
+
if (!deps['tailwind-merge']) missingDeps.push('tailwind-merge');
|
|
31
|
+
|
|
32
|
+
if (missingDeps.length > 0) {
|
|
33
|
+
console.log(chalk.yellow(`ā ļø Installing missing dependencies: ${missingDeps.join(', ')}`));
|
|
34
|
+
try {
|
|
35
|
+
execSync(`npm install ${missingDeps.join(' ')}`, { stdio: 'inherit' });
|
|
36
|
+
console.log(chalk.green('ā
Dependencies installed'));
|
|
37
|
+
} catch (error) {
|
|
38
|
+
console.log(chalk.yellow('ā ļø Install dependencies manually:'));
|
|
39
|
+
console.log(chalk.cyan(` npm install ${missingDeps.join(' ')}`));
|
|
40
|
+
}
|
|
41
|
+
} else {
|
|
42
|
+
console.log(chalk.green('ā
All dependencies already installed'));
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
// 3. Check Tailwind CSS
|
|
46
|
+
const tailwindConfigs = [
|
|
47
|
+
'tailwind.config.js',
|
|
48
|
+
'tailwind.config.ts',
|
|
49
|
+
'tailwind.config.cjs',
|
|
50
|
+
'tailwind.config.mjs'
|
|
51
|
+
];
|
|
52
|
+
|
|
53
|
+
const hasTailwindConfig = tailwindConfigs.some(config =>
|
|
54
|
+
fs.existsSync(path.join(userProjectRoot, config))
|
|
55
|
+
);
|
|
56
|
+
|
|
57
|
+
if (!hasTailwindConfig) {
|
|
58
|
+
console.log(chalk.yellow('\nā ļø Tailwind CSS not found!'));
|
|
59
|
+
console.log(chalk.cyan(' Installing Tailwind CSS...'));
|
|
60
|
+
try {
|
|
61
|
+
execSync('npm install -D tailwindcss postcss autoprefixer', { stdio: 'inherit' });
|
|
62
|
+
execSync('npx tailwindcss init -p', { stdio: 'inherit' });
|
|
63
|
+
console.log(chalk.green('ā
Tailwind CSS installed'));
|
|
64
|
+
} catch (error) {
|
|
65
|
+
console.log(chalk.yellow('ā ļø Install Tailwind CSS manually:'));
|
|
66
|
+
console.log(chalk.cyan(' 1. npm install -D tailwindcss postcss autoprefixer'));
|
|
67
|
+
console.log(chalk.cyan(' 2. npx tailwindcss init -p'));
|
|
68
|
+
}
|
|
69
|
+
} else {
|
|
70
|
+
console.log(chalk.green('ā
Tailwind CSS already installed'));
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
// 4. Update Tailwind config untuk include components
|
|
74
|
+
const tailwindConfigPath = tailwindConfigs.find(config =>
|
|
75
|
+
fs.existsSync(path.join(userProjectRoot, config))
|
|
76
|
+
);
|
|
77
|
+
|
|
78
|
+
if (tailwindConfigPath) {
|
|
79
|
+
try {
|
|
80
|
+
const configContent = fs.readFileSync(path.join(userProjectRoot, tailwindConfigPath), 'utf-8');
|
|
81
|
+
if (!configContent.includes('./components')) {
|
|
82
|
+
console.log(chalk.blue('āļø Updating Tailwind config...'));
|
|
83
|
+
const updatedContent = configContent.replace(
|
|
84
|
+
/content:\s*\[([\s\S]*?)\]/,
|
|
85
|
+
`content: [
|
|
86
|
+
'./index.html',
|
|
87
|
+
'./src/**/*.{js,ts,jsx,tsx}',
|
|
88
|
+
'./components/**/*.{js,ts,jsx,tsx}'
|
|
89
|
+
]`
|
|
90
|
+
);
|
|
91
|
+
fs.writeFileSync(path.join(userProjectRoot, tailwindConfigPath), updatedContent);
|
|
92
|
+
console.log(chalk.green('ā
Tailwind config updated'));
|
|
93
|
+
}
|
|
94
|
+
} catch (error) {
|
|
95
|
+
// Skip jika error
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
// 5. Create directories INSIDE src
|
|
100
|
+
const srcDir = path.join(userProjectRoot, 'src');
|
|
101
|
+
const libDir = path.join(srcDir, 'lib');
|
|
102
|
+
const componentsDir = path.join(srcDir, 'components', 'ui');
|
|
103
|
+
|
|
104
|
+
// Create src jika belum ada (untuk project yang tidak punya src)
|
|
105
|
+
if (!fs.existsSync(srcDir)) {
|
|
106
|
+
fs.mkdirSync(srcDir, { recursive: true });
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
// Create lib/utils.js inside src
|
|
110
|
+
if (!fs.existsSync(libDir)) {
|
|
111
|
+
fs.mkdirSync(libDir, { recursive: true });
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
const utilsPath = path.join(libDir, 'utils.js');
|
|
115
|
+
if (!fs.existsSync(utilsPath)) {
|
|
116
|
+
const utilsTemplate = `import { clsx } from 'clsx';
|
|
117
|
+
import { twMerge } from 'tailwind-merge';
|
|
118
|
+
|
|
119
|
+
export function cn(...inputs) {
|
|
120
|
+
return twMerge(clsx(inputs));
|
|
121
|
+
}`;
|
|
122
|
+
|
|
123
|
+
fs.writeFileSync(utilsPath, utilsTemplate);
|
|
124
|
+
console.log(chalk.green('ā
Created: src/lib/utils.js'));
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
// Create components directory inside src
|
|
128
|
+
if (!fs.existsSync(componentsDir)) {
|
|
129
|
+
fs.mkdirSync(componentsDir, { recursive: true });
|
|
130
|
+
console.log(chalk.green('ā
Created: src/components/ui directory'));
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
// 6. Success message
|
|
134
|
+
console.log(chalk.green.bold('\nš Purvex UI initialized successfully!'));
|
|
135
|
+
console.log(chalk.white('\nTo add components:'));
|
|
136
|
+
console.log(chalk.cyan(' npx purvex-ui add button'));
|
|
137
|
+
console.log(chalk.cyan(' npx purvex-ui add card'));
|
|
138
|
+
|
|
139
|
+
console.log(chalk.white('\nStart using:'));
|
|
140
|
+
console.log(chalk.gray(' import { Button } from "@/components/ui/button"'));
|
|
141
|
+
|
|
142
|
+
} catch (error) {
|
|
143
|
+
console.error(chalk.red('ā Error during init:'), error.message);
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
module.exports = { initProject };
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
const { execSync } = require('child_process');
|
|
2
|
+
const chalk = require('chalk');
|
|
3
|
+
|
|
4
|
+
async function installDeps() {
|
|
5
|
+
try {
|
|
6
|
+
console.log(chalk.blue.bold('š¦ Installing Purvex UI dependencies...\n'));
|
|
7
|
+
|
|
8
|
+
// Install clsx dan tailwind-merge
|
|
9
|
+
console.log(chalk.blue('Installing clsx and tailwind-merge...'));
|
|
10
|
+
execSync('npm install clsx tailwind-merge', { stdio: 'inherit' });
|
|
11
|
+
|
|
12
|
+
// Install Tailwind CSS
|
|
13
|
+
console.log(chalk.blue('\nInstalling Tailwind CSS...'));
|
|
14
|
+
execSync('npm install -D tailwindcss postcss autoprefixer', { stdio: 'inherit' });
|
|
15
|
+
execSync('npx tailwindcss init -p', { stdio: 'inherit' });
|
|
16
|
+
|
|
17
|
+
console.log(chalk.green.bold('\nā
All dependencies installed!'));
|
|
18
|
+
console.log(chalk.white('\nNext:'));
|
|
19
|
+
console.log(chalk.cyan(' npx purvex-ui init'));
|
|
20
|
+
|
|
21
|
+
} catch (error) {
|
|
22
|
+
console.error(chalk.red('ā Error installing dependencies:'), error.message);
|
|
23
|
+
console.log(chalk.yellow('\nInstall manually:'));
|
|
24
|
+
console.log(chalk.cyan(' npm install clsx tailwind-merge'));
|
|
25
|
+
console.log(chalk.cyan(' npm install -D tailwindcss postcss autoprefixer'));
|
|
26
|
+
console.log(chalk.cyan(' npx tailwindcss init -p'));
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
module.exports = { installDeps };
|
|
@@ -0,0 +1,482 @@
|
|
|
1
|
+
const chalk = require('chalk');
|
|
2
|
+
const { execSync } = require('child_process');
|
|
3
|
+
const fs = require('fs');
|
|
4
|
+
const path = require('path');
|
|
5
|
+
|
|
6
|
+
async function setupProject() {
|
|
7
|
+
try {
|
|
8
|
+
console.log(chalk.green.bold('š Setting up Purvex UI project with Tailwind v4...\n'));
|
|
9
|
+
|
|
10
|
+
const userProjectRoot = process.cwd();
|
|
11
|
+
|
|
12
|
+
// 1. Check jika React project
|
|
13
|
+
const packageJsonPath = path.join(userProjectRoot, 'package.json');
|
|
14
|
+
if (!fs.existsSync(packageJsonPath)) {
|
|
15
|
+
console.log(chalk.red('ā Not a React project'));
|
|
16
|
+
console.log(chalk.yellow('Create React project first:'));
|
|
17
|
+
console.log(chalk.cyan(' npx create-vite@latest . --template react'));
|
|
18
|
+
return;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, 'utf-8'));
|
|
22
|
+
|
|
23
|
+
// 2. Install dependencies (Tailwind v4 TERBARU)
|
|
24
|
+
console.log(chalk.blue('š¦ Installing Purvex UI dependencies (Tailwind v4)...'));
|
|
25
|
+
|
|
26
|
+
try {
|
|
27
|
+
// Install clsx dan tailwind-merge
|
|
28
|
+
execSync('npm install clsx tailwind-merge', { stdio: 'inherit' });
|
|
29
|
+
|
|
30
|
+
// Install Tailwind v4 (VERSI TERBARU)
|
|
31
|
+
execSync('npm install -D tailwindcss@latest @tailwindcss/vite', { stdio: 'inherit' });
|
|
32
|
+
|
|
33
|
+
console.log(chalk.green('ā
Dependencies installed'));
|
|
34
|
+
} catch (error) {
|
|
35
|
+
console.log(chalk.yellow('ā ļø Install manually:'));
|
|
36
|
+
console.log(chalk.cyan(' npm install clsx tailwind-merge'));
|
|
37
|
+
console.log(chalk.cyan(' npm install -D tailwindcss@latest @tailwindcss/vite'));
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
// 3. Setup untuk Tailwind v4 - TIDAK PERLU tailwind.config.js lagi
|
|
41
|
+
console.log(chalk.blue('āļø Setting up Tailwind CSS v4...'));
|
|
42
|
+
|
|
43
|
+
const tailwindConfigPath = path.join(userProjectRoot, 'tailwind.config.js');
|
|
44
|
+
const postcssConfigPath = path.join(userProjectRoot, 'postcss.config.js');
|
|
45
|
+
|
|
46
|
+
// Hapus file config lama jika ada
|
|
47
|
+
if (fs.existsSync(tailwindConfigPath)) {
|
|
48
|
+
fs.unlinkSync(tailwindConfigPath);
|
|
49
|
+
console.log(chalk.yellow('šļø Removed old tailwind.config.js (not needed for v4)'));
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
if (fs.existsSync(postcssConfigPath)) {
|
|
53
|
+
fs.unlinkSync(postcssConfigPath);
|
|
54
|
+
console.log(chalk.yellow('šļø Removed old postcss.config.js (not needed for v4)'));
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
// 4. Update Vite config untuk Tailwind v4
|
|
58
|
+
console.log(chalk.blue('š§ Configuring Vite for Tailwind v4...'));
|
|
59
|
+
|
|
60
|
+
const viteConfigs = ['vite.config.js', 'vite.config.ts'];
|
|
61
|
+
let viteConfigFile = null;
|
|
62
|
+
let isTypeScript = false;
|
|
63
|
+
|
|
64
|
+
for (const config of viteConfigs) {
|
|
65
|
+
if (fs.existsSync(path.join(userProjectRoot, config))) {
|
|
66
|
+
viteConfigFile = config;
|
|
67
|
+
isTypeScript = config.endsWith('.ts');
|
|
68
|
+
break;
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
// Import untuk TypeScript atau JavaScript
|
|
73
|
+
const importStatement = isTypeScript ?
|
|
74
|
+
`import tailwindcss from '@tailwindcss/vite'` :
|
|
75
|
+
`import tailwindcss from '@tailwindcss/vite'`;
|
|
76
|
+
|
|
77
|
+
if (!viteConfigFile) {
|
|
78
|
+
// Buat vite.config.js baru untuk Tailwind v4
|
|
79
|
+
viteConfigFile = 'vite.config.js';
|
|
80
|
+
const viteConfigContent = `import { defineConfig } from 'vite'
|
|
81
|
+
import react from '@vitejs/plugin-react'
|
|
82
|
+
import tailwindcss from '@tailwindcss/vite'
|
|
83
|
+
import path from 'path'
|
|
84
|
+
|
|
85
|
+
export default defineConfig({
|
|
86
|
+
plugins: [
|
|
87
|
+
react(),
|
|
88
|
+
tailwindcss(),
|
|
89
|
+
],
|
|
90
|
+
resolve: {
|
|
91
|
+
alias: {
|
|
92
|
+
'@': path.resolve(__dirname, './src'),
|
|
93
|
+
},
|
|
94
|
+
},
|
|
95
|
+
})`;
|
|
96
|
+
|
|
97
|
+
fs.writeFileSync(path.join(userProjectRoot, viteConfigFile), viteConfigContent);
|
|
98
|
+
console.log(chalk.green(`ā
Created: ${viteConfigFile} with Tailwind v4`));
|
|
99
|
+
} else {
|
|
100
|
+
// Update existing vite config untuk Tailwind v4
|
|
101
|
+
const configPath = path.join(userProjectRoot, viteConfigFile);
|
|
102
|
+
let configContent = fs.readFileSync(configPath, 'utf-8');
|
|
103
|
+
|
|
104
|
+
// Tambah import @tailwindcss/vite
|
|
105
|
+
if (!configContent.includes("@tailwindcss/vite")) {
|
|
106
|
+
configContent = importStatement + '\n' + configContent;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
// Tambahkan tailwindcss() ke plugins
|
|
110
|
+
if (configContent.includes('plugins:')) {
|
|
111
|
+
if (configContent.includes('tailwindcss(),')) {
|
|
112
|
+
console.log(chalk.green('ā
Tailwind v4 already configured in Vite'));
|
|
113
|
+
} else {
|
|
114
|
+
// Tambahkan ke array plugins
|
|
115
|
+
configContent = configContent.replace(
|
|
116
|
+
/plugins:\s*\[([\s\S]*?)\]/,
|
|
117
|
+
`plugins: [
|
|
118
|
+
$1\n tailwindcss(),`
|
|
119
|
+
);
|
|
120
|
+
// Tutup bracket dengan benar
|
|
121
|
+
configContent = configContent.replace(/tailwindcss\(\),\s*$/, 'tailwindcss(),\n ]');
|
|
122
|
+
}
|
|
123
|
+
} else {
|
|
124
|
+
// Tambah section plugins baru
|
|
125
|
+
configContent = configContent.replace(
|
|
126
|
+
/export default defineConfig\({([\s\S]*?)}\)/,
|
|
127
|
+
`export default defineConfig({
|
|
128
|
+
plugins: [
|
|
129
|
+
react(),
|
|
130
|
+
tailwindcss(),
|
|
131
|
+
],$1})`
|
|
132
|
+
);
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
// Add path alias jika belum ada
|
|
136
|
+
if (!configContent.includes("'@': path.resolve")) {
|
|
137
|
+
// Add path import jika belum ada
|
|
138
|
+
if (!configContent.includes("import path from 'path'")) {
|
|
139
|
+
configContent = configContent.replace(
|
|
140
|
+
/import.*from.*['"]@vitejs\/plugin-react['"]/,
|
|
141
|
+
`import path from 'path'\n$&`
|
|
142
|
+
);
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
// Add alias configuration
|
|
146
|
+
if (configContent.includes('resolve:')) {
|
|
147
|
+
// Update existing resolve
|
|
148
|
+
configContent = configContent.replace(
|
|
149
|
+
/resolve:\s*{([\s\S]*?)}/,
|
|
150
|
+
`resolve: {
|
|
151
|
+
alias: {
|
|
152
|
+
'@': path.resolve(__dirname, './src'),
|
|
153
|
+
},$1}`
|
|
154
|
+
);
|
|
155
|
+
} else {
|
|
156
|
+
// Add new resolve section
|
|
157
|
+
configContent = configContent.replace(
|
|
158
|
+
/export default defineConfig\({([\s\S]*?)}\)/,
|
|
159
|
+
`export default defineConfig({
|
|
160
|
+
$1
|
|
161
|
+
resolve: {
|
|
162
|
+
alias: {
|
|
163
|
+
'@': path.resolve(__dirname, './src'),
|
|
164
|
+
},
|
|
165
|
+
},
|
|
166
|
+
})`
|
|
167
|
+
);
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
fs.writeFileSync(configPath, configContent);
|
|
172
|
+
console.log(chalk.green(`ā
Updated: ${viteConfigFile} for Tailwind v4`));
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
// 5. Setup CSS file dengan Tailwind v4 syntax
|
|
176
|
+
console.log(chalk.blue('šØ Setting up CSS with Tailwind v4...'));
|
|
177
|
+
|
|
178
|
+
const srcDir = path.join(userProjectRoot, 'src');
|
|
179
|
+
const indexPath = path.join(srcDir, 'index.css');
|
|
180
|
+
|
|
181
|
+
// CSS untuk Tailwind v4 - Menggunakan @import bukan @tailwind
|
|
182
|
+
const cssContent = `/* Purvex UI - Tailwind v4 */
|
|
183
|
+
@import 'tailwindcss';
|
|
184
|
+
|
|
185
|
+
@import url('https://fonts.googleapis.com/css2?family=Montserrat:ital,wght@0,100..900;1,100..900&display=swap');
|
|
186
|
+
|
|
187
|
+
* {
|
|
188
|
+
font-family: "Montserrat", sans-serif;
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
/* CSS Variables untuk Purple Elegant Theme */
|
|
192
|
+
@theme {
|
|
193
|
+
--color-background: hsl(0 0% 100%);
|
|
194
|
+
--color-foreground: hsl(222.2 84% 4.9%);
|
|
195
|
+
|
|
196
|
+
--color-card: hsl(0 0% 100%);
|
|
197
|
+
--color-card-foreground: hsl(222.2 84% 4.9%);
|
|
198
|
+
|
|
199
|
+
--color-popover: hsl(0 0% 100%);
|
|
200
|
+
--color-popover-foreground: hsl(222.2 84% 4.9%);
|
|
201
|
+
|
|
202
|
+
/* PRIMARY - UNGU ELEGANT #7C3AED */
|
|
203
|
+
--color-primary: hsl(262.1 83.3% 57.8%);
|
|
204
|
+
--color-primary-foreground: hsl(210 40% 98%);
|
|
205
|
+
|
|
206
|
+
--color-secondary: hsl(210 40% 96.1%);
|
|
207
|
+
--color-secondary-foreground: hsl(222.2 47.4% 11.2%);
|
|
208
|
+
|
|
209
|
+
--color-muted: hsl(210 40% 96.1%);
|
|
210
|
+
--color-muted-foreground: hsl(215.4 16.3% 46.9%);
|
|
211
|
+
|
|
212
|
+
--color-accent: hsl(210 40% 96.1%);
|
|
213
|
+
--color-accent-foreground: hsl(222.2 47.4% 11.2%);
|
|
214
|
+
|
|
215
|
+
--color-destructive: hsl(0 84.2% 60.2%);
|
|
216
|
+
--color-destructive-foreground: hsl(210 40% 98%);
|
|
217
|
+
|
|
218
|
+
--color-border: hsl(214.3 31.8% 91.4%);
|
|
219
|
+
--color-input: hsl(214.3 31.8% 91.4%);
|
|
220
|
+
--color-ring: hsl(262.1 83.3% 57.8%);
|
|
221
|
+
|
|
222
|
+
--radius: 0.5rem;
|
|
223
|
+
|
|
224
|
+
/* Dark Mode Variables */
|
|
225
|
+
--color-dark-background: hsl(222.2 84% 4.9%);
|
|
226
|
+
--color-dark-foreground: hsl(210 40% 98%);
|
|
227
|
+
|
|
228
|
+
--color-dark-card: hsl(222.2 84% 4.9%);
|
|
229
|
+
--color-dark-card-foreground: hsl(210 40% 98%);
|
|
230
|
+
|
|
231
|
+
--color-dark-popover: hsl(222.2 84% 4.9%);
|
|
232
|
+
--color-dark-popover-foreground: hsl(210 40% 98%);
|
|
233
|
+
|
|
234
|
+
--color-dark-primary: hsl(263.4 70% 50.4%);
|
|
235
|
+
--color-dark-primary-foreground: hsl(210 40% 98%);
|
|
236
|
+
|
|
237
|
+
--color-dark-secondary: hsl(217.2 32.6% 17.5%);
|
|
238
|
+
--color-dark-secondary-foreground: hsl(210 40% 98%);
|
|
239
|
+
|
|
240
|
+
--color-dark-muted: hsl(217.2 32.6% 17.5%);
|
|
241
|
+
--color-dark-muted-foreground: hsl(215 20.2% 65.1%);
|
|
242
|
+
|
|
243
|
+
--color-dark-accent: hsl(217.2 32.6% 17.5%);
|
|
244
|
+
--color-dark-accent-foreground: hsl(210 40% 98%);
|
|
245
|
+
|
|
246
|
+
--color-dark-destructive: hsl(0 62.8% 30.6%);
|
|
247
|
+
--color-dark-destructive-foreground: hsl(210 40% 98%);
|
|
248
|
+
|
|
249
|
+
--color-dark-border: hsl(217.2 32.6% 17.5%);
|
|
250
|
+
--color-dark-input: hsl(217.2 32.6% 17.5%);
|
|
251
|
+
--color-dark-ring: hsl(263.4 70% 50.4%);
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
/* Custom Utility Classes */
|
|
255
|
+
@layer utilities {
|
|
256
|
+
.bg-background {
|
|
257
|
+
background-color: var(--color-background);
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
.text-foreground {
|
|
261
|
+
color: var(--color-foreground);
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
.border {
|
|
265
|
+
border-color: var(--color-border);
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
.bg-primary {
|
|
269
|
+
background-color: var(--color-primary);
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
.text-primary-foreground {
|
|
273
|
+
color: var(--color-primary-foreground);
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
/* Dark Mode Classes */
|
|
277
|
+
.dark .bg-background {
|
|
278
|
+
background-color: var(--color-dark-background);
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
.dark .text-foreground {
|
|
282
|
+
color: var(--color-dark-foreground);
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
.dark .border {
|
|
286
|
+
border-color: var(--color-dark-border);
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
.dark .bg-primary {
|
|
290
|
+
background-color: var(--color-dark-primary);
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
.dark .text-primary-foreground {
|
|
294
|
+
color: var(--color-dark-primary-foreground);
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
/* Gradient Utilities */
|
|
298
|
+
.bg-gradient-purple {
|
|
299
|
+
background: linear-gradient(to right, #7c3aed, #6d28d9, #5b21b6);
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
.bg-gradient-purple-dark {
|
|
303
|
+
background: linear-gradient(to right, #1f2937, #6b21a8, #1f2937);
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
/* Border Radius Utilities */
|
|
307
|
+
.rounded-lg {
|
|
308
|
+
border-radius: var(--radius);
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
.rounded-md {
|
|
312
|
+
border-radius: calc(var(--radius) - 2px);
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
.rounded-sm {
|
|
316
|
+
border-radius: calc(var(--radius) - 4px);
|
|
317
|
+
}
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
/* Base Styles */
|
|
321
|
+
@layer base {
|
|
322
|
+
* {
|
|
323
|
+
border-color: var(--color-border);
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
body {
|
|
327
|
+
background-color: var(--color-background);
|
|
328
|
+
color: var(--color-foreground);
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
/* Dark Mode */
|
|
332
|
+
.dark * {
|
|
333
|
+
border-color: var(--color-dark-border);
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
.dark body {
|
|
337
|
+
background-color: var(--color-dark-background);
|
|
338
|
+
color: var(--color-dark-foreground);
|
|
339
|
+
}
|
|
340
|
+
}`;
|
|
341
|
+
|
|
342
|
+
if (fs.existsSync(indexPath)) {
|
|
343
|
+
fs.writeFileSync(indexPath, cssContent);
|
|
344
|
+
console.log(chalk.green('ā
Updated: src/index.css for Tailwind v4'));
|
|
345
|
+
} else {
|
|
346
|
+
if (!fs.existsSync(srcDir)) {
|
|
347
|
+
fs.mkdirSync(srcDir, { recursive: true });
|
|
348
|
+
}
|
|
349
|
+
fs.writeFileSync(indexPath, cssContent);
|
|
350
|
+
console.log(chalk.green('ā
Created: src/index.css for Tailwind v4'));
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
// 6. Create Purvex UI directories
|
|
354
|
+
const libDir = path.join(srcDir, 'lib');
|
|
355
|
+
const componentsDir = path.join(srcDir, 'components', 'ui');
|
|
356
|
+
|
|
357
|
+
if (!fs.existsSync(libDir)) {
|
|
358
|
+
fs.mkdirSync(libDir, { recursive: true });
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
if (!fs.existsSync(componentsDir)) {
|
|
362
|
+
fs.mkdirSync(componentsDir, { recursive: true });
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
// 7. Create utils.js
|
|
366
|
+
const utilsPath = path.join(libDir, 'utils.js');
|
|
367
|
+
const utilsTemplate = `import { clsx } from 'clsx';
|
|
368
|
+
import { twMerge } from 'tailwind-merge';
|
|
369
|
+
|
|
370
|
+
export function cn(...inputs) {
|
|
371
|
+
return twMerge(clsx(inputs));
|
|
372
|
+
}`;
|
|
373
|
+
|
|
374
|
+
fs.writeFileSync(utilsPath, utilsTemplate);
|
|
375
|
+
console.log(chalk.green('ā
Created: src/lib/utils.js'));
|
|
376
|
+
|
|
377
|
+
// 8. Buat tailwind.config.js sederhana jika diperlukan
|
|
378
|
+
// (Optional untuk advanced configuration)
|
|
379
|
+
const simpleTailwindConfig = `/**
|
|
380
|
+
* Tailwind v4 Configuration
|
|
381
|
+
* Note: Most configuration is now done via CSS @theme directive
|
|
382
|
+
*/
|
|
383
|
+
|
|
384
|
+
export default {
|
|
385
|
+
// Content configuration
|
|
386
|
+
content: [
|
|
387
|
+
'./index.html',
|
|
388
|
+
'./src/**/*.{js,ts,jsx,tsx}',
|
|
389
|
+
'./components/**/*.{js,ts,jsx,tsx}',
|
|
390
|
+
],
|
|
391
|
+
|
|
392
|
+
// Optional plugins
|
|
393
|
+
plugins: [],
|
|
394
|
+
}`;
|
|
395
|
+
|
|
396
|
+
fs.writeFileSync(path.join(userProjectRoot, 'tailwind.config.js'), simpleTailwindConfig);
|
|
397
|
+
console.log(chalk.yellow('š Created optional tailwind.config.js (minimal)'));
|
|
398
|
+
|
|
399
|
+
// 9. Install @types/node untuk TypeScript support
|
|
400
|
+
if (fs.existsSync(path.join(userProjectRoot, 'tsconfig.json'))) {
|
|
401
|
+
try {
|
|
402
|
+
execSync('npm install -D @types/node', { stdio: 'inherit' });
|
|
403
|
+
console.log(chalk.green('ā
Installed @types/node for TypeScript'));
|
|
404
|
+
} catch (error) {
|
|
405
|
+
// Skip jika error
|
|
406
|
+
}
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
// 10. Update package.json scripts jika perlu
|
|
410
|
+
try {
|
|
411
|
+
const pkgJson = JSON.parse(fs.readFileSync(packageJsonPath, 'utf-8'));
|
|
412
|
+
|
|
413
|
+
if (!pkgJson.scripts || !pkgJson.scripts.dev) {
|
|
414
|
+
pkgJson.scripts = {
|
|
415
|
+
...pkgJson.scripts,
|
|
416
|
+
dev: 'vite',
|
|
417
|
+
build: 'vite build',
|
|
418
|
+
preview: 'vite preview'
|
|
419
|
+
};
|
|
420
|
+
|
|
421
|
+
fs.writeFileSync(packageJsonPath, JSON.stringify(pkgJson, null, 2));
|
|
422
|
+
console.log(chalk.green('ā
Updated package.json scripts'));
|
|
423
|
+
}
|
|
424
|
+
} catch (error) {
|
|
425
|
+
// Ignore error
|
|
426
|
+
}
|
|
427
|
+
|
|
428
|
+
// 11. Success message
|
|
429
|
+
console.log(chalk.green.bold('\nš Purvex UI setup complete with Tailwind v4!'));
|
|
430
|
+
console.log(chalk.white('\nā
Everything installed automatically:'));
|
|
431
|
+
console.log(chalk.gray(' ⢠Tailwind CSS v4 (LATEST)'));
|
|
432
|
+
console.log(chalk.gray(' ⢠@tailwindcss/vite plugin'));
|
|
433
|
+
console.log(chalk.gray(' ⢠clsx & tailwind-merge'));
|
|
434
|
+
console.log(chalk.gray(' ⢠Path alias (@/ ā src/)'));
|
|
435
|
+
console.log(chalk.gray(' ⢠Purple Elegant color theme (#7C3AED)'));
|
|
436
|
+
console.log(chalk.gray(' ⢠CSS variables system in CSS file'));
|
|
437
|
+
|
|
438
|
+
console.log(chalk.white('\nšÆ Next steps:'));
|
|
439
|
+
console.log(chalk.cyan(' 1. npx purvex-ui add button'));
|
|
440
|
+
console.log(chalk.cyan(' 2. npx purvex-ui add card'));
|
|
441
|
+
|
|
442
|
+
console.log(chalk.white('\nš Start development server:'));
|
|
443
|
+
console.log(chalk.cyan(' npm run dev'));
|
|
444
|
+
|
|
445
|
+
console.log(chalk.white('\nš Project structure created:'));
|
|
446
|
+
console.log(chalk.gray(' src/components/ui/ ā Your components'));
|
|
447
|
+
console.log(chalk.gray(' src/lib/utils.js ā cn() utility'));
|
|
448
|
+
console.log(chalk.gray(' vite.config.js ā Tailwind v4 plugin configured'));
|
|
449
|
+
console.log(chalk.gray(' src/index.css ā Theme & Tailwind v4 imports'));
|
|
450
|
+
|
|
451
|
+
console.log(chalk.white('\nšØ Theme: Purple Elegant'));
|
|
452
|
+
console.log(chalk.gray(' Primary: #7C3AED (Elegant Purple)'));
|
|
453
|
+
console.log(chalk.gray(' Dark mode: Black + Purple'));
|
|
454
|
+
console.log(chalk.gray(' Includes gradient utilities'));
|
|
455
|
+
|
|
456
|
+
console.log(chalk.white('\nā” Tailwind v4 Features:'));
|
|
457
|
+
console.log(chalk.gray(' ⢠CSS-first configuration'));
|
|
458
|
+
console.log(chalk.gray(' ⢠No PostCSS needed'));
|
|
459
|
+
console.log(chalk.gray(' ⢠Faster build times'));
|
|
460
|
+
console.log(chalk.gray(' ⢠@theme directive in CSS'));
|
|
461
|
+
|
|
462
|
+
console.log(chalk.white('\nš” Tip: Add more components with:'));
|
|
463
|
+
console.log(chalk.cyan(' npx purvex-ui add [component-name]'));
|
|
464
|
+
|
|
465
|
+
console.log(chalk.yellow('\nā ļø Important: Tailwind v4 uses different syntax!'));
|
|
466
|
+
console.log(chalk.gray(' - Use @import "tailwindcss" not @tailwind directives'));
|
|
467
|
+
console.log(chalk.gray(' - Configure theme in CSS with @theme directive'));
|
|
468
|
+
console.log(chalk.gray(' - No tailwind.config.js needed (minimal version created)'));
|
|
469
|
+
|
|
470
|
+
} catch (error) {
|
|
471
|
+
console.error(chalk.red('ā Setup error:'), error.message);
|
|
472
|
+
console.log(chalk.yellow('\nš” Manual setup steps for Tailwind v4:'));
|
|
473
|
+
console.log(chalk.cyan(' 1. npm install clsx tailwind-merge'));
|
|
474
|
+
console.log(chalk.cyan(' 2. npm install -D tailwindcss@latest @tailwindcss/vite'));
|
|
475
|
+
console.log(chalk.cyan(' 3. Add tailwindcss() to vite.config.js plugins'));
|
|
476
|
+
console.log(chalk.cyan(' 4. Add @import "tailwindcss" to CSS file'));
|
|
477
|
+
console.log(chalk.cyan(' 5. Add @theme directive for custom colors'));
|
|
478
|
+
console.log(chalk.cyan(' 6. Add path alias to vite.config.js'));
|
|
479
|
+
}
|
|
480
|
+
}
|
|
481
|
+
|
|
482
|
+
module.exports = { setupProject };
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
const fs = require('fs');
|
|
2
|
+
const path = require('path');
|
|
3
|
+
const chalk = require('chalk');
|
|
4
|
+
const { execSync } = require('child_process');
|
|
5
|
+
|
|
6
|
+
async function uninstallProject() {
|
|
7
|
+
try {
|
|
8
|
+
console.log(chalk.red.bold('šļø Uninstalling Purvex UI...\n'));
|
|
9
|
+
|
|
10
|
+
const userProjectRoot = process.cwd();
|
|
11
|
+
|
|
12
|
+
// 1. Confirm
|
|
13
|
+
const readline = require('readline').createInterface({
|
|
14
|
+
input: process.stdin,
|
|
15
|
+
output: process.stdout
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
const answer = await new Promise((resolve) => {
|
|
19
|
+
readline.question(
|
|
20
|
+
chalk.yellow('ā Remove Purvex UI files and dependencies? (y/N): '),
|
|
21
|
+
resolve
|
|
22
|
+
);
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
readline.close();
|
|
26
|
+
|
|
27
|
+
if (answer.toLowerCase() !== 'y') {
|
|
28
|
+
console.log(chalk.gray('Uninstall cancelled'));
|
|
29
|
+
return;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
console.log(chalk.blue('š Removing Purvex UI files...'));
|
|
33
|
+
|
|
34
|
+
// 2. Hapus file/folder
|
|
35
|
+
const filesToRemove = [
|
|
36
|
+
'src/components/ui',
|
|
37
|
+
'src/lib'
|
|
38
|
+
];
|
|
39
|
+
|
|
40
|
+
filesToRemove.forEach(file => {
|
|
41
|
+
const filePath = path.join(userProjectRoot, file);
|
|
42
|
+
if (fs.existsSync(filePath)) {
|
|
43
|
+
fs.rmSync(filePath, { recursive: true, force: true });
|
|
44
|
+
console.log(chalk.green(`ā
Removed: ${file}`));
|
|
45
|
+
}
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
// 3. Uninstall dependencies
|
|
49
|
+
console.log(chalk.blue('\nš¦ Removing dependencies...'));
|
|
50
|
+
|
|
51
|
+
try {
|
|
52
|
+
// Uninstall regular deps
|
|
53
|
+
execSync('npm uninstall clsx tailwind-merge', { stdio: 'inherit' });
|
|
54
|
+
|
|
55
|
+
// Uninstall dev deps
|
|
56
|
+
const devDeps = ['tailwindcss', 'postcss', 'autoprefixer', '@tailwindcss/vite'];
|
|
57
|
+
devDeps.forEach(dep => {
|
|
58
|
+
try {
|
|
59
|
+
execSync(`npm uninstall -D ${dep}`, { stdio: 'pipe' });
|
|
60
|
+
} catch (e) {
|
|
61
|
+
// Skip jika dep tidak ada
|
|
62
|
+
}
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
console.log(chalk.green('ā
Dependencies removed'));
|
|
66
|
+
} catch (error) {
|
|
67
|
+
console.log(chalk.yellow('ā ļø Remove dependencies manually:'));
|
|
68
|
+
console.log(chalk.cyan(' npm uninstall clsx tailwind-merge'));
|
|
69
|
+
console.log(chalk.cyan(' npm uninstall -D tailwindcss postcss autoprefixer @tailwindcss/vite'));
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
// 4. Optional: Remove config files
|
|
73
|
+
console.log(chalk.blue('\nāļø Cleaning up config files...'));
|
|
74
|
+
|
|
75
|
+
const configFiles = [
|
|
76
|
+
'tailwind.config.js',
|
|
77
|
+
'tailwind.config.ts',
|
|
78
|
+
'tailwind.config.cjs',
|
|
79
|
+
'postcss.config.js',
|
|
80
|
+
'postcss.config.cjs'
|
|
81
|
+
];
|
|
82
|
+
|
|
83
|
+
configFiles.forEach(config => {
|
|
84
|
+
const configPath = path.join(userProjectRoot, config);
|
|
85
|
+
if (fs.existsSync(configPath)) {
|
|
86
|
+
const answer2 = require('readline-sync').question(
|
|
87
|
+
chalk.yellow(`Remove ${config}? (y/N): `)
|
|
88
|
+
);
|
|
89
|
+
|
|
90
|
+
if (answer2.toLowerCase() === 'y') {
|
|
91
|
+
fs.unlinkSync(configPath);
|
|
92
|
+
console.log(chalk.green(`ā
Removed: ${config}`));
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
// 5. Revert vite.config.js
|
|
98
|
+
const viteConfigs = ['vite.config.js', 'vite.config.ts'];
|
|
99
|
+
viteConfigs.forEach(config => {
|
|
100
|
+
const configPath = path.join(userProjectRoot, config);
|
|
101
|
+
if (fs.existsSync(configPath)) {
|
|
102
|
+
console.log(chalk.yellow(`\nā ļø Need to manually revert ${config}:`));
|
|
103
|
+
console.log(chalk.cyan(' Remove tailwindcss() plugin from vite.config.js'));
|
|
104
|
+
console.log(chalk.cyan(' Remove @tailwindcss/vite import if exists'));
|
|
105
|
+
}
|
|
106
|
+
});
|
|
107
|
+
|
|
108
|
+
// 6. Revert index.css
|
|
109
|
+
const cssFiles = ['src/index.css', 'src/App.css', 'src/main.css'];
|
|
110
|
+
cssFiles.forEach(css => {
|
|
111
|
+
const cssPath = path.join(userProjectRoot, css);
|
|
112
|
+
if (fs.existsSync(cssPath)) {
|
|
113
|
+
let cssContent = fs.readFileSync(cssPath, 'utf-8');
|
|
114
|
+
if (cssContent.includes('@import "tailwindcss"')) {
|
|
115
|
+
cssContent = cssContent.replace(/@import "tailwindcss";?\n?/, '');
|
|
116
|
+
fs.writeFileSync(cssPath, cssContent);
|
|
117
|
+
console.log(chalk.green(`ā
Cleaned: ${css}`));
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
});
|
|
121
|
+
|
|
122
|
+
console.log(chalk.green.bold('\nš Purvex UI successfully uninstalled!'));
|
|
123
|
+
console.log(chalk.white('\nProject restored to pre-Purvex UI state.'));
|
|
124
|
+
|
|
125
|
+
} catch (error) {
|
|
126
|
+
console.error(chalk.red('ā Uninstall error:'), error.message);
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
module.exports = { uninstallProject };
|
package/src/index.js
ADDED
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
const { Command } = require('commander');
|
|
4
|
+
const chalk = require('chalk');
|
|
5
|
+
const boxen = require('boxen');
|
|
6
|
+
|
|
7
|
+
// Banner
|
|
8
|
+
const banner = boxen.default(
|
|
9
|
+
chalk.green.bold('⨠Purvex UI') +
|
|
10
|
+
chalk.white(' - React Component Generator\n') +
|
|
11
|
+
chalk.gray('Fast, customizable UI components for React + Tailwind'),
|
|
12
|
+
{
|
|
13
|
+
padding: 1,
|
|
14
|
+
margin: 1,
|
|
15
|
+
borderStyle: 'round',
|
|
16
|
+
borderColor: 'cyan'
|
|
17
|
+
}
|
|
18
|
+
);
|
|
19
|
+
|
|
20
|
+
console.log(banner);
|
|
21
|
+
|
|
22
|
+
// Setup CLI
|
|
23
|
+
const program = new Command();
|
|
24
|
+
|
|
25
|
+
program
|
|
26
|
+
.name('purvex-ui')
|
|
27
|
+
.description('CLI untuk Purvex UI - Generator komponen React dengan Tailwind CSS')
|
|
28
|
+
.version('0.1.0');
|
|
29
|
+
|
|
30
|
+
// Command: init
|
|
31
|
+
program
|
|
32
|
+
.command('init')
|
|
33
|
+
.description('Initialize Purvex UI di project kamu')
|
|
34
|
+
.action(async () => {
|
|
35
|
+
const { initProject } = require('./commands/init');
|
|
36
|
+
await initProject();
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
// Command: add
|
|
40
|
+
program
|
|
41
|
+
.command('add <component>')
|
|
42
|
+
.description('Tambahkan komponen ke project')
|
|
43
|
+
.option('-p, --path <path>', 'Custom path untuk komponen (default: components/ui)', 'components/ui')
|
|
44
|
+
.action(async (component, options) => {
|
|
45
|
+
const { addComponent } = require('./commands/add');
|
|
46
|
+
await addComponent(component, options);
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
// Command: list
|
|
50
|
+
program
|
|
51
|
+
.command('list')
|
|
52
|
+
.description('Lihat semua komponen yang tersedia')
|
|
53
|
+
.action(async () => {
|
|
54
|
+
const fs = require('fs');
|
|
55
|
+
const path = require('path');
|
|
56
|
+
|
|
57
|
+
try {
|
|
58
|
+
const registryPath = path.join(__dirname, '..', '..', '..', 'registry', 'components.json');
|
|
59
|
+
|
|
60
|
+
if (!fs.existsSync(registryPath)) {
|
|
61
|
+
console.log(chalk.red('ā Registry tidak ditemukan'));
|
|
62
|
+
return;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
const registryContent = fs.readFileSync(registryPath, 'utf-8');
|
|
66
|
+
const registry = JSON.parse(registryContent);
|
|
67
|
+
|
|
68
|
+
console.log(chalk.blue.bold('\nš¦ Komponen yang tersedia:\n'));
|
|
69
|
+
registry.components.forEach(comp => {
|
|
70
|
+
console.log(chalk.cyan(` ⢠${comp.name}`));
|
|
71
|
+
});
|
|
72
|
+
console.log(chalk.gray(`\nTotal: ${registry.components.length} komponen`));
|
|
73
|
+
|
|
74
|
+
} catch (error) {
|
|
75
|
+
console.error(chalk.red('ā Error:'), error.message);
|
|
76
|
+
}
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
// Command: setup
|
|
81
|
+
program
|
|
82
|
+
.command('setup')
|
|
83
|
+
.description('Setup Purvex UI with Tailwind v4 and Purple Elegant theme')
|
|
84
|
+
.action(async () => {
|
|
85
|
+
const { setupProject } = require('./commands/setup');
|
|
86
|
+
await setupProject();
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
// Command: fix
|
|
90
|
+
program
|
|
91
|
+
.command('fix')
|
|
92
|
+
.description('Fix Purvex UI configuration (path alias, colors, etc)')
|
|
93
|
+
.action(async () => {
|
|
94
|
+
const { fixProject } = require('./commands/fix');
|
|
95
|
+
await fixProject();
|
|
96
|
+
});
|
|
97
|
+
|
|
98
|
+
// Command: update-colors
|
|
99
|
+
// program
|
|
100
|
+
// .command('update-colors')
|
|
101
|
+
// .description('Update color palette to Purple Elegant theme')
|
|
102
|
+
// .action(async () => {
|
|
103
|
+
// const { updateColors } = require('./commands/update-colors');
|
|
104
|
+
// await updateColors();
|
|
105
|
+
// });
|
|
106
|
+
|
|
107
|
+
// Command: install
|
|
108
|
+
program
|
|
109
|
+
.command('install')
|
|
110
|
+
.description('Install all required dependencies automatically')
|
|
111
|
+
.action(async () => {
|
|
112
|
+
const { installDeps } = require('./commands/install');
|
|
113
|
+
await installDeps();
|
|
114
|
+
});
|
|
115
|
+
|
|
116
|
+
// Command: uninstall
|
|
117
|
+
program
|
|
118
|
+
.command('uninstall')
|
|
119
|
+
.description('Remove Purvex UI from your project')
|
|
120
|
+
.action(async () => {
|
|
121
|
+
const { uninstallProject } = require('./commands/uninstall');
|
|
122
|
+
await uninstallProject();
|
|
123
|
+
});
|
|
124
|
+
|
|
125
|
+
// Parse arguments
|
|
126
|
+
program.parse();
|