diff --git a/package.json b/package.json index 93be3a67..7cca11ad 100644 --- a/package.json +++ b/package.json @@ -28,6 +28,7 @@ "klaw": "^2.1.1", "log-chopper": "^1.0.2", "moment": "^2.20.1", + "read-pkg": "^3.0.0", "rwlockfile": "^2.0.17", "semver": "5.4.1", "string-similarity": "^1.2.0", @@ -38,8 +39,8 @@ "devDependencies": { "@cli-engine/command": "^12.1.0", "@cli-engine/config": "^5.1.0", - "@cli-engine/util": "^1.0.13", - "@heroku-cli/command": "^7.0.12", + "@cli-engine/util": "^1.1.2", + "@heroku-cli/command": "^7.0.13", "@heroku-cli/tslint": "^1.1.2", "@types/ansi-styles": "^2.0.30", "@types/chalk": "^2.2.0", @@ -64,6 +65,7 @@ "tar-fs": "^1.16.0", "ts-jest": "^22.0.1", "ts-node": "4.1.0", + "tsconfig-paths": "^2.7.3", "tslint": "^5.8.0", "typescript": "2.6.2" }, diff --git a/src/deps.ts b/src/deps.ts index f65d4650..138e5be4 100644 --- a/src/deps.ts +++ b/src/deps.ts @@ -5,6 +5,7 @@ import { HTTP } from 'http-call' import * as klaw from 'klaw' import * as moment from 'moment' import semver = require('semver') +// import readPkg = require('read-pkg') import command = require('./command') import help = require('./commands/help') @@ -29,6 +30,7 @@ export default { get assync(): typeof assync.default { return fetch('assync').default }, get filesize(): any { return fetch('filesize') }, get globby(): typeof globby { return fetch('globby') }, + get readPkg(): any { return fetch('read-pkg') }, // local get Help(): typeof help.default { return fetch('./commands/help').default }, diff --git a/src/plugins/builtin.ts b/src/plugins/builtin.ts index 0deeadc0..088f0aea 100644 --- a/src/plugins/builtin.ts +++ b/src/plugins/builtin.ts @@ -7,12 +7,7 @@ import { Plugin } from './plugin' export class Builtin extends Plugin { constructor(config: Config) { const root = path.join(__dirname, '../..') - const pjson = require(path.join(root, 'package.json')) - super({ config, type: 'builtin', root, pjson }) - } - - public get commandsDir() { - return path.join(__dirname, '..', 'commands') + super({ config, type: 'builtin', root }) } protected async commandIDsFromDir(): Promise { diff --git a/src/plugins/link.ts b/src/plugins/link.ts index f67f4ffa..97b35622 100644 --- a/src/plugins/link.ts +++ b/src/plugins/link.ts @@ -8,7 +8,7 @@ import Config from '../config' import deps from '../deps' import { PluginManifest } from './manifest' -import { IPluginOptions, IPluginPJSON, Plugin, PluginType } from './plugin' +import { IPluginOptions, Plugin, PluginType } from './plugin' function touch(f: string) { fs.utimesSync(f, new Date(), new Date()) @@ -18,10 +18,6 @@ function pjsonPath(root: string) { return path.join(root, 'package.json') } -function linkPJSON(root: string): Promise { - return deps.file.readJSON(pjsonPath(root)) -} - export interface IManifestPlugin { name: string root: string @@ -186,10 +182,11 @@ export class LinkPlugins { private async loadPlugin(root: string, refresh = false) { if (!await deps.file.exists(root)) return + const pkg = await deps.readPkg(root) let p = new LinkPlugin({ config: this.config, root, - pjson: await linkPJSON(root), + pjson: pkg.pkg, type: 'link', }) await p.refresh(refresh) diff --git a/src/plugins/main.ts b/src/plugins/main.ts index 574a1dc4..d3b1e8ac 100644 --- a/src/plugins/main.ts +++ b/src/plugins/main.ts @@ -1,63 +1,10 @@ -import * as path from 'path' - -import { ILoadResult } from '../command' import Config from '../config' -import deps from '../deps' import { Plugin } from './plugin' -interface TSConfig { - compilerOptions: { - rootDir?: string - outDir?: string - } -} - export class MainPlugin extends Plugin { - protected skipCache: boolean - private tsconfig?: TSConfig - private _commandsDir: string - constructor(config: Config) { const root = config.root! - const pjson = require(`${root}/package.json`) - super({ config, type: 'main', root, pjson }) - } - - public async load(): Promise { - if (this.result) return this.result - this.tsconfig = await this.fetchTSConfig() - this._commandsDir = this.config.commandsDir! - if (this.tsconfig) { - this.debug('tsconfig.json found, skipping cache for main commands') - this.skipCache = true - let { rootDir, outDir } = this.tsconfig.compilerOptions - if (rootDir && outDir) { - try { - this.debug('using ts files for main commands') - require('ts-node/register') - const lib = path.join(this.config.root!, outDir) - const src = path.join(this.config.root!, rootDir) - const relative = path.relative(lib, this.config.commandsDir!) - this._commandsDir = path.join(src, relative) - } catch (err) { - this.debug(err) - } - } - } - return super.load() - } - - protected get commandsDir() { - return this._commandsDir - } - - private async fetchTSConfig(): Promise { - try { - const tsconfig = await deps.file.readJSON(path.join(this.root, 'tsconfig.json')) - return tsconfig.compilerOptions && tsconfig - } catch (err) { - if (err.code !== 'ENOENT') throw err - } + super({ config, type: 'main', root }) } } diff --git a/src/plugins/plugin.ts b/src/plugins/plugin.ts index 669d8312..824764b1 100644 --- a/src/plugins/plugin.ts +++ b/src/plugins/plugin.ts @@ -39,10 +39,17 @@ export interface IPluginModule { export interface IPluginOptions { config: Config root: string - pjson: IPluginPJSON + pjson?: IPluginPJSON type: PluginType } +interface TSConfig { + compilerOptions: { + rootDir?: string + outDir?: string + } +} + export abstract class Plugin implements ICommandManager { public type: PluginType public name: string @@ -50,18 +57,19 @@ export abstract class Plugin implements ICommandManager { public tag?: string public pjson: IPluginPJSON public root: string + public commandsDir?: string protected config: Config protected debug: any protected lock: RWLockfile protected result: ILoadResult - protected skipCache?: boolean private _module: Promise private cache: PluginManifest + private tsconfig?: TSConfig constructor(opts: IPluginOptions) { this.config = opts.config this.root = opts.root - this.pjson = opts.pjson + this.pjson = opts.pjson || deps.readPkg.sync(this.root) if (!this.pjson['cli-engine']) this.pjson['cli-engine'] = {} this.name = this.name || this.pjson.name this.version = this.version || this.pjson.version @@ -76,6 +84,8 @@ export abstract class Plugin implements ICommandManager { @rwlockfile('lock', 'read') public async load(): Promise { if (this.result) return this.result + this.tsconfig = await this.fetchTSConfig() + this.commandsDir = this.fetchCommandsDir() this.result = { commands: await this.commands(), topics: await this.topics(), @@ -114,7 +124,7 @@ export abstract class Plugin implements ICommandManager { } protected async commands(): Promise { - const cache: ICommandInfo[] = await this.cacheFetch('commands', async () => { + const cache: ICommandInfo[] = await this.cache.fetch('commands', async () => { this.debug('fetching commands') const commands = await deps .assync([this.commandsFromModule(), this.commandsFromDir()]) @@ -148,7 +158,7 @@ export abstract class Plugin implements ICommandManager { } protected async topics(): Promise { - const cache: ITopic[] = await this.cacheFetch('topics', async () => { + const cache: ITopic[] = await this.cache.fetch('topics', async () => { this.debug('fetching topics') const m = await this.fetchModule() if (!m) return [] @@ -159,9 +169,27 @@ export abstract class Plugin implements ICommandManager { return cache } - protected get commandsDir(): string | undefined { - let d = this.pjson['cli-engine'].commands - if (d) return path.join(this.root, d) + protected fetchCommandsDir(): string | undefined { + let commandsDir = this.pjson['cli-engine'].commands + if (!commandsDir) return + commandsDir = path.join(this.root, commandsDir) + if (this.tsconfig) { + this.debug('tsconfig.json found, skipping cache for main commands') + let { rootDir, outDir } = this.tsconfig.compilerOptions + if (rootDir && outDir) { + try { + this.debug('using ts files') + require('ts-node').register() + const lib = path.join(this.root, outDir) + const src = path.join(this.root, rootDir) + const relative = path.relative(lib, commandsDir) + commandsDir = path.join(src, relative) + } catch (err) { + this.debug(err) + } + } + } + return commandsDir } protected async commandIDsFromDir(): Promise { @@ -256,8 +284,13 @@ export abstract class Plugin implements ICommandManager { })()) } - private cacheFetch(key: string, fn: () => Promise) { - return this.skipCache ? fn() : this.cache.fetch(key, fn) + private async fetchTSConfig(): Promise { + try { + const tsconfig = await deps.file.readJSON(path.join(this.root, 'tsconfig.json')) + return tsconfig.compilerOptions && tsconfig + } catch (err) { + if (err.code !== 'ENOENT') throw err + } } private convertConfig(config: Config): Partial { diff --git a/tsconfig.json b/tsconfig.json index 089d310d..71c434d3 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -8,7 +8,7 @@ "noUnusedLocals": true, "noUnusedParameters": true, "outDir": "./lib", - "rootDir": "./src", + "rootDirs": ["./src"], "strict": true, "target": "es2016" }, diff --git a/yarn.lock b/yarn.lock index 8aa8b05b..754c17e9 100644 --- a/yarn.lock +++ b/yarn.lock @@ -10,7 +10,7 @@ esutils "^2.0.2" js-tokens "^3.0.0" -"@cli-engine/command@^12.0.3", "@cli-engine/command@^12.1.0": +"@cli-engine/command@^12.1.0": version "12.1.0" resolved "https://registry.yarnpkg.com/@cli-engine/command/-/command-12.1.0.tgz#86300ad3c7f51b1c539f9c7b998204f52016e0a7" dependencies: @@ -18,23 +18,24 @@ chalk "^2.3.0" cli-flags "^2.0.7" -"@cli-engine/config@^5.0.11", "@cli-engine/config@^5.1.0": +"@cli-engine/config@^5.1.0": version "5.1.0" resolved "https://registry.yarnpkg.com/@cli-engine/config/-/config-5.1.0.tgz#d8ac7122317bc505dc09e7fd778777602d6b7568" -"@cli-engine/engine@^6.1.25": - version "6.1.29" - resolved "https://registry.yarnpkg.com/@cli-engine/engine/-/engine-6.1.29.tgz#7b40b030db138f14ee05659e14cfc618777dab51" +"@cli-engine/engine@^6.1.30": + version "6.1.30" + resolved "https://registry.yarnpkg.com/@cli-engine/engine/-/engine-6.1.30.tgz#0a3f15bb6a68d3fe191dd42dba1ec8c4c86a6ecd" dependencies: - "@heroku-cli/color" "^1.0.4" + "@heroku-cli/color" "^1.1.1" assync "^1.0.4" cli-ux "^2.0.21" cross-spawn "^5.1.0" debug "^3.1.0" filesize "^3.5.11" fs-extra "^5.0.0" + globby "^7.1.1" graceful-fs "^4.1.11" - http-call "^4.0.6" + http-call "^4.0.8" klaw "^2.1.1" log-chopper "^1.0.2" moment "^2.20.1" @@ -42,7 +43,6 @@ semver "5.4.1" string-similarity "^1.2.0" strip-ansi "4.0.0" - tar-fs "^1.16.0" ts-lodash "4.0.9" yarn "^1.3.2" @@ -50,17 +50,23 @@ version "0.0.0" resolved "https://registry.yarnpkg.com/@cli-engine/screen/-/screen-0.0.0.tgz#c2e847c7d4d998490c9097282bf21637d5162116" -"@cli-engine/util@^1.0.13": - version "1.0.13" - resolved "https://registry.yarnpkg.com/@cli-engine/util/-/util-1.0.13.tgz#bc9fec8c9df59110133d0d0ef7e26075466c2de5" +"@cli-engine/util@^1.1.2": + version "1.1.2" + resolved "https://registry.yarnpkg.com/@cli-engine/util/-/util-1.1.2.tgz#c76be91343ec5bb9dde7b4add9522b8bde7fdd29" dependencies: - "@cli-engine/command" "^12.0.3" - "@cli-engine/config" "^5.0.11" - "@cli-engine/engine" "^6.1.25" + "@cli-engine/command" "^12.1.0" + "@cli-engine/config" "^5.1.0" + "@cli-engine/engine" "^6.1.30" + "@heroku-cli/color" "^1.1.1" cli-ux "^2.0.21" cross-spawn "^5.1.0" del "^3.0.0" + execa "^0.8.0" fs-extra "^5.0.0" + handlebars "^4.0.11" + jest-diff "^22.0.3" + read-pkg-up "^3.0.0" + ts-lodash "^4.0.9" tslib "^1.8.1" "@heroku-cli/color@^1.0.4", "@heroku-cli/color@^1.1.1": @@ -72,16 +78,16 @@ strip-ansi "^4.0.0" supports-color "^5.1.0" -"@heroku-cli/command@^7.0.12": - version "7.0.12" - resolved "https://registry.yarnpkg.com/@heroku-cli/command/-/command-7.0.12.tgz#a1fea2c1dcd715ce071a98c92da1bcc5d535b2dc" +"@heroku-cli/command@^7.0.13": + version "7.0.13" + resolved "https://registry.yarnpkg.com/@heroku-cli/command/-/command-7.0.13.tgz#d2eafe24e4a0e1857f8ac81e84791e5ca392b6b9" dependencies: - "@cli-engine/command" "^12.0.3" - "@cli-engine/config" "^5.0.11" + "@cli-engine/command" "^12.1.0" + "@cli-engine/config" "^5.1.0" cli-ux "^2.0.21" heroku-client "3.0.6" - http-call "^4.0.6" - netrc-parser "^2.0.6" + http-call "^4.0.8" + netrc-parser "^3.0.0" "@heroku-cli/tslint@^1.1.2": version "1.1.2" @@ -902,6 +908,10 @@ deep-is@~0.1.3: version "0.1.3" resolved "https://registry.yarnpkg.com/deep-is/-/deep-is-0.1.3.tgz#b369d6fb5dbc13eecf524f91b070feedc357cf34" +deepmerge@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/deepmerge/-/deepmerge-2.0.1.tgz#25c1c24f110fb914f80001b925264dd77f3f4312" + default-require-extensions@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/default-require-extensions/-/default-require-extensions-1.0.0.tgz#f37ea15d3e13ffd9b437d33e1a75b5fb97874cb8" @@ -979,7 +989,7 @@ end-of-stream@^1.0.0, end-of-stream@^1.1.0: dependencies: once "^1.4.0" -error-ex@^1.2.0: +error-ex@^1.2.0, error-ex@^1.3.1: version "1.3.1" resolved "https://registry.yarnpkg.com/error-ex/-/error-ex-1.3.1.tgz#f855a86ce61adc4e8621c3cda21e7a7612c3a8dc" dependencies: @@ -1163,7 +1173,7 @@ find-up@^1.0.0: path-exists "^2.0.0" pinkie-promise "^2.0.0" -find-up@^2.1.0: +find-up@^2.0.0, find-up@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/find-up/-/find-up-2.1.0.tgz#45d1b7e506c717ddd482775a2b77920a3c0c57a7" dependencies: @@ -1341,7 +1351,7 @@ growly@^1.3.0: version "1.3.0" resolved "https://registry.yarnpkg.com/growly/-/growly-1.3.0.tgz#f10748cbe76af964b7c96c93c6bcc28af120c081" -handlebars@^4.0.3: +handlebars@^4.0.11, handlebars@^4.0.3: version "4.0.11" resolved "https://registry.yarnpkg.com/handlebars/-/handlebars-4.0.11.tgz#630a35dfe0294bc281edae6ffc5d329fc7982dcc" dependencies: @@ -1453,7 +1463,7 @@ html-encoding-sniffer@^1.0.1: dependencies: whatwg-encoding "^1.0.1" -http-call@^4.0.6, http-call@^4.0.8: +http-call@^4.0.8: version "4.0.8" resolved "https://registry.yarnpkg.com/http-call/-/http-call-4.0.8.tgz#fd0207764958a3f1238bb3344ff912b32ddfa64a" dependencies: @@ -2042,6 +2052,10 @@ jsesc@^1.3.0: version "1.3.0" resolved "https://registry.yarnpkg.com/jsesc/-/jsesc-1.3.0.tgz#46c3fec8c1892b12b0833db9bc7622176dbab34b" +json-parse-better-errors@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/json-parse-better-errors/-/json-parse-better-errors-1.0.1.tgz#50183cd1b2d25275de069e9e71b467ac9eab973a" + json-schema-traverse@^0.3.0: version "0.3.1" resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-0.3.1.tgz#349a6d44c53a51de89b40805c5d5e59b417d3340" @@ -2140,6 +2154,15 @@ load-json-file@^1.0.0: pinkie-promise "^2.0.0" strip-bom "^2.0.0" +load-json-file@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/load-json-file/-/load-json-file-4.0.0.tgz#2f5f45ab91e33216234fd53adab668eb4ec0993b" + dependencies: + graceful-fs "^4.1.2" + parse-json "^4.0.0" + pify "^3.0.0" + strip-bom "^3.0.0" + locate-path@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-2.0.0.tgz#2b568b265eec944c6d9c0de9c3dbbbca0354cd8e" @@ -2276,10 +2299,11 @@ natural-compare@^1.4.0: version "1.4.0" resolved "https://registry.yarnpkg.com/natural-compare/-/natural-compare-1.4.0.tgz#4abebfeed7541f2c27acfb29bdbbd15c8d5ba4f7" -netrc-parser@^2.0.6: - version "2.0.6" - resolved "https://registry.yarnpkg.com/netrc-parser/-/netrc-parser-2.0.6.tgz#0a21b96810dbb2c3ac42fac0a260bf838d20ef2f" +netrc-parser@^3.0.0: + version "3.0.1" + resolved "https://registry.yarnpkg.com/netrc-parser/-/netrc-parser-3.0.1.tgz#c5671c21046f90185a4976e8b378441f24a5b4d7" dependencies: + graceful-fs "^4.1.11" lex "^1.7.9" nock@^9.1.5: @@ -2486,6 +2510,13 @@ parse-json@^2.2.0: dependencies: error-ex "^1.2.0" +parse-json@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/parse-json/-/parse-json-4.0.0.tgz#be35f5425be1f7f6c747184f98a788cb99477ee0" + dependencies: + error-ex "^1.3.1" + json-parse-better-errors "^1.0.1" + parse-passwd@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/parse-passwd/-/parse-passwd-1.0.0.tgz#6d5b934a456993b23d37f40a382d6f1666a8e5c6" @@ -2660,6 +2691,13 @@ read-pkg-up@^1.0.1: find-up "^1.0.0" read-pkg "^1.0.0" +read-pkg-up@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/read-pkg-up/-/read-pkg-up-3.0.0.tgz#3ed496685dba0f8fe118d0691dc51f4a1ff96f07" + dependencies: + find-up "^2.0.0" + read-pkg "^3.0.0" + read-pkg@^1.0.0: version "1.1.0" resolved "https://registry.yarnpkg.com/read-pkg/-/read-pkg-1.1.0.tgz#f5ffaa5ecd29cb31c0474bca7d756b6bb29e3f28" @@ -2668,6 +2706,14 @@ read-pkg@^1.0.0: normalize-package-data "^2.3.2" path-type "^1.0.0" +read-pkg@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/read-pkg/-/read-pkg-3.0.0.tgz#9cbc686978fee65d16c00e2b19c237fcf6e38389" + dependencies: + load-json-file "^4.0.0" + normalize-package-data "^2.3.2" + path-type "^3.0.0" + readable-stream@^2.0.0, readable-stream@^2.0.1, readable-stream@^2.0.2, readable-stream@^2.0.5, readable-stream@^2.0.6, readable-stream@^2.1.4: version "2.3.3" resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.3.3.tgz#368f2512d79f9d46fdfc71349ae7878bbc1eb95c" @@ -3047,7 +3093,7 @@ strip-indent@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/strip-indent/-/strip-indent-2.0.0.tgz#5ef8db295d01e6ed6cbf7aab96998d7822527b68" -strip-json-comments@^2.0.0, strip-json-comments@~2.0.1: +strip-json-comments@^2.0.0, strip-json-comments@^2.0.1, strip-json-comments@~2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-2.0.1.tgz#3c531942e908c2697c0ec344858c286c7ca0a60a" @@ -3175,7 +3221,7 @@ ts-jest@^22.0.1: source-map-support "^0.5.0" yargs "^10.0.3" -ts-lodash@4.0.9, ts-lodash@^4.0.8: +ts-lodash@4.0.9, ts-lodash@^4.0.8, ts-lodash@^4.0.9: version "4.0.9" resolved "https://registry.yarnpkg.com/ts-lodash/-/ts-lodash-4.0.9.tgz#088d7ceefbf36c8c4bcdf28372249015ccafd429" dependencies: @@ -3196,6 +3242,14 @@ ts-node@4.1.0: v8flags "^3.0.0" yn "^2.0.0" +tsconfig-paths@^2.7.3: + version "2.7.3" + resolved "https://registry.yarnpkg.com/tsconfig-paths/-/tsconfig-paths-2.7.3.tgz#ef36ca628591fd49f37ee6b1a816ec4a3230c1d3" + dependencies: + deepmerge "^2.0.1" + strip-bom "^3.0.0" + strip-json-comments "^2.0.1" + tsconfig@^7.0.0: version "7.0.0" resolved "https://registry.yarnpkg.com/tsconfig/-/tsconfig-7.0.0.tgz#84538875a4dc216e5c4a5432b3a4dec3d54e91b7"