8000 Feat/bun support by jairoFernandez · Pull Request #4 · ideascoldigital/cli-maker · GitHub
[go: up one dir, main page]
More Web Proxy on the site http://driver.im/
Skip to content

Feat/bun support #4

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 4 commits into from
Mar 18, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 11 additions & 1 deletion src/bin/cli.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,20 @@ const cli = new CLI(
"A simple CLI builder",
{
interactive: true,
version: '1.0.0',
version: '1.0.1',
}
);

const versionCommand = {
name: 'version',
description: 'Show CLI version',
params: [],
action: () => {
console.log(`\n${cli.getName()} version: ${cli.getOptions()?.version}\n`);
}
};

cli.command(createCommand);
cli.command(versionCommand);

cli.parse(process.argv);
28 changes: 19 additions & 9 deletions src/bin/commands/common.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,16 +4,24 @@ import { promises as fs, readdirSync, mkdirSync } from 'node:fs';
import * as templates from '../templates'
import * as test_templates from '../test_templates'

export async function initializeProject() {
export async function initializeProject(packageManager: string = 'npm') {
console.log('Generating base project...');
execSync('npm init -y', { stdio: 'inherit' });
const initCommand = packageManager === 'bun' ? 'bun init' : 'npm init -y';
execSync(initCommand, { stdio: 'inherit' });
}

export async function createProjectStructure() {
console.log('Creating project structure...');
execSync('mkdir -p src/commands src/utils src/tests', { stdio: 'inherit' });
execSync('touch src/index.ts', { stdio: 'inherit' });
execSync('mkdir dist', { stdio: 'inherit' });
try {
execSync('mkdir -p src/commands src/utils src/tests', { stdio: 'inherit' });
execSync('touch src/index.ts', { stdio: 'inherit' });
execSync('mkdir -p dist', { stdio: 'inherit' });
console.log('✅ Project structure created successfully!');
} catch (error) {
if (error instanceof Error) {
console.error('❌ Error creating project structure:', error.message);
}
}
}

export async function createGitIgnore() {
Expand Down Expand Up @@ -120,8 +128,9 @@ export async function createReadmeFile(cliName: string, cliDescription: string)
}
}

