Skip to content

Commit

Permalink
feat: add cli module
Browse files Browse the repository at this point in the history
  • Loading branch information
nailiable committed Nov 5, 2024
1 parent 269a70d commit 3033ba4
Show file tree
Hide file tree
Showing 12 changed files with 428 additions and 6 deletions.
3 changes: 3 additions & 0 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,13 @@
"Containerable",
"datasource",
"Fastly",
"gitee",
"gitly",
"inversify",
"jexl",
"Naily",
"nailyjs",
"nodegit",
"nuxt",
"paramtypes",
"pluginutils",
Expand Down
2 changes: 2 additions & 0 deletions naily.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ import { defineConfig } from './packages/config/src/helper'

export default defineConfig({
naily: {
cli: {},

typeorm: {
type: 'sqlite',
database: path.join(cwd(), './node_modules/.cache/naily/typeorm.db'),
Expand Down
4 changes: 4 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
"scripts": {
"new": "pnpm create es-project@latest",
"build": "pnpm -r build",
"build:cli": "pnpm -F @nailyjs/cli build",
"build:ioc": "pnpm -F @nailyjs/ioc build",
"build:backend": "pnpm -F @nailyjs/backend build",
"build:config": "pnpm -F @nailyjs/config build",
Expand All @@ -24,6 +25,8 @@
"test": "vitest",
"test:ui": "vitest --ui --coverage",
"swx": "pnpm tsx scripts/swc.ts",
"tsx": "tsx",
"naily": "naily",

"changeset": "changeset",
"version-packages": "changeset version",
Expand All @@ -33,6 +36,7 @@
"devDependencies": {
"@antfu/eslint-config": "^3.7.3",
"@changesets/cli": "^2.27.9",
"@nailyjs/cli": "workspace:*",
"@swc/core": "^1.7.42",
"@types/js-yaml": "^4.0.9",
"@types/node": "^22.7.5",
Expand Down
5 changes: 5 additions & 0 deletions packages/cli/logo.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
_ __ _ __
/ |/ /___ _ (_)/ /__ __
/ // _ `// // // // /
/_/|_/ \_,_//_//_/ \_, /
IOC Framework /___/
43 changes: 43 additions & 0 deletions packages/cli/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
{
"name": "@nailyjs/cli",
"type": "module",
"version": "2.0.0",
"description": "The CLI for Naily.js",
"author": "Naily Zero <[email protected]> (https://naily.cc)",
"exports": {
".": {
"types": "./dist/index.d.ts",
"import": "./dist/index.js",
"require": "./dist/index.cjs"
}
},
"main": "./dist/index.cjs",
"module": "./dist/index.js",
"types": "./dist/index.d.ts",
"publishConfig": {
"access": "public"
},
"bin": {
"naily": "./dist/cli.js"
},
"files": [
"dist",
"logo.txt"
],
"scripts": {
"build": "tsup",
"watch": "tsup -w"
},
"dependencies": {
"@nailyjs/config": "workspace:*",
"@nailyjs/ioc": "workspace:*",
"commander": "^12.1.0",
"gitly": "^3.0.2",
"ora": "^8.1.1",
"prompts": "^2.4.2"
},
"devDependencies": {
"@types/prompts": "^2.4.9",
"tsup": "^8.3.0"
}
}
6 changes: 6 additions & 0 deletions packages/cli/src/cli.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import { ConfigPlugin } from '@nailyjs/config'
import { CliBootstrap } from './index'

new CliBootstrap()
.use(ConfigPlugin())
.run()
101 changes: 101 additions & 0 deletions packages/cli/src/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
import child_process from 'node:child_process'
import fs from 'node:fs'
import path from 'node:path'
import { argv, exit, stderr, stdout } from 'node:process'
import { AbstractBootstrap } from '@nailyjs/ioc'
import { Command, program } from 'commander'
import git from 'gitly'
import ora from 'ora'
import prompts from 'prompts'
import { LogoWriter } from './write-logo'

export class CliBootstrap extends AbstractBootstrap {
async runNewProject(): Promise<void> {
const projectPath = await prompts({
type: 'text',
name: 'path',
message: 'Where do you want to create the project?',
initial: '.',
validate: (value: string) => {
if (value.length <= 0) return 'Path cannot be empty'
if (!fs.existsSync(path.resolve(value))) return true
if (fs.readdirSync(path.resolve(value)).length > 0)
return 'Path already exists and is not empty. Please choose another path or remove it first.'
return true
},
format: (value: string) => path.resolve(value),
}).catch(() => exit(0))
if (!projectPath.path) return exit(0)
console.log('✔ Will creating project at:', projectPath.path)
const template = await prompts({
type: 'autocomplete',
name: 'template',
message: 'Select a template:',
choices: [
{ title: 'backend-app', value: 'template-backend-app', description: 'Basic naily backend application' },
{ title: 'vitesse-naily', value: 'vitesse-naily', description: 'Antfu\'s Vitesse with Naily' },
],
}).catch(() => exit(0))
if (!template.template) return exit(0)
console.log('✔ Selected template:', template.template)
const packageManager = await prompts({
type: 'select',
name: 'packageManager',
message: 'Select a package manager:',
choices: [
{ title: 'npm', value: 'npm', description: 'Default package manager' },
{ title: 'yarn', value: 'yarn', description: 'Fast, reliable, and secure dependency management' },
{ title: 'pnpm', value: 'pnpm', description: 'Fast, disk space efficient package manager' },
{ title: 'bun', value: 'bun', description: 'Bun have a Node.js-compatible package manager.' },
{ title: 'Install at later', value: 'none', description: 'Install deps at later' },
],
}).catch(() => exit(0))
if (!packageManager.packageManager) return exit(0)
console.log('✔ Selected package manager:', packageManager.packageManager)
const spinner = ora('Creating project...').start()

// eslint-disable-next-line ts/ban-ts-comment
// @ts-expect-error
const [tarPath, realPath] = await ((typeof git === 'function' ? git : git.default) as typeof import('gitly').default)(`github:nailyjs/${template.template}.git`, projectPath.path, {
backend: 'axios',
})
if (!tarPath || !realPath)
return spinner.fail('Failed to create project, please try again.') as any
spinner.succeed('Project created successfully.')

if (packageManager.packageManager !== 'none') {
spinner.text = `Installing dependencies using ${packageManager.packageManager}`
console.log('\n')
const child_process_exec = child_process.exec(packageManager.packageManager === 'yarn' ? 'FORCE_COLOR=1 yarn' : `FORCE_COLOR=1 ${packageManager.packageManager} install`, {
cwd: projectPath.path,
})
child_process_exec.stdout?.pipe(stdout)
child_process_exec.stderr?.pipe(stderr)
child_process_exec.on('exit', () => {
console.log('\n')
spinner.succeed('Dependencies installed successfully.')
exit(0)
})
}
}

setupCommander(): Command {
program
.name('naily')
.version('0.0.1', '-v, --version', 'Output the current version')
.description('Naily CLI')

program.command('new')
.alias('n')
.description('Create a new naily project')
.action(this.runNewProject.bind(this))

return program
}

async run(): Promise<void> {
await this.getPluginRunner().runBeforeRun()
LogoWriter.getInstance(this).write(true)
this.setupCommander().parse(argv)
}
}
15 changes: 15 additions & 0 deletions packages/cli/src/types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
export interface CliConfiguration {
/** Path to the logo file. the file must a txt file. */
banner?: `${string}.txt` | false
}

declare global {
namespace Naily {
namespace Configuration {
interface NailyUserConfig {
/** Configuration for the naily cli. */
cli?: CliConfiguration
}
}
}
}
36 changes: 36 additions & 0 deletions packages/cli/src/write-logo.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import fs from 'node:fs'
import path from 'node:path'
import { Value } from '@nailyjs/config'
import { ClassWrapper, Container, Injectable } from '@nailyjs/ioc'

let __dirname = globalThis.__dirname
if (!globalThis.__dirname)
__dirname = import.meta.dirname

@Injectable()
export class LogoWriter {
constructor(
@Value('naily.cli.banner')
private readonly logoPath: `${string}.txt` | false,
) {}

write(clear: boolean = true): void {
if (clear === true) console.clear()
if (this.logoPath === false) return

if (typeof this.logoPath === 'string') {
const logoPath = path.resolve(this.logoPath)
if (fs.existsSync(logoPath)) return console.log(fs.readFileSync(logoPath, 'utf-8'))
}

const defaultLogoPath = path.join(__dirname, '../logo.txt')
if (fs.existsSync(defaultLogoPath)) console.log(fs.readFileSync(defaultLogoPath, 'utf-8'))
else console.log('Naily CLI')
}

static getInstance(container: Container): LogoWriter {
const writerWrapper = container.getContainer().get(LogoWriter) as ClassWrapper<LogoWriter>
if (writerWrapper) return writerWrapper.getClassFactory().getOrCreateInstance()
return container.createClassWrapper(LogoWriter).getClassFactory().getOrCreateInstance()
}
}
10 changes: 10 additions & 0 deletions packages/cli/tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
{
"compilerOptions": {
"target": "ES2022",
"emitDecoratorMetadata": true,
"experimentalDecorators": true,
"module": "ES2022",
"moduleResolution": "Bundler"
},
"include": ["src"]
}
12 changes: 12 additions & 0 deletions packages/cli/tsup.config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import { defineConfig } from 'tsup'

export default defineConfig({
entry: {
index: './src/index.ts',
cli: './src/cli.ts',
},
dts: true,
sourcemap: true,
clean: true,
format: ['cjs', 'esm'],
})
Loading

0 comments on commit 3033ba4

Please sign in to comment.