Skip to content

Commit

Permalink
fix(typescript): permit catching errors by returning a value in error…
Browse files Browse the repository at this point in the history
… hook (#116)
  • Loading branch information
gr2m committed Oct 4, 2022
1 parent 12cab66 commit 445b99d
Show file tree
Hide file tree
Showing 4 changed files with 96 additions and 89 deletions.
96 changes: 48 additions & 48 deletions index.d.ts
Original file line number Diff line number Diff line change
@@ -1,34 +1,34 @@
type HookMethod<Options, Result> = (
options: Options
) => Result | Promise<Result>
) => Result | Promise<Result>;

type BeforeHook<Options> = (options: Options) => void | Promise<void>
type BeforeHook<Options> = (options: Options) => void | Promise<void>;
type ErrorHook<Options, Error> = (
error: Error,
options: Options
) => void | Promise<void>
) => unknown | Promise<unknown>;
type AfterHook<Options, Result> = (
result: Result,
options: Options
) => void | Promise<void>
) => void | Promise<void>;
type WrapHook<Options, Result> = (
hookMethod: HookMethod<Options, Result>,
options: Options
) => Result | Promise<Result>
) => Result | Promise<Result>;

type AnyHook<Options, Result, Error> =
| BeforeHook<Options>
| ErrorHook<Options, Error>
| AfterHook<Options, Result>
| WrapHook<Options, Result>
| WrapHook<Options, Result>;

type TypeStoreKeyLong = 'Options' | 'Result' | 'Error'
type TypeStoreKeyShort = 'O' | 'R' | 'E'
type TypeStoreKeyLong = "Options" | "Result" | "Error";
type TypeStoreKeyShort = "O" | "R" | "E";
type TypeStore =
| ({ [key in TypeStoreKeyLong]?: any } &
{ [key in TypeStoreKeyShort]?: never })
| ({ [key in TypeStoreKeyLong]?: never } &
{ [key in TypeStoreKeyShort]?: any })
{ [key in TypeStoreKeyShort]?: any });
type GetType<
Store extends TypeStore,
LongKey extends TypeStoreKeyLong,
Expand All @@ -37,7 +37,7 @@ type GetType<
? Store[LongKey]
: ShortKey extends keyof Store
? Store[ShortKey]
: any
: any;

export interface HookCollection<
HooksType extends Record<string, TypeStore> = Record<
Expand All @@ -52,135 +52,135 @@ export interface HookCollection<
<Name extends HookName>(
name: Name | Name[],
hookMethod: HookMethod<
GetType<HooksType[Name], 'Options', 'O'>,
GetType<HooksType[Name], 'Result', 'R'>
GetType<HooksType[Name], "Options", "O">,
GetType<HooksType[Name], "Result", "R">
>,
options?: GetType<HooksType[Name], 'Options', 'O'>
): Promise<GetType<HooksType[Name], 'Result', 'R'>>
options?: GetType<HooksType[Name], "Options", "O">
): Promise<GetType<HooksType[Name], "Result", "R">>;
/**
* Add `before` hook for given `name`
*/
before<Name extends HookName>(
name: Name,
beforeHook: BeforeHook<GetType<HooksType[Name], 'Options', 'O'>>
): void
beforeHook: BeforeHook<GetType<HooksType[Name], "Options", "O">>
): void;
/**
* Add `error` hook for given `name`
*/
error<Name extends HookName>(
name: Name,
errorHook: ErrorHook<
GetType<HooksType[Name], 'Options', 'O'>,
GetType<HooksType[Name], 'Error', 'E'>
GetType<HooksType[Name], "Options", "O">,
GetType<HooksType[Name], "Error", "E">
>
): void
): void;
/**
* Add `after` hook for given `name`
*/
after<Name extends HookName>(
name: Name,
afterHook: AfterHook<
GetType<HooksType[Name], 'Options', 'O'>,
GetType<HooksType[Name], 'Result', 'R'>
GetType<HooksType[Name], "Options", "O">,
GetType<HooksType[Name], "Result", "R">
>
): void
): void;
/**
* Add `wrap` hook for given `name`
*/
wrap<Name extends HookName>(
name: Name,
wrapHook: WrapHook<
GetType<HooksType[Name], 'Options', 'O'>,
GetType<HooksType[Name], 'Result', 'R'>
GetType<HooksType[Name], "Options", "O">,
GetType<HooksType[Name], "Result", "R">
>
): void
): void;
/**
* Remove added hook for given `name`
*/
remove<Name extends HookName>(
name: Name,
hook: AnyHook<
GetType<HooksType[Name], 'Options', 'O'>,
GetType<HooksType[Name], 'Result', 'R'>,
GetType<HooksType[Name], 'Error', 'E'>
GetType<HooksType[Name], "Options", "O">,
GetType<HooksType[Name], "Result", "R">,
GetType<HooksType[Name], "Error", "E">
>
): void
): void;
/**
* Public API
*/
api: Pick<
HookCollection<HooksType>,
'before' | 'error' | 'after' | 'wrap' | 'remove'
>
"before" | "error" | "after" | "wrap" | "remove"
>;
}

