mirror of
https://github.com/robonen/tools.git
synced 2026-03-20 02:44:45 +00:00
feat(cli): update CLI tool for project creation with package.json and config generation
This commit is contained in:
189
bin/cli.ts
Normal file
189
bin/cli.ts
Normal file
@@ -0,0 +1,189 @@
|
||||
#!/usr/bin/env node
|
||||
|
||||
import { mkdir, writeFile } from 'node:fs/promises';
|
||||
import { defineCommand, runMain } from 'citty';
|
||||
import { resolve } from 'node:path';
|
||||
import { splitByCase } from 'scule';
|
||||
|
||||
async function getLatestPackageVersion(packageName: string) {
|
||||
try {
|
||||
const response = await fetch(`https://registry.npmjs.org/${packageName}`);
|
||||
const data = await response.json();
|
||||
|
||||
if (!response.ok) {
|
||||
console.warn(`Failed to fetch latest version for ${packageName}, using fallback`);
|
||||
return null;
|
||||
}
|
||||
|
||||
const latestVersion = data['dist-tags']?.latest as string | undefined;
|
||||
|
||||
if (!latestVersion)
|
||||
return null;
|
||||
|
||||
return {
|
||||
version: latestVersion,
|
||||
versionRange: `^${latestVersion}`,
|
||||
};
|
||||
} catch (error) {
|
||||
console.warn(`Error fetching version for ${packageName}: ${error.message}`);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
const PACKAGE_MANAGER_DEFAULT = 'pnpm@10.10.0';
|
||||
const NODE_VERSION = '>=22.15.0';
|
||||
const VITE_VERSION_DEFAULT = '^5.4.8';
|
||||
const VITE_DTS_VERSION_DEFAULT = '^4.2.2';
|
||||
const PATHE_VERSION_DEFAULT = '^1.1.2';
|
||||
const DEFAULT_DIR = 'packages';
|
||||
|
||||
const generatePackageJson = async (name: string, path: string, hasVite: boolean) => {
|
||||
const [
|
||||
packageManagerVersion,
|
||||
viteVersion,
|
||||
viteDtsVersion,
|
||||
patheVersion,
|
||||
] = await Promise.all([
|
||||
getLatestPackageVersion('pnpm').then(v => v?.version || PACKAGE_MANAGER_DEFAULT),
|
||||
hasVite ? getLatestPackageVersion('vite').then(v => v?.versionRange || VITE_VERSION_DEFAULT) : VITE_VERSION_DEFAULT,
|
||||
hasVite ? getLatestPackageVersion('vite-plugin-dts').then(v => v?.versionRange || VITE_DTS_VERSION_DEFAULT) : VITE_DTS_VERSION_DEFAULT,
|
||||
hasVite ? getLatestPackageVersion('pathe').then(v => v?.versionRange || PATHE_VERSION_DEFAULT) : PATHE_VERSION_DEFAULT,
|
||||
]);
|
||||
|
||||
const data = {
|
||||
name,
|
||||
private: true,
|
||||
version: '0.0.0',
|
||||
license: 'UNLICENSED',
|
||||
description: '',
|
||||
keywords: [],
|
||||
author: 'Robonen Andrew <robonenandrew@gmail.com>',
|
||||
repository: {
|
||||
type: 'git',
|
||||
url: 'git+https://github.com/robonen/tools.git',
|
||||
directory: path,
|
||||
},
|
||||
packageManager: `pnpm@${packageManagerVersion}`,
|
||||
engines: {
|
||||
node: NODE_VERSION,
|
||||
},
|
||||
type: 'module',
|
||||
files: ['dist'],
|
||||
main: './dist/index.umd.js',
|
||||
module: './dist/index.js',
|
||||
types: './dist/index.d.ts',
|
||||
exports: {
|
||||
'.': {
|
||||
import: './dist/index.js',
|
||||
require: './dist/index.umd.js',
|
||||
types: './dist/index.d.ts',
|
||||
},
|
||||
},
|
||||
scripts: {
|
||||
test: 'echo \"Error: no test specified\" && exit 1',
|
||||
...(hasVite && {
|
||||
dev: 'vite',
|
||||
build: 'vite build',
|
||||
preview: 'vite preview',
|
||||
}),
|
||||
},
|
||||
devDependencies: {
|
||||
'@robonen/tsconfig': 'workspace:*',
|
||||
...(hasVite && {
|
||||
vite: viteVersion,
|
||||
'vite-plugin-dts': viteDtsVersion,
|
||||
pathe: patheVersion,
|
||||
}),
|
||||
},
|
||||
};
|
||||
|
||||
return JSON.stringify(data, null, 2);
|
||||
};
|
||||
|
||||
const generateJsrJson = (name: string) => {
|
||||
const data = {
|
||||
name,
|
||||
version: '0.0.0',
|
||||
exports: './src/index.ts',
|
||||
};
|
||||
|
||||
return JSON.stringify(data, null, 2);
|
||||
};
|
||||
|
||||
const generateViteConfig = () => `import { defineConfig } from 'vite';
|
||||
import dts from 'vite-plugin-dts';
|
||||
import { resolve } from 'pathe';
|
||||
|
||||
export default defineConfig({
|
||||
resolve: {
|
||||
alias: {
|
||||
'@': resolve(__dirname, './src'),
|
||||
},
|
||||
},
|
||||
plugins: [
|
||||
dts({
|
||||
insertTypesEntry: true,
|
||||
exclude: '**/*.test.ts',
|
||||
}),
|
||||
],
|
||||
});
|
||||
`;
|
||||
|
||||
const generateTsConfig = () => {
|
||||
const data = {
|
||||
extends: '@robonen/tsconfig/tsconfig.json',
|
||||
};
|
||||
|
||||
return JSON.stringify(data, null, 2);
|
||||
};
|
||||
|
||||
const generateReadme = (name: string) => `# ${name}`;
|
||||
|
||||
const createCommand = defineCommand({
|
||||
meta: {
|
||||
name: "create",
|
||||
version: "1.0.0",
|
||||
description: "Command to create a new project",
|
||||
},
|
||||
args: {
|
||||
name: {
|
||||
type: 'positional',
|
||||
description: "Name of the project",
|
||||
required: true,
|
||||
},
|
||||
path: {
|
||||
type: 'positional',
|
||||
description: "Relative path to the project folder",
|
||||
required: false,
|
||||
},
|
||||
vite: {
|
||||
type: 'boolean',
|
||||
description: "Add Vite to the project",
|
||||
required: false,
|
||||
},
|
||||
},
|
||||
async run({ args }) {
|
||||
const path = args.path ?? `./${DEFAULT_DIR}/${splitByCase(args.name).at(-1)}`;
|
||||
const resolvedPath = resolve(path);
|
||||
const hasVite = args.vite ?? false;
|
||||
|
||||
console.log(`Creating project in ${resolvedPath}`);
|
||||
|
||||
await mkdir(resolvedPath, { recursive: true });
|
||||
|
||||
const packageJson = await generatePackageJson(args.name, path, hasVite);
|
||||
await writeFile(`${resolvedPath}/package.json`, packageJson);
|
||||
await writeFile(`${resolvedPath}/jsr.json`, generateJsrJson(args.name));
|
||||
await writeFile(`${resolvedPath}/tsconfig.json`, generateTsConfig());
|
||||
await writeFile(`${resolvedPath}/README.md`, generateReadme(args.name));
|
||||
|
||||
if (hasVite) {
|
||||
await mkdir(`${resolvedPath}/src`, { recursive: true });
|
||||
await writeFile(`${resolvedPath}/vite.config.ts`, generateViteConfig());
|
||||
}
|
||||
|
||||
console.log(`Project created successfully`);
|
||||
},
|
||||
});
|
||||
|
||||
runMain(createCommand);
|
||||
Reference in New Issue
Block a user