export async function createCliTestFile(cliName: string, cliDescription: string) {
const result = test_templates.testCli.replace(/{{cliName}}/g, cliName).replace(/{{cliDescription}}/g, cliDescription);
export async function createCliTestFile(cliName: string, cliDescription: string, packageManager: string = 'npm') {
const template = packageManager === 'bun' ? test_templates.testCliBun : test_templates.testCliNpm;
const result = template.replace(/{{cliName}}/g, cliName).replace(/{{cliDescription}}/g, cliDescription);

try {
await createFileWithDirectories('src/tests/cli/cli.test.ts', result);
Expand All @@ -131,9 +140,10 @@ export async function createCliTestFile(cliName: string, cliDescription: string)
}
}

export async function createTestLibFile() {
export async function createTestLibFile(packageManager: string = 'npm') {
try {
await createFileWithDirectories('src/tests/lib/lib.test.ts', test_templates.testLib);
const template = packageManager === 'bun' ? test_templates.testLibBun : test_templates.testLibNpm;
await createFileWithDirectories('src/tests/lib/lib.test.ts', template);
console.log('lib.test.ts has been generated!');< 8000 /td>
} catch (err) {
console.error('Failed to generate lib.test.ts:', err);
Expand Down
34 changes: 26 additions & 8 deletions src/bin/commands/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,13 @@ export const createCommand: Command = {
required: true,
type: ParamType.Email
},
{
name: "package_manager",
description: "Select your preferred package manager",
required: true,
type: ParamType.List,
options: ['npm', 'bun']
},
{
name: "git_init",
description: "Do you want to initialize a git repository?",
Expand All @@ -39,13 +46,18 @@ export const createCommand: Command = {
},
],
action: async (args: any) => {
const {name, description, author, email} = args;
const {name, description, author, email, package_manager} = args;
const projectName = name.split('/')[1] || name;

const isValid = await libraries.validateProjectDirectory(projectName);
if (!isValid) {
return;
}

const isEmpty = commons.isFolderEmpty();
if (!isEmpty) {
const data = name.split('/')[1]
commons.createNewFolder(data);
commons.moveToFolder(data);
commons.createNewFolder(projectName);
commons.moveToFolder(projectName);
}

await commons.initializeProject();
Expand All @@ -59,7 +71,13 @@ export const createCommand: Command = {
await commons.generateIndexTs(name, description);
await commons.generateCommandExample();

const newScripts = {
const newScripts = package_manager === 'bun' ? {
"build": "bun build ./src/index.ts ./src/bin/cli.ts --target=node --outdir ./dist --format cjs",
"build:test": "bun build ./src/tests/*.ts --target=node --outdir dist/tests --format cjs",
"test": "bun test",
"prepublishOnly": "bun run build",
"start": "bun run src/cli.ts"
} : {
"build": "tsc",
"build:test": "tsc -p tsconfig.test.json",
"test": "npm run build:test && find dist/tests -name '*.test.js' -exec node {} \\;",
Expand All @@ -70,9 +88,9 @@ export const createCommand: Command = {
await libraries.addScriptsToPackageJson(newScripts, name, description, author, email);
await commons.createBinFile();
await commons.createReadmeFile(name, description);
await commons.createCliTestFile(name, description);
await commons.createTestLibFile();
await libraries.installDependencies();
await commons.createCliTestFile(name, description, package_manager);
await commons.createTestLibFile(package_manager);
await libraries.installDependencies(package_manager, name);

if (args.git_init === 'yes') {
await commons.initializeGit();
Expand Down
26 changes: 21 additions & 5 deletions src/bin/libraries.ts
Original file line number Diff line number Diff line change
Expand Up @@ -53,12 +53,28 @@ export async function addScriptsToPackageJson(
}
}

export async function installDependencies() {
export async function validateProjectDirectory(name: string): Promise<boolean> {
try {
await fs.access(name);
console.log(`\n❌ Error: The folder "${name}" already exists.`);
console.log('Please choose a different name or delete the existing folder.\n');
return false;
} catch {
return true;
}
}

export async function installDependencies(package_manager = 'npm', projectName: string) {
const installCmd = package_manager === 'bun' ? 'bun add' : 'npm install';
const devFlag = package_manager === 'bun' ? '--dev' : '--save-dev';

console.log('Installing TypeScript and @types/node...');
execSync('npm install --save-dev typescript @types/node', { stdio: 'inherit' });
execSync(`${installCmd} ${devFlag} typescript @types/node`, { stdio: 'inherit' });

console.log('Installing @ideascol/cli-maker...');
execSync('npm install @ideascol/cli-maker', { stdio: 'inherit' });
execSync('npm install', { stdio: 'inherit' });
execSync('npm run start', { stdio: 'inherit' });
execSync(`${installCmd} @ideascol/cli-maker`, { stdio: 'inherit' });
execSync(`${package_manager} run start`, { stdio: 'inherit' });

console.log('\n🎉 Success! Your CLI project has been created.');
console.log(`\nNext steps:\n cd ${projectName}\n ${package_manager} start\n\nEnjoy building your CLI! 🚀\n`);
}
13 changes: 12 additions & 1 deletion src/bin/templates.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,19 +24,30 @@ export const templateReadme = `# {{cliName}}

## Quick start
\`\`\`bash
# Using npm
npx {{cliName}}

# Using bun
bunx {{cliName}}
\`\`\`

## Installation

\`\`\`bash
# Using npm
npm install -g {{cliName}}

# Using bun
bun install -g {{cliName}}
\`\`\`

## Usage as cli
\`\`\`bash
# Using npm
npm link # to test the cli locally

# npm link, to test the cli locally
# Using bun
bun link # to test the cli locally

{{binName}} greet --name=John
\`\`\`
Expand Down
49 changes: 41 additions & 8 deletions src/bin/test_templates.ts
Original file line number Diff line number Diff line change
@@ -1,38 +1,71 @@
export const testCli = `import test from 'node:test';
import { describe, it } from 'node:test';
import assert from 'node:assert/strict';
export const testCliBun = `import { describe, test, expect } from "bun:test";

import { cli } from '../../cli';

describe("CLI", () => {
test("Validate CLI default params", () => {
expect(cli.getName()).toBe("{{cliName}}");
expect(cli.getDescription()).toBe("{{cliDescription}}");
expect(cli.getCommands().length).toBe(1);
expect(cli.getOptions()?.interactive).toBe(true);
expect(cli.getOptions()?.version).toBe("1.0.0");
});

test("required params for first command", () => {
const firstCommand = cli.getCommands()[0];

expect(cli.getCommands().length).toBe(1);
expect(firstCommand.params.length).toBe(1);
expect(firstCommand.params[0].required).toBe(true);
});
});
`

export const testCliNpm = `import { describe, it } from "node:test";
import { strict as assert } from "node:assert";

import { cli } from '../../cli';

describe("CLI", () => {
it("Validate CLI default params", () => {
assert.equal(cli.getName(), "{{cliName}}");
assert.equal(cli.getDescription(), "{{cliDescription}}");
assert.equal(cli.getCommands().length, 1);
assert.equal(cli.getOptions()?.interactive, true);
assert.equal(cli.getOptions()?.version, "1.0.0");
});

test("required params for first command", () => {
it("required params for first command", () => {
const firstCommand = cli.getCommands()[0];

assert.equal(cli.getCommands().length, 1);
assert.equal(firstCommand.params.length, 1);
assert.equal(firstCommand.params[0].required, true);
});
});

`

export const testLib = `import test from 'node:test';
import { describe, it } from 'node:test';
import assert from 'node:assert/strict';
export const testLibBun = `import { describe, test, expect } from "bun:test";

import { Greet } from '../../lib';

describe("Lib", () => {
test("should return the message", () => {
const message = Greet('John');
assert.equal(message, 'Hello, John!');
expect(message).toBe('Hello, John!');
});
});
`

export const testLibNpm = `import { describe, it } from "node:test";
import { strict as assert } from "node:assert";

import { Greet } from '../../lib';

describe("Lib", () => {
it("should return the message", () => {
const message = Greet('John');
assert.equal(message, 'Hello, John!');
});
});`
14 changes: 9 additions & 5 deletions src/command/command.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,8 +42,12 @@ export class CLI {
const [nodePath, scriptPath, ...args] = argv;
const commandName = args[0];

if (!commandName) {
this.help();
if (!commandName || commandName === '--version') {
if (commandName === '--version') {
console.log(`\n${Colors.FgGreen}${this.name} version: ${this.options?.version}${Colors.Reset}\n`);
} else {
this.help();
}
return;
}

Expand Down Expand Up @@ -167,7 +171,7 @@ export class CLI {
const paramName = key.slice(2);
const commandParam = command.params.find(p => p.name === paramName);
if (commandParam) {
const validation = this.validateParam(value, commandParam.type, commandParam.required, commandParam.options);
const validation = this.validateParam(value, commandParam.type, commandParam.required, commandParam.options, paramName);
if (validation.error) {
return { error: validation.error };
}
Expand All @@ -180,8 +184,8 @@ export class CLI {
return { result };
}

private validateParam(value: string | undefined, type?: ParamType, isRequired?: boolean, options?: any[]): ValidatorResult {
return this.validator.validateParam(value, type, isRequired, options)
private validateParam(value: string | undefined, type?: ParamType, isRequired?: boolean, options?: any[], paramName?: string): ValidatorResult {
return this.validator.validateParam(value, type, isRequired, options, paramName)
}

private findParamType(paramName: string): { name: string; description: string; type?: ParamType; required?: boolean; options?: any[] } | undefined {
Expand Down
4 changes: 2 additions & 2 deletions src/command/validator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,9 @@ export interface ValidatorResult {
}

export class Validator {
public validateParam(value: string | undefined, type?: ParamType, isRequired?: boolean, options?: any[]): ValidatorResult {
public validateParam(value: string | undefined, type?: ParamType, isRequired?: boolean, options?: any[], paramName?: string): ValidatorResult {
if (this.checkEmpty(value) && isRequired) {
return { error: `${Colors.FgRed}Missing required parameter${Colors.Reset}` };
return { error: `${Colors.FgRed}Missing required parameter${paramName ? `: ${paramName}` : ''}${Colors.Reset}` };
} else if (this.checkEmpty(value) && !isRequired) {
return { value: undefined, error: "" };
}
Expand Down
Loading
0