Skip to content

Commit

Permalink
feat: 基于插件的cli初步实现
Browse files Browse the repository at this point in the history
  • Loading branch information
xuasir committed Jan 28, 2021
1 parent f7a678d commit 4bac795
Show file tree
Hide file tree
Showing 29 changed files with 403 additions and 81 deletions.
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -102,3 +102,6 @@ dist

# TernJS port file
.tern-port

# dist
packages/**/lib/
5 changes: 5 additions & 0 deletions packages/cli-shared-utils/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@
"name": "@xus/cli-shared-utils",
"version": "0.0.1",
"description": "xus cli-shared-utils",
"main": "lib/index.js",
"typings": "lib/index.d.ts",
"keywords": [
"cli-shared-utils"
],
Expand All @@ -21,5 +23,8 @@
},
"bugs": {
"url": "https://github.com/xus-code/bundle-tools/issues"
},
"dependencies": {
"joi": "^17.3.0"
}
}
5 changes: 5 additions & 0 deletions packages/cli-shared-utils/src/env.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
const EnvPrefix = `XUS_CLI`

export function createEnvName(name: string): string {
return `${EnvPrefix}_${name}`
}
4 changes: 4 additions & 0 deletions packages/cli-shared-utils/src/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
export * from './logger'
export * from './module'
export * from './validator'
export * from './env'
25 changes: 25 additions & 0 deletions packages/cli-shared-utils/src/logger.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
type LoggerType = 'warn' | 'error' | 'info'

function logger(type: LoggerType = 'info') {
return function (msg: string): void {
console[type](msg)
}
}

export function warn(msg: string): void {
logger('warn')(`
[xus-cli Warning]: ${msg}
`)
}

export function error(msg: string): void {
logger('error')(`
[xus-cli error]: ${msg}
`)
}

export function info(msg: string): void {
logger('info')(`
[xus-cli info]: ${msg}
`)
}
12 changes: 12 additions & 0 deletions packages/cli-shared-utils/src/module.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
export async function loadModule<T = any>(
modulePath: string
): Promise<[Error | null, T]> {
let error: Error | null = null
let moduleContent
try {
moduleContent = await import(modulePath)
} catch (err) {
error = err
}
return [error, moduleContent.default || null]
}
25 changes: 25 additions & 0 deletions packages/cli-shared-utils/src/validator.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import joi, { Root, ObjectSchema } from 'joi'

type CreateFn<T = any> = (joi: Root) => ObjectSchema<T>

export function createSchema<T = any>(fn: CreateFn<T>): ObjectSchema<T> {
let schema = fn(joi)
if (typeof schema === 'object' && typeof schema.validate !== 'function') {
schema = joi.object(schema)
}
return schema
}

type ValidateCb = (message: string) => void

export function validate(
obj: Record<string, any>,
schema: ObjectSchema,
cb: ValidateCb
): void {
const { error } = schema.validate(obj)
if (error) {
cb(error.details[0].message)
process.exit(1)
}
}
8 changes: 0 additions & 8 deletions packages/cli/lib/Cli.d.ts

This file was deleted.

17 changes: 0 additions & 17 deletions packages/cli/lib/Cli.js

This file was deleted.

6 changes: 0 additions & 6 deletions packages/cli/lib/PluginAPI.d.ts

This file was deleted.

9 changes: 0 additions & 9 deletions packages/cli/lib/PluginAPI.js

This file was deleted.

2 changes: 0 additions & 2 deletions packages/cli/lib/index.d.ts

This file was deleted.

20 changes: 0 additions & 20 deletions packages/cli/lib/index.js

This file was deleted.

1 change: 1 addition & 0 deletions packages/cli/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
},
"dependencies": {
"@types/minimist": "^1.2.1",
"@xus/cli-shared-utils": "^0.0.1",
"minimist": "^1.2.5"
}
}
77 changes: 63 additions & 14 deletions packages/cli/src/Cli.ts
Original file line number Diff line number Diff line change
@@ -1,20 +1,69 @@
// types
import { Commands, Args, RawArgs, Plugin } from './types'
import { error } from '@xus/cli-shared-utils'
import ConfigManager, { IConfigManager } from './manager/ConfigManager'
import PathManager, { IPathManager } from './manager/PathManager'
import EnvManager, { IEnvManager } from './manager/EnvManager'
import PluginAPI from './PluginAPI'
import { BuiltInPlugins } from './builtInPlugins'

