176 lines
4.8 KiB
JavaScript
176 lines
4.8 KiB
JavaScript
import fs from 'fs'
|
|
import path from 'path'
|
|
import readline from 'readline'
|
|
import { spawn } from 'child_process'
|
|
|
|
const NO_COPY = [
|
|
'node_modules',
|
|
'dist',
|
|
'package-lock.json',
|
|
'.git',
|
|
'config.js',
|
|
]
|
|
|
|
const templateDir = path.join(process.cwd(), 'template')
|
|
const dropsDir = path.join(process.cwd(), 'drops')
|
|
|
|
const kebabCase = (str) =>
|
|
str
|
|
.replace(/([a-z])([A-Z])/g, '$1-$2')
|
|
.replace(/[\s_]+/g, '-')
|
|
.toLowerCase()
|
|
|
|
const pascalCase = (str) => {
|
|
const camel = str.replace(/[-_\s]+(.)?/g, (_, c) =>
|
|
c ? c.toUpperCase() : '',
|
|
)
|
|
return camel.charAt(0).toUpperCase() + camel.slice(1)
|
|
}
|
|
const titleCase = (str) => {
|
|
const words = str.split('-')
|
|
return words
|
|
.map((word) => word.charAt(0).toUpperCase() + word.slice(1))
|
|
.join(' ')
|
|
}
|
|
|
|
const copyDir = (src, dest) => {
|
|
fs.mkdirSync(dest, { recursive: true })
|
|
const entries = fs.readdirSync(src, { withFileTypes: true })
|
|
|
|
for (const entry of entries) {
|
|
const srcPath = path.join(src, entry.name)
|
|
const destPath = path.join(dest, entry.name)
|
|
|
|
if (NO_COPY.includes(entry.name)) continue
|
|
|
|
if (entry.isDirectory()) {
|
|
copyDir(srcPath, destPath)
|
|
} else {
|
|
fs.copyFileSync(srcPath, destPath)
|
|
}
|
|
}
|
|
}
|
|
|
|
const updatePackageJson = (dir, name) => {
|
|
const pkgPath = path.join(dir, 'package.json')
|
|
const pkg = JSON.parse(fs.readFileSync(pkgPath, 'utf-8'))
|
|
pkg.name = name
|
|
fs.writeFileSync(pkgPath, JSON.stringify(pkg, null, 2) + '\n')
|
|
}
|
|
|
|
const replaceFileString = (
|
|
dir,
|
|
filePath,
|
|
oldString,
|
|
newString,
|
|
newFilePath,
|
|
) => {
|
|
const fullPath = path.join(dir, filePath)
|
|
const fileContent = fs.readFileSync(fullPath, 'utf-8')
|
|
const updatedContent = fileContent.replace(oldString, newString)
|
|
|
|
if (newFilePath) {
|
|
fs.writeFileSync(path.join(dir, newFilePath), updatedContent)
|
|
} else {
|
|
fs.writeFileSync(fullPath, updatedContent)
|
|
}
|
|
}
|
|
|
|
const updateViteConfig = (dir, name) => {
|
|
replaceFileString(dir, 'vite.config.js', /\$NAME/g, name)
|
|
}
|
|
|
|
const updateVue = (dir, kebabName, pascalName) => {
|
|
replaceFileString(dir, 'src/App.vue', /Name/g, pascalName)
|
|
replaceFileString(
|
|
dir,
|
|
'src/components/Name.vue',
|
|
/\$name/g,
|
|
kebabName,
|
|
`src/components/${pascalName}.vue`,
|
|
)
|
|
|
|
fs.unlinkSync(path.join(dir, 'src/components/Name.vue'))
|
|
}
|
|
|
|
const updateIndexHtml = (dir, name) => {
|
|
replaceFileString(dir, 'index.html', /\$NAME/g, name)
|
|
}
|
|
|
|
const updateMetaJson = (dir, kebabName, titleName) => {
|
|
const metaPath = path.join(dir, 'public/meta.json')
|
|
if (fs.existsSync(metaPath)) {
|
|
let content = fs.readFileSync(metaPath, 'utf-8')
|
|
content = content.replace(/\$name/g, kebabName)
|
|
content = content.replace(/\$NAME/g, titleName)
|
|
|
|
// Update the date to today's date
|
|
const today = new Date().toISOString().split('T')[0]
|
|
content = content.replace(/"date":\s*"[^"]*"/, `"date": "${today}"`)
|
|
|
|
fs.writeFileSync(metaPath, content)
|
|
}
|
|
}
|
|
|
|
const runCommand = (command, args, cwd = process.cwd()) => {
|
|
return new Promise((resolve, reject) => {
|
|
const child = spawn(command, args, {
|
|
cwd,
|
|
stdio: 'inherit',
|
|
shell: true,
|
|
})
|
|
child.on('close', (code) => {
|
|
if (code === 0) resolve()
|
|
else reject(new Error(`Command failed with code ${code}`))
|
|
})
|
|
child.on('error', reject)
|
|
})
|
|
}
|
|
|
|
const rl = readline.createInterface({
|
|
input: process.stdin,
|
|
output: process.stdout,
|
|
})
|
|
|
|
rl.question('Enter drop name: ', async (name) => {
|
|
const trimmedName = name.trim()
|
|
const kebabName = kebabCase(trimmedName)
|
|
const pascalName = pascalCase(trimmedName)
|
|
const titleName = titleCase(kebabName)
|
|
|
|
if (!kebabName) {
|
|
console.error('Error: drop name cannot be empty')
|
|
rl.close()
|
|
process.exit(1)
|
|
}
|
|
|
|
const destDir = path.join(dropsDir, kebabName)
|
|
|
|
if (fs.existsSync(destDir)) {
|
|
console.error(`Error: drop "${kebabName}" already exists`)
|
|
rl.close()
|
|
process.exit(1)
|
|
}
|
|
|
|
try {
|
|
console.log(`Creating drop "${kebabName}"...`)
|
|
copyDir(templateDir, destDir)
|
|
|
|
// Update template files
|
|
updatePackageJson(destDir, kebabName)
|
|
updateViteConfig(destDir, kebabName)
|
|
updateVue(destDir, kebabName, pascalName)
|
|
updateIndexHtml(destDir, titleName)
|
|
updateMetaJson(destDir, kebabName, titleName)
|
|
console.log(`Drop "${kebabName}" created successfully`)
|
|
|
|
console.log('Running npm install...')
|
|
await runCommand('npm', ['install'])
|
|
console.log('npm install complete!')
|
|
} catch (err) {
|
|
console.error('Error:', err.message)
|
|
process.exit(1)
|
|
}
|
|
|
|
rl.close()
|
|
})
|