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

Commit

Permalink
fix: simplify loading
Browse files Browse the repository at this point in the history
  • Loading branch information
jdx committed Jan 19, 2018
1 parent bbe2418 commit 875eff9
Show file tree
Hide file tree
Showing 7 changed files with 68 additions and 123 deletions.
4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
"fs-extra": "^5.0.0",
"load-json-file": "^4.0.0",
"lodash": "^4.17.4",
"read-pkg-up": "^3.0.0"
"read-pkg": "^3.0.0"
},
"devDependencies": {
"@dxcli/dev": "^1.1.3",
Expand All @@ -25,7 +25,7 @@
"@types/lodash": "^4.14.93",
"@types/mocha": "^2.2.46",
"@types/node": "^9.3.0",
"@types/read-pkg-up": "^3.0.1",
"@types/read-pkg": "^3.0.0",
"chai": "^4.1.2",
"eslint": "^4.15.0",
"eslint-config-dxcli": "^1.1.4",
Expand Down
106 changes: 39 additions & 67 deletions src/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,18 +2,20 @@ import * as fs from 'fs-extra'
import * as readJSON from 'load-json-file'
import * as os from 'os'
import * as path from 'path'
import * as readPkgUp from 'read-pkg-up'
import * as readPkg from 'read-pkg'
import {inspect} from 'util'

import {IEngine} from './engine'
import {ICLIPJSON, IPluginPJSON, normalizePJSON} from './pjson'
import {IPJSON, normalizePJSON} from './pjson'

const _pjson = require('../package.json')
const _base = `${_pjson.name}@${_pjson.version}`

export type PlatformTypes = 'darwin' | 'linux' | 'win32' | 'aix' | 'freebsd' | 'openbsd' | 'sunos'
export type ArchTypes = 'arm' | 'arm64' | 'mips' | 'mipsel' | 'ppc' | 'ppc64' | 's390' | 's390x' | 'x32' | 'x64' | 'x86'

export interface IConfigBase {
export interface IConfig {
_base: string
root: string
arch: string
bin: string
Expand All @@ -26,30 +28,18 @@ export interface IConfigBase {
home: string
hooks: {[k: string]: string[]}
name: string
pjson: IPluginPJSON | ICLIPJSON
pjson: IPJSON
platform: string
shell: string
tsconfig: TSConfig | undefined
userAgent: string
version: string
windows: boolean
debug: number
}

export interface IPluginConfig extends IConfigBase {
type: 'plugin'
pjson: IPluginPJSON
}

export interface ICLIConfig extends IConfigBase {
type: 'cli'
pjson: ICLIPJSON
engine: IEngine
engine?: IEngine
npmRegistry: string
}

export type IConfig = IPluginConfig | ICLIConfig

export interface TSConfig {
compilerOptions: {
rootDir?: string
Expand All @@ -64,17 +54,11 @@ export interface ConfigOptions {

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

export abstract class ConfigBase implements IConfigBase {
static tsNode: any
export class Config {
/**
* registers ts-node for reading typescript source (./src) instead of compiled js files (./lib)
* there are likely issues doing this any the tsconfig.json files are not compatible with others
*/
static registerTSNode() {
if (this.tsNode) return
return this.tsNode = require('ts-node').register()
}

readonly _base = _base
arch: string
bin: string
Expand All @@ -96,19 +80,18 @@ export abstract class ConfigBase implements IConfigBase {
tsconfig: TSConfig | undefined
debug: number = 0
hooks: {[k: string]: string[]}
engine?: IEngine
npmRegistry: string

constructor() {
this.arch = (os.arch() === 'ia32' ? 'x86' : os.arch() as any)
this.platform = os.platform() as any
this.windows = this.platform === 'win32'
}

async load({name, root}: {name?: string, root: string}) {
root = await findRootByName(name, root)
const pkg = await readPkgUp({cwd: root})
this.root = path.dirname(pkg.path)
debug('found root at', this.root)
this.pjson = normalizePJSON(pkg.pkg)
async load(root: string, pjson: readPkg.Package) {
this.root = root
this.pjson = normalizePJSON(pjson)

this.name = this.pjson.name
this.version = this.pjson.version
Expand All @@ -127,6 +110,7 @@ export abstract class ConfigBase implements IConfigBase {
this.tsconfig = await this._tsConfig()
this.commandsDir = await this._libToSrcPath(this.pjson.dxcli.commands)
this.hooks = await this._hooks()
this.npmRegistry = this.scopedEnvVar('NPM_REGISTRY') || this.pjson.dxcli.npmRegistry || 'https://registry.yarnpkg.com'

return this
}
Expand Down Expand Up @@ -189,7 +173,7 @@ export abstract class ConfigBase implements IConfigBase {
const relative = path.relative(lib, orig) // ./commands
const out = path.join(src, relative) // ./src/commands
debug('using ts files at', out)
ConfigBase.registerTSNode()
registerTSNode()
// this can be a directory of commands or point to a hook file
// if it's a directory, we check if the path exists. If so, return the path to the directory.
// For hooks, it might point to a module, not a file. Something like "./hooks/myhook"
Expand Down Expand Up @@ -234,47 +218,14 @@ export abstract class ConfigBase implements IConfigBase {
}
}

export class PluginConfig extends ConfigBase implements IPluginConfig {
static async create({name, root = __dirname}: ConfigOptions) {
const config = new this()
await config.load({root, name})
return config
}

readonly type: 'plugin' = 'plugin'
pjson: IPluginPJSON
}

export class CLIConfig extends ConfigBase implements ICLIConfig {
static async create({name, root = __dirname}: ConfigOptions) {
const config = new this()
await config.load({name, root})
return config
}

readonly type: 'cli' = 'cli'
pjson: ICLIPJSON
engine: IEngine
npmRegistry: string

async load({root, name}: {root: string, name?: string}) {
await super.load({root, name})
this.npmRegistry = this.scopedEnvVar('NPM_REGISTRY') || this.pjson.dxcli.npmRegistry || 'https://registry.yarnpkg.com'
return this
}
}

export type Config = PluginConfig | CLIConfig

/**
* find package root
* for packages installed into node_modules this will go up directories until
* it finds a node_modules directory with the plugin installed into it
*
* This is needed because of the deduping npm does
*/
async function findRootByName(name: string | undefined, root: string) {
if (!name) return root
async function findPkg(name: string | undefined, root: string) {
// essentially just "cd .."
function* up(from: string) {
while (path.dirname(from) !== from) {
Expand All @@ -284,12 +235,33 @@ async function findRootByName(name: string | undefined, root: string) {
yield from
}
for (let next of up(root)) {
const cur = path.join(next, 'node_modules', name, 'package.json')
let cur
if (name) {
cur = path.join(next, 'node_modules', name, 'package.json')
} else {
cur = path.join(next, 'package.json')
}
if (await fs.pathExists(cur)) return cur
}
return root
}

export function isIConfig(o: any): o is IConfig {
return !!o._base
}

export async function read({name, root = __dirname}: ConfigOptions): Promise<IConfig> {
const pkgPath = await findPkg(name, root)
if (!pkgPath) throw new Error(`could not find package.json with ${inspect({name, root})}`)
debug('found package.json at %s from %s', pkgPath, root)
const pkg = await readPkg(pkgPath)
const config = new Config()
await config.load(path.dirname(pkgPath), pkg)
return config
}

let tsNode = false
function registerTSNode() {
if (tsNode) return
require('ts-node').register()
tsNode = true
}
4 changes: 2 additions & 2 deletions src/hooks.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
import {ICommand} from './command'
import {IConfig} from './config'
import {IPluginPJSON} from './pjson'
import {IPJSON} from './pjson'
import {IPluginModule} from './plugin'

export interface IHooks {
init: {}
update: {}
'plugins:parse': {
module: IPluginModule
pjson: IPluginPJSON
pjson: IPJSON
}
prerun: {
Command: ICommand
Expand Down
2 changes: 1 addition & 1 deletion src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,4 @@ export * from './config'
export * from './topic'
export * from './plugin'
export * from './engine'
export {IPJSON, IPluginPJSON, ICLIPJSON} from './pjson'
export {IPJSON} from './pjson'
63 changes: 18 additions & 45 deletions src/pjson.ts
Original file line number Diff line number Diff line change
@@ -1,83 +1,56 @@
import * as _ from 'lodash'
import {Package} from 'read-pkg-up'
import {Package} from 'read-pkg'

export interface IRawPJSONBase extends Package {
export interface IRawPJSON extends Package {
name: string
version: string
dxcli?: {
bin?: string
dirname?: string
commands?: string
hooks?: { [name: string]: string | string[] }
npmRegistry?: string
plugins?: string[] | string
topics?: {
[k: string]: {
description?: string
subtopics?: IPJSONBase['dxcli']['topics']
subtopics?: IPJSON['dxcli']['topics']
hidden?: boolean
}
}
}
}

export interface IRawPluginPJSON extends IRawPJSONBase {
dxcli?: IRawPJSONBase['dxcli'] & {
type?: 'plugin'
}
}

export interface IRawCLIPJSON extends IRawPJSONBase {
dxcli: IRawPJSONBase['dxcli'] & {
type: 'cli'
npmRegistry?: string
}
}

export interface IPJSONBase extends IRawPJSONBase {
export interface IPJSON extends IRawPJSON {
name: string
version: string
dxcli: {
bin: string
dirname: string
bin?: string
npmRegistry?: string
dirname?: string
commands?: string
hooks: { [name: string]: string[] }
plugins?: string[] | string
topics: {
[k: string]: {
description?: string
subtopics?: IPJSONBase['dxcli']['topics']
subtopics?: IPJSON['dxcli']['topics']
hidden?: boolean
}
}
}
}

export interface IPluginPJSON extends IPJSONBase {
dxcli: IPJSONBase['dxcli'] & {
type: 'plugin'
}
}

export interface ICLIPJSON extends IPJSONBase {
dxcli: IPJSONBase['dxcli'] & {
type: 'cli'
npmRegistry?: string
}
}

export type IPJSON = IPluginPJSON | ICLIPJSON

export function normalizePJSON(pjson: IRawCLIPJSON): ICLIPJSON
export function normalizePJSON(pjson: IRawPluginPJSON): IPluginPJSON
export function normalizePJSON(input: IRawPluginPJSON | IRawCLIPJSON): any {
const dxcli: IRawPluginPJSON['dxcli'] | IRawCLIPJSON['dxcli'] = {...(input.dxcli! || input['cli-engine'])}
dxcli.hooks = _.mapValues(dxcli.hooks, _.castArray)
dxcli.type = dxcli.type || 'plugin'
dxcli.bin = dxcli.bin || input.name
dxcli.dirname = dxcli.dirname || dxcli.bin
dxcli.topics = dxcli.topics || {}
export function normalizePJSON(input: IRawPJSON): IPJSON {
const dxcli: IRawPJSON['dxcli'] = {...(input.dxcli! || input['cli-engine'])}
return {
...input,
dxcli,
dxcli: {
topics: {},
bin: input.name,
dirname: dxcli.bin || input.name,
...dxcli,
hooks: _.mapValues(dxcli.hooks, _.castArray),
},
}
}
6 changes: 3 additions & 3 deletions test/config.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,13 @@ import {describe, expect, it} from '@dxcli/dev-test'
import * as os from 'os'
import * as path from 'path'

import {PluginConfig} from '../src'
import * as Config from '../src'

const pluginRoot = (plugin: string) => path.resolve(__dirname, '../plugins', plugin)

const testPlugin = (plugin: string, description: string, fn: (config: PluginConfig) => void) => {
const testPlugin = (plugin: string, description: string, fn: (config: Config.IConfig) => void) => {
it(`${plugin}: ${description}`, async () => {
const config = await PluginConfig.create({name: plugin, root: pluginRoot(plugin)})
const config = await Config.read(plugin === '@heroku-cli/config-edit' ? {root: __dirname, name: plugin} : {root: pluginRoot(plugin)})
fn(config)
})
}
Expand Down
6 changes: 3 additions & 3 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -355,9 +355,9 @@
version "2.4.0"
resolved "https://registry.yarnpkg.com/@types/normalize-package-data/-/normalize-package-data-2.4.0.tgz#e486d0d97396d79beedd0a6e33f4534ff6b4973e"

"@types/read-pkg-up@^3.0.1":
version "3.0.1"
resolved "https://registry.yarnpkg.com/@types/read-pkg-up/-/read-pkg-up-3.0.1.tgz#05911057c3be1aade38bfecbb9a8554dbd0582be"
"@types/read-pkg@^3.0.0":
version "3.0.0"
resolved "https://registry.yarnpkg.com/@types/read-pkg/-/read-pkg-3.0.0.tgz#17ab6f0b396a58a5567ee387f558f2caedc8ae53"
dependencies:
"@types/normalize-package-data" "*"

Expand Down

0 comments on commit 875eff9

Please sign in to comment.