Skip to content
This repository has been archived by the owner on Aug 22, 2023. It is now read-only.

Commit

Permalink
fix: improve plugin typing
Browse files Browse the repository at this point in the history
  • Loading branch information
jdx committed Feb 3, 2018
1 parent 9af2a66 commit 9a6e1d5
Show file tree
Hide file tree
Showing 4 changed files with 99 additions and 67 deletions.
10 changes: 6 additions & 4 deletions src/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ export type Options = Plugin.Options | string | IConfig | undefined
const debug = require('debug')('@anycli/config')

export interface IConfig extends Plugin.IPlugin {
pjson: PJSON.CLI
/**
* process.arch
*/
Expand Down Expand Up @@ -83,7 +84,7 @@ export interface IConfig extends Plugin.IPlugin {
* npm registry to use for installing plugins
*/
npmRegistry: string
userPJSON?: PJSON
userPJSON?: PJSON.User

runCommand(id: string, argv?: string[]): Promise<void>
}
Expand All @@ -103,7 +104,8 @@ export class Config extends Plugin.Plugin implements IConfig {
userAgent: string
debug: number = 0
npmRegistry: string
userPJSON?: PJSON
pjson!: PJSON.CLI
userPJSON?: PJSON.User

constructor(opts: Plugin.Options) {
super(opts)
Expand All @@ -127,7 +129,7 @@ export class Config extends Plugin.Plugin implements IConfig {

try {
const devPlugins = this.pjson.anycli.devPlugins
if (devPlugins) this.loadPlugins(...devPlugins)
if (devPlugins) this.loadPlugins(this.root, devPlugins)
} catch (err) {
cli.warn(err)
}
Expand All @@ -136,7 +138,7 @@ export class Config extends Plugin.Plugin implements IConfig {
const userPJSONPath = path.join(this.dataDir, 'package.json')
const pjson = this.userPJSON = readPkg.sync(userPJSONPath) as any
if (!pjson.anycli) pjson.anycli = {schema: 1}
this.loadPlugins(...pjson.anycli.plugins)
this.loadPlugins(userPJSONPath, pjson.anycli.plugins)
} catch (err) {
if (err.code !== 'ENOENT') cli.warn(err)
}
Expand Down
8 changes: 4 additions & 4 deletions src/manifest.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,17 +3,17 @@ import * as globby from 'globby'
import * as _ from 'lodash'
import * as path from 'path'

import * as Config from '.'
import {Command} from './command'

export interface Manifest {
version: string
commands: {[id: string]: Config.Command}
commands: {[id: string]: Command}
}

const debug = require('debug')('@anycli/config')

export namespace Manifest {
export type FindCommandCB = (id: string) => Config.Command.Class
export type FindCommandCB = (id: string) => Command.Class

export function build(version: string, dir: string, findCommand: FindCommandCB): Manifest {
debug(`loading IDs from ${dir}`)
Expand All @@ -27,7 +27,7 @@ export namespace Manifest {
debug('found ids', ids)
let commands = ids.map(id => {
try {
return [id, Config.Command.toCached(findCommand(id))]
return [id, Command.toCached(findCommand(id))]
} catch (err) {
cli.warn(err)
}
Expand Down
79 changes: 50 additions & 29 deletions src/pjson.ts
Original file line number Diff line number Diff line change
@@ -1,40 +1,61 @@
import {Package} from 'read-pkg'

export interface PJSON extends Package {
name: string
version: string
export interface PJSON {
dependencies?: {[name: string]: string}
anycli: {
schema?: number
bin?: string
npmRegistry?: string
pluginScope?: string
dirname?: string
commands?: string
hooks?: { [name: string]: string[] }
plugins?: (string | PJSON.Plugin)[]
devPlugins?: (string | PJSON.Plugin)[]
title?: string
description?: string
topics?: {
[k: string]: {
description?: string
subtopics?: PJSON['anycli']['topics']
hidden?: boolean
}
}
}
}

export namespace PJSON {
export type Plugin = User | Link
export interface User {
type: 'user',
name: string,
tag?: string,
}
export interface Link {
type: 'link'
export interface Plugin extends PJSON, Package {
name: string
root: string
version: string
anycli: PJSON['anycli'] & {
schema?: number
title?: string
description?: string
hooks?: { [name: string]: (string | string[]) }
commands?: string
plugins?: string[]
devPlugins?: string[]
topics?: {
[k: string]: {
description?: string
subtopics?: Plugin['anycli']['topics']
hidden?: boolean
}
}
}
}

export interface CLI extends Plugin {
anycli: Plugin['anycli'] & {
schema?: number
bin?: string
npmRegistry?: string
pluginScope?: string
dirname?: string
}
}

export interface User extends PJSON {
private?: boolean
anycli: PJSON['anycli'] & {
plugins?: (string | PluginTypes.User | PluginTypes.Link)[] }
}

export type PluginTypes = PluginTypes.User | PluginTypes.Link
export namespace PluginTypes {
export interface User {
type: 'user',
name: string,
tag?: string,
}
export interface Link {
type: 'link'
name: string
root: string
}
}
}
69 changes: 39 additions & 30 deletions src/plugin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,10 @@ import * as path from 'path'
import * as readPkg from 'read-pkg'
import {inspect} from 'util'

import * as Config from '.'
import {Command} from './command'
import {Manifest} from './manifest'
import {PJSON} from './pjson'
import {Topic} from './topic'
import {tsPath} from './ts_node'
import {undefault} from './util'

Expand Down Expand Up @@ -38,7 +41,7 @@ export interface IPlugin {
*
* parsed with read-pkg
*/
pjson: Config.PJSON
pjson: PJSON.Plugin | PJSON.CLI
/**
* used to tell the user how the plugin was installed
* examples: core, link, user, dev
Expand All @@ -62,31 +65,31 @@ export interface IPlugin {
*/
valid: boolean

readonly commands: Config.Command.Plugin[]
readonly commands: Command.Plugin[]
readonly commandIDs: string[]
readonly topics: Config.Topic[]
findCommand(id: string, opts: {must: true}): Config.Command.Plugin
findCommand(id: string, opts?: {must: boolean}): Config.Command.Plugin | undefined
findTopic(id: string, opts: {must: true}): Config.Topic
findTopic(id: string, opts?: {must: boolean}): Config.Topic | undefined
readonly topics: Topic[]
findCommand(id: string, opts: {must: true}): Command.Plugin
findCommand(id: string, opts?: {must: boolean}): Command.Plugin | undefined
findTopic(id: string, opts: {must: true}): Topic
findTopic(id: string, opts?: {must: boolean}): Topic | undefined
runHook<T extends {}>(event: string, opts?: T): Promise<void>
}

const debug = require('debug')('@anycli/config')
const _pjson = require('../package.json')

const loadedPlugins: {[name: string]: Plugin} = {}

export class Plugin implements IPlugin {
static loadedPlugins: {[name: string]: Plugin} = {}

_base = `${_pjson.name}@${_pjson.version}`
name!: string
version!: string
pjson!: Config.PJSON
pjson!: PJSON.Plugin
type: string
root!: string
tag?: string
manifest!: Config.Manifest
_topics!: Config.Topic[]
manifest!: Manifest
_topics!: Topic[]
plugins: IPlugin[] = []
hooks!: {[k: string]: string[]}
valid!: boolean
Expand All @@ -97,8 +100,8 @@ export class Plugin implements IPlugin {
this.type = opts.type || 'core'
const root = findRoot(opts.name, opts.root)
if (!root) throw new Error(`could not find package.json with ${inspect(opts)}`)
if (loadedPlugins[root]) return loadedPlugins[root]
loadedPlugins[root] = this
if (Plugin.loadedPlugins[root]) return Plugin.loadedPlugins[root]
Plugin.loadedPlugins[root] = this
this.root = root
debug('reading plugin %s', root)
this.pjson = readPkg.sync(path.join(root, 'package.json')) as any
Expand All @@ -113,7 +116,7 @@ export class Plugin implements IPlugin {
this.hooks = _.mapValues(this.pjson.anycli.hooks || {}, _.castArray)

this.manifest = this._manifest()
this.loadPlugins(...this.pjson.anycli.plugins || [])
this.loadPlugins(this.root, this.pjson.anycli.plugins || [])
}

get commandsDir() {
Expand Down Expand Up @@ -145,9 +148,9 @@ export class Plugin implements IPlugin {
return commands
}

findCommand(id: string, opts: {must: true}): Config.Command.Plugin
findCommand(id: string, opts?: {must: boolean}): Config.Command.Plugin | undefined
findCommand(id: string, opts: {must?: boolean} = {}): Config.Command.Plugin | undefined {
findCommand(id: string, opts: {must: true}): Command.Plugin
findCommand(id: string, opts?: {must: boolean}): Command.Plugin | undefined
findCommand(id: string, opts: {must?: boolean} = {}): Command.Plugin | undefined {
let command = this.manifest.commands[id]
if (command) return {...command, load: () => this._findCommand(id)}
for (let plugin of this.plugins) {
Expand All @@ -157,7 +160,7 @@ export class Plugin implements IPlugin {
if (opts.must) throw new Error(`command ${id} not found`)
}

_findCommand(id: string): Config.Command.Class {
_findCommand(id: string): Command.Class {
const search = (cmd: any) => {
if (_.isFunction(cmd.run)) return cmd
return Object.values(cmd).find((cmd: any) => _.isFunction(cmd.run))
Expand All @@ -169,8 +172,8 @@ export class Plugin implements IPlugin {
return cmd
}

findTopic(id: string, opts: {must: true}): Config.Topic
findTopic(id: string, opts?: {must: boolean}): Config.Topic | undefined
findTopic(id: string, opts: {must: true}): Topic
findTopic(id: string, opts?: {must: boolean}): Topic | undefined
findTopic(name: string, opts: {must?: boolean} = {}) {
let topic = this.topics.find(t => t.name === name)
if (topic) return topic
Expand Down Expand Up @@ -203,11 +206,11 @@ export class Plugin implements IPlugin {
// findTopic(id: string, opts: {must: true}): ITopic
// findTopic(id: string, opts?: {must: boolean}): ITopic | undefined

protected _manifest(): Config.Manifest {
protected _manifest(): Manifest {
const readManifest = () => {
try {
const p = path.join(this.root, '.anycli.manifest.json')
const manifest: Config.Manifest = loadJSON.sync(p)
const manifest: Manifest = loadJSON.sync(p)
if (manifest.version !== this.version) {
cli.warn(`Mismatched version in ${this.name} plugin manifest. Expected: ${this.version} Received: ${manifest.version}`)
} else {
Expand All @@ -222,19 +225,25 @@ export class Plugin implements IPlugin {
let manifest = readManifest()
if (manifest) return manifest
}
if (this.commandsDir) return Config.Manifest.build(this.version, this.commandsDir, id => this._findCommand(id))
if (this.commandsDir) return Manifest.build(this.version, this.commandsDir, id => this._findCommand(id))
return {version: this.version, commands: {}}
}

protected loadPlugins(...plugins: Config.PJSON.Plugin[]) {
protected loadPlugins(root: string, plugins: (string | PJSON.Plugin)[]) {
if (!plugins.length) return
if (!plugins || !plugins.length) return
debug('loading plugins', plugins)
for (let plugin of plugins || []) {
try {
let opts: Options = {type: this.type, root: this.root}
if (typeof plugin === 'string') opts.name = plugin
else opts = {...opts, ...plugin}
let opts: Options = {type: this.type, root}
if (typeof plugin === 'string') {
opts.name = plugin
} else {
opts.name = plugin.name || opts.name
opts.type = plugin.type || opts.type
opts.tag = plugin.tag || opts.tag
opts.root = plugin.root || opts.root
}
this.plugins.push(new Plugin(opts))
} catch (err) {
cli.warn(err)
Expand All @@ -244,7 +253,7 @@ export class Plugin implements IPlugin {
}
}

function topicsToArray(input: any, base?: string): Config.Topic[] {
function topicsToArray(input: any, base?: string): Topic[] {
if (!input) return []
base = base ? `${base}:` : ''
if (Array.isArray(input)) {
Expand Down

0 comments on commit 9a6e1d5

Please sign in to comment.