export interface HookSingular<Options, Result, Error> {
/**
* Invoke before and after hooks
*/
(hookMethod: HookMethod<Options, Result>, options?: Options): Promise<Result>
(hookMethod: HookMethod<Options, Result>, options?: Options): Promise<Result>;
/**
* Add `before` hook
*/
before(beforeHook: BeforeHook<Options>): void
before(beforeHook: BeforeHook<Options>): void;
/**
* Add `error` hook
*/
error(errorHook: ErrorHook<Options, Error>): void
error(errorHook: ErrorHook<Options, Error>): void;
/**
* Add `after` hook
*/
after(afterHook: AfterHook<Options, Result>): void
after(afterHook: AfterHook<Options, Result>): void;
/**
* Add `wrap` hook
*/
wrap(wrapHook: WrapHook<Options, Result>): void
wrap(wrapHook: WrapHook<Options, Result>): void;
/**
* Remove added hook
*/
remove(hook: AnyHook<Options, Result, Error>): void
remove(hook: AnyHook<Options, Result, Error>): void;
/**
* Public API
*/
api: Pick<
HookSingular<Options, Result, Error>,
'before' | 'error' | 'after' | 'wrap' | 'remove'
>
"before" | "error" | "after" | "wrap" | "remove"
>;
}

type Collection = new <
HooksType extends Record<string, TypeStore> = Record<
string,
{ Options: any; Result: any; Error: any }
>
>() => HookCollection<HooksType>
>() => HookCollection<HooksType>;
type Singular = new <
Options = any,
Result = any,
Error = any
>() => HookSingular<Options, Result, Error>
>() => HookSingular<Options, Result, Error>;

interface Hook {
new <
HooksType extends Record<string, TypeStore> = Record<
string,
{ Options: any; Result: any; Error: any }
>
>(): HookCollection<HooksType>
>(): HookCollection<HooksType>;

/**
* Creates a collection of hooks
*/
Collection: Collection
Collection: Collection;

/**
* Creates a nameless hook that supports strict typings
*/
Singular: Singular
Singular: Singular;
}

export const Hook: Hook
export const Collection: Collection
export const Singular: Singular
export const Hook: Hook;
export const Collection: Collection;
export const Singular: Singular;

export default Hook
export default Hook;
82 changes: 43 additions & 39 deletions index.js
Original file line number Diff line number Diff line change
@@ -1,57 +1,61 @@
var register = require('./lib/register')
var addHook = require('./lib/add')
var removeHook = require('./lib/remove')
var register = require("./lib/register");
var addHook = require("./lib/add");
var removeHook = require("./lib/remove");

// bind with array of arguments: https://stackoverflow.com/a/21792913
var bind = Function.bind
var bindable = bind.bind(bind)

