create-vilo
Advanced tools
Comparing version 1.0.14 to 1.0.15
438
bin/cli.js
#!/usr/bin/env node | ||
import { program } from "commander"; | ||
@@ -14,264 +13,199 @@ import inquirer from "inquirer"; | ||
import { execSync } from "child_process"; | ||
// Get the directory name of the current module | ||
const __filename = fileURLToPath(import.meta.url); | ||
const __dirname = dirname(__filename); | ||
// Update available templates to include both JS and TS versions | ||
const availableTemplates = [ | ||
"lit", | ||
"olova", | ||
"preact", | ||
"qwik", | ||
"react", | ||
"solid", | ||
"svelte", | ||
"vanilla", | ||
"vue", | ||
]; | ||
// Prompt for template selection | ||
const promptForTemplate = async () => { | ||
const questions = [ | ||
{ | ||
type: "list", | ||
name: "template", | ||
message: "Select a framework:", | ||
choices: availableTemplates, | ||
default: "olova", | ||
}, | ||
{ | ||
type: "list", | ||
name: "language", | ||
message: "Select a variant:", | ||
choices: ["JavaScript", "TypeScript"], | ||
default: "JavaScript", | ||
}, | ||
]; | ||
return await inquirer.prompt(questions); | ||
}; | ||
const getTemplateFromGitHub = async (template, language) => { | ||
const octokit = new Octokit(); | ||
const owner = "vilojs"; | ||
const repo = "vilo"; | ||
const basePath = "packages/create-vilo"; | ||
try { | ||
// First, get the list of templates | ||
const templatesResponse = await octokit.rest.repos.getContent({ | ||
owner, | ||
repo, | ||
path: basePath, | ||
ref: "main", | ||
}); | ||
const templateName = | ||
language === "TypeScript" | ||
? `template-${template}-ts` | ||
: `template-${template}`; | ||
const templateDir = templatesResponse.data.find( | ||
(item) => item.name === templateName | ||
); | ||
if (!templateDir) { | ||
throw new Error(`Template '${template}' not found on GitHub`); | ||
const __filename = fileURLToPath(import.meta.url), | ||
__dirname = dirname(__filename), | ||
availableTemplates = [ | ||
"lit", | ||
"olova", | ||
"preact", | ||
"qwik", | ||
"react", | ||
"solid", | ||
"svelte", | ||
"vanilla", | ||
"vue", | ||
], | ||
promptForTemplate = async () => { | ||
const e = [ | ||
{ | ||
type: "list", | ||
name: "template", | ||
message: "Select a framework:", | ||
choices: availableTemplates, | ||
default: "olova", | ||
}, | ||
{ | ||
type: "list", | ||
name: "language", | ||
message: "Select a variant:", | ||
choices: ["JavaScript", "TypeScript"], | ||
default: "JavaScript", | ||
}, | ||
]; | ||
return await inquirer.prompt(e); | ||
}, | ||
getTemplateFromGitHub = async (e, a) => { | ||
const t = new Octokit(), | ||
r = "vilojs", | ||
o = "vilo"; | ||
try { | ||
const n = await t.rest.repos.getContent({ | ||
owner: r, | ||
repo: o, | ||
path: "packages/create-vilo", | ||
ref: "main", | ||
}), | ||
c = "TypeScript" === a ? `template-${e}-ts` : `template-${e}`, | ||
i = n.data.find((e) => e.name === c); | ||
if (!i) throw new Error(`Template '${e}' not found on GitHub`); | ||
await t.rest.repos.getContent({ | ||
owner: r, | ||
repo: o, | ||
path: i.path, | ||
ref: "main", | ||
}); | ||
const s = `https://api.github.com/repos/${r}/${o}/zipball/main`, | ||
p = await fetch(s), | ||
l = await p.arrayBuffer(); | ||
return { buffer: Buffer.from(l), path: i.path }; | ||
} catch (e) { | ||
throw new Error( | ||
`Oops! TypeScript isn’t supported for this—${e.message} try again!: ` | ||
); | ||
} | ||
// Now, get the content of the specific template | ||
const templateContent = await octokit.rest.repos.getContent({ | ||
owner, | ||
repo, | ||
path: templateDir.path, | ||
ref: "main", | ||
}); | ||
// Download the template as a zip file | ||
const zipUrl = `https://api.github.com/repos/${owner}/${repo}/zipball/main`; | ||
const zipResponse = await fetch(zipUrl); | ||
const arrayBuffer = await zipResponse.arrayBuffer(); | ||
const buffer = Buffer.from(arrayBuffer); | ||
return { buffer, path: templateDir.path }; | ||
} catch (error) { | ||
throw new Error(`Failed to fetch template from GitHub: ${error.message}`); | ||
} | ||
}; | ||
const validateProjectName = (projectName) => { | ||
if (fs.existsSync(path.resolve(process.cwd(), projectName))) { | ||
console.error(chalk.red(`Directory ${projectName} already exists`)); | ||
process.exit(1); | ||
} | ||
const validNameRegex = /^[a-z0-9-_]+$/i; | ||
if (!validNameRegex.test(projectName)) { | ||
console.error( | ||
chalk.red( | ||
"Project name can only contain letters, numbers, dashes and underscores" | ||
) | ||
); | ||
process.exit(1); | ||
} | ||
}; | ||
const detectPackageManager = () => { | ||
try { | ||
execSync("pnpm --version", { stdio: "ignore" }); | ||
return "pnpm"; | ||
} catch { | ||
}, | ||
validateProjectName = (e) => { | ||
fs.existsSync(path.resolve(process.cwd(), e)) && | ||
(console.error(chalk.red(`Directory ${e} already exists`)), | ||
process.exit(1)); | ||
/^[a-z0-9-_]+$/i.test(e) || | ||
(console.error( | ||
chalk.red( | ||
"Project name can only contain letters, numbers, dashes and underscores" | ||
) | ||
), | ||
process.exit(1)); | ||
}, | ||
detectPackageManager = () => { | ||
try { | ||
execSync("yarn --version", { stdio: "ignore" }); | ||
return "yarn"; | ||
return execSync("pnpm --version", { stdio: "ignore" }), "pnpm"; | ||
} catch { | ||
return "npm"; // Default to npm if neither pnpm nor yarn is detected | ||
try { | ||
return execSync("yarn --version", { stdio: "ignore" }), "yarn"; | ||
} catch { | ||
return "npm"; | ||
} | ||
} | ||
} | ||
}; | ||
const createProject = async (projectName, template, language) => { | ||
const spinner = ora("Creating project...").start(); | ||
try { | ||
const { buffer, path: templatePath } = await getTemplateFromGitHub( | ||
template, | ||
language | ||
); | ||
const targetPath = path.resolve(process.cwd(), projectName); | ||
// Extract the template | ||
const zip = new AdmZip(buffer); | ||
const zipEntries = zip.getEntries(); | ||
const repoName = zipEntries[0].entryName.split("/")[0]; // Get the root folder name | ||
zipEntries.forEach((entry) => { | ||
if (entry.entryName.startsWith(`${repoName}/${templatePath}/`)) { | ||
const relativePath = entry.entryName.replace( | ||
`${repoName}/${templatePath}/`, | ||
"" | ||
); | ||
if (relativePath) { | ||
const fullPath = path.join(targetPath, relativePath); | ||
if (entry.isDirectory) { | ||
fs.mkdirpSync(fullPath); | ||
} else { | ||
const parentDir = path.dirname(fullPath); | ||
fs.mkdirpSync(parentDir); | ||
fs.writeFileSync(fullPath, entry.getData()); | ||
}, | ||
createProject = async (e, a, t) => { | ||
const r = ora("Creating project...").start(); | ||
try { | ||
const { buffer: o, path: n } = await getTemplateFromGitHub(a, t), | ||
c = path.resolve(process.cwd(), e), | ||
i = new AdmZip(o).getEntries(), | ||
s = i[0].entryName.split("/")[0]; | ||
i.forEach((e) => { | ||
if (e.entryName.startsWith(`${s}/${n}/`)) { | ||
const a = e.entryName.replace(`${s}/${n}/`, ""); | ||
if (a) { | ||
const t = path.join(c, a); | ||
if (e.isDirectory) fs.mkdirpSync(t); | ||
else { | ||
const a = path.dirname(t); | ||
fs.mkdirpSync(a), fs.writeFileSync(t, e.getData()); | ||
} | ||
} | ||
} | ||
}); | ||
const p = path.join(c, "package.json"); | ||
if (await fs.pathExists(p)) { | ||
const a = await fs.readJson(p); | ||
(a.name = e), await fs.writeJson(p, a, { spaces: 2 }); | ||
} | ||
}); | ||
// Handle package.json if it exists | ||
const pkgPath = path.join(targetPath, "package.json"); | ||
if (await fs.pathExists(pkgPath)) { | ||
const pkg = await fs.readJson(pkgPath); | ||
pkg.name = projectName; | ||
await fs.writeJson(pkgPath, pkg, { spaces: 2 }); | ||
const l = detectPackageManager(); | ||
if ( | ||
(r.succeed("Project created successfully!"), | ||
console.log(chalk.green("\n✨ To get started:")), | ||
console.log(chalk.cyan(` cd ${e}`)), | ||
await fs.pathExists(p)) | ||
) | ||
switch (l) { | ||
case "pnpm": | ||
console.log(chalk.cyan(" pnpm install")), | ||
console.log(chalk.cyan(" pnpm run dev")); | ||
break; | ||
case "yarn": | ||
console.log(chalk.cyan(" yarn")), | ||
console.log(chalk.cyan(" yarn dev")); | ||
break; | ||
default: | ||
console.log(chalk.cyan(" npm install")), | ||
console.log(chalk.cyan(" npm run dev")); | ||
} | ||
} catch (e) { | ||
r.fail("Error creating project:"), | ||
console.error(chalk.red(e.message)), | ||
process.exit(1); | ||
} | ||
const packageManager = detectPackageManager(); | ||
spinner.succeed("Project created successfully!"); | ||
console.log(chalk.green("\n✨ To get started:")); | ||
console.log(chalk.cyan(` cd ${projectName}`)); | ||
if (await fs.pathExists(pkgPath)) { | ||
switch (packageManager) { | ||
case "pnpm": | ||
console.log(chalk.cyan(" pnpm install")); | ||
console.log(chalk.cyan(" pnpm run dev")); | ||
break; | ||
case "yarn": | ||
console.log(chalk.cyan(" yarn")); | ||
console.log(chalk.cyan(" yarn dev")); | ||
break; | ||
default: | ||
console.log(chalk.cyan(" npm install")); | ||
console.log(chalk.cyan(" npm run dev")); | ||
}, | ||
init = async () => { | ||
program | ||
.name("create-vilo") | ||
.usage("<project-name> [options]") | ||
.argument("[project-name]", "name of the project") | ||
.option("-t, --template <template>", "template to use") | ||
.option( | ||
"-l, --language <language>", | ||
"language variant (JavaScript or TypeScript)" | ||
) | ||
.parse(); | ||
const e = program.opts(); | ||
let [a] = program.args, | ||
t = e.template, | ||
r = e.language; | ||
if (!a) { | ||
const { name: e } = await inquirer.prompt([ | ||
{ | ||
type: "input", | ||
name: "name", | ||
message: "Project name:", | ||
validate: (e) => "" !== e.trim() || "Project name cannot be empty", | ||
}, | ||
]); | ||
a = e; | ||
} | ||
if ( | ||
(validateProjectName(a), | ||
t && | ||
!availableTemplates.includes(t) && | ||
(console.log( | ||
chalk.yellow( | ||
`Template '${t}' not found. Please choose from available templates.` | ||
) | ||
), | ||
(t = null)), | ||
t) | ||
) { | ||
if (!r) { | ||
const { selectedLanguage: e } = await inquirer.prompt([ | ||
{ | ||
type: "list", | ||
name: "selectedLanguage", | ||
message: "Select a variant:", | ||
choices: ["JavaScript", "TypeScript"], | ||
default: "JavaScript", | ||
}, | ||
]); | ||
r = e; | ||
} | ||
} else { | ||
const e = await promptForTemplate(); | ||
(t = e.template), (r = e.language); | ||
} | ||
} catch (err) { | ||
spinner.fail("Error creating project:"); | ||
console.error(chalk.red(err.message)); | ||
process.exit(1); | ||
} | ||
}; | ||
const init = async () => { | ||
program | ||
.name("create-vilo") | ||
.usage("<project-name> [options]") | ||
.argument("[project-name]", "name of the project") | ||
.option("-t, --template <template>", "template to use") | ||
.option( | ||
"-l, --language <language>", | ||
"language variant (JavaScript or TypeScript)" | ||
) | ||
.parse(); | ||
const options = program.opts(); | ||
let [projectName] = program.args; | ||
let template = options.template; | ||
let language = options.language; | ||
// If project name is not provided, prompt for it | ||
if (!projectName) { | ||
const { name } = await inquirer.prompt([ | ||
{ | ||
type: "input", | ||
name: "name", | ||
message: "Project name:", | ||
validate: (input) => { | ||
if (input.trim() === "") { | ||
return "Project name cannot be empty"; | ||
} | ||
return true; | ||
}, | ||
}, | ||
]); | ||
projectName = name; | ||
} | ||
validateProjectName(projectName); | ||
if (template && !availableTemplates.includes(template)) { | ||
console.log( | ||
chalk.yellow( | ||
`Template '${template}' not found. Please choose from available templates.` | ||
) | ||
); | ||
template = null; | ||
} | ||
// If template is provided, use it without prompting | ||
if (!template) { | ||
const answer = await promptForTemplate(); | ||
template = answer.template; | ||
language = answer.language; | ||
} else if (!language) { | ||
// If template is provided but language is not, prompt only for language | ||
const { selectedLanguage } = await inquirer.prompt([ | ||
{ | ||
type: "list", | ||
name: "selectedLanguage", | ||
message: "Select a variant:", | ||
choices: ["JavaScript", "TypeScript"], | ||
default: "JavaScript", | ||
}, | ||
]); | ||
language = selectedLanguage; | ||
} | ||
console.log( | ||
chalk.blue( | ||
`Creating a new ${language} project with the ${template} template...` | ||
) | ||
); | ||
await createProject(projectName, template, language); | ||
}; | ||
init().catch((err) => { | ||
console.error(chalk.red("An unexpected error occurred:"), err); | ||
process.exit(1); | ||
chalk.blue(`Creating a new ${r} project with the ${t} template...`) | ||
), | ||
await createProject(a, t, r); | ||
}; | ||
init().catch((e) => { | ||
console.error(chalk.red("An unexpected error occurred:"), e), process.exit(1); | ||
}); |
{ | ||
"name": "create-vilo", | ||
"version": "1.0.14", | ||
"version": "1.0.15", | ||
"description": "Create projects with one command", | ||
@@ -5,0 +5,0 @@ "main": "index.js", |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
7032
210