create-vhs
Version:
Create a VHS monorepo with one command.
187 lines (178 loc) • 5.66 kB
JavaScript
import path from "node:path";
import fs from "fs-extra";
import { glob } from "glob";
export function validateProjectName(name) {
// Valid npm package name regex
const validNameRegex = /^(?:@[a-z0-9-*~][a-z0-9-*._~]*\/)?[a-z0-9-~][a-z0-9-._~]*$/;
return (validNameRegex.test(name) &&
name.length <= 214 &&
!name.startsWith(".") &&
!name.startsWith("_") &&
name === name.toLowerCase() &&
!name.includes(" "));
}
export async function replaceTemplateVariables(projectPath, pattern, variables) {
try {
const files = await glob(pattern, {
cwd: projectPath,
ignore: ["node_modules/**", ".git/**"],
});
for (const file of files) {
const filePath = path.join(projectPath, file);
if (await fs.pathExists(filePath)) {
const stat = await fs.stat(filePath);
if (stat.isFile()) {
let content = await fs.readFile(filePath, "utf8");
// Replace template variables
for (const [key, value] of Object.entries(variables)) {
const regex = new RegExp(`{{\\s*${key}\\s*}}`, "g");
content = content.replace(regex, String(value));
}
await fs.writeFile(filePath, content);
}
}
}
}
catch (error) {
// Ignore glob errors for non-existent patterns
console.warn(`Warning: Pattern ${pattern} did not match any files`);
}
}
export async function updatePackageJson(projectPath, projectName, options) {
const packageJsonPath = path.join(projectPath, "package.json");
if (await fs.pathExists(packageJsonPath)) {
const packageJson = await fs.readJson(packageJsonPath);
// Update basic info
packageJson.name = projectName;
packageJson.version = "0.1.0";
packageJson.description =
packageJson.description ||
`${projectName} - Generated by create-my-bun-app`;
// Remove template-specific fields
packageJson.repository = undefined;
packageJson.bugs = undefined;
packageJson.homepage = undefined;
// Update scripts based on package manager
if (packageJson.scripts) {
const scripts = packageJson.scripts;
// Update package manager commands
if (options.packageManager !== "bun") {
// biome-ignore lint/complexity/noForEach: <explanation>
Object.keys(scripts).forEach((script) => {
if (scripts[script]?.includes("bun ")) {
scripts[script] = scripts[script]?.replace(/bun /g, `${options.packageManager} `);
}
});
}
// Feature-specific scripts
// if (options.features.includes("biome")) {
// scripts.lint = "biome check .";
// scripts["lint:fix"] = "biome check . --apply";
// scripts.format = "biome format . --write";
// }
}
// Workspace configuration for monorepos
if (["basic", "mini-app"].includes(options.template)) {
packageJson.workspaces = ["apps/*", "packages/*"];
}
await fs.writeJson(packageJsonPath, packageJson, { spaces: 2 });
}
}
export async function updateTsConfig(projectPath, options) {
const tsConfigPath = path.join(projectPath, "tsconfig.json");
const tsConfig = {
compilerOptions: {
target: "ES2022",
module: "ESNext",
moduleResolution: "bundler",
allowImportingTsExtensions: true,
allowSyntheticDefaultImports: true,
esModuleInterop: true,
forceConsistentCasingInFileNames: true,
strict: true,
noUncheckedIndexedAccess: true,
skipLibCheck: true,
resolveJsonModule: true,
allowJs: true,
jsx: "react-jsx",
composite: false,
incremental: true,
},
include: ["**/*.ts", "**/*.tsx"],
exclude: ["node_modules", "dist", "build"],
};
// Remove undefined values
// biome-ignore lint/complexity/noForEach: <explanation>
Object.keys(tsConfig.compilerOptions).forEach((key) => {
if (tsConfig.compilerOptions[key] === undefined) {
delete tsConfig.compilerOptions[key];
}
});
await fs.writeJson(tsConfigPath, tsConfig, { spaces: 2 });
}
export async function createGitIgnore(projectPath, options) {
const gitIgnoreContent = `
# Dependencies
node_modules/
.pnp
.pnp.js
# Testing
coverage/
# Build outputs
dist/
build/
.next/
# Environment variables
.env
.env.local
.env.development.local
.env.test.local
.env.production.local
# Logs
npm-debug.log*
yarn-debug.log*
yarn-error.log*
bun.lockb
# Runtime data
pids
*.pid
*.seed
*.pid.lock
# IDE
.vscode/
.idea/
*.swp
*.swo
*~
# OS
.DS_Store
.DS_Store?
._*
.Spotlight-V100
.Trashes
ehthumbs.db
Thumbs.db
# Turborepo
.turbo/
# Docker
.dockerignore
`.trim();
await fs.writeFile(path.join(projectPath, ".gitignore"), gitIgnoreContent);
}
export function getPackageManagerCommand(packageManager) {
const commands = {
bun: "bun",
npm: "npm run",
yarn: "yarn",
pnpm: "pnpm",
};
return commands[packageManager] || "bun";
}
export function formatProjectName(name) {
return name
.toLowerCase()
.replace(/[^a-z0-9-]/g, "-")
.replace(/^-+|-+$/g, "")
.replace(/-+/g, "-");
}
//# sourceMappingURL=utils.js.map