function bindApi (hook, state, name) {
var removeHookRef = bindable(removeHook, null).apply(null, name ? [state, name] : [state])
hook.api = { remove: removeHookRef }
hook.remove = removeHookRef

;['before', 'error', 'after', 'wrap'].forEach(function (kind) {
var args = name ? [state, kind, name] : [state, kind]
hook[kind] = hook.api[kind] = bindable(addHook, null).apply(null, args)
})
var bind = Function.bind;
var bindable = bind.bind(bind);

function bindApi(hook, state, name) {
var removeHookRef = bindable(removeHook, null).apply(
null,
name ? [state, name] : [state]
);
hook.api = { remove: removeHookRef };
hook.remove = removeHookRef;
["before", "error", "after", "wrap"].forEach(function (kind) {
var args = name ? [state, kind, name] : [state, kind];
hook[kind] = hook.api[kind] = bindable(addHook, null).apply(null, args);
});
}

function HookSingular () {
var singularHookName = 'h'
function HookSingular() {
var singularHookName = "h";
var singularHookState = {
registry: {}
}
var singularHook = register.bind(null, singularHookState, singularHookName)
bindApi(singularHook, singularHookState, singularHookName)
return singularHook
registry: {},
};
var singularHook = register.bind(null, singularHookState, singularHookName);
bindApi(singularHook, singularHookState, singularHookName);
return singularHook;
}

function HookCollection () {
function HookCollection() {
var state = {
registry: {}
}
registry: {},
};

var hook = register.bind(null, state)
bindApi(hook, state)
var hook = register.bind(null, state);
bindApi(hook, state);

return hook
return hook;
}

var collectionHookDeprecationMessageDisplayed = false
function Hook () {
var collectionHookDeprecationMessageDisplayed = false;
function Hook() {
if (!collectionHookDeprecationMessageDisplayed) {
console.warn('[before-after-hook]: "Hook()" repurposing warning, use "Hook.Collection()". Read more: https://git.io/upgrade-before-after-hook-to-1.4')
collectionHookDeprecationMessageDisplayed = true
console.warn(
'[before-after-hook]: "Hook()" repurposing warning, use "Hook.Collection()". Read more: https://git.io/upgrade-before-after-hook-to-1.4'
);
collectionHookDeprecationMessageDisplayed = true;
}
return HookCollection()
return HookCollection();
}

Hook.Singular = HookSingular.bind()
Hook.Collection = HookCollection.bind()
Hook.Singular = HookSingular.bind();
Hook.Collection = HookCollection.bind();

module.exports = Hook
module.exports = Hook;
// expose constructors as a named property for TypeScript
module.exports.Hook = Hook
module.exports.Singular = Hook.Singular
module.exports.Collection = Hook.Collection
module.exports.Hook = Hook;
module.exports.Singular = Hook.Singular;
module.exports.Collection = Hook.Collection;
4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,8 @@
"prebuild": "rimraf dist && mkdirp dist",
"build": "browserify index.js --standalone=Hook > dist/before-after-hook.js",
"postbuild": "uglifyjs dist/before-after-hook.js -mc > dist/before-after-hook.min.js",
"lint": "prettier --check '{lib,test,examples}/**/*' README.md package.json",
"lint:fix": "prettier --write '{lib,test,examples}/**/*' README.md package.json",
"lint": "prettier --check '{lib,test,examples}/**/*' 'index.*' README.md package.json",
"lint:fix": "prettier --write '{lib,test,examples}/**/*' 'index.*' README.md package.json",
"pretest": "npm run -s lint",
"test": "npm run -s test:node | tap-spec",
"posttest": "npm run validate:ts",
Expand Down
3 changes: 3 additions & 0 deletions test/typescript-validate.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ const hookMethod = (options: Options): number => {
const beforeHook = (_options: Options): void => {};
const afterHook = (_result: number, _options: Options): void => {};
const errorHook = (_error: any, _options: Options): void => {};
const errorCatchHook = (_error: any, _options: Options) => "ok";
const wrapHook = (hookMethod: any, options: Options): number => {
beforeHook(options);
const result = hookMethod(options);
Expand All @@ -41,6 +42,7 @@ const hooks = new Collection();
hooks.before("life", beforeHook);
hooks.after("life", afterHook);
hooks.error("life", errorHook);
hooks.error("life", errorCatchHook);
hooks.wrap("life", wrapHook);

hooks("life", hookMethod);
Expand All @@ -51,6 +53,7 @@ const hook = new Singular<Options, number>();
hook.before(beforeHook);
hook.after(afterHook);
hook.error(errorHook);
hook.error(errorCatchHook);
hook.wrap(wrapHook);

hook(hookMethod, options);

0 comments on commit 445b99d

Please sign in to comment.