From f2674d9289e569466e4a2ba4b4fa2744ddf32960 Mon Sep 17 00:00:00 2001 From: Rafa Mel Date: Mon, 6 May 2019 13:42:34 +0200 Subject: [PATCH] feat(core, utils): uses globals to manage core and options state; removes unused utils --- package-lock.json | 12 ++---- package.json | 2 - src/constants.ts | 2 - src/core/index.ts | 73 ++++++-------------------------- src/core/options.ts | 49 +++++++++++---------- src/utils/object-base.ts | 9 ---- src/utils/version-range.ts | 18 -------- test/utils/version-range.test.ts | 41 ------------------ 8 files changed, 42 insertions(+), 164 deletions(-) delete mode 100644 src/utils/object-base.ts delete mode 100644 src/utils/version-range.ts delete mode 100644 test/utils/version-range.test.ts diff --git a/package-lock.json b/package-lock.json index e2f6eb7..d0c864f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1242,9 +1242,9 @@ "dev": true }, "@types/fs-extra": { - "version": "5.0.5", - "resolved": "https://registry.npmjs.org/@types/fs-extra/-/fs-extra-5.0.5.tgz", - "integrity": "sha512-w7iqhDH9mN8eLClQOYTkhdYUOSpp25eXxfc6VbFOGtzxW34JcvctH2bKjj4jD4++z4R5iO5D+pg48W2e03I65A==", + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/@types/fs-extra/-/fs-extra-5.1.0.tgz", + "integrity": "sha512-AInn5+UBFIK9FK5xc9yP5e3TQSPNNgjHByqYcj9g5elVBnDQcQL7PlO1CIRy2gWlbwK7UPYqi7vRvFA44dCmYQ==", "dev": true, "requires": { "@types/node": "*" @@ -1356,12 +1356,6 @@ "resolved": "https://registry.npmjs.org/@types/normalize-package-data/-/normalize-package-data-2.4.0.tgz", "integrity": "sha512-f5j5b/Gf71L+dbqxIpQ4Z2WlmI/mPJ0fOkGGmFgtb6sAu97EPczzbS3/tJKxmcYDj55OX6ssqwDAWOHIYDRDGA==" }, - "@types/object-hash": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/@types/object-hash/-/object-hash-1.2.0.tgz", - "integrity": "sha512-0JKYQRatHdzijO/ni7JV5eHUJWaMRpGvwiABk8U5iAk5Corm0yLNEfYGNkZWYc+wCyCKKpg0+TsZIvP8AymIYA==", - "dev": true - }, "@types/pify": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/@types/pify/-/pify-3.0.2.tgz", diff --git a/package.json b/package.json index eef98ec..6d29d8b 100644 --- a/package.json +++ b/package.json @@ -72,7 +72,6 @@ "@types/jest": "^24.0.12", "@types/js-yaml": "^3.12.1", "@types/loglevel": "^1.5.4", - "@types/object-hash": "^1.2.0", "@types/pify": "^3.0.2", "@types/prompts": "^2.4.0", "@types/read-pkg-up": "^3.0.1", @@ -128,7 +127,6 @@ "js-yaml": "^3.13.1", "loglevel": "^1.6.1", "manage-path": "^2.0.0", - "object-hash": "^1.3.1", "pify": "^4.0.1", "promist": "^0.5.3", "prompts": "^2.0.4", diff --git a/src/constants.ts b/src/constants.ts index cf94251..808fb99 100644 --- a/src/constants.ts +++ b/src/constants.ts @@ -14,8 +14,6 @@ export const KPO_PATH = require.resolve('./bin/kpo'); export const CONCURRENTLY_PATH = require.resolve( 'concurrently/bin/concurrently' ); -// Environment variable name for core state storage -export const KPO_ENV_STATE = 'KPO_STATE'; /* Shared between instances: changes might imply a major version release */ export const LOG_ENV_KEY = 'kpo_log'; export const GLOBALS_KEY = 'kpo_globals'; diff --git a/src/core/index.ts b/src/core/index.ts index 1c8f5cf..116e4c7 100644 --- a/src/core/index.ts +++ b/src/core/index.ts @@ -7,28 +7,24 @@ import { setScope, getChildren } from './scope'; import { getTask, getAllTasks } from './tasks'; import { IPaths, ILoaded, ITask, ITasks, IChild } from './types'; import getBin from './bin'; -import exec from '~/utils/exec'; import run from './run'; import logger from '~/utils/logger'; +import exec from '~/utils/exec'; import { TCoreOptions, IExecOptions, TScript } from '~/types'; import { rejects } from 'errorish'; import { absolute } from '~/utils/file'; import wrapCore from './wrap'; -import { loadPackage } from 'cli-belt'; -import ob from '~/utils/object-base'; -import inVersionRange from '~/utils/version-range'; -import { KPO_ENV_STATE, DEFAULT_LOG_LEVEL } from '~/constants'; import guardian from '~/utils/guardian'; +import globals from '~/globals'; export interface ICoreState { - version: string | null; scopes: string[]; cwd: string; paths: IPaths; } -export const state: ICoreState = { - version: null, +/* Shared between instances: changes might imply a major version release */ +export const state: ICoreState = globals('core', { scopes: [], cwd: process.cwd(), paths: { @@ -36,60 +32,22 @@ export const state: ICoreState = { kpo: null, pkg: null } -}; +}).get(); let loaded: ILoaded = { kpo: null, pkg: null }; +const cache = (fn: () => T): (() => T) => _cache(() => options.id, fn); -function cache(fn: () => T): () => T { - return _cache(() => options.id, fn); -} - -// Core changes might constitute major version changes even if internal, -// as core state is overwritten for different kpo instances below const core = wrapCore( // These will run in order before any core function call [ async () => guardian(), - async function initialize(): Promise { - if (state.version) return; - - const pkg = await loadPackage(__dirname, { title: false }); - if (!pkg.version) throw Error(`kpo version couldn't be retrieved`); - state.version = pkg.version; - - const encoded = process.env[KPO_ENV_STATE]; - if (encoded) { - const decoded = ob.decode(encoded); - const inRange = inVersionRange( - decoded && decoded.core && decoded.core.version, - pkg.version - ); - if (!inRange) { - throw Error( - `Local kpo version (${pkg.version.trim()})` + - ` doesn't match executing version (${decoded.core.version.trim()})` - ); - } - - if (path.relative(state.cwd, decoded.core.cwd)) { - // if cwd has changed, that's an explicit signal for kpo to run - // in a new directory, hence we won't recover core state, - // and only recover log level from options state - options.setBase({ log: decoded.options.log || DEFAULT_LOG_LEVEL }); - } else { - // otherwise, we'll recover both core and options state - Object.assign(state, decoded.core); - options.setBase(decoded.options); - } - - process.chdir(state.paths.directory); - } - }, cache(async function(): Promise { + const raw = options.raw(); + state.paths = await getSelfPaths({ cwd: state.cwd, - directory: options.raw.directory || undefined, - file: options.raw.file || undefined + directory: raw.directory || undefined, + file: raw.file || undefined }); process.chdir(state.paths.directory); @@ -98,9 +56,9 @@ const core = wrapCore( loaded = await load(state.paths); // options cwd can only be set on scope options (on load()) - state.paths.directory = options.raw.cwd + state.paths.directory = raw.cwd ? absolute({ - path: options.raw.cwd, + path: raw.cwd, // we're setting it relative to the file cwd: state.paths.kpo ? path.parse(state.paths.kpo).dir @@ -108,17 +66,12 @@ const core = wrapCore( }) : state.paths.directory; process.chdir(state.paths.directory); - - process.env[KPO_ENV_STATE] = ob.encode({ - core: state, - options: options.raw - }); }) ], // Core functions { async get(key: T): Promise { - return options.raw[key]; + return options.raw()[key]; }, async scopes(): Promise { return state.scopes; diff --git a/src/core/options.ts b/src/core/options.ts index 42eb81c..8f8b1a8 100644 --- a/src/core/options.ts +++ b/src/core/options.ts @@ -1,54 +1,56 @@ import { ICliOptions, IScopeOptions, TCoreOptions, IOfType } from '~/types'; -import { DEFAULT_LOG_LEVEL } from '~/constants'; +import { DEFAULT_LOG_LEVEL, LOG_ENV_KEY } from '~/constants'; import { setLevel } from '~/utils/logger'; -import hash from 'object-hash'; +import globals from '~/globals'; +import cache from '~/utils/cache'; -// Option changes should constitute major version changes even if internal, -// as they're overwritten for different kpo instances on core load -export const state = { +/* Shared between instances: changes might imply a major version release */ +export const state = globals('options', { + id: 0, base: { file: null, directory: null, env: {}, silent: false, - log: DEFAULT_LOG_LEVEL + log: process.env[LOG_ENV_KEY] || DEFAULT_LOG_LEVEL } as TCoreOptions, cli: {} as ICliOptions, scope: {} as IScopeOptions -}; +}).get(); -let id = ''; -let force = 0; let options: TCoreOptions = {}; merge(); export default { - get id(): string { - return id; + get id(): number { + return state.id; }, // Raw should only be called from core (after initialization) - get raw(): TCoreOptions { - return Object.assign({}, options); - }, + raw: cache( + () => state.id, + (): TCoreOptions => { + merge(); + return Object.assign({}, options); + } + ), setBase(opts: TCoreOptions): void { Object.assign(state.base, opts); - merge(); + state.id += 1; }, setCli(opts: ICliOptions): void { Object.assign(state.cli, stripUndefined(opts)); - merge(); + state.id += 1; }, setScope(opts: IScopeOptions = {}): void { Object.assign(state.scope, opts); - merge(); + state.id += 1; }, resetScope(): void { state.scope = {}; - merge(); + state.id += 1; }, forceUpdate(): void { - force += 1; - merge(); + state.id += 1; } }; @@ -63,9 +65,10 @@ function merge(): void { options.directory = state.cli.directory || state.base.directory; // Set logging level - if (options.log) setLevel(options.log); - // Set id to object hash - id = hash(options) + force; + if (options.log) { + setLevel(options.log); + process.env[LOG_ENV_KEY] = options.log; + } } function stripUndefined(obj: IOfType): IOfType { diff --git a/src/utils/object-base.ts b/src/utils/object-base.ts deleted file mode 100644 index 4624af8..0000000 --- a/src/utils/object-base.ts +++ /dev/null @@ -1,9 +0,0 @@ -function encode(value: any): string { - return Buffer.from(JSON.stringify(value)).toString('base64'); -} - -function decode(value: string): any { - return JSON.parse(String(Buffer.from(value, 'base64'))); -} - -export default { encode, decode }; diff --git a/src/utils/version-range.ts b/src/utils/version-range.ts deleted file mode 100644 index 70fd74c..0000000 --- a/src/utils/version-range.ts +++ /dev/null @@ -1,18 +0,0 @@ -import { clean, diff } from 'semver'; - -export default function inVersionRange( - executable?: string, - local?: string -): boolean { - const versions = [executable && clean(executable), local && clean(local)]; - if (!versions[0] || !versions[1]) throw Error(`Version could not be parsed`); - - const release = diff(versions[0] as string, versions[1] as string); - - // `false` if difference is a major version or we're on v0.x.x - return ( - release !== 'major' && - release !== 'premajor' && - (!release || (versions[0] as string)[0] !== '0') - ); -} diff --git a/test/utils/version-range.test.ts b/test/utils/version-range.test.ts deleted file mode 100644 index aa84456..0000000 --- a/test/utils/version-range.test.ts +++ /dev/null @@ -1,41 +0,0 @@ -import inVersionRange from '~/utils/version-range'; - -describe(`all`, () => { - test(`errors out for undefined`, () => { - expect(() => inVersionRange('1.0.1')).toThrowErrorMatchingInlineSnapshot( - `"Version could not be parsed"` - ); - expect(() => inVersionRange(undefined, '1.0.1')).toThrowError(); - expect(() => inVersionRange()).toThrowError(); - }); -}); -describe(`stable`, () => { - test(`true for equal`, () => { - expect(inVersionRange('1.0.1', '1.0.1')).toBe(true); - }); - test(`true for patch`, () => { - expect(inVersionRange('1.0.1', '1.0.2')).toBe(true); - }); - test(`true for minor`, () => { - expect(inVersionRange('1.1.1', '1.2.1')).toBe(true); - expect(inVersionRange('1.2.9', '1.1.1')).toBe(true); - }); - test(`false for major`, () => { - expect(inVersionRange('1.1.1', '2.1.1')).toBe(false); - }); -}); -describe(`unstable`, () => { - test(`true for equal`, () => { - expect(inVersionRange('0.1.1', '0.1.1')).toBe(true); - }); - test(`false for patch`, () => { - expect(inVersionRange('0.1.1', '0.1.2')).toBe(false); - }); - test(`false for minor`, () => { - expect(inVersionRange('0.1.1', '0.2.1')).toBe(false); - expect(inVersionRange('0.2.9', '0.1.1')).toBe(false); - }); - test(`false for major`, () => { - expect(inVersionRange('0.1.1', '1.1.1')).toBe(false); - }); -});