-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathindex.js
More file actions
119 lines (104 loc) · 3.84 KB
/
Copy pathindex.js
File metadata and controls
119 lines (104 loc) · 3.84 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
#!/usr/bin/env node
const fs = require('fs');
const path = require('path');
const { exec, execSync } = require('child_process');
const readline = require('readline');
// Minimal progress bar (used for the file copy step)
function progress(current, total, title) {
const percent = Math.round((current / total) * 100);
const filled = Math.round((current / total) * 20);
const bar = '█'.repeat(filled) + '░'.repeat(20 - filled);
process.stdout.write(`\r${title}: [${bar}] ${percent}%`);
if (current >= total) console.log();
}
// Recursive copy (renames gitignore.template -> .gitignore along the way)
function copyDir(src, dest, callback) {
if (!fs.existsSync(src)) return;
if (fs.statSync(src).isDirectory()) {
if (!fs.existsSync(dest)) fs.mkdirSync(dest);
fs.readdirSync(src).forEach(item => {
const srcPath = path.join(src, item);
const destPath = path.join(
dest,
item === 'gitignore.template' ? '.gitignore' : item
);
copyDir(srcPath, destPath, callback);
});
} else {
fs.copyFileSync(src, dest);
callback && callback();
}
}
// Count files (so the copy progress bar knows the total)
function countFiles(dir) {
if (!fs.existsSync(dir)) return 0;
return fs.readdirSync(dir).reduce((count, item) => {
const fullPath = path.join(dir, item);
return count + (fs.statSync(fullPath).isDirectory() ? countFiles(fullPath) : 1);
}, 0);
}
// Indeterminate spinner for long-running async tasks
function spin(msg) {
const frames = ['⠋', '⠙', '⠹', '⠸', '⠼', '⠴', '⠦', '⠧', '⠇', '⠏'];
let i = 0;
const interval = setInterval(
() => process.stdout.write(`\r${frames[i++ % frames.length]} ${msg}`),
80
);
return (ok = true) => {
clearInterval(interval);
process.stdout.write(`\r${ok ? '✓' : '✗'} ${msg}\n`);
};
}
// Run a shell command without blocking the event loop (keeps the spinner alive)
function run(cmd, cwd) {
return new Promise((resolve, reject) => {
exec(cmd, { cwd, maxBuffer: 10 * 1024 * 1024 }, error =>
error ? reject(error) : resolve()
);
});
}
// Main
async function create(name) {
const templateDir = path.join(__dirname, 'template');
if (fs.existsSync(name)) {
console.log(`❌ Directory '${name}' already exists!`);
process.exit(1);
}
console.log(`\n📁 Creating ${name}...\n`);
// Copy template files
const total = countFiles(templateDir);
let current = 0;
copyDir(templateDir, name, () => progress(++current, total, '📄 Copying'));
console.log('✓ Files copied!\n');
// Install dependencies
const stopInstall = spin('📦 Installing packages...');
try {
await run('npm install --silent', name);
stopInstall();
} catch (error) {
stopInstall(false);
console.log('❌ Install failed:', error.message);
process.exit(1);
}
// Initialize a git repository (optional — never fatal)
try {
execSync('git init', { cwd: name, stdio: 'pipe' });
execSync('git add .', { cwd: name, stdio: 'pipe' });
execSync('git commit -m "Initial commit from neo-portfolio"', { cwd: name, stdio: 'pipe' });
console.log('✓ Git repository initialized!');
} catch {
console.log('⚠️ Skipped git init (optional).');
}
console.log(`\n🎉 Done!\n\nNext steps:\n cd ${name}\n npm run dev\n`);
}
// Entry point — accept the name as an argument, or prompt for it
if (process.argv[2]) {
create(process.argv[2]);
} else {
const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
rl.question('Project name [neo-portfolio]: ', answer => {
rl.close();
create(answer.trim() || 'neo-portfolio');
});
}