@orchard9ai/create-vite-vike-tauri
Version:
Create Vite + Vike + Tauri applications with Orchard9 design system
953 lines (945 loc) • 35.2 kB
JavaScript
var __defProp = Object.defineProperty;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __esm = (fn, res) => function __init() {
return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
};
var __export = (target, all) => {
for (var name in all)
__defProp(target, name, { get: all[name], enumerable: true });
};
// ../../node_modules/.pnpm/tsup@8.5.0_jiti@2.4.2_postcss@8.5.3_typescript@5.8.3_yaml@2.8.0/node_modules/tsup/assets/esm_shims.js
import path from "path";
import { fileURLToPath } from "url";
var init_esm_shims = __esm({
"../../node_modules/.pnpm/tsup@8.5.0_jiti@2.4.2_postcss@8.5.3_typescript@5.8.3_yaml@2.8.0/node_modules/tsup/assets/esm_shims.js"() {
"use strict";
}
});
// src/utils/TemplateEngine.ts
import path3 from "path";
import fs2 from "fs-extra";
import { fileURLToPath as fileURLToPath2 } from "url";
var TemplateEngine;
var init_TemplateEngine = __esm({
"src/utils/TemplateEngine.ts"() {
"use strict";
init_esm_shims();
TemplateEngine = class {
constructor(context) {
const __dirname3 = path3.dirname(fileURLToPath2(import.meta.url));
this.templatesDir = __dirname3.includes("/dist") ? path3.join(__dirname3, "../templates") : path3.join(__dirname3, "../../templates");
this.context = context;
}
/**
* Process a template file and substitute variables
*/
processTemplate(templateContent) {
let processed = templateContent;
Object.entries(this.context).forEach(([key, value]) => {
const placeholder = `{{${key}}}`;
processed = processed.replace(new RegExp(placeholder, "g"), value);
});
return processed;
}
/**
* Copy base template files to target directory
*/
async copyBaseTemplate(targetDir) {
const baseTemplateDir = path3.join(this.templatesDir, "base");
await this.copyTemplateDirectory(baseTemplateDir, targetDir);
}
/**
* Copy Vike template files to target directory
*/
async copyVikeTemplate(targetDir) {
const vikeTemplateDir = path3.join(this.templatesDir, "vike");
await this.copyTemplateDirectory(vikeTemplateDir, targetDir);
await this.mergePackageJsonAdditions(targetDir, "vike");
}
/**
* Copy mobile template files to target directory
*/
async copyMobileTemplate(targetDir) {
const mobileTemplateDir = path3.join(this.templatesDir, "mobile");
if (await fs2.pathExists(mobileTemplateDir)) {
await this.copyTemplateDirectory(mobileTemplateDir, targetDir);
await this.mergePackageJsonAdditions(targetDir, "mobile");
}
}
/**
* Copy notification template files to target directory
*/
async copyNotificationTemplate(targetDir) {
const notificationTemplateDir = path3.join(this.templatesDir, "notifications");
if (await fs2.pathExists(notificationTemplateDir)) {
await this.copyTemplateDirectory(notificationTemplateDir, targetDir);
await this.addNotificationDependencies(targetDir);
await this.addNotificationCapabilities(targetDir);
}
}
/**
* Copy a template directory recursively, processing templates
*/
async copyTemplateDirectory(sourceDir, targetDir) {
if (!await fs2.pathExists(sourceDir)) {
return;
}
const items = await fs2.readdir(sourceDir, { withFileTypes: true });
for (const item of items) {
const sourcePath = path3.join(sourceDir, item.name);
const targetPath = path3.join(targetDir, this.getTargetFilename(item.name));
if (item.isDirectory()) {
await fs2.ensureDir(targetPath);
await this.copyTemplateDirectory(sourcePath, targetPath);
} else {
await fs2.ensureDir(path3.dirname(targetPath));
if (this.isTemplateFile(item.name)) {
const templateContent = await fs2.readFile(sourcePath, "utf-8");
const processedContent = this.processTemplate(templateContent);
await fs2.writeFile(targetPath, processedContent);
} else {
await fs2.copy(sourcePath, targetPath);
}
}
}
}
/**
* Merge package.json additions for a specific template type
*/
async mergePackageJsonAdditions(targetDir, templateType) {
const additionsPath = path3.join(this.templatesDir, templateType, "package.json.additions.json");
if (await fs2.pathExists(additionsPath)) {
const packageJsonPath = path3.join(targetDir, "package.json");
const additions = await fs2.readJson(additionsPath);
const existingPackageJson = await fs2.readJson(packageJsonPath);
if (additions.scripts) {
existingPackageJson.scripts = { ...existingPackageJson.scripts, ...additions.scripts };
}
if (additions.dependencies) {
existingPackageJson.dependencies = {
...existingPackageJson.dependencies,
...additions.dependencies
};
}
if (additions.devDependencies) {
existingPackageJson.devDependencies = {
...existingPackageJson.devDependencies,
...additions.devDependencies
};
}
await fs2.writeJson(packageJsonPath, existingPackageJson, { spaces: 2 });
}
}
/**
* Add notification dependencies to package.json and Cargo.toml
*/
async addNotificationDependencies(targetDir) {
const packageJsonPath = path3.join(targetDir, "package.json");
if (await fs2.pathExists(packageJsonPath)) {
const packageJson2 = await fs2.readJson(packageJsonPath);
if (!packageJson2.dependencies) {
packageJson2.dependencies = {};
}
packageJson2.dependencies["@tauri-apps/plugin-notification"] = "^2.0.0";
await fs2.writeJson(packageJsonPath, packageJson2, { spaces: 2 });
}
const cargoTomlPath = path3.join(targetDir, "src-tauri", "Cargo.toml");
if (await fs2.pathExists(cargoTomlPath)) {
let cargoContent = await fs2.readFile(cargoTomlPath, "utf-8");
if (!cargoContent.includes("tauri-plugin-notification")) {
const dependenciesMatch = cargoContent.match(/\[dependencies\]/);
if (dependenciesMatch) {
const insertIndex = dependenciesMatch.index + dependenciesMatch[0].length;
const beforeDeps = cargoContent.slice(0, insertIndex);
const afterDeps = cargoContent.slice(insertIndex);
cargoContent = beforeDeps + '\ntauri-plugin-notification = "2"' + afterDeps;
await fs2.writeFile(cargoTomlPath, cargoContent);
}
}
}
const libRsPath = path3.join(targetDir, "src-tauri", "src", "lib.rs");
if (await fs2.pathExists(libRsPath)) {
let libContent = await fs2.readFile(libRsPath, "utf-8");
if (!libContent.includes("tauri_plugin_notification::init()")) {
libContent = libContent.replace(
/\.plugin\(tauri_plugin_os::init\(\)\)/g,
".plugin(tauri_plugin_os::init())\n .plugin(tauri_plugin_notification::init())"
);
await fs2.writeFile(libRsPath, libContent);
}
}
}
/**
* Add notification capabilities by overwriting existing capabilities files
*/
async addNotificationCapabilities(targetDir) {
const capabilitiesDir = path3.join(targetDir, "src-tauri", "capabilities");
const notificationCapabilitiesDir = path3.join(
this.templatesDir,
"notifications",
"capabilities"
);
if (await fs2.pathExists(notificationCapabilitiesDir)) {
const files = await fs2.readdir(notificationCapabilitiesDir);
for (const file of files) {
const sourcePath = path3.join(notificationCapabilitiesDir, file);
const targetPath = path3.join(capabilitiesDir, file);
await fs2.copy(sourcePath, targetPath);
}
}
}
/**
* Check if a file is a template file (ends with .template)
*/
isTemplateFile(filename) {
return filename.endsWith(".template");
}
/**
* Get the target filename by removing .template extension
*/
getTargetFilename(filename) {
if (this.isTemplateFile(filename)) {
return filename.replace(/\.template$/, "");
}
return filename;
}
/**
* Get list of files that would be created (for dry run)
*/
async getFilesToCreate(options) {
const files = [];
await this.collectFiles(path3.join(this.templatesDir, "base"), "", files);
if (options.vike) {
await this.collectFiles(path3.join(this.templatesDir, "vike"), "", files);
}
if (options.mobile) {
const mobileDir = path3.join(this.templatesDir, "mobile");
if (await fs2.pathExists(mobileDir)) {
await this.collectFiles(mobileDir, "", files);
}
}
if (options.notifications) {
const notificationDir = path3.join(this.templatesDir, "notifications");
if (await fs2.pathExists(notificationDir)) {
await this.collectFiles(notificationDir, "", files);
}
}
return files.map((file) => this.getTargetFilename(file));
}
/**
* Recursively collect all files in a directory
*/
async collectFiles(dir, relativePath, files) {
if (!await fs2.pathExists(dir)) {
return;
}
const items = await fs2.readdir(dir, { withFileTypes: true });
for (const item of items) {
const itemPath = path3.join(relativePath, item.name);
if (item.isDirectory()) {
await this.collectFiles(path3.join(dir, item.name), itemPath, files);
} else if (!item.name.endsWith(".additions.json")) {
files.push(itemPath);
}
}
}
};
}
});
// src/generators/ProjectGenerator.ts
var ProjectGenerator_exports = {};
__export(ProjectGenerator_exports, {
ProjectGenerator: () => ProjectGenerator
});
import path4 from "path";
import fs3 from "fs-extra";
import { spawn as spawn2 } from "child_process";
var ProjectGenerator;
var init_ProjectGenerator = __esm({
"src/generators/ProjectGenerator.ts"() {
"use strict";
init_esm_shims();
init_TemplateEngine();
ProjectGenerator = class {
constructor(options) {
this.options = options;
const context = {
PROJECT_NAME: options.name,
PROJECT_IDENTIFIER: options.name.replace(/[^a-zA-Z0-9]/g, ""),
PACKAGE_MANAGER: options.packageManager || "pnpm",
BEFORE_DEV_COMMAND: options.vike ? "pnpm dev:vike" : "pnpm dev",
BEFORE_BUILD_COMMAND: options.vike ? "pnpm build:vike" : "pnpm build",
PROJECT_TITLE: options.seo?.title || "",
PROJECT_DESCRIPTION: options.seo?.description || "",
PROJECT_KEYWORDS: options.seo?.keywords || "",
PROJECT_AUTHOR: options.seo?.author || ""
};
this.templateEngine = new TemplateEngine(context);
}
async generate() {
try {
const { targetDir, dryRun } = this.options;
if (dryRun) {
return this.dryRun();
}
await fs3.ensureDir(targetDir);
await this.templateEngine.copyBaseTemplate(targetDir);
if (this.options.vike) {
await this.templateEngine.copyVikeTemplate(targetDir);
}
if (this.options.mobile) {
await this.templateEngine.copyMobileTemplate(targetDir);
}
if (this.options.notifications) {
await this.templateEngine.copyNotificationTemplate(targetDir);
}
if (!this.options.skipInstall) {
await this.installDependencies();
}
if (this.options.mobile && !this.options.skipInstall) {
await this.initializeMobilePlatforms();
}
if (!this.options.skipGit) {
await this.initializeGit();
}
return {
projectPath: targetDir,
success: true,
nextSteps: this.getNextSteps()
};
} catch (error) {
return {
projectPath: this.options.targetDir,
success: false,
errors: [error instanceof Error ? error.message : "Unknown error"],
nextSteps: []
};
}
}
async dryRun() {
const files = await this.templateEngine.getFilesToCreate({
vike: Boolean(this.options.vike),
mobile: Boolean(this.options.mobile),
notifications: Boolean(this.options.notifications)
});
console.info("\n\u{1F4C1} Files that would be created:");
files.forEach((file) => {
console.info(` ${path4.join(this.options.targetDir, file)}`);
});
return {
projectPath: this.options.targetDir,
success: true,
nextSteps: this.getNextSteps()
};
}
async installDependencies() {
try {
await this.runPackageCommand("install", []);
} catch (error) {
throw new Error(`Failed to install dependencies: ${error}`);
}
}
async runPackageCommand(command, packages, isDev = false) {
const { packageManager = "pnpm", targetDir } = this.options;
const args = [command];
if (isDev) {
args.push("-D");
}
args.push(...packages);
if (packageManager === "pnpm") {
args.push("--ignore-workspace");
}
const child = spawn2(packageManager, args, {
cwd: targetDir,
stdio: "inherit",
shell: true
});
return new Promise((resolve, reject) => {
child.on("close", (code) => {
if (code === 0) {
resolve();
} else {
reject(new Error(`${packageManager} ${command} failed with code ${code}`));
}
});
});
}
async initializeMobilePlatforms() {
console.info("\n\u{1F4F1} Initializing mobile platforms...");
if (process.platform === "darwin") {
try {
console.info(" Initializing iOS platform...");
await this.runTauriCommand(["ios", "init"]);
console.info(" \u2705 iOS platform initialized");
} catch (error) {
console.warn(
` \u26A0\uFE0F iOS initialization failed: ${error instanceof Error ? error.message : String(error)}`
);
console.warn(" Make sure Xcode is installed and try running: pnpm tauri ios init");
}
}
if (process.env.ANDROID_HOME || process.env.ANDROID_SDK_ROOT) {
try {
console.info(" Initializing Android platform...");
await this.runTauriCommand(["android", "init"]);
console.info(" \u2705 Android platform initialized");
} catch (error) {
console.warn(
` \u26A0\uFE0F Android initialization failed: ${error instanceof Error ? error.message : String(error)}`
);
console.warn(" Make sure Android SDK is installed and ANDROID_HOME is set");
console.warn(" Then try running: pnpm tauri android init");
}
} else {
console.warn(" \u26A0\uFE0F Android SDK not found (ANDROID_HOME not set)");
console.warn(" Install Android Studio and set ANDROID_HOME environment variable");
console.warn(" See MOBILE_SETUP.md for detailed setup instructions");
console.warn(" Then run: pnpm tauri android init");
}
}
async runTauriCommand(args) {
const { packageManager = "pnpm", targetDir } = this.options;
const command = packageManager === "npm" ? "npx" : packageManager;
const fullArgs = command === "npx" ? ["tauri", ...args] : ["tauri", ...args];
const child = spawn2(command, fullArgs, {
cwd: targetDir,
stdio: "inherit",
shell: true
});
return new Promise((resolve, reject) => {
child.on("close", (code) => {
if (code === 0) {
resolve();
} else {
reject(new Error(`tauri ${args.join(" ")} failed with code ${code}`));
}
});
});
}
async initializeGit() {
try {
const child = spawn2("git", ["init"], {
cwd: this.options.targetDir,
stdio: "inherit",
shell: true
});
return new Promise((resolve, reject) => {
child.on("close", (code) => {
if (code === 0) {
resolve();
} else {
reject(new Error(`git init failed with code ${code}`));
}
});
});
} catch (error) {
throw new Error(`Failed to initialize git: ${error}`);
}
}
getNextSteps() {
const { name, packageManager = "pnpm", vike, mobile, skipInstall } = this.options;
const steps = [`cd ${name}`];
if (skipInstall) {
steps.push(`${packageManager} install`);
}
if (mobile && skipInstall) {
if (process.platform === "darwin") {
steps.push(`${packageManager} tauri ios init # Initialize iOS platform`);
}
steps.push(`${packageManager} tauri android init # Initialize Android platform`);
}
if (vike) {
steps.push(`${packageManager} run dev:vike # Start Vike dev server`);
steps.push(`${packageManager} run tauri:dev # Start Tauri app`);
} else {
steps.push(`${packageManager} run tauri:dev # Start development`);
}
if (mobile) {
steps.push("");
steps.push("\u{1F4F1} Mobile Development:");
if (process.platform === "darwin") {
steps.push(`${packageManager} tauri ios dev # Run on iOS simulator/device`);
}
steps.push(`${packageManager} tauri android dev # Run on Android emulator/device`);
}
steps.push("");
steps.push(`${packageManager} run tauri:build # Build for production`);
return steps;
}
};
}
});
// src/cli.ts
init_esm_shims();
import { program } from "commander";
import chalk from "chalk";
import inquirer from "inquirer";
import ora from "ora";
import path5 from "path";
import fs4 from "fs-extra";
import { fileURLToPath as fileURLToPath3 } from "url";
// src/index.ts
init_esm_shims();
// src/utils/validators.ts
init_esm_shims();
import path2 from "path";
import fs from "fs-extra";
import { spawn } from "child_process";
import validateNpmName from "validate-npm-package-name";
function validateProjectName(name) {
const errors = [];
const warnings = [];
if (!name || typeof name !== "string") {
return {
valid: false,
errors: ["Project name is required"],
warnings: []
};
}
if (name.trim() !== name) {
errors.push("Project name cannot have leading or trailing whitespace");
}
if (name.length === 0) {
errors.push("Project name cannot be empty");
}
if (name.length > 214) {
errors.push("Project name cannot be longer than 214 characters");
}
const reservedNames = [
"node_modules",
"favicon.ico",
"package.json",
"package-lock.json",
"pnpm-lock.yaml",
"yarn.lock",
"npm",
"pnpm",
"yarn",
"node",
"git",
"src",
"dist",
"build",
"public",
"static",
"assets",
"components",
"pages",
"styles",
"scripts",
"config",
"test",
"tests",
"__tests__",
"spec",
"specs",
"__spec__",
".git",
".gitignore",
".env",
".env.local",
".env.development",
".env.production",
"readme",
"readme.md",
"license",
"license.md",
"changelog",
"changelog.md",
"contributing",
"contributing.md"
];
if (reservedNames.includes(name.toLowerCase())) {
errors.push(`Project name "${name}" is reserved and cannot be used`);
}
const invalidChars = /[<>:"/\\|?*\x00-\x1f]/;
if (invalidChars.test(name)) {
errors.push("Project name contains invalid characters for file systems");
}
const windowsReserved = /^(con|prn|aux|nul|com[1-9]|lpt[1-9])$/i;
if (windowsReserved.test(name)) {
errors.push(`Project name "${name}" is reserved on Windows systems`);
}
const npmValidation = validateNpmName(name);
if (!npmValidation.validForNewPackages) {
errors.push(...npmValidation.errors || []);
warnings.push(...npmValidation.warnings || []);
}
return {
valid: errors.length === 0,
errors,
warnings
};
}
async function validateTargetPath(targetPath) {
const errors = [];
const warnings = [];
if (!targetPath || typeof targetPath !== "string") {
return {
valid: false,
errors: ["Target path is required"],
warnings: []
};
}
try {
const resolvedPath = path2.resolve(targetPath);
if (resolvedPath.length > 250) {
warnings.push("Target path is very long and may cause issues on some systems");
}
const parentDir = path2.dirname(resolvedPath);
try {
const parentStats = await fs.stat(parentDir);
if (!parentStats.isDirectory()) {
errors.push("Parent directory is not a directory");
} else {
try {
await fs.access(parentDir, fs.constants.W_OK);
} catch {
errors.push("Parent directory is not writable");
}
}
} catch {
errors.push("Parent directory does not exist or is not accessible");
}
if (await fs.pathExists(resolvedPath)) {
const stats = await fs.stat(resolvedPath);
if (stats.isDirectory()) {
const items = await fs.readdir(resolvedPath);
if (items.length > 0) {
warnings.push("Target directory exists and is not empty");
}
} else {
errors.push("Target path exists but is not a directory");
}
}
const pathComponents = resolvedPath.split(path2.sep);
for (const component of pathComponents) {
if (component.startsWith(".") && component !== "." && component !== "..") {
warnings.push("Path contains hidden directories");
break;
}
}
} catch (error) {
errors.push(
`Path validation failed: ${error instanceof Error ? error.message : "Unknown error"}`
);
}
return {
valid: errors.length === 0,
errors,
warnings
};
}
async function checkCommand(command) {
return new Promise((resolve) => {
const child = spawn(command, ["--version"], {
stdio: "ignore",
shell: true
});
child.on("close", (code) => {
resolve(code === 0);
});
child.on("error", () => {
resolve(false);
});
setTimeout(() => {
child.kill();
resolve(false);
}, 5e3);
});
}
async function verifySystemRequirements(packageManager = "pnpm") {
const errors = [];
const warnings = [];
let node = false;
let rust = false;
let packageManagerAvailable = false;
try {
const nodeVersion = process.version;
const versionParts = nodeVersion.split(".");
if (versionParts.length > 0 && versionParts[0]) {
const major = parseInt(versionParts[0].substring(1));
if (major >= 18) {
node = true;
if (major < 20) {
warnings.push(`Node.js ${nodeVersion} works but Node.js 20+ is recommended`);
}
} else {
errors.push(`Node.js 18+ required (found ${nodeVersion})`);
}
} else {
errors.push("Could not parse Node.js version");
}
} catch {
errors.push("Could not determine Node.js version");
}
try {
rust = await checkCommand("rustc");
if (!rust) {
errors.push("Rust is required for Tauri development. Install from https://rustup.rs/");
} else {
const cargoAvailable = await checkCommand("cargo");
if (!cargoAvailable) {
warnings.push("Cargo not found, but Rust is available");
}
}
} catch {
errors.push("Could not check Rust installation");
}
try {
packageManagerAvailable = await checkCommand(packageManager);
if (!packageManagerAvailable) {
errors.push(`${packageManager} is not installed or not available in PATH`);
if (packageManager === "pnpm") {
warnings.push("You can install pnpm with: npm install -g pnpm");
} else if (packageManager === "yarn") {
warnings.push("You can install yarn with: npm install -g yarn");
}
}
} catch {
errors.push(`Could not check ${packageManager} installation`);
}
if (process.platform === "linux") {
warnings.push(
"On Linux, you may need additional dependencies. See: https://tauri.app/v1/guides/getting-started/prerequisites#setting-up-linux"
);
}
return {
node,
rust,
packageManager: packageManagerAvailable,
errors,
warnings
};
}
async function validateProjectOptions(options) {
const errors = [];
const warnings = [];
const nameValidation = validateProjectName(options.name);
errors.push(...nameValidation.errors);
warnings.push(...nameValidation.warnings);
const pathValidation = await validateTargetPath(options.targetDir);
errors.push(...pathValidation.errors);
warnings.push(...pathValidation.warnings);
const systemValidation = await verifySystemRequirements(options.packageManager);
errors.push(...systemValidation.errors);
warnings.push(...systemValidation.warnings);
return {
valid: errors.length === 0,
errors,
warnings
};
}
// src/index.ts
async function createApp(options) {
const { ProjectGenerator: ProjectGenerator2 } = await Promise.resolve().then(() => (init_ProjectGenerator(), ProjectGenerator_exports));
const generator = new ProjectGenerator2(options);
return await generator.generate();
}
// src/cli.ts
var __dirname2 = path5.dirname(fileURLToPath3(import.meta.url));
var packageJson = JSON.parse(fs4.readFileSync(path5.join(__dirname2, "../package.json"), "utf-8"));
var version = packageJson.version;
program.name("create-vite-vike-tauri").description("Create a new Vite + Vike + Tauri application with Orchard9 design system").version(version).argument("[name]", "project name").option("--vike", "add Vike SSR support").option("--mobile", "add mobile platform support (iOS/Android)").option("--notifications", "add native notification support with examples").option("--skip-install", "skip dependency installation").option("--skip-git", "skip git initialization").option("--dry-run", "preview what would be created without actually creating files").option("--pnpm", "use pnpm as package manager (default)").option("--npm", "use npm as package manager").option("--yarn", "use yarn as package manager").action(async (name, options) => {
console.log(
chalk.blue(`
\u2554\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2557
\u2551 Orchard9 Vite + Vike + Tauri Creator \u2551
\u2551 Version ${version} \u2551
\u255A\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u255D
`)
);
try {
let packageManager = "pnpm";
if (options.npm) packageManager = "npm";
if (options.yarn) packageManager = "yarn";
if (!name) {
const { projectName } = await inquirer.prompt([
{
type: "input",
name: "projectName",
message: "What is your project name?",
default: "my-tauri-app",
validate: (input) => {
const validation = validateProjectName(input);
if (!validation.valid) {
return validation.errors.join(", ");
}
return true;
}
}
]);
name = projectName;
}
const targetDir = path5.resolve(process.cwd(), name);
const spinner = ora("Validating project options...").start();
try {
const validation = await validateProjectOptions({
name,
targetDir,
packageManager
});
if (!validation.valid) {
spinner.fail("Validation failed");
console.error(chalk.red("\nValidation errors:"));
validation.errors.forEach((err) => console.error(chalk.red(` - ${err}`)));
if (validation.warnings.length > 0) {
console.warn(chalk.yellow("\nWarnings:"));
validation.warnings.forEach((warn) => console.warn(chalk.yellow(` - ${warn}`)));
}
process.exit(1);
}
if (validation.warnings.length > 0) {
spinner.warn("Validation completed with warnings");
console.warn(chalk.yellow("\nWarnings:"));
validation.warnings.forEach((warn) => console.warn(chalk.yellow(` - ${warn}`)));
} else {
spinner.succeed("Project options validated");
}
} catch (error) {
spinner.fail("Validation failed");
console.error(
chalk.red(
`
Validation error: ${error instanceof Error ? error.message : "Unknown error"}`
)
);
process.exit(1);
}
const isCI = process.env["CI"] === "true" || process.env["NODE_ENV"] === "test";
const needsPrompts = !options.dryRun && !isCI && (options.vike === void 0 || options.mobile === void 0 || options.notifications === void 0);
const answers = needsPrompts ? await inquirer.prompt([
{
type: "confirm",
name: "vike",
message: "Add Vike SSR support?",
default: false,
when: () => options.vike === void 0
},
{
type: "confirm",
name: "mobile",
message: "Add mobile platform support (iOS/Android)?",
default: false,
when: () => options.mobile === void 0
},
{
type: "confirm",
name: "notifications",
message: "Add native notification support with examples?",
default: false,
when: () => options.notifications === void 0
}
]) : {};
const vikeEnabled = options.vike ?? answers["vike"] ?? false;
const seoAnswers = needsPrompts && vikeEnabled ? await inquirer.prompt([
{
type: "input",
name: "title",
message: "Project title (for SEO):",
default: name.charAt(0).toUpperCase() + name.slice(1),
validate: (input) => input.trim().length > 0 || "Title is required"
},
{
type: "input",
name: "description",
message: "Project description (for SEO):",
default: `A modern Vite + Vike + Tauri application`,
validate: (input) => input.trim().length > 0 || "Description is required"
},
{
type: "input",
name: "keywords",
message: "Keywords (comma-separated):",
default: "vite,vike,tauri,react,typescript"
},
{
type: "input",
name: "author",
message: "Author name:",
default: "Your Name"
}
]) : {};
const finalOptions = {
name,
vike: vikeEnabled,
mobile: options.mobile ?? answers["mobile"] ?? false,
notifications: options.notifications ?? answers["notifications"] ?? false,
skipInstall: options.skipInstall,
skipGit: options.skipGit,
dryRun: options.dryRun,
packageManager,
seo: {
title: seoAnswers["title"] || name.charAt(0).toUpperCase() + name.slice(1),
description: seoAnswers["description"] || `A modern Vite + Vike + Tauri application`,
keywords: seoAnswers["keywords"] || "vite,vike,tauri,react,typescript",
author: seoAnswers["author"] || "Your Name"
}
};
console.log(chalk.cyan("\nProject configuration:"));
console.log(chalk.gray(` Name: ${finalOptions.name}`));
console.log(chalk.gray(` Location: ${targetDir}`));
console.log(chalk.gray(` Package Manager: ${finalOptions.packageManager}`));
console.log(chalk.gray(` Vike SSR: ${finalOptions.vike ? "Yes" : "No"}`));
console.log(chalk.gray(` Mobile Support: ${finalOptions.mobile ? "Yes" : "No"}`));
console.log(chalk.gray(` Notifications: ${finalOptions.notifications ? "Yes" : "No"}`));
if (finalOptions.dryRun) {
console.log(chalk.yellow("\n\u{1F50D} Dry run mode - no files will be created"));
}
if (fs4.existsSync(targetDir) && !finalOptions.dryRun) {
const { overwrite } = await inquirer.prompt([
{
type: "confirm",
name: "overwrite",
message: `Directory ${name} already exists. Overwrite?`,
default: false
}
]);
if (!overwrite) {
console.log(chalk.yellow("Operation cancelled"));
process.exit(0);
}
await fs4.remove(targetDir);
}
const createSpinner = ora("Creating project...").start();
try {
const result = await createApp({
name: finalOptions.name,
targetDir,
vike: finalOptions.vike,
mobile: finalOptions.mobile,
notifications: finalOptions.notifications,
skipInstall: finalOptions.skipInstall,
skipGit: finalOptions.skipGit,
packageManager: finalOptions.packageManager,
dryRun: finalOptions.dryRun,
seo: finalOptions.seo
});
if (result.success) {
createSpinner.succeed("Project created successfully!");
console.log(chalk.green("\n\u2728 Next steps:"));
result.nextSteps.forEach((step, index) => {
console.log(chalk.gray(` ${index + 1}. ${step}`));
});
} else {
createSpinner.fail("Failed to create project");
result.errors?.forEach((err) => console.error(chalk.red(` - ${err}`)));
process.exit(1);
}
} catch {
createSpinner.fail("Generator implementation pending");
console.log(chalk.yellow("\nThe generator logic is not yet fully implemented."));
console.log(chalk.gray("This task is marked as completed for the CLI entry point."));
console.log(chalk.green("\n\u2728 Next steps (when implemented):"));
console.log(chalk.gray(` 1. cd ${name}`));
console.log(chalk.gray(` 2. ${packageManager} install`));
console.log(chalk.gray(` 3. ${packageManager} run tauri:dev`));
}
} catch (error) {
console.error(chalk.red("\\n\u274C Error creating project:"), error);
process.exit(1);
}
});
program.parse();
//# sourceMappingURL=cli.js.map