Skip to content

Commit

Permalink
feat(core, utils): uses globals to manage core and options state; rem…
Browse files Browse the repository at this point in the history
…oves unused utils
  • Loading branch information
rafamel committed May 6, 2019
1 parent f84e99d commit f2674d9
Show file tree
Hide file tree
Showing 8 changed files with 42 additions and 164 deletions.
12 changes: 3 additions & 9 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 0 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down Expand Up @@ -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",
Expand Down
2 changes: 0 additions & 2 deletions src/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand Down
73 changes: 13 additions & 60 deletions src/core/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,89 +7,47 @@ 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: {
directory: process.cwd(),
kpo: null,
pkg: null
}
};
}).get();

let loaded: ILoaded = { kpo: null, pkg: null };
const cache = <T>(fn: () => T): (() => T) => _cache(() => options.id, fn);

function cache<T>(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<void> {
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<void> {
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);

Expand All @@ -98,27 +56,22 @@ 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
: state.paths.directory
})
: state.paths.directory;
process.chdir(state.paths.directory);

process.env[KPO_ENV_STATE] = ob.encode({
core: state,
options: options.raw
});
})
],
// Core functions
{
async get<T extends keyof TCoreOptions>(key: T): Promise<TCoreOptions[T]> {
return options.raw[key];
return options.raw()[key];
},
async scopes(): Promise<string[]> {
return state.scopes;
Expand Down
49 changes: 26 additions & 23 deletions src/core/options.ts
Original file line number Diff line number Diff line change
@@ -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;
}
};

Expand All @@ -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<any>): IOfType<any> {
Expand Down
9 changes: 0 additions & 9 deletions src/utils/object-base.ts

This file was deleted.

18 changes: 0 additions & 18 deletions src/utils/version-range.ts

This file was deleted.

41 changes: 0 additions & 41 deletions test/utils/version-range.test.ts

This file was deleted.

0 comments on commit f2674d9

Please sign in to comment.