From ee809f5bd0f019bec4189a592d561b65365f4b74 Mon Sep 17 00:00:00 2001 From: Rafa Mel Date: Wed, 24 Apr 2019 10:27:26 +0200 Subject: [PATCH] refactor(state): refactors state --- src/bin/main/index.ts | 7 +- src/state/index.ts | 16 ++-- src/state/load/index.ts | 9 +- src/state/paths/index.ts | 82 ++++--------------- src/state/paths/retrieve/bin.ts | 7 ++ .../paths/{get-file.ts => retrieve/files.ts} | 15 ++-- src/state/paths/retrieve/index.ts | 17 ++++ src/state/paths/retrieve/paths.ts | 26 ++++++ src/state/scope.ts | 14 ++-- src/state/types.ts | 28 +++++++ 10 files changed, 128 insertions(+), 93 deletions(-) create mode 100644 src/state/paths/retrieve/bin.ts rename src/state/paths/{get-file.ts => retrieve/files.ts} (81%) create mode 100644 src/state/paths/retrieve/index.ts create mode 100644 src/state/paths/retrieve/paths.ts create mode 100644 src/state/types.ts diff --git a/src/bin/main/index.ts b/src/bin/main/index.ts index 4f28a3b..bcc39b7 100644 --- a/src/bin/main/index.ts +++ b/src/bin/main/index.ts @@ -83,6 +83,7 @@ export default async function main(argv: string[]): Promise { }); let first = cmd._.shift(); + let isScoped = false; while (!first || first[0] === '@') { if (!first) { console.log(help + '\n'); @@ -97,8 +98,12 @@ export default async function main(argv: string[]): Promise { first = command.length ? `:${command.join(':')}` : (cmd._.shift() as string); + + isScoped = true; + } + if (isScoped) { + logger.info('Scope: ' + chalk.bold('@' + state.get('scopes').join(' @'))); } - logger.info('Scope: ' + chalk.bold('@' + state.get('scopes').join(' @'))); // TODO if (first[0] === ':') { diff --git a/src/state/index.ts b/src/state/index.ts index f823487..a251a46 100644 --- a/src/state/index.ts +++ b/src/state/index.ts @@ -1,11 +1,12 @@ -import paths, { IPaths } from './paths'; +import paths from './paths'; import mergewith from 'lodash.mergewith'; import { IBaseOptions, IScopeOptions } from '~/types'; import { DEFAULT_LOG_LEVEL } from '~/constants'; import { setLevel } from '~/utils/logger'; import { lazy } from 'promist'; -import load, { ILoaded } from './load'; +import load from './load'; import scope from './scope'; +import { IPaths, ILoaded } from './types'; export const states = { base: { @@ -19,7 +20,7 @@ export const states = { options: {} as IScopeOptions, internal: { - scopes: ['self'] as string[] + scopes: [] as string[] } }; @@ -41,9 +42,12 @@ export default { merge(); }, async setScope(name: string): Promise { - const scopeName = await scope(name); - if (scopeName) states.internal.scopes.push(scopeName); - this.setOptions(); + const definition = await scope(name); + if (definition) { + states.internal.scopes.push(definition.name); + this.setBase({ file: null, directory: definition.directory }); + this.setOptions(); + } }, get(key: keyof TState): any { return state[key]; diff --git a/src/state/load/index.ts b/src/state/load/index.ts index 0570419..34303b1 100644 --- a/src/state/load/index.ts +++ b/src/state/load/index.ts @@ -1,15 +1,10 @@ import fs from 'fs-extra'; -import { IScripts, IOfType } from '~/types'; +import { IOfType } from '~/types'; import { rejects } from 'errorish'; -import { IBasePaths } from '~/state/paths'; +import { IBasePaths, ILoaded } from '../types'; import hash from 'object-hash'; import readFile from './read-file'; -export interface ILoaded { - kpo: IScripts | null; - pkg: IOfType | null; -} - const cache: IOfType = {}; export default async function load(paths: IBasePaths): Promise { const key = hash(paths); diff --git a/src/state/paths/index.ts b/src/state/paths/index.ts index d6e23e4..da927c0 100644 --- a/src/state/paths/index.ts +++ b/src/state/paths/index.ts @@ -1,87 +1,39 @@ import path from 'path'; -import getFile from './get-file'; -import logger from '~/utils/logger'; import { wrap } from '~/utils/errors'; import load from '../load'; import state from '../index'; -import up from 'find-up'; +import { IPathsOpts, IPaths } from '../types'; +import { getSelfPaths, getRootPaths } from './retrieve'; -export interface IPathsOpts { - file?: string; - directory?: string; -} - -export interface IBasePaths { - kpo: string | null; - pkg: string | null; - bin: string[]; - directory: string; -} - -export interface IPaths extends IBasePaths { - root: IBasePaths | null; - children: IBasePaths[]; -} - -/** - * - if no `file` or `directory` are passed, it will **recurse up** from `cwd` to find both the `package.json` and `kpo.scripts` files. If the `package.json` is found closer to `cwd` or no `kpo.scripts` is found, then its `kpo.path` key will be used -if found- to locate the `kpo.scripts` file. - * - if only `file` is passed, it will get the `kpo.scripts` on that location, and **recurse up** to find a `package.json`. - * - if a `file` and a `directory` are passed, it will get the `kpo.scripts` on `file` location, and only use a `package.json` if it's found in `directory`. - * - if only a `directory` is passed, it will try to find both the `kpo.script` and `package.json` files on `directory`; if no `kpo.scripts` is found exactly on directory, it will fall back to the `kpo.path` in `package.json` -if found. - */ export default async function paths(opts: IPathsOpts): Promise { // It will be strict if directory exists (it's passed on cli), // otherwise it will recurse up w/ strict = false. - const base = await trunk(opts, Boolean(opts.directory)); - await load(base); + const self = await getSelfPaths(opts); + await load(self); // has to to called after load to wait for scope options to modify state - const root = await trunk( - { directory: state.get('root') || path.join(base.directory, '../') }, - false + const rootDir = state.get('root'); + const root = await getRootPaths( + rootDir || path.join(self.directory, '../') ).catch(async (err) => { - return state.get('root') - ? wrap.rejects(err, { - message: `root scope couldn't be retrieved: ${state.get('root')}` - }) - : null; + // don't fail if root directory wasn't explicitly passed via options, + // just set as null + if (!rootDir) return null; + + return wrap.rejects(err, { + message: `root scope couldn't be retrieved: ${state.get('root')}` + }); }); return { - ...base, + ...self, // add also root bin path - bin: [base.bin[0]] + bin: [self.bin[0]] .concat(root ? root.bin[0] : []) - .concat(base.bin.slice(1)) + .concat(self.bin.slice(1)) .concat(root ? root.bin.slice(1) : []) .filter((x, i, arr) => x && arr.indexOf(x) === i), root, children: [] }; } - -export async function trunk( - opts: IPathsOpts, - strict: boolean -): Promise { - const { kpo, pkg } = await getFile(opts, strict); - let dir = path.parse(pkg || kpo || process.cwd()).dir; - - if (kpo) logger.debug('kpo configuration file found at: ' + kpo); - if (pkg) logger.debug('package.json found at: ' + pkg); - if (!kpo && !pkg) { - throw Error(`No file or package.json was found in directory`); - } - - return { - kpo: kpo, - pkg: pkg, - directory: dir, - bin: await getBin(dir) - }; -} - -export async function getBin(dir: string): Promise { - const bin = await up('node_modules/.bin', { cwd: dir }); - return bin ? [bin].concat(await getBin(path.join(dir, '../'))) : []; -} diff --git a/src/state/paths/retrieve/bin.ts b/src/state/paths/retrieve/bin.ts new file mode 100644 index 0000000..604b997 --- /dev/null +++ b/src/state/paths/retrieve/bin.ts @@ -0,0 +1,7 @@ +import path from 'path'; +import up from 'find-up'; + +export default async function getBin(dir: string): Promise { + const bin = await up('node_modules/.bin', { cwd: dir }); + return bin ? [bin].concat(await getBin(path.join(dir, '../'))) : []; +} diff --git a/src/state/paths/get-file.ts b/src/state/paths/retrieve/files.ts similarity index 81% rename from src/state/paths/get-file.ts rename to src/state/paths/retrieve/files.ts index a090466..4e916ac 100644 --- a/src/state/paths/get-file.ts +++ b/src/state/paths/retrieve/files.ts @@ -4,18 +4,23 @@ import { rejects } from 'errorish'; import { FILE_NAME } from '~/constants'; import { find, exists } from '~/utils/file'; -export interface IGetFile { +export interface IGetFiles { kpo: string | null; pkg: string | null; } -export default async function getFile( +/** + * - if `strict` is `false`, it will look recursively in `directory` -if passed, otherwise `cwd`- for both `package.json` and `kpo.scripts` files. + * - if `strict` is `true`, files will be expected to be in exactly `directory`. + * - if `file` is provided, that will determine the path for the `kpo.scripts` file, and will make the `directory` to look for the `package.json` its directory, unless otherwise provided. + */ +export default async function getFiles( opts: { file?: string; directory?: string; }, strict: boolean -): Promise { +): Promise { const cwd = process.cwd(); const directory = @@ -39,7 +44,7 @@ export async function getExplicit( file: string, directory: string | undefined, strict: boolean -): Promise { +): Promise { const { ext } = path.parse(file); const validExt = ['.js', '.json', '.yml', '.yaml'].includes(ext); if (!validExt) return Promise.reject(Error(`Extension ${ext} is not valid`)); @@ -57,7 +62,7 @@ export async function getExplicit( export async function getDefault( directory: string, strict: boolean -): Promise { +): Promise { let dir = path.join(path.parse(directory).dir, path.parse(directory).base); let kpo: string | null | undefined = await find( diff --git a/src/state/paths/retrieve/index.ts b/src/state/paths/retrieve/index.ts new file mode 100644 index 0000000..5d37314 --- /dev/null +++ b/src/state/paths/retrieve/index.ts @@ -0,0 +1,17 @@ +import { IPathsOpts, IBasePaths } from '../../types'; +import getPaths from './paths'; + +/** + * - `file` determines the path for the `kpo.scripts` file; if not passed, it will be resolved from `directory`. + * - `directory` determines the path to look for the `package.json` and `kpo.scripts` files; if passed, files will be expected to be exactly in that directory, otherwise `directory` will be `cwd` and the search will **recurse up** until the root folder is reached. If a `package.json` is found closer to `directory` than any `kpo.scripts` containing a `kpo.path` key, that path for a `kpo.scripts` file will take precedence. + */ +export async function getSelfPaths(opts: IPathsOpts): Promise { + return getPaths(opts, Boolean(opts.directory)); +} + +/** + * Will always recurse up + */ +export async function getRootPaths(directory: string): Promise { + return getPaths({ directory }, false); +} diff --git a/src/state/paths/retrieve/paths.ts b/src/state/paths/retrieve/paths.ts new file mode 100644 index 0000000..30196d9 --- /dev/null +++ b/src/state/paths/retrieve/paths.ts @@ -0,0 +1,26 @@ +import path from 'path'; +import getFiles from './files'; +import logger from '~/utils/logger'; +import getBin from './bin'; +import { IPathsOpts, IBasePaths } from '../../types'; + +export default async function getPaths( + opts: IPathsOpts, + strict: boolean +): Promise { + const { kpo, pkg } = await getFiles(opts, strict); + + if (kpo) logger.debug('kpo configuration file found at: ' + kpo); + if (pkg) logger.debug('package.json found at: ' + pkg); + if (!kpo && !pkg) { + throw Error(`No file or package.json was found in directory`); + } + + const dir = path.parse((pkg || kpo) as string).dir; + return { + kpo: kpo, + pkg: pkg, + directory: dir, + bin: await getBin(dir) + }; +} diff --git a/src/state/scope.ts b/src/state/scope.ts index 2505638..9111a79 100644 --- a/src/state/scope.ts +++ b/src/state/scope.ts @@ -1,13 +1,14 @@ import state from './index'; import logger from '~/utils/logger'; +import { IScopeDefinition } from './types'; -export default async function setScope(scope: string): Promise { - if (scope === 'self') return null; - +export default async function scope( + scope: string +): Promise { const paths = await state.paths(); if (scope === 'root') { if (paths.root) { - return set('root', paths.root.directory); + return { name: 'root', directory: paths.root.directory }; } else { logger.debug('root scope was not found and was assigned to self'); return null; @@ -16,8 +17,3 @@ export default async function setScope(scope: string): Promise { throw Error(`Scope ${scope} was not found`); } - -export function set(name: string, path: string): string { - state.setBase({ file: null, directory: path }); - return name; -} diff --git a/src/state/types.ts b/src/state/types.ts new file mode 100644 index 0000000..542386e --- /dev/null +++ b/src/state/types.ts @@ -0,0 +1,28 @@ +import { IScripts, IOfType } from '~/types'; + +export interface IPathsOpts { + file?: string; + directory?: string; +} + +export interface IBasePaths { + kpo: string | null; + pkg: string | null; + bin: string[]; + directory: string; +} + +export interface IPaths extends IBasePaths { + root: IBasePaths | null; + children: IBasePaths[]; +} + +export interface ILoaded { + kpo: IScripts | null; + pkg: IOfType | null; +} + +export interface IScopeDefinition { + name: string; + directory: string; +}