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

Commit

Permalink
fix: async
Browse files Browse the repository at this point in the history
  • Loading branch information
jdx committed Feb 7, 2018
1 parent 3ff9853 commit c97877a
Show file tree
Hide file tree
Showing 3 changed files with 91 additions and 63 deletions.
82 changes: 49 additions & 33 deletions src/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import {PJSON} from './pjson'
import * as Plugin from './plugin'
import {Topic} from './topic'
import {tsPath} from './ts_node'
import {compact, flatMap, loadJSONSync, uniq} from './util'
import {compact, flatMap, loadJSON, uniq} from './util'

export type PlatformTypes = 'darwin' | 'linux' | 'win32' | 'aix' | 'freebsd' | 'openbsd' | 'sunos'
export type ArchTypes = 'arm' | 'arm64' | 'mips' | 'mipsel' | 'ppc' | 'ppc64' | 's390' | 's390x' | 'x32' | 'x64' | 'x86'
Expand Down Expand Up @@ -113,30 +113,32 @@ const _pjson = require('../package.json')

export class Config implements IConfig {
_base = `${_pjson.name}@${_pjson.version}`
name: string
version: string
root: string
arch: ArchTypes
bin: string
cacheDir: string
configDir: string
dataDir: string
dirname: string
errlog: string
home: string
platform: PlatformTypes
shell: string
windows: boolean
userAgent: string
name!: string
version!: string
root!: string
arch!: ArchTypes
bin!: string
cacheDir!: string
configDir!: string
dataDir!: string
dirname!: string
errlog!: string
home!: string
platform!: PlatformTypes
shell!: string
windows!: boolean
userAgent!: string
debug: number = 0
npmRegistry: string
pjson: PJSON.CLI
npmRegistry!: string
pjson!: PJSON.CLI
userPJSON?: PJSON.User
plugins: Plugin.IPlugin[] = []
protected warned = false

constructor(opts: Options) {
this.loadPlugins(opts.root, 'core', [{root: opts.root}], {must: true})
constructor(public options: Options) {}

async load() {
await this.loadPlugins(this.options.root, 'core', [{root: this.options.root}], {must: true})
const plugin = this.plugins[0]
this.root = plugin.root
this.pjson = plugin.pjson
Expand All @@ -160,31 +162,42 @@ export class Config implements IConfig {

this.npmRegistry = this.scopedEnvVar('NPM_REGISTRY') || this.pjson.anycli.npmRegistry || 'https://registry.yarnpkg.com'

debug('config done')
await Promise.all([
this.loadCorePlugins(),
this.loadUserPlugins(),
this.loadDevPlugins(),
])
}

async loadCorePlugins() {
if (this.pjson.anycli.plugins) {
this.loadPlugins(this.root, 'core', this.pjson.anycli.plugins)
await this.loadPlugins(this.root, 'core', this.pjson.anycli.plugins)
}
}

if (opts.devPlugins !== false) {
async loadDevPlugins() {
if (this.options.devPlugins !== false) {
try {
const devPlugins = this.pjson.anycli.devPlugins
if (devPlugins) this.loadPlugins(this.root, 'dev', devPlugins)
if (devPlugins) await this.loadPlugins(this.root, 'dev', devPlugins)
} catch (err) {
process.emitWarning(err)
}
}
}

if (opts.userPlugins !== false) {
async loadUserPlugins() {
if (this.options.userPlugins !== false) {
try {
const userPJSONPath = path.join(this.dataDir, 'package.json')
const pjson = this.userPJSON = loadJSONSync(userPJSONPath)
const pjson = this.userPJSON = await loadJSON(userPJSONPath)
if (!pjson.anycli) pjson.anycli = {schema: 1}
this.loadPlugins(userPJSONPath, 'user', pjson.anycli.plugins)
await this.loadPlugins(userPJSONPath, 'user', pjson.anycli.plugins)
} catch (err) {
if (err.code !== 'ENOENT') process.emitWarning(err)
}
}

debug('config done')
}

async runHook<T extends Hooks, K extends keyof T>(event: K, opts: T[K]) {
Expand Down Expand Up @@ -324,11 +337,11 @@ export class Config implements IConfig {
} catch {}
return 0
}
protected loadPlugins(root: string, type: string, plugins: (string | {root?: string, name?: string, tag?: string})[], options: {must?: boolean} = {}) {
protected async loadPlugins(root: string, type: string, plugins: (string | {root?: string, name?: string, tag?: string})[], options: {must?: boolean} = {}) {
if (!plugins.length) return
if (!plugins || !plugins.length) return
debug('loading plugins', plugins)
for (let plugin of plugins || []) {
await Promise.all((plugins || []).map(async plugin => {
try {
let opts: Options = {type, root}
if (typeof plugin === 'string') {
Expand All @@ -339,12 +352,13 @@ export class Config implements IConfig {
opts.root = plugin.root || opts.root
}
let instance = new Plugin.Plugin(opts)
await instance.load()
this.plugins.push(instance)
} catch (err) {
if (options.must) throw err
this.warn(err, 'loadPlugins')
}
}
}))
}

protected warn(err: any, scope?: string) {
Expand All @@ -355,10 +369,12 @@ export class Config implements IConfig {
}
}
export type LoadOptions = Options | string | IConfig | undefined
export function load(opts: LoadOptions = (module.parent && module.parent && module.parent.parent && module.parent.parent.filename) || __dirname) {
export async function load(opts: LoadOptions = (module.parent && module.parent && module.parent.parent && module.parent.parent.filename) || __dirname) {
if (typeof opts === 'string') opts = {root: opts}
if (isConfig(opts)) return opts
return new Config(opts)
let config = new Config(opts)
await config.load()
return config
}

function isConfig(o: any): o is IConfig {
Expand Down
56 changes: 26 additions & 30 deletions src/plugin.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import {error} from '@anycli/errors'
import * as fs from 'fs'
import * as Globby from 'globby'
import * as path from 'path'
import {inspect} from 'util'
Expand All @@ -10,7 +9,7 @@ import {Manifest} from './manifest'
import {PJSON} from './pjson'
import {Topic} from './topic'
import {tsPath} from './ts_node'
import {compact, flatMap, loadJSONSync, mapValues} from './util'
import {compact, exists, flatMap, loadJSON, mapValues} from './util'

export interface Options {
root: string
Expand Down Expand Up @@ -75,32 +74,29 @@ const _pjson = require('../package.json')
export class Plugin implements IPlugin {
// static loadedPlugins: {[name: string]: Plugin} = {}
_base = `${_pjson.name}@${_pjson.version}`
name: string
version: string
pjson: PJSON.Plugin
type: string
root: string
name!: string
version!: string
pjson!: PJSON.Plugin
type!: string
root!: string
tag?: string
manifest: Manifest
commands: Command.Plugin[]
hooks: {[k: string]: string[]}
manifest!: Manifest
commands!: Command.Plugin[]
hooks!: {[k: string]: string[]}
valid = false
alreadyLoaded = false
protected warned = false

constructor(opts: Options) {
this.type = opts.type || 'core'
this.tag = opts.tag
const root = findRoot(opts.name, opts.root)
if (!root) throw new Error(`could not find package.json with ${inspect(opts)}`)
// if (Plugin.loadedPlugins[root]) {
// Plugin.loadedPlugins[root].alreadyLoaded = true
// return Plugin.loadedPlugins[root]
// }
// Plugin.loadedPlugins[root] = this
constructor(public options: Options) {}

async load() {
this.type = this.options.type || 'core'
this.tag = this.options.tag
const root = await findRoot(this.options.name, this.options.root)
if (!root) throw new Error(`could not find package.json with ${inspect(this.options)}`)
this.root = root
debug('reading %s plugin %s', this.type, root)
this.pjson = loadJSONSync(path.join(root, 'package.json')) as any
this.pjson = await loadJSON(path.join(root, 'package.json')) as any
this.name = this.pjson.name
this.version = this.pjson.version
if (this.pjson.anycli) {
Expand All @@ -111,7 +107,7 @@ export class Plugin implements IPlugin {

this.hooks = mapValues(this.pjson.anycli.hooks || {}, i => Array.isArray(i) ? i : [i])

this.manifest = this._manifest(!!opts.ignoreManifest)
this.manifest = await this._manifest(!!this.options.ignoreManifest)
this.commands = Object.entries(this.manifest.commands)
.map(([id, c]) => ({...c, load: () => this.findCommand(id, {must: true})}))
}
Expand Down Expand Up @@ -170,11 +166,11 @@ export class Plugin implements IPlugin {
return cmd
}

protected _manifest(ignoreManifest: boolean): Manifest {
const readManifest = () => {
protected async _manifest(ignoreManifest: boolean): Promise<Manifest> {
const readManifest = async () => {
try {
const p = path.join(this.root, '.anycli.manifest.json')
const manifest: Manifest = loadJSONSync(p)
const manifest: Manifest = await loadJSON(p)
if (!process.env.ANYCLI_NEXT_VERSION && manifest.version !== this.version) {
process.emitWarning(`Mismatched version in ${this.name} plugin manifest. Expected: ${this.version} Received: ${manifest.version}`)
} else {
Expand All @@ -186,7 +182,7 @@ export class Plugin implements IPlugin {
}
}
if (!ignoreManifest) {
let manifest = readManifest()
let manifest = await readManifest()
if (manifest) return manifest
}

Expand Down Expand Up @@ -231,7 +227,7 @@ function topicsToArray(input: any, base?: string): Topic[] {
*
* This is needed because of the deduping npm does
*/
function findRoot(name: string | undefined, root: string) {
async function findRoot(name: string | undefined, root: string) {
// essentially just "cd .."
function* up(from: string) {
while (path.dirname(from) !== from) {
Expand All @@ -244,14 +240,14 @@ function findRoot(name: string | undefined, root: string) {
let cur
if (name) {
cur = path.join(next, 'node_modules', name, 'package.json')
if (fs.existsSync(cur)) return path.dirname(cur)
if (await exists(cur)) return path.dirname(cur)
try {
let pkg = loadJSONSync(path.join(next, 'package.json'))
let pkg = await loadJSON(path.join(next, 'package.json'))
if (pkg.name === name) return next
} catch {}
} else {
cur = path.join(next, 'package.json')
if (fs.existsSync(cur)) return path.dirname(cur)
if (await exists(cur)) return path.dirname(cur)
}
}
}
16 changes: 16 additions & 0 deletions src/util.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,22 @@ export function loadJSONSync(path: string): any {
return JSON.parse(fs.readFileSync(path, 'utf8'))
}

export function exists(path: string): Promise<boolean> {
return new Promise(resolve => fs.exists(path, resolve))
}

export function loadJSON(path: string): Promise<any> {
// let loadJSON
// try { loadJSON = require('load-json-file') } catch {}
// if (loadJSON) return loadJSON.sync(path)
return new Promise((resolve, reject) => {
fs.readFile(path, 'utf8', (err, d) => {
if (err) reject(err)
else resolve(JSON.parse(d))
})
})
}

export function compact<T>(a: (T | undefined)[]): T[] {
return a.filter((a): a is T => !!a)
}
Expand Down

0 comments on commit c97877a

Please sign in to comment.