class Cli {
ctxPath: string = process.cwd()
constructor(ctxPath: string) {
this.ctxPath = ctxPath
private initialized = false
plugins: Plugin[] = []
commands: Commands = {}
// manager
PathManager: IPathManager
EnvManager: IEnvManager
ConfigManager: IConfigManager

constructor(context: string) {
this.PathManager = new PathManager(context)
this.EnvManager = new EnvManager(this.PathManager)
this.ConfigManager = new ConfigManager(this.PathManager)

this.plugins = this.resolvePlugins()
}

// 启动 cli
async setupCli(): Promise<void> {
if (this.initialized) return
this.initialized = true
// 1. load config
await this.ConfigManager.loadUserConfig()
// 2. apply plugins
this.plugins.forEach(({ id, apply }) => {
// some skip plugins ??
apply(new PluginAPI(id, this), this.ConfigManager.projectConfig)
})
}

resolvePlugins(): Plugin[] {
// TODO: user install plugin
return BuiltInPlugins
}
async run(
commandName: string,
args: { [key: string]: any },
rawArgs: any[]
): Promise<any> {
console.log(`command `, commandName)
console.log(`args `, args)
console.log(`rawArgs `, rawArgs)
// 1. valid commandName (help)
// 2. init cli (load plugin / config...)
// 3. get compiler task run

async run(commandName: string, args: Args, rawArgs: RawArgs): Promise<any> {
// 1. setup
await this.setupCli()

// 2. get command task run
const command = this.commands[commandName] || null
if (!command) {
error(`
unknown command ${commandName}
`)
process.exit(1)
}

// remove command
args._.shift()
rawArgs.shift()

const { fn } = command
return fn(args, rawArgs)
}
}

export type CliInstance = InstanceType<typeof Cli>

export default Cli
48 changes: 46 additions & 2 deletions packages/cli/src/PluginAPI.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,51 @@
import { IConfigManager } from './manager/ConfigManager'
import { IEnvManager } from './manager/EnvManager'
import { CommandFn, CommandOps, Commands } from './types'
import { IPathManager } from './manager/PathManager'
import { CliInstance } from './Cli'

class PluginAPI {
constructor(public id: string, public service: any) {}
id: string
private service: CliInstance

constructor(id: string, service: CliInstance) {
this.id = id
this.service = service
}

get PathManager(): IPathManager {
return this.service.PathManager
}

get EnvManager(): IEnvManager {
return this.service.EnvManager
}

get ConfigManager(): IConfigManager {
return this.service.ConfigManager
}

// registerCommander(name: string, opt, fn) {}
get commands(): Commands {
return this.service.commands
}

registerCommand(name: string, fn: CommandFn): void
registerCommand(name: string, ops: CommandOps, fn: CommandFn): void
registerCommand(name: string, ops: any, fn?: any): void {
if (typeof ops === 'function') {
fn = ops
ops = null
}

this.service.commands[name] = {
fn,
ops
}
}

// registerConfigValidator(configName: string): void
}

export type IPluginAPI = InstanceType<typeof PluginAPI>

export default PluginAPI
8 changes: 8 additions & 0 deletions packages/cli/src/builtInPlugins/commands/help.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import { IPluginAPI } from '../../PluginAPI'

export default function (api: IPluginAPI): void {
api.registerCommand('help', (args) => {
console.log(`args `, args)
console.log(`all command `, Object.keys(api.commands))
})
}
8 changes: 8 additions & 0 deletions packages/cli/src/builtInPlugins/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import CHelp from './commands/help'

export const BuiltInPlugins = [
{
id: 'built-in:commands/help',
apply: CHelp
}
]
6 changes: 3 additions & 3 deletions packages/cli/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
#!/usr/bin/env node
import minimist from 'minimist'
import Cli from './Cli'
// collect builtIn plugins
// const plugins = []
import { EnvEnum } from './utils/constants'

// 1. init cli
const cli = new Cli(process.env.XUS_BUNDLE_CONTEXT || process.cwd())
const cli = new Cli(process.env[EnvEnum.context] || process.cwd())
// 2. get args
const rawArgs = process.argv.slice(2)
const args = minimist(rawArgs)
Expand Down
44 changes: 44 additions & 0 deletions packages/cli/src/manager/ConfigManager.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
import { ProjectConfig } from '../types'
import { loadModule } from '@xus/cli-shared-utils'
import { IPathManager } from './PathManager'

class ConfigManager {
private PathManager: IPathManager

private finalConfig: ProjectConfig = {}

get projectConfig(): ProjectConfig {
return this.finalConfig
}

constructor(pathManager: IPathManager) {
this.PathManager = pathManager
}

async loadUserConfig(): Promise<void> {
// 1. load userConfig
let userConfig: ProjectConfig
const [err, configContent] = await loadModule<ProjectConfig>(
this.PathManager.userConfigPath
)
if (err) {
userConfig = {}
}
userConfig = configContent
// 2. merge default config
// 3. valid

this.finalConfig = userConfig
}

// for plugin
async loadConfig<T = any>(configPath: string): Promise<T | null> {
const [err, configContent] = await loadModule<T>(configPath)
if (err) return null
return configContent || null
}
}

export type IConfigManager = InstanceType<typeof ConfigManager>

export default ConfigManager

0 comments on commit 4bac795

Please sign in to comment.