├── .gitattributes ├── .babelrc ├── .gitignore ├── .editorconfig ├── eslint.config.mjs ├── .prettierrc.js ├── lib ├── util-browser.js ├── __tests__ │ ├── HookStackOverflow.js │ ├── AsyncParallelHooks.js │ ├── SyncHooks.js │ ├── MultiHook.js │ ├── Hook.js │ ├── SyncBailHook.js │ ├── SyncHook.js │ ├── AsyncSeriesHooks.js │ ├── SyncWaterfallHook.js │ ├── HookCodeFactory.js │ ├── __snapshots__ │ │ ├── SyncHooks.js.snap │ │ └── HookCodeFactory.js.snap │ └── HookTester.js ├── index.js ├── AsyncSeriesHook.js ├── AsyncParallelHook.js ├── AsyncSeriesLoopHook.js ├── MultiHook.js ├── SyncHook.js ├── AsyncSeriesBailHook.js ├── SyncLoopHook.js ├── SyncBailHook.js ├── AsyncSeriesWaterfallHook.js ├── SyncWaterfallHook.js ├── HookMap.js ├── AsyncParallelBailHook.js ├── Hook.js └── HookCodeFactory.js ├── LICENSE ├── package.json ├── .github └── workflows │ └── test.yml ├── tapable.d.ts └── README.md /.gitattributes: -------------------------------------------------------------------------------- 1 | * text=auto 2 | -------------------------------------------------------------------------------- /.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": [ 3 | [ 4 | "@babel/preset-env", 5 | { 6 | "targets": { 7 | "node": "current" 8 | } 9 | } 10 | ] 11 | ] 12 | } 13 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /node_modules 2 | /coverage 3 | 4 | ############ 5 | ## Windows 6 | ############ 7 | 8 | # Windows image file caches 9 | Thumbs.db 10 | 11 | # Folder config file 12 | Desktop.ini 13 | 14 | # Logs 15 | *.log 16 | 17 | # IDEs 18 | .idea 19 | .vscode 20 | 21 | # Caches 22 | .cache 23 | .eslintcache 24 | .cspellcache 25 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | indent_style = tab 5 | indent_size = 2 6 | charset = utf-8 7 | trim_trailing_whitespace = true 8 | insert_final_newline = true 9 | max_line_length = 80 10 | 11 | [*.{yml,yaml,json}] 12 | indent_style = space 13 | indent_size = 2 14 | 15 | [*.md] 16 | trim_trailing_whitespace = false 17 | 18 | [*.snap] 19 | trim_trailing_whitespace = false 20 | -------------------------------------------------------------------------------- /eslint.config.mjs: -------------------------------------------------------------------------------- 1 | import { defineConfig } from "eslint/config"; 2 | import config from "eslint-config-webpack"; 3 | 4 | export default defineConfig([ 5 | { 6 | extends: [config], 7 | rules: { 8 | "no-new-func": "off" 9 | } 10 | }, 11 | { 12 | languageOptions: { 13 | parserOptions: { 14 | ecmaVersion: 2018 15 | } 16 | }, 17 | files: ["lib/__tests__/**/*.js"] 18 | } 19 | ]); 20 | -------------------------------------------------------------------------------- /.prettierrc.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | module.exports = { 4 | printWidth: 80, 5 | useTabs: true, 6 | tabWidth: 2, 7 | trailingComma: "none", 8 | arrowParens: "always", 9 | overrides: [ 10 | { 11 | files: "*.json", 12 | options: { 13 | parser: "json", 14 | useTabs: false 15 | } 16 | }, 17 | { 18 | files: "*.{cts,mts,ts}", 19 | options: { 20 | parser: "typescript" 21 | } 22 | } 23 | ] 24 | }; 25 | -------------------------------------------------------------------------------- /lib/util-browser.js: -------------------------------------------------------------------------------- 1 | /* 2 | MIT License http://www.opensource.org/licenses/mit-license.php 3 | Author Tobias Koppers @sokra 4 | */ 5 | "use strict"; 6 | 7 | module.exports.deprecate = (fn, msg) => { 8 | let once = true; 9 | return function deprecate() { 10 | if (once) { 11 | // eslint-disable-next-line no-console 12 | console.warn(`DeprecationWarning: ${msg}`); 13 | once = false; 14 | } 15 | // eslint-disable-next-line prefer-rest-params 16 | return fn.apply(this, arguments); 17 | }; 18 | }; 19 | -------------------------------------------------------------------------------- /lib/__tests__/HookStackOverflow.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | const AsyncSeriesHook = require("../AsyncSeriesHook"); 4 | 5 | describe("HookStackOverflow", () => { 6 | it("should not crash when compiling a large hook", () => { 7 | const hook = new AsyncSeriesHook(["a", "b"]); 8 | 9 | for (let i = 0; i < 10; i++) { 10 | hook.tap("TestPlugin", (_a, _b) => {}); 11 | hook.tapAsync("TestPlugin", (a, b, callback) => callback()); 12 | hook.tapPromise("TestPlugin", (_a, _b) => Promise.resolve()); 13 | } 14 | 15 | expect(hook.taps).toBeDefined(); 16 | 17 | return hook.promise(1, 2); 18 | }); 19 | }); 20 | -------------------------------------------------------------------------------- /lib/__tests__/AsyncParallelHooks.js: -------------------------------------------------------------------------------- 1 | /* 2 | MIT License http://www.opensource.org/licenses/mit-license.php 3 | Author Tobias Koppers @sokra 4 | */ 5 | "use strict"; 6 | 7 | const AsyncParallelBailHook = require("../AsyncParallelBailHook"); 8 | const AsyncParallelHook = require("../AsyncParallelHook"); 9 | const HookTester = require("./HookTester"); 10 | 11 | describe("AsyncParallelHook", () => { 12 | it("should have to correct behavior", async () => { 13 | const tester = new HookTester((args) => new AsyncParallelHook(args)); 14 | 15 | const result = await tester.run(); 16 | 17 | expect(result).toMatchSnapshot(); 18 | }, 15000); 19 | }); 20 | 21 | describe("AsyncParallelBailHook", () => { 22 | it("should have to correct behavior", async () => { 23 | const tester = new HookTester((args) => new AsyncParallelBailHook(args)); 24 | 25 | const result = await tester.run(); 26 | 27 | expect(result).toMatchSnapshot(); 28 | }, 15000); 29 | }); 30 | -------------------------------------------------------------------------------- /lib/index.js: -------------------------------------------------------------------------------- 1 | /* 2 | MIT License http://www.opensource.org/licenses/mit-license.php 3 | Author Tobias Koppers @sokra 4 | */ 5 | "use strict"; 6 | 7 | module.exports.AsyncParallelBailHook = require("./AsyncParallelBailHook"); 8 | module.exports.AsyncParallelHook = require("./AsyncParallelHook"); 9 | module.exports.AsyncSeriesBailHook = require("./AsyncSeriesBailHook"); 10 | module.exports.AsyncSeriesHook = require("./AsyncSeriesHook"); 11 | module.exports.AsyncSeriesLoopHook = require("./AsyncSeriesLoopHook"); 12 | module.exports.AsyncSeriesWaterfallHook = require("./AsyncSeriesWaterfallHook"); 13 | module.exports.HookMap = require("./HookMap"); 14 | module.exports.MultiHook = require("./MultiHook"); 15 | module.exports.SyncBailHook = require("./SyncBailHook"); 16 | module.exports.SyncHook = require("./SyncHook"); 17 | module.exports.SyncLoopHook = require("./SyncLoopHook"); 18 | module.exports.SyncWaterfallHook = require("./SyncWaterfallHook"); 19 | module.exports.__esModule = true; 20 | -------------------------------------------------------------------------------- /lib/AsyncSeriesHook.js: -------------------------------------------------------------------------------- 1 | /* 2 | MIT License http://www.opensource.org/licenses/mit-license.php 3 | Author Tobias Koppers @sokra 4 | */ 5 | "use strict"; 6 | 7 | const Hook = require("./Hook"); 8 | const HookCodeFactory = require("./HookCodeFactory"); 9 | 10 | class AsyncSeriesHookCodeFactory extends HookCodeFactory { 11 | content({ onError, onDone }) { 12 | return this.callTapsSeries({ 13 | onError: (i, err, next, doneBreak) => onError(err) + doneBreak(true), 14 | onDone 15 | }); 16 | } 17 | } 18 | 19 | const factory = new AsyncSeriesHookCodeFactory(); 20 | 21 | function COMPILE(options) { 22 | factory.setup(this, options); 23 | return factory.create(options); 24 | } 25 | 26 | function AsyncSeriesHook(args = [], name = undefined) { 27 | const hook = new Hook(args, name); 28 | hook.constructor = AsyncSeriesHook; 29 | hook.compile = COMPILE; 30 | hook._call = undefined; 31 | hook.call = undefined; 32 | return hook; 33 | } 34 | 35 | AsyncSeriesHook.prototype = null; 36 | 37 | module.exports = AsyncSeriesHook; 38 | -------------------------------------------------------------------------------- /lib/AsyncParallelHook.js: -------------------------------------------------------------------------------- 1 | /* 2 | MIT License http://www.opensource.org/licenses/mit-license.php 3 | Author Tobias Koppers @sokra 4 | */ 5 | "use strict"; 6 | 7 | const Hook = require("./Hook"); 8 | const HookCodeFactory = require("./HookCodeFactory"); 9 | 10 | class AsyncParallelHookCodeFactory extends HookCodeFactory { 11 | content({ onError, onDone }) { 12 | return this.callTapsParallel({ 13 | onError: (i, err, done, doneBreak) => onError(err) + doneBreak(true), 14 | onDone 15 | }); 16 | } 17 | } 18 | 19 | const factory = new AsyncParallelHookCodeFactory(); 20 | 21 | function COMPILE(options) { 22 | factory.setup(this, options); 23 | return factory.create(options); 24 | } 25 | 26 | function AsyncParallelHook(args = [], name = undefined) { 27 | const hook = new Hook(args, name); 28 | hook.constructor = AsyncParallelHook; 29 | hook.compile = COMPILE; 30 | hook._call = undefined; 31 | hook.call = undefined; 32 | return hook; 33 | } 34 | 35 | AsyncParallelHook.prototype = null; 36 | 37 | module.exports = AsyncParallelHook; 38 | -------------------------------------------------------------------------------- /lib/AsyncSeriesLoopHook.js: -------------------------------------------------------------------------------- 1 | /* 2 | MIT License http://www.opensource.org/licenses/mit-license.php 3 | Author Tobias Koppers @sokra 4 | */ 5 | "use strict"; 6 | 7 | const Hook = require("./Hook"); 8 | const HookCodeFactory = require("./HookCodeFactory"); 9 | 10 | class AsyncSeriesLoopHookCodeFactory extends HookCodeFactory { 11 | content({ onError, onDone }) { 12 | return this.callTapsLooping({ 13 | onError: (i, err, next, doneBreak) => onError(err) + doneBreak(true), 14 | onDone 15 | }); 16 | } 17 | } 18 | 19 | const factory = new AsyncSeriesLoopHookCodeFactory(); 20 | 21 | function COMPILE(options) { 22 | factory.setup(this, options); 23 | return factory.create(options); 24 | } 25 | 26 | function AsyncSeriesLoopHook(args = [], name = undefined) { 27 | const hook = new Hook(args, name); 28 | hook.constructor = AsyncSeriesLoopHook; 29 | hook.compile = COMPILE; 30 | hook._call = undefined; 31 | hook.call = undefined; 32 | return hook; 33 | } 34 | 35 | AsyncSeriesLoopHook.prototype = null; 36 | 37 | module.exports = AsyncSeriesLoopHook; 38 | -------------------------------------------------------------------------------- /lib/MultiHook.js: -------------------------------------------------------------------------------- 1 | /* 2 | MIT License http://www.opensource.org/licenses/mit-license.php 3 | Author Tobias Koppers @sokra 4 | */ 5 | "use strict"; 6 | 7 | class MultiHook { 8 | constructor(hooks, name = undefined) { 9 | this.hooks = hooks; 10 | this.name = name; 11 | } 12 | 13 | tap(options, fn) { 14 | for (const hook of this.hooks) { 15 | hook.tap(options, fn); 16 | } 17 | } 18 | 19 | tapAsync(options, fn) { 20 | for (const hook of this.hooks) { 21 | hook.tapAsync(options, fn); 22 | } 23 | } 24 | 25 | tapPromise(options, fn) { 26 | for (const hook of this.hooks) { 27 | hook.tapPromise(options, fn); 28 | } 29 | } 30 | 31 | isUsed() { 32 | for (const hook of this.hooks) { 33 | if (hook.isUsed()) return true; 34 | } 35 | return false; 36 | } 37 | 38 | intercept(interceptor) { 39 | for (const hook of this.hooks) { 40 | hook.intercept(interceptor); 41 | } 42 | } 43 | 44 | withOptions(options) { 45 | return new MultiHook( 46 | this.hooks.map((hook) => hook.withOptions(options)), 47 | this.name 48 | ); 49 | } 50 | } 51 | 52 | module.exports = MultiHook; 53 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License 2 | 3 | Copyright JS Foundation and other contributors 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /lib/SyncHook.js: -------------------------------------------------------------------------------- 1 | /* 2 | MIT License http://www.opensource.org/licenses/mit-license.php 3 | Author Tobias Koppers @sokra 4 | */ 5 | "use strict"; 6 | 7 | const Hook = require("./Hook"); 8 | const HookCodeFactory = require("./HookCodeFactory"); 9 | 10 | class SyncHookCodeFactory extends HookCodeFactory { 11 | content({ onError, onDone, rethrowIfPossible }) { 12 | return this.callTapsSeries({ 13 | onError: (i, err) => onError(err), 14 | onDone, 15 | rethrowIfPossible 16 | }); 17 | } 18 | } 19 | 20 | const factory = new SyncHookCodeFactory(); 21 | 22 | const TAP_ASYNC = () => { 23 | throw new Error("tapAsync is not supported on a SyncHook"); 24 | }; 25 | 26 | const TAP_PROMISE = () => { 27 | throw new Error("tapPromise is not supported on a SyncHook"); 28 | }; 29 | 30 | function COMPILE(options) { 31 | factory.setup(this, options); 32 | return factory.create(options); 33 | } 34 | 35 | function SyncHook(args = [], name = undefined) { 36 | const hook = new Hook(args, name); 37 | hook.constructor = SyncHook; 38 | hook.tapAsync = TAP_ASYNC; 39 | hook.tapPromise = TAP_PROMISE; 40 | hook.compile = COMPILE; 41 | return hook; 42 | } 43 | 44 | SyncHook.prototype = null; 45 | 46 | module.exports = SyncHook; 47 | -------------------------------------------------------------------------------- /lib/AsyncSeriesBailHook.js: -------------------------------------------------------------------------------- 1 | /* 2 | MIT License http://www.opensource.org/licenses/mit-license.php 3 | Author Tobias Koppers @sokra 4 | */ 5 | "use strict"; 6 | 7 | const Hook = require("./Hook"); 8 | const HookCodeFactory = require("./HookCodeFactory"); 9 | 10 | class AsyncSeriesBailHookCodeFactory extends HookCodeFactory { 11 | content({ onError, onResult, resultReturns, onDone }) { 12 | return this.callTapsSeries({ 13 | onError: (i, err, next, doneBreak) => onError(err) + doneBreak(true), 14 | onResult: (i, result, next) => 15 | `if(${result} !== undefined) {\n${onResult( 16 | result 17 | )}\n} else {\n${next()}}\n`, 18 | resultReturns, 19 | onDone 20 | }); 21 | } 22 | } 23 | 24 | const factory = new AsyncSeriesBailHookCodeFactory(); 25 | 26 | function COMPILE(options) { 27 | factory.setup(this, options); 28 | return factory.create(options); 29 | } 30 | 31 | function AsyncSeriesBailHook(args = [], name = undefined) { 32 | const hook = new Hook(args, name); 33 | hook.constructor = AsyncSeriesBailHook; 34 | hook.compile = COMPILE; 35 | hook._call = undefined; 36 | hook.call = undefined; 37 | return hook; 38 | } 39 | 40 | AsyncSeriesBailHook.prototype = null; 41 | 42 | module.exports = AsyncSeriesBailHook; 43 | -------------------------------------------------------------------------------- /lib/SyncLoopHook.js: -------------------------------------------------------------------------------- 1 | /* 2 | MIT License http://www.opensource.org/licenses/mit-license.php 3 | Author Tobias Koppers @sokra 4 | */ 5 | "use strict"; 6 | 7 | const Hook = require("./Hook"); 8 | const HookCodeFactory = require("./HookCodeFactory"); 9 | 10 | class SyncLoopHookCodeFactory extends HookCodeFactory { 11 | content({ onError, onDone, rethrowIfPossible }) { 12 | return this.callTapsLooping({ 13 | onError: (i, err) => onError(err), 14 | onDone, 15 | rethrowIfPossible 16 | }); 17 | } 18 | } 19 | 20 | const factory = new SyncLoopHookCodeFactory(); 21 | 22 | const TAP_ASYNC = () => { 23 | throw new Error("tapAsync is not supported on a SyncLoopHook"); 24 | }; 25 | 26 | const TAP_PROMISE = () => { 27 | throw new Error("tapPromise is not supported on a SyncLoopHook"); 28 | }; 29 | 30 | function COMPILE(options) { 31 | factory.setup(this, options); 32 | return factory.create(options); 33 | } 34 | 35 | function SyncLoopHook(args = [], name = undefined) { 36 | const hook = new Hook(args, name); 37 | hook.constructor = SyncLoopHook; 38 | hook.tapAsync = TAP_ASYNC; 39 | hook.tapPromise = TAP_PROMISE; 40 | hook.compile = COMPILE; 41 | return hook; 42 | } 43 | 44 | SyncLoopHook.prototype = null; 45 | 46 | module.exports = SyncLoopHook; 47 | -------------------------------------------------------------------------------- /lib/SyncBailHook.js: -------------------------------------------------------------------------------- 1 | /* 2 | MIT License http://www.opensource.org/licenses/mit-license.php 3 | Author Tobias Koppers @sokra 4 | */ 5 | "use strict"; 6 | 7 | const Hook = require("./Hook"); 8 | const HookCodeFactory = require("./HookCodeFactory"); 9 | 10 | class SyncBailHookCodeFactory extends HookCodeFactory { 11 | content({ onError, onResult, resultReturns, onDone, rethrowIfPossible }) { 12 | return this.callTapsSeries({ 13 | onError: (i, err) => onError(err), 14 | onResult: (i, result, next) => 15 | `if(${result} !== undefined) {\n${onResult( 16 | result 17 | )};\n} else {\n${next()}}\n`, 18 | resultReturns, 19 | onDone, 20 | rethrowIfPossible 21 | }); 22 | } 23 | } 24 | 25 | const factory = new SyncBailHookCodeFactory(); 26 | 27 | const TAP_ASYNC = () => { 28 | throw new Error("tapAsync is not supported on a SyncBailHook"); 29 | }; 30 | 31 | const TAP_PROMISE = () => { 32 | throw new Error("tapPromise is not supported on a SyncBailHook"); 33 | }; 34 | 35 | function COMPILE(options) { 36 | factory.setup(this, options); 37 | return factory.create(options); 38 | } 39 | 40 | function SyncBailHook(args = [], name = undefined) { 41 | const hook = new Hook(args, name); 42 | hook.constructor = SyncBailHook; 43 | hook.tapAsync = TAP_ASYNC; 44 | hook.tapPromise = TAP_PROMISE; 45 | hook.compile = COMPILE; 46 | return hook; 47 | } 48 | 49 | SyncBailHook.prototype = null; 50 | 51 | module.exports = SyncBailHook; 52 | -------------------------------------------------------------------------------- /lib/AsyncSeriesWaterfallHook.js: -------------------------------------------------------------------------------- 1 | /* 2 | MIT License http://www.opensource.org/licenses/mit-license.php 3 | Author Tobias Koppers @sokra 4 | */ 5 | "use strict"; 6 | 7 | const Hook = require("./Hook"); 8 | const HookCodeFactory = require("./HookCodeFactory"); 9 | 10 | class AsyncSeriesWaterfallHookCodeFactory extends HookCodeFactory { 11 | content({ onError, onResult, _onDone }) { 12 | return this.callTapsSeries({ 13 | onError: (i, err, next, doneBreak) => onError(err) + doneBreak(true), 14 | onResult: (i, result, next) => { 15 | let code = ""; 16 | code += `if(${result} !== undefined) {\n`; 17 | code += `${this._args[0]} = ${result};\n`; 18 | code += "}\n"; 19 | code += next(); 20 | return code; 21 | }, 22 | onDone: () => onResult(this._args[0]) 23 | }); 24 | } 25 | } 26 | 27 | const factory = new AsyncSeriesWaterfallHookCodeFactory(); 28 | 29 | function COMPILE(options) { 30 | factory.setup(this, options); 31 | return factory.create(options); 32 | } 33 | 34 | function AsyncSeriesWaterfallHook(args = [], name = undefined) { 35 | if (args.length < 1) { 36 | throw new Error("Waterfall hooks must have at least one argument"); 37 | } 38 | const hook = new Hook(args, name); 39 | hook.constructor = AsyncSeriesWaterfallHook; 40 | hook.compile = COMPILE; 41 | hook._call = undefined; 42 | hook.call = undefined; 43 | return hook; 44 | } 45 | 46 | AsyncSeriesWaterfallHook.prototype = null; 47 | 48 | module.exports = AsyncSeriesWaterfallHook; 49 | -------------------------------------------------------------------------------- /lib/__tests__/SyncHooks.js: -------------------------------------------------------------------------------- 1 | /* 2 | MIT License http://www.opensource.org/licenses/mit-license.php 3 | Author Tobias Koppers @sokra 4 | */ 5 | "use strict"; 6 | 7 | const SyncBailHook = require("../SyncBailHook"); 8 | const SyncHook = require("../SyncHook"); 9 | const SyncLoopHook = require("../SyncLoopHook"); 10 | const SyncWaterfallHook = require("../SyncWaterfallHook"); 11 | const HookTester = require("./HookTester"); 12 | 13 | describe("SyncHook", () => { 14 | it("should have to correct behavior", async () => { 15 | const tester = new HookTester((args) => new SyncHook(args)); 16 | 17 | const result = await tester.run(true); 18 | 19 | expect(result).toMatchSnapshot(); 20 | }, 15000); 21 | }); 22 | 23 | describe("SyncBailHook", () => { 24 | it("should have to correct behavior", async () => { 25 | const tester = new HookTester((args) => new SyncBailHook(args)); 26 | 27 | const result = await tester.run(true); 28 | 29 | expect(result).toMatchSnapshot(); 30 | }, 15000); 31 | }); 32 | 33 | describe("SyncWaterfallHook", () => { 34 | it("should have to correct behavior", async () => { 35 | const tester = new HookTester((args) => new SyncWaterfallHook(args)); 36 | 37 | const result = await tester.run(true); 38 | 39 | expect(result).toMatchSnapshot(); 40 | }, 15000); 41 | }); 42 | 43 | describe("SyncLoopHook", () => { 44 | it("should have to correct behavior", async () => { 45 | const tester = new HookTester((args) => new SyncLoopHook(args)); 46 | 47 | const result = await tester.runForLoop(true); 48 | 49 | expect(result).toMatchSnapshot(); 50 | }, 15000); 51 | }); 52 | -------------------------------------------------------------------------------- /lib/SyncWaterfallHook.js: -------------------------------------------------------------------------------- 1 | /* 2 | MIT License http://www.opensource.org/licenses/mit-license.php 3 | Author Tobias Koppers @sokra 4 | */ 5 | "use strict"; 6 | 7 | const Hook = require("./Hook"); 8 | const HookCodeFactory = require("./HookCodeFactory"); 9 | 10 | class SyncWaterfallHookCodeFactory extends HookCodeFactory { 11 | content({ onError, onResult, resultReturns, rethrowIfPossible }) { 12 | return this.callTapsSeries({ 13 | onError: (i, err) => onError(err), 14 | onResult: (i, result, next) => { 15 | let code = ""; 16 | code += `if(${result} !== undefined) {\n`; 17 | code += `${this._args[0]} = ${result};\n`; 18 | code += "}\n"; 19 | code += next(); 20 | return code; 21 | }, 22 | onDone: () => onResult(this._args[0]), 23 | doneReturns: resultReturns, 24 | rethrowIfPossible 25 | }); 26 | } 27 | } 28 | 29 | const factory = new SyncWaterfallHookCodeFactory(); 30 | 31 | const TAP_ASYNC = () => { 32 | throw new Error("tapAsync is not supported on a SyncWaterfallHook"); 33 | }; 34 | 35 | const TAP_PROMISE = () => { 36 | throw new Error("tapPromise is not supported on a SyncWaterfallHook"); 37 | }; 38 | 39 | function COMPILE(options) { 40 | factory.setup(this, options); 41 | return factory.create(options); 42 | } 43 | 44 | function SyncWaterfallHook(args = [], name = undefined) { 45 | if (args.length < 1) { 46 | throw new Error("Waterfall hooks must have at least one argument"); 47 | } 48 | const hook = new Hook(args, name); 49 | hook.constructor = SyncWaterfallHook; 50 | hook.tapAsync = TAP_ASYNC; 51 | hook.tapPromise = TAP_PROMISE; 52 | hook.compile = COMPILE; 53 | return hook; 54 | } 55 | 56 | SyncWaterfallHook.prototype = null; 57 | 58 | module.exports = SyncWaterfallHook; 59 | -------------------------------------------------------------------------------- /lib/HookMap.js: -------------------------------------------------------------------------------- 1 | /* 2 | MIT License http://www.opensource.org/licenses/mit-license.php 3 | Author Tobias Koppers @sokra 4 | */ 5 | "use strict"; 6 | 7 | const util = require("util"); 8 | 9 | const defaultFactory = (key, hook) => hook; 10 | 11 | class HookMap { 12 | constructor(factory, name = undefined) { 13 | this._map = new Map(); 14 | this.name = name; 15 | this._factory = factory; 16 | this._interceptors = []; 17 | } 18 | 19 | get(key) { 20 | return this._map.get(key); 21 | } 22 | 23 | for(key) { 24 | const hook = this.get(key); 25 | if (hook !== undefined) { 26 | return hook; 27 | } 28 | let newHook = this._factory(key); 29 | const interceptors = this._interceptors; 30 | for (let i = 0; i < interceptors.length; i++) { 31 | newHook = interceptors[i].factory(key, newHook); 32 | } 33 | this._map.set(key, newHook); 34 | return newHook; 35 | } 36 | 37 | intercept(interceptor) { 38 | this._interceptors.push( 39 | Object.assign( 40 | { 41 | factory: defaultFactory 42 | }, 43 | interceptor 44 | ) 45 | ); 46 | } 47 | } 48 | 49 | HookMap.prototype.tap = util.deprecate(function tap(key, options, fn) { 50 | return this.for(key).tap(options, fn); 51 | }, "HookMap#tap(key,…) is deprecated. Use HookMap#for(key).tap(…) instead."); 52 | 53 | HookMap.prototype.tapAsync = util.deprecate(function tapAsync( 54 | key, 55 | options, 56 | fn 57 | ) { 58 | return this.for(key).tapAsync(options, fn); 59 | }, "HookMap#tapAsync(key,…) is deprecated. Use HookMap#for(key).tapAsync(…) instead."); 60 | 61 | HookMap.prototype.tapPromise = util.deprecate(function tapPromise( 62 | key, 63 | options, 64 | fn 65 | ) { 66 | return this.for(key).tapPromise(options, fn); 67 | }, "HookMap#tapPromise(key,…) is deprecated. Use HookMap#for(key).tapPromise(…) instead."); 68 | 69 | module.exports = HookMap; 70 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "tapable", 3 | "version": "2.3.0", 4 | "description": "Just a little module for plugins.", 5 | "homepage": "https://github.com/webpack/tapable", 6 | "repository": { 7 | "type": "git", 8 | "url": "http://github.com/webpack/tapable.git" 9 | }, 10 | "funding": { 11 | "type": "opencollective", 12 | "url": "https://opencollective.com/webpack" 13 | }, 14 | "license": "MIT", 15 | "author": "Tobias Koppers @sokra", 16 | "main": "lib/index.js", 17 | "browser": { 18 | "util": "./lib/util-browser.js" 19 | }, 20 | "types": "./tapable.d.ts", 21 | "files": ["lib", "!lib/__tests__", "tapable.d.ts"], 22 | "scripts": { 23 | "lint": "yarn lint:code && yarn fmt:check", 24 | "lint:code": "eslint --cache .", 25 | "fmt": "yarn fmt:base --log-level warn --write", 26 | "fmt:check": "yarn fmt:base --check", 27 | "fmt:base": "node ./node_modules/prettier/bin/prettier.cjs --cache --ignore-unknown .", 28 | "fix": "yarn fix:code && yarn fmt", 29 | "fix:code": "yarn lint:code --fix", 30 | "test": "jest" 31 | }, 32 | "jest": { 33 | "transform": { 34 | "__tests__[\\\\/].+\\.js$": "babel-jest" 35 | } 36 | }, 37 | "devDependencies": { 38 | "@babel/core": "^7.4.4", 39 | "@babel/preset-env": "^7.4.4", 40 | "@eslint/js": "^9.28.0", 41 | "@eslint/markdown": "^7.1.0", 42 | "@stylistic/eslint-plugin": "^5.2.3", 43 | "babel-jest": "^24.8.0", 44 | "globals": "^16.2.0", 45 | "eslint": "^9.28.0", 46 | "eslint-config-webpack": "^4.6.3", 47 | "eslint-config-prettier": "^10.1.5", 48 | "eslint-plugin-import": "^2.31.0", 49 | "eslint-plugin-jest": "^29.0.1", 50 | "eslint-plugin-n": "^17.19.0", 51 | "eslint-plugin-prettier": "^5.4.1", 52 | "eslint-plugin-unicorn": "^60.0.0", 53 | "jest": "^24.8.0", 54 | "prettier": "^3.5.3", 55 | "prettier-1": "npm:prettier@^1" 56 | }, 57 | "engines": { 58 | "node": ">=6" 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /lib/__tests__/MultiHook.js: -------------------------------------------------------------------------------- 1 | /* 2 | MIT License http://www.opensource.org/licenses/mit-license.php 3 | Author Tobias Koppers @sokra 4 | */ 5 | "use strict"; 6 | 7 | const MultiHook = require("../MultiHook"); 8 | 9 | describe("MultiHook", () => { 10 | const redirectedMethods = ["tap", "tapAsync", "tapPromise"]; 11 | for (const name of redirectedMethods) { 12 | it(`should redirect ${name}`, () => { 13 | const calls = []; 14 | const fakeHook = { 15 | [name]: (options, fn) => { 16 | calls.push({ options, fn }); 17 | } 18 | }; 19 | new MultiHook([fakeHook, fakeHook])[name]("options", "fn"); 20 | expect(calls).toEqual([ 21 | { options: "options", fn: "fn" }, 22 | { options: "options", fn: "fn" } 23 | ]); 24 | }); 25 | } 26 | 27 | it("should redirect intercept", () => { 28 | const calls = []; 29 | const fakeHook = { 30 | intercept: (interceptor) => { 31 | calls.push(interceptor); 32 | } 33 | }; 34 | new MultiHook([fakeHook, fakeHook]).intercept("interceptor"); 35 | expect(calls).toEqual(["interceptor", "interceptor"]); 36 | }); 37 | 38 | it("should redirect withOptions", () => { 39 | const calls = []; 40 | const fakeHook = { 41 | withOptions: (options) => { 42 | calls.push(options); 43 | return { 44 | tap: (options, fn) => { 45 | calls.push({ options, fn }); 46 | } 47 | }; 48 | } 49 | }; 50 | const newHook = new MultiHook([fakeHook, fakeHook]).withOptions("options"); 51 | newHook.tap("options", "fn"); 52 | expect(calls).toEqual([ 53 | "options", 54 | "options", 55 | { options: "options", fn: "fn" }, 56 | { options: "options", fn: "fn" } 57 | ]); 58 | }); 59 | 60 | it("should redirect isUsed", () => { 61 | const fakeHook1 = { 62 | isUsed: () => true 63 | }; 64 | const fakeHook2 = { 65 | isUsed: () => false 66 | }; 67 | expect(new MultiHook([fakeHook1, fakeHook1]).isUsed()).toBe(true); 68 | expect(new MultiHook([fakeHook1, fakeHook2]).isUsed()).toBe(true); 69 | expect(new MultiHook([fakeHook2, fakeHook1]).isUsed()).toBe(true); 70 | expect(new MultiHook([fakeHook2, fakeHook2]).isUsed()).toBe(false); 71 | }); 72 | }); 73 | -------------------------------------------------------------------------------- /lib/__tests__/Hook.js: -------------------------------------------------------------------------------- 1 | /* 2 | MIT License http://www.opensource.org/licenses/mit-license.php 3 | Author Tobias Koppers @sokra 4 | */ 5 | "use strict"; 6 | 7 | const SyncHook = require("../SyncHook"); 8 | 9 | describe("Hook", () => { 10 | it("should allow to insert hooks before others and in stages", () => { 11 | const hook = new SyncHook(); 12 | 13 | const calls = []; 14 | hook.tap("A", () => calls.push("A")); 15 | hook.tap( 16 | { 17 | name: "B", 18 | before: "A" 19 | }, 20 | () => calls.push("B") 21 | ); 22 | 23 | calls.length = 0; 24 | hook.call(); 25 | expect(calls).toEqual(["B", "A"]); 26 | 27 | hook.tap( 28 | { 29 | name: "C", 30 | before: ["A", "B"] 31 | }, 32 | () => calls.push("C") 33 | ); 34 | 35 | calls.length = 0; 36 | hook.call(); 37 | expect(calls).toEqual(["C", "B", "A"]); 38 | 39 | hook.tap( 40 | { 41 | name: "D", 42 | before: "B" 43 | }, 44 | () => calls.push("D") 45 | ); 46 | 47 | calls.length = 0; 48 | hook.call(); 49 | expect(calls).toEqual(["C", "D", "B", "A"]); 50 | 51 | hook.tap( 52 | { 53 | name: "E", 54 | stage: -5 55 | }, 56 | () => calls.push("E") 57 | ); 58 | hook.tap( 59 | { 60 | name: "F", 61 | stage: -3 62 | }, 63 | () => calls.push("F") 64 | ); 65 | 66 | calls.length = 0; 67 | hook.call(); 68 | expect(calls).toEqual(["E", "F", "C", "D", "B", "A"]); 69 | }); 70 | 71 | it("should work with `withOptions`", () => { 72 | const hook = new SyncHook(); 73 | 74 | const calls = []; 75 | 76 | hook 77 | .withOptions({ 78 | stage: -10 79 | }) 80 | .tap("A", () => calls.push("A")); 81 | 82 | hook.tap( 83 | { 84 | name: "B", 85 | stage: 0 86 | }, 87 | () => calls.push("B") 88 | ); 89 | 90 | calls.length = 0; 91 | hook.call(); 92 | expect(calls).toEqual(["A", "B"]); 93 | }); 94 | 95 | it("should throw without a valid name", () => { 96 | const hook = new SyncHook(); 97 | expect(() => hook.tap("", () => {})).toThrow( 98 | new Error("Missing name for tap") 99 | ); 100 | expect(() => hook.tap(" ", () => {})).toThrow( 101 | new Error("Missing name for tap") 102 | ); 103 | }); 104 | }); 105 | -------------------------------------------------------------------------------- /.github/workflows/test.yml: -------------------------------------------------------------------------------- 1 | name: Test 2 | 3 | on: 4 | push: 5 | branches: [main] 6 | pull_request: 7 | branches: [main] 8 | 9 | permissions: 10 | contents: read # to fetch code (actions/checkout) 11 | 12 | jobs: 13 | lint: 14 | runs-on: ubuntu-latest 15 | steps: 16 | - uses: actions/checkout@v4 17 | - name: Use Node.js 18 | uses: actions/setup-node@v4 19 | with: 20 | node-version: lts/* 21 | cache: yarn 22 | - run: yarn --frozen-lockfile 23 | - run: yarn lint 24 | test: 25 | strategy: 26 | fail-fast: false 27 | matrix: 28 | os: [ubuntu-latest, windows-latest, macos-latest] 29 | node-version: [6.x, 8.x, 10.x, 12.x, 14.x, 16.x, 18.x, 20.x, 22.x, 24.x] 30 | runs-on: ${{ matrix.os }} 31 | steps: 32 | - uses: actions/checkout@v4 33 | - uses: actions/github-script@v7 34 | id: calculate_architecture 35 | with: 36 | result-encoding: string 37 | script: | 38 | if ('${{ matrix.os }}' === 'macos-latest' && ('${{ matrix['node-version'] }}' === '6.x' || '${{ matrix['node-version'] }}' === '8.x' || '${{ matrix['node-version'] }}' === '10.x' || '${{ matrix['node-version'] }}' === '12.x' || '${{ matrix['node-version'] }}' === '14.x')) { 39 | return "x64" 40 | } else { 41 | return '' 42 | } 43 | - name: Use Node.js ${{ matrix.node-version }} 44 | uses: actions/setup-node@v4 45 | with: 46 | node-version: ${{ matrix.node-version }} 47 | architecture: ${{ steps.calculate_architecture.outputs.result }} 48 | cache: yarn 49 | - name: Install dependencies 50 | run: yarn --frozen-lockfile --ignore-engines 51 | if: matrix.node-version == '6.x' || matrix.node-version == '8.x' || matrix.node-version == '10.x' || matrix.node-version == '12.x' || matrix.node-version == '14.x' || matrix.node-version == '16.x' || matrix.node-version == '18.x' 52 | - name: Install dependencies 53 | run: yarn --frozen-lockfile 54 | if: matrix.node-version != '6.x' && matrix.node-version != '8.x' && matrix.node-version != '10.x' && matrix.node-version != '12.x' && matrix.node-version != '14.x' && matrix.node-version != '16.x' && matrix.node-version != '18.x' 55 | - name: Run tests with coverage 56 | run: yarn test --ci --coverage 57 | - uses: codecov/codecov-action@v5 58 | with: 59 | flags: integration 60 | token: ${{ secrets.CODECOV_TOKEN }} 61 | -------------------------------------------------------------------------------- /lib/AsyncParallelBailHook.js: -------------------------------------------------------------------------------- 1 | /* 2 | MIT License http://www.opensource.org/licenses/mit-license.php 3 | Author Tobias Koppers @sokra 4 | */ 5 | "use strict"; 6 | 7 | const Hook = require("./Hook"); 8 | const HookCodeFactory = require("./HookCodeFactory"); 9 | 10 | class AsyncParallelBailHookCodeFactory extends HookCodeFactory { 11 | content({ onError, onResult, onDone }) { 12 | let code = ""; 13 | code += `var _results = new Array(${this.options.taps.length});\n`; 14 | code += "var _checkDone = function() {\n"; 15 | code += "for(var i = 0; i < _results.length; i++) {\n"; 16 | code += "var item = _results[i];\n"; 17 | code += "if(item === undefined) return false;\n"; 18 | code += "if(item.result !== undefined) {\n"; 19 | code += onResult("item.result"); 20 | code += "return true;\n"; 21 | code += "}\n"; 22 | code += "if(item.error) {\n"; 23 | code += onError("item.error"); 24 | code += "return true;\n"; 25 | code += "}\n"; 26 | code += "}\n"; 27 | code += "return false;\n"; 28 | code += "}\n"; 29 | code += this.callTapsParallel({ 30 | onError: (i, err, done, doneBreak) => { 31 | let code = ""; 32 | code += `if(${i} < _results.length && ((_results.length = ${ 33 | i + 1 34 | }), (_results[${i}] = { error: ${err} }), _checkDone())) {\n`; 35 | code += doneBreak(true); 36 | code += "} else {\n"; 37 | code += done(); 38 | code += "}\n"; 39 | return code; 40 | }, 41 | onResult: (i, result, done, doneBreak) => { 42 | let code = ""; 43 | code += `if(${i} < _results.length && (${result} !== undefined && (_results.length = ${ 44 | i + 1 45 | }), (_results[${i}] = { result: ${result} }), _checkDone())) {\n`; 46 | code += doneBreak(true); 47 | code += "} else {\n"; 48 | code += done(); 49 | code += "}\n"; 50 | return code; 51 | }, 52 | onTap: (i, run, done, _doneBreak) => { 53 | let code = ""; 54 | if (i > 0) { 55 | code += `if(${i} >= _results.length) {\n`; 56 | code += done(); 57 | code += "} else {\n"; 58 | } 59 | code += run(); 60 | if (i > 0) code += "}\n"; 61 | return code; 62 | }, 63 | onDone 64 | }); 65 | return code; 66 | } 67 | } 68 | 69 | const factory = new AsyncParallelBailHookCodeFactory(); 70 | 71 | function COMPILE(options) { 72 | factory.setup(this, options); 73 | return factory.create(options); 74 | } 75 | 76 | function AsyncParallelBailHook(args = [], name = undefined) { 77 | const hook = new Hook(args, name); 78 | hook.constructor = AsyncParallelBailHook; 79 | hook.compile = COMPILE; 80 | hook._call = undefined; 81 | hook.call = undefined; 82 | return hook; 83 | } 84 | 85 | AsyncParallelBailHook.prototype = null; 86 | 87 | module.exports = AsyncParallelBailHook; 88 | -------------------------------------------------------------------------------- /lib/__tests__/SyncBailHook.js: -------------------------------------------------------------------------------- 1 | /* 2 | MIT License http://www.opensource.org/licenses/mit-license.php 3 | Author Tobias Koppers @sokra 4 | */ 5 | "use strict"; 6 | 7 | const SyncBailHook = require("../SyncBailHook"); 8 | 9 | function pify(fn) { 10 | return new Promise((resolve, reject) => { 11 | fn((err, result) => { 12 | if (err) reject(err); 13 | else resolve(result); 14 | }); 15 | }); 16 | } 17 | 18 | describe("SyncBailHook", () => { 19 | it("should allow to create sync bail hooks", async () => { 20 | const h1 = new SyncBailHook(["a"]); 21 | const h2 = new SyncBailHook(["a", "b"]); 22 | 23 | const r = h1.call(1); 24 | expect(r).toBeUndefined(); 25 | 26 | h1.tap("A", (_a) => undefined); 27 | h2.tap("A", (a, b) => [a, b]); 28 | 29 | expect(h1.call(1)).toBeUndefined(); 30 | expect(await h1.promise(1)).toBeUndefined(); 31 | expect(await pify((cb) => h1.callAsync(1, cb))).toBeUndefined(); 32 | expect(h2.call(1, 2)).toEqual([1, 2]); 33 | expect(await h2.promise(1, 2)).toEqual([1, 2]); 34 | expect(await pify((cb) => h2.callAsync(1, 2, cb))).toEqual([1, 2]); 35 | 36 | h1.tap("B", (a) => `ok${a}`); 37 | h2.tap("B", (_a, _b) => "wrong"); 38 | 39 | expect(h1.call(10)).toBe("ok10"); 40 | expect(await h1.promise(10)).toBe("ok10"); 41 | expect(await pify((cb) => h1.callAsync(10, cb))).toBe("ok10"); 42 | expect(h2.call(10, 20)).toEqual([10, 20]); 43 | expect(await h2.promise(10, 20)).toEqual([10, 20]); 44 | expect(await pify((cb) => h2.callAsync(10, 20, cb))).toEqual([10, 20]); 45 | }); 46 | 47 | it("should bail on non-null return", async () => { 48 | const h1 = new SyncBailHook(["a"]); 49 | const mockCall1 = jest.fn(); 50 | const mockCall2 = jest.fn(() => "B"); 51 | const mockCall3 = jest.fn(() => "C"); 52 | h1.tap("A", mockCall1); 53 | h1.tap("B", mockCall2); 54 | h1.tap("C", mockCall3); 55 | expect(h1.call()).toBe("B"); 56 | expect(mockCall1).toHaveBeenCalledTimes(1); 57 | expect(mockCall2).toHaveBeenCalledTimes(1); 58 | expect(mockCall3).toHaveBeenCalledTimes(0); 59 | }); 60 | 61 | it("should allow to intercept calls", () => { 62 | const hook = new SyncBailHook(["x"]); 63 | 64 | const mockCall = jest.fn(); 65 | const mockTap = jest.fn((x) => x); 66 | 67 | hook.intercept({ 68 | call: mockCall, 69 | tap: mockTap 70 | }); 71 | 72 | hook.call(5); 73 | 74 | expect(mockCall).toHaveBeenLastCalledWith(5); 75 | expect(mockTap).not.toHaveBeenCalled(); 76 | 77 | hook.tap("test", () => 10); 78 | 79 | hook.call(7); 80 | 81 | expect(mockCall).toHaveBeenLastCalledWith(7); 82 | expect(mockTap).toHaveBeenCalled(); 83 | }); 84 | 85 | it("should throw on tapAsync", () => { 86 | const hook = new SyncBailHook(["x"]); 87 | expect(() => hook.tapAsync()).toThrow(/tapAsync/); 88 | }); 89 | 90 | it("should throw on tapPromise", () => { 91 | const hook = new SyncBailHook(["x"]); 92 | expect(() => hook.tapPromise()).toThrow(/tapPromise/); 93 | }); 94 | 95 | it("should not crash with many plugins", () => { 96 | const hook = new SyncBailHook(["x"]); 97 | for (let i = 0; i < 1000; i++) { 98 | hook.tap("Test", () => 42); 99 | } 100 | expect(hook.call()).toBe(42); 101 | }); 102 | }); 103 | -------------------------------------------------------------------------------- /lib/__tests__/SyncHook.js: -------------------------------------------------------------------------------- 1 | /* 2 | MIT License http://www.opensource.org/licenses/mit-license.php 3 | Author Tobias Koppers @sokra 4 | */ 5 | "use strict"; 6 | 7 | const SyncHook = require("../SyncHook"); 8 | 9 | describe("SyncHook", () => { 10 | it("should allow to create sync hooks", async () => { 11 | const h0 = new SyncHook(); 12 | const h1 = new SyncHook(["test"]); 13 | const h2 = new SyncHook(["test", "arg2"]); 14 | const h3 = new SyncHook(["test", "arg2", "arg3"]); 15 | 16 | h0.call(); 17 | await h0.promise(); 18 | await new Promise((resolve) => { 19 | h0.callAsync(resolve); 20 | }); 21 | 22 | const mock0 = jest.fn(); 23 | h0.tap("A", mock0); 24 | 25 | h0.call(); 26 | 27 | expect(mock0).toHaveBeenLastCalledWith(); 28 | 29 | const mock1 = jest.fn(); 30 | h0.tap("B", mock1); 31 | 32 | h0.call(); 33 | 34 | expect(mock1).toHaveBeenLastCalledWith(); 35 | 36 | const mock2 = jest.fn(); 37 | const mock3 = jest.fn(); 38 | const mock4 = jest.fn(); 39 | const mock5 = jest.fn(); 40 | 41 | h1.tap("C", mock2); 42 | h2.tap("D", mock3); 43 | h3.tap("E", mock4); 44 | h3.tap("F", mock5); 45 | 46 | h1.call("1"); 47 | h2.call("1", 2); 48 | h3.call("1", 2, 3); 49 | 50 | expect(mock2).toHaveBeenLastCalledWith("1"); 51 | expect(mock3).toHaveBeenLastCalledWith("1", 2); 52 | expect(mock4).toHaveBeenLastCalledWith("1", 2, 3); 53 | expect(mock5).toHaveBeenLastCalledWith("1", 2, 3); 54 | 55 | await new Promise((resolve) => { 56 | h1.callAsync("a", resolve); 57 | }); 58 | await h2.promise("a", "b"); 59 | await new Promise((resolve) => { 60 | h3.callAsync("a", "b", "c", resolve); 61 | }); 62 | 63 | expect(mock2).toHaveBeenLastCalledWith("a"); 64 | expect(mock3).toHaveBeenLastCalledWith("a", "b"); 65 | expect(mock4).toHaveBeenLastCalledWith("a", "b", "c"); 66 | expect(mock5).toHaveBeenLastCalledWith("a", "b", "c"); 67 | 68 | await h3.promise("x", "y"); 69 | 70 | expect(mock4).toHaveBeenLastCalledWith("x", "y", undefined); 71 | expect(mock5).toHaveBeenLastCalledWith("x", "y", undefined); 72 | }); 73 | 74 | it("should sync execute hooks", () => { 75 | const h1 = new SyncHook(["a"]); 76 | const mockCall1 = jest.fn(); 77 | const mockCall2 = jest.fn(() => "B"); 78 | const mockCall3 = jest.fn(() => "C"); 79 | h1.tap("A", mockCall1); 80 | h1.tap("B", mockCall2); 81 | h1.tap("C", mockCall3); 82 | expect(h1.call()).toBeUndefined(); 83 | expect(mockCall1).toHaveBeenCalledTimes(1); 84 | expect(mockCall2).toHaveBeenCalledTimes(1); 85 | expect(mockCall3).toHaveBeenCalledTimes(1); 86 | }); 87 | 88 | it("should allow to intercept calls", () => { 89 | const hook = new SyncHook(["arg1", "arg2"]); 90 | 91 | const mockCall = jest.fn(); 92 | const mock0 = jest.fn(); 93 | const mockRegister = jest.fn((_x) => ({ 94 | name: "huh", 95 | type: "sync", 96 | fn: mock0 97 | })); 98 | 99 | const mock1 = jest.fn(); 100 | hook.tap("Test1", mock1); 101 | 102 | hook.intercept({ 103 | call: mockCall, 104 | register: mockRegister 105 | }); 106 | 107 | const mock2 = jest.fn(); 108 | hook.tap("Test2", mock2); 109 | 110 | hook.call(1, 2); 111 | 112 | expect(mockCall).toHaveBeenLastCalledWith(1, 2); 113 | expect(mockRegister).toHaveBeenLastCalledWith({ 114 | type: "sync", 115 | name: "Test2", 116 | fn: mock2 117 | }); 118 | expect(mock1).not.toHaveBeenLastCalledWith(1, 2); 119 | expect(mock2).not.toHaveBeenLastCalledWith(1, 2); 120 | expect(mock0).toHaveBeenLastCalledWith(1, 2); 121 | }); 122 | 123 | it("should throw error on tapAsync", () => { 124 | const hook = new SyncHook(["arg1", "arg2"]); 125 | expect(() => hook.tapAsync()).toThrow(/tapAsync/); 126 | }); 127 | 128 | it("should throw error on tapPromise", () => { 129 | const hook = new SyncHook(["arg1", "arg2"]); 130 | expect(() => hook.tapPromise()).toThrow(/tapPromise/); 131 | }); 132 | }); 133 | -------------------------------------------------------------------------------- /lib/__tests__/AsyncSeriesHooks.js: -------------------------------------------------------------------------------- 1 | /* 2 | MIT License http://www.opensource.org/licenses/mit-license.php 3 | Author Tobias Koppers @sokra 4 | */ 5 | "use strict"; 6 | 7 | const AsyncSeriesBailHook = require("../AsyncSeriesBailHook"); 8 | const AsyncSeriesHook = require("../AsyncSeriesHook"); 9 | const AsyncSeriesLoopHook = require("../AsyncSeriesLoopHook"); 10 | const AsyncSeriesWaterfallHook = require("../AsyncSeriesWaterfallHook"); 11 | const HookTester = require("./HookTester"); 12 | 13 | describe("AsyncSeriesHook", () => { 14 | it("should not have call method", () => { 15 | const hook = new AsyncSeriesHook([]); 16 | expect(hook.call).toBeUndefined(); 17 | expect(typeof hook.callAsync).toBe("function"); 18 | expect(typeof hook.promise).toBe("function"); 19 | }); 20 | 21 | it("should have tap method", (done) => { 22 | const hook = new AsyncSeriesHook([]); 23 | const mockTap = jest.fn(); 24 | hook.tap("somePlugin", mockTap); 25 | hook.callAsync(() => done()); 26 | expect(mockTap).toHaveBeenCalledTimes(1); 27 | }); 28 | 29 | it("should have promise method", (done) => { 30 | const hook = new AsyncSeriesHook([]); 31 | const mockTap = jest.fn(); 32 | hook.tap("somePlugin", mockTap); 33 | hook.promise().then(() => done()); 34 | expect(mockTap).toHaveBeenCalledTimes(1); 35 | }); 36 | 37 | it("should have to correct behavior", async () => { 38 | const tester = new HookTester((args) => new AsyncSeriesHook(args)); 39 | 40 | const result = await tester.run(); 41 | 42 | expect(result).toMatchSnapshot(); 43 | }); 44 | }); 45 | 46 | describe("AsyncSeriesBailHook", () => { 47 | it("should have to correct behavior", async () => { 48 | const tester = new HookTester((args) => new AsyncSeriesBailHook(args)); 49 | 50 | const result = await tester.run(); 51 | 52 | expect(result).toMatchSnapshot(); 53 | }); 54 | 55 | it("should not crash with many plugins", () => { 56 | const hook = new AsyncSeriesBailHook(["x"]); 57 | for (let i = 0; i < 1000; i++) { 58 | hook.tap("Test", () => 42); 59 | } 60 | hook.tapAsync("Test", (x, callback) => callback(null, 42)); 61 | hook.tapPromise("Test", (_x) => Promise.resolve(42)); 62 | return expect(hook.promise()).resolves.toBe(42); 63 | }); 64 | }); 65 | 66 | describe("AsyncSeriesWaterfallHook", () => { 67 | it("should have to correct behavior", async () => { 68 | const tester = new HookTester((args) => new AsyncSeriesWaterfallHook(args)); 69 | 70 | const result = await tester.run(); 71 | 72 | expect(result).toMatchSnapshot(); 73 | }); 74 | 75 | it("should work with undefined", async () => { 76 | const hook = new AsyncSeriesWaterfallHook(["x"]); 77 | hook.tap("number", () => 42); 78 | hook.tap("undefined", () => undefined); 79 | return expect(hook.promise()).resolves.toBe(42); 80 | }); 81 | 82 | it("should work with void", async () => { 83 | const hook = new AsyncSeriesWaterfallHook(["x"]); 84 | hook.tap("number", () => 42); 85 | hook.tap("undefined", () => {}); 86 | return expect(hook.promise()).resolves.toBe(42); 87 | }); 88 | 89 | it("should work with undefined and number again", async () => { 90 | const hook = new AsyncSeriesWaterfallHook(["x"]); 91 | hook.tap("number", () => 42); 92 | hook.tap("undefined", () => {}); 93 | hook.tap("number-again", () => 43); 94 | return expect(hook.promise()).resolves.toBe(43); 95 | }); 96 | 97 | it("should work with null", async () => { 98 | const hook = new AsyncSeriesWaterfallHook(["x"]); 99 | hook.tap("number", () => 42); 100 | hook.tap("undefined", () => null); 101 | return expect(hook.promise()).resolves.toBeNull(); 102 | }); 103 | 104 | it("should work with different types", async () => { 105 | const hook = new AsyncSeriesWaterfallHook(["x"]); 106 | hook.tap("number", () => 42); 107 | hook.tap("string", () => "string"); 108 | return expect(hook.promise()).resolves.toBe("string"); 109 | }); 110 | }); 111 | 112 | describe("AsyncSeriesLoopHook", () => { 113 | it("should have to correct behavior", async () => { 114 | const tester = new HookTester((args) => new AsyncSeriesLoopHook(args)); 115 | 116 | const result = await tester.runForLoop(); 117 | 118 | expect(result).toMatchSnapshot(); 119 | }); 120 | }); 121 | -------------------------------------------------------------------------------- /lib/Hook.js: -------------------------------------------------------------------------------- 1 | /* 2 | MIT License http://www.opensource.org/licenses/mit-license.php 3 | Author Tobias Koppers @sokra 4 | */ 5 | "use strict"; 6 | 7 | const util = require("util"); 8 | 9 | const deprecateContext = util.deprecate( 10 | () => {}, 11 | "Hook.context is deprecated and will be removed" 12 | ); 13 | 14 | function CALL_DELEGATE(...args) { 15 | this.call = this._createCall("sync"); 16 | return this.call(...args); 17 | } 18 | 19 | function CALL_ASYNC_DELEGATE(...args) { 20 | this.callAsync = this._createCall("async"); 21 | return this.callAsync(...args); 22 | } 23 | 24 | function PROMISE_DELEGATE(...args) { 25 | this.promise = this._createCall("promise"); 26 | return this.promise(...args); 27 | } 28 | 29 | class Hook { 30 | constructor(args = [], name = undefined) { 31 | this._args = args; 32 | this.name = name; 33 | this.taps = []; 34 | this.interceptors = []; 35 | this._call = CALL_DELEGATE; 36 | this.call = CALL_DELEGATE; 37 | this._callAsync = CALL_ASYNC_DELEGATE; 38 | this.callAsync = CALL_ASYNC_DELEGATE; 39 | this._promise = PROMISE_DELEGATE; 40 | this.promise = PROMISE_DELEGATE; 41 | this._x = undefined; 42 | 43 | // eslint-disable-next-line no-self-assign 44 | this.compile = this.compile; 45 | // eslint-disable-next-line no-self-assign 46 | this.tap = this.tap; 47 | // eslint-disable-next-line no-self-assign 48 | this.tapAsync = this.tapAsync; 49 | // eslint-disable-next-line no-self-assign 50 | this.tapPromise = this.tapPromise; 51 | } 52 | 53 | compile(_options) { 54 | throw new Error("Abstract: should be overridden"); 55 | } 56 | 57 | _createCall(type) { 58 | return this.compile({ 59 | taps: this.taps, 60 | interceptors: this.interceptors, 61 | args: this._args, 62 | type 63 | }); 64 | } 65 | 66 | _tap(type, options, fn) { 67 | if (typeof options === "string") { 68 | options = { 69 | name: options.trim() 70 | }; 71 | } else if (typeof options !== "object" || options === null) { 72 | throw new Error("Invalid tap options"); 73 | } 74 | if (typeof options.name !== "string" || options.name === "") { 75 | throw new Error("Missing name for tap"); 76 | } 77 | if (typeof options.context !== "undefined") { 78 | deprecateContext(); 79 | } 80 | options = Object.assign({ type, fn }, options); 81 | options = this._runRegisterInterceptors(options); 82 | this._insert(options); 83 | } 84 | 85 | tap(options, fn) { 86 | this._tap("sync", options, fn); 87 | } 88 | 89 | tapAsync(options, fn) { 90 | this._tap("async", options, fn); 91 | } 92 | 93 | tapPromise(options, fn) { 94 | this._tap("promise", options, fn); 95 | } 96 | 97 | _runRegisterInterceptors(options) { 98 | for (const interceptor of this.interceptors) { 99 | if (interceptor.register) { 100 | const newOptions = interceptor.register(options); 101 | if (newOptions !== undefined) { 102 | options = newOptions; 103 | } 104 | } 105 | } 106 | return options; 107 | } 108 | 109 | withOptions(options) { 110 | const mergeOptions = (opt) => 111 | Object.assign({}, options, typeof opt === "string" ? { name: opt } : opt); 112 | 113 | return { 114 | name: this.name, 115 | tap: (opt, fn) => this.tap(mergeOptions(opt), fn), 116 | tapAsync: (opt, fn) => this.tapAsync(mergeOptions(opt), fn), 117 | tapPromise: (opt, fn) => this.tapPromise(mergeOptions(opt), fn), 118 | intercept: (interceptor) => this.intercept(interceptor), 119 | isUsed: () => this.isUsed(), 120 | withOptions: (opt) => this.withOptions(mergeOptions(opt)) 121 | }; 122 | } 123 | 124 | isUsed() { 125 | return this.taps.length > 0 || this.interceptors.length > 0; 126 | } 127 | 128 | intercept(interceptor) { 129 | this._resetCompilation(); 130 | this.interceptors.push(Object.assign({}, interceptor)); 131 | if (interceptor.register) { 132 | for (let i = 0; i < this.taps.length; i++) { 133 | this.taps[i] = interceptor.register(this.taps[i]); 134 | } 135 | } 136 | } 137 | 138 | _resetCompilation() { 139 | this.call = this._call; 140 | this.callAsync = this._callAsync; 141 | this.promise = this._promise; 142 | } 143 | 144 | _insert(item) { 145 | this._resetCompilation(); 146 | let before; 147 | if (typeof item.before === "string") { 148 | before = new Set([item.before]); 149 | } else if (Array.isArray(item.before)) { 150 | before = new Set(item.before); 151 | } 152 | let stage = 0; 153 | if (typeof item.stage === "number") { 154 | stage = item.stage; 155 | } 156 | let i = this.taps.length; 157 | while (i > 0) { 158 | i--; 159 | const tap = this.taps[i]; 160 | this.taps[i + 1] = tap; 161 | const xStage = tap.stage || 0; 162 | if (before) { 163 | if (before.has(tap.name)) { 164 | before.delete(tap.name); 165 | continue; 166 | } 167 | if (before.size > 0) { 168 | continue; 169 | } 170 | } 171 | if (xStage > stage) { 172 | continue; 173 | } 174 | i++; 175 | break; 176 | } 177 | this.taps[i] = item; 178 | } 179 | } 180 | 181 | Object.setPrototypeOf(Hook.prototype, null); 182 | 183 | module.exports = Hook; 184 | -------------------------------------------------------------------------------- /tapable.d.ts: -------------------------------------------------------------------------------- 1 | type FixedSizeArray = T extends 0 2 | ? void[] 3 | : ReadonlyArray & { 4 | 0: U; 5 | length: T; 6 | }; 7 | type Measure = T extends 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 8 | ? T 9 | : never; 10 | type Append = { 11 | 0: [U]; 12 | 1: [T[0], U]; 13 | 2: [T[0], T[1], U]; 14 | 3: [T[0], T[1], T[2], U]; 15 | 4: [T[0], T[1], T[2], T[3], U]; 16 | 5: [T[0], T[1], T[2], T[3], T[4], U]; 17 | 6: [T[0], T[1], T[2], T[3], T[4], T[5], U]; 18 | 7: [T[0], T[1], T[2], T[3], T[4], T[5], T[6], U]; 19 | 8: [T[0], T[1], T[2], T[3], T[4], T[5], T[6], T[7], U]; 20 | }[Measure]; 21 | type AsArray = T extends any[] ? T : [T]; 22 | 23 | declare class UnsetAdditionalOptions { 24 | _UnsetAdditionalOptions: true; 25 | } 26 | type IfSet = X extends UnsetAdditionalOptions ? {} : X; 27 | 28 | type Callback = (error: E | null, result?: T) => void; 29 | type InnerCallback = (error?: E | null | false, result?: T) => void; 30 | 31 | type FullTap = Tap & { 32 | type: "sync" | "async" | "promise"; 33 | fn: Function; 34 | }; 35 | 36 | type Tap = TapOptions & { 37 | name: string; 38 | }; 39 | 40 | type TapOptions = { 41 | before?: string; 42 | stage?: number; 43 | }; 44 | 45 | interface HookInterceptor { 46 | name?: string; 47 | tap?: (tap: FullTap & IfSet) => void; 48 | call?: (...args: any[]) => void; 49 | loop?: (...args: any[]) => void; 50 | error?: (err: Error) => void; 51 | result?: (result: R) => void; 52 | done?: () => void; 53 | register?: ( 54 | tap: FullTap & IfSet 55 | ) => FullTap & IfSet; 56 | } 57 | 58 | type ArgumentNames = FixedSizeArray; 59 | 60 | declare class Hook { 61 | constructor(args?: ArgumentNames>, name?: string); 62 | name: string | undefined; 63 | interceptors: HookInterceptor[]; 64 | taps: FullTap[]; 65 | intercept(interceptor: HookInterceptor): void; 66 | isUsed(): boolean; 67 | callAsync(...args: Append, Callback>): void; 68 | promise(...args: AsArray): Promise; 69 | tap( 70 | options: string | (Tap & IfSet), 71 | fn: (...args: AsArray) => R 72 | ): void; 73 | withOptions( 74 | options: TapOptions & IfSet 75 | ): Omit; 76 | } 77 | 78 | export class SyncHook< 79 | T, 80 | R = void, 81 | AdditionalOptions = UnsetAdditionalOptions 82 | > extends Hook { 83 | call(...args: AsArray): R; 84 | } 85 | 86 | export class SyncBailHook< 87 | T, 88 | R, 89 | AdditionalOptions = UnsetAdditionalOptions 90 | > extends SyncHook {} 91 | export class SyncLoopHook< 92 | T, 93 | AdditionalOptions = UnsetAdditionalOptions 94 | > extends SyncHook {} 95 | export class SyncWaterfallHook< 96 | T, 97 | R = AsArray[0], 98 | AdditionalOptions = UnsetAdditionalOptions 99 | > extends SyncHook {} 100 | 101 | declare class AsyncHook< 102 | T, 103 | R, 104 | AdditionalOptions = UnsetAdditionalOptions 105 | > extends Hook { 106 | tapAsync( 107 | options: string | (Tap & IfSet), 108 | fn: (...args: Append, InnerCallback>) => void 109 | ): void; 110 | tapPromise( 111 | options: string | (Tap & IfSet), 112 | fn: (...args: AsArray) => Promise 113 | ): void; 114 | } 115 | 116 | export class AsyncParallelHook< 117 | T, 118 | AdditionalOptions = UnsetAdditionalOptions 119 | > extends AsyncHook {} 120 | export class AsyncParallelBailHook< 121 | T, 122 | R, 123 | AdditionalOptions = UnsetAdditionalOptions 124 | > extends AsyncHook {} 125 | export class AsyncSeriesHook< 126 | T, 127 | AdditionalOptions = UnsetAdditionalOptions 128 | > extends AsyncHook {} 129 | export class AsyncSeriesBailHook< 130 | T, 131 | R, 132 | AdditionalOptions = UnsetAdditionalOptions 133 | > extends AsyncHook {} 134 | export class AsyncSeriesLoopHook< 135 | T, 136 | AdditionalOptions = UnsetAdditionalOptions 137 | > extends AsyncHook {} 138 | export class AsyncSeriesWaterfallHook< 139 | T, 140 | R = AsArray[0], 141 | AdditionalOptions = UnsetAdditionalOptions 142 | > extends AsyncHook {} 143 | 144 | type HookFactory = (key: K) => H; 145 | 146 | interface HookMapInterceptor { 147 | factory?: (key: K, hook: H) => H; 148 | } 149 | 150 | export class HookMap { 151 | constructor(factory: HookFactory, name?: string); 152 | name: string | undefined; 153 | get(key: any): H | undefined; 154 | for(key: any): H; 155 | intercept(interceptor: HookMapInterceptor): void; 156 | } 157 | 158 | type AnyHook = Hook; 159 | 160 | export class TypedHookMap> { 161 | constructor(factory: HookFactory, name?: string); 162 | name: string | undefined; 163 | get(key: K): M[K] | undefined; 164 | for(key: K): M[K]; 165 | intercept(interceptor: HookMapInterceptor): void; 166 | } 167 | 168 | export class MultiHook { 169 | constructor(hooks: H[], name?: string); 170 | name: string | undefined; 171 | tap(options: string | Tap, fn?: Function): void; 172 | tapAsync(options: string | Tap, fn?: Function): void; 173 | tapPromise(options: string | Tap, fn?: Function): void; 174 | } 175 | -------------------------------------------------------------------------------- /lib/__tests__/SyncWaterfallHook.js: -------------------------------------------------------------------------------- 1 | /* 2 | MIT License http://www.opensource.org/licenses/mit-license.php 3 | Author Tobias Koppers @sokra 4 | */ 5 | "use strict"; 6 | 7 | const SyncWaterfallHook = require("../SyncWaterfallHook"); 8 | 9 | function pify(fn) { 10 | return new Promise((resolve, reject) => { 11 | fn((err, result) => { 12 | if (err) reject(err); 13 | else resolve(result); 14 | }); 15 | }); 16 | } 17 | 18 | describe("SyncWaterfallHook", () => { 19 | it("should throw an error when hook has no argument", () => { 20 | expect(() => new SyncWaterfallHook()).toThrow( 21 | "Waterfall hooks must have at least one argument" 22 | ); 23 | }); 24 | 25 | it("should work", () => { 26 | const hook = new SyncWaterfallHook(["x"]); 27 | hook.tap("number", () => 42); 28 | hook.tap("string", () => "str"); 29 | hook.tap("false", () => false); 30 | return expect(hook.call()).toBe(false); 31 | }); 32 | 33 | it("should work with undefined", async () => { 34 | const hook = new SyncWaterfallHook(["x"]); 35 | hook.tap("number", () => 42); 36 | hook.tap("undefined", () => undefined); 37 | return expect(hook.call()).toBe(42); 38 | }); 39 | 40 | it("should work with void", async () => { 41 | const hook = new SyncWaterfallHook(["x"]); 42 | hook.tap("number", () => 42); 43 | hook.tap("undefined", () => {}); 44 | return expect(hook.call()).toBe(42); 45 | }); 46 | 47 | it("should work with undefined and number again", async () => { 48 | const hook = new SyncWaterfallHook(["x"]); 49 | hook.tap("number", () => 42); 50 | hook.tap("undefined", () => {}); 51 | hook.tap("number-again", () => 43); 52 | return expect(hook.call()).toBe(43); 53 | }); 54 | 55 | it("should work with null", async () => { 56 | const hook = new SyncWaterfallHook(["x"]); 57 | hook.tap("number", () => 42); 58 | hook.tap("undefined", () => null); 59 | return expect(hook.call()).toBeNull(); 60 | }); 61 | 62 | it("should work with different types", async () => { 63 | const hook = new SyncWaterfallHook(["x"]); 64 | hook.tap("number", () => 42); 65 | hook.tap("string", () => "string"); 66 | return expect(hook.call()).toBe("string"); 67 | }); 68 | 69 | it("should allow to create sync hooks", async () => { 70 | const hook = new SyncWaterfallHook(["arg1", "arg2"]); 71 | 72 | const mock0 = jest.fn((arg) => `${arg},0`); 73 | const mock1 = jest.fn((arg) => `${arg},1`); 74 | const mock2 = jest.fn((arg) => `${arg},2`); 75 | hook.tap("A", mock0); 76 | hook.tap("B", mock1); 77 | hook.tap("C", mock2); 78 | 79 | const returnValue0 = hook.call("sync", "a2"); 80 | expect(returnValue0).toBe("sync,0,1,2"); 81 | expect(mock0).toHaveBeenLastCalledWith("sync", "a2"); 82 | expect(mock1).toHaveBeenLastCalledWith("sync,0", "a2"); 83 | expect(mock2).toHaveBeenLastCalledWith("sync,0,1", "a2"); 84 | 85 | const returnValue1 = await new Promise((resolve) => { 86 | hook.callAsync("async", "a2", (...args) => resolve(args)); 87 | }); 88 | 89 | expect(returnValue1).toEqual([null, "async,0,1,2"]); 90 | expect(mock0).toHaveBeenLastCalledWith("async", "a2"); 91 | expect(mock1).toHaveBeenLastCalledWith("async,0", "a2"); 92 | expect(mock2).toHaveBeenLastCalledWith("async,0,1", "a2"); 93 | 94 | const returnValue2 = await hook.promise("promise", "a2"); 95 | 96 | expect(returnValue2).toBe("promise,0,1,2"); 97 | expect(mock0).toHaveBeenLastCalledWith("promise", "a2"); 98 | expect(mock1).toHaveBeenLastCalledWith("promise,0", "a2"); 99 | expect(mock2).toHaveBeenLastCalledWith("promise,0,1", "a2"); 100 | }); 101 | 102 | it("should allow to intercept calls", () => { 103 | const hook = new SyncWaterfallHook(["arg1", "arg2"]); 104 | 105 | const mockCall = jest.fn(); 106 | const mock0 = jest.fn(() => "mock0"); 107 | const mockRegister = jest.fn((_x) => ({ 108 | name: "huh", 109 | type: "sync", 110 | fn: mock0 111 | })); 112 | 113 | const mock1 = jest.fn(() => "mock1"); 114 | hook.tap("Test1", mock1); 115 | 116 | hook.intercept({ 117 | call: mockCall, 118 | register: mockRegister 119 | }); 120 | 121 | const mock2 = jest.fn(() => "mock2"); 122 | hook.tap("Test2", mock2); 123 | 124 | const returnValue = hook.call(1, 2); 125 | 126 | expect(returnValue).toBe("mock0"); 127 | expect(mockCall).toHaveBeenLastCalledWith(1, 2); 128 | expect(mockRegister).toHaveBeenLastCalledWith({ 129 | type: "sync", 130 | name: "Test2", 131 | fn: mock2 132 | }); 133 | expect(mock1).not.toHaveBeenLastCalledWith(1, 2); 134 | expect(mock2).not.toHaveBeenLastCalledWith(1, 2); 135 | expect(mock0.mock.calls).toEqual([ 136 | [1, 2], 137 | ["mock0", 2] 138 | ]); 139 | }); 140 | 141 | it("should allow to create waterfall hooks", async () => { 142 | const h1 = new SyncWaterfallHook(["a"]); 143 | const h2 = new SyncWaterfallHook(["a", "b"]); 144 | 145 | expect(h1.call(1)).toBe(1); 146 | 147 | h1.tap("A", (_a) => undefined); 148 | h2.tap("A", (a, b) => [a, b]); 149 | 150 | expect(h1.call(1)).toBe(1); 151 | expect(await h1.promise(1)).toBe(1); 152 | expect(await pify((cb) => h1.callAsync(1, cb))).toBe(1); 153 | expect(h2.call(1, 2)).toEqual([1, 2]); 154 | expect(await h2.promise(1, 2)).toEqual([1, 2]); 155 | expect(await pify((cb) => h2.callAsync(1, 2, cb))).toEqual([1, 2]); 156 | 157 | let count = 1; 158 | count = h1.call(count + ++count); // 1 + 2 => 3 159 | count = h1.call(count + ++count); // 3 + 4 => 7 160 | count = h1.call(count + ++count); // 7 + 8 => 15 161 | expect(count).toBe(15); 162 | }); 163 | 164 | it("should throw when args have length less than 1", () => { 165 | expect(() => { 166 | // eslint-disable-next-line no-new 167 | new SyncWaterfallHook([]); 168 | }).toThrow(/Waterfall/); 169 | }); 170 | 171 | it("should allow to intercept calls #2", () => { 172 | const hook = new SyncWaterfallHook(["x"]); 173 | 174 | const mockCall = jest.fn(); 175 | const mockTap = jest.fn((x) => x); 176 | 177 | hook.intercept({ 178 | call: mockCall, 179 | tap: mockTap 180 | }); 181 | 182 | hook.call(5); 183 | 184 | expect(mockCall).toHaveBeenLastCalledWith(5); 185 | expect(mockTap).not.toHaveBeenCalled(); 186 | 187 | hook.tap("test", () => 10); 188 | 189 | hook.call(7); 190 | 191 | expect(mockCall).toHaveBeenLastCalledWith(7); 192 | expect(mockTap).toHaveBeenCalled(); 193 | }); 194 | 195 | it("should throw on tapAsync", () => { 196 | const hook = new SyncWaterfallHook(["x"]); 197 | expect(() => hook.tapAsync()).toThrow(/tapAsync/); 198 | }); 199 | 200 | it("should throw on tapPromise", () => { 201 | const hook = new SyncWaterfallHook(["x"]); 202 | expect(() => hook.tapPromise()).toThrow(/tapPromise/); 203 | }); 204 | }); 205 | -------------------------------------------------------------------------------- /lib/__tests__/HookCodeFactory.js: -------------------------------------------------------------------------------- 1 | /* 2 | MIT License http://www.opensource.org/licenses/mit-license.php 3 | Author Tobias Koppers @sokra 4 | */ 5 | "use strict"; 6 | 7 | const HookCodeFactory = require("../HookCodeFactory"); 8 | 9 | const expectNoSyntaxError = (code) => { 10 | // eslint-disable-next-line no-new 11 | new Function("a, b, c", code); 12 | }; 13 | 14 | const pretty = (code) => 15 | require("prettier-1").format(code, { filepath: "code.js" }); 16 | 17 | describe("HookCodeFactory", () => { 18 | describe("callTap", () => { 19 | const factoryConfigurations = { 20 | "no args, no intercept": { 21 | args: [], 22 | taps: [ 23 | { 24 | type: "sync" 25 | }, 26 | { 27 | type: "async" 28 | }, 29 | { 30 | type: "promise" 31 | } 32 | ], 33 | interceptors: [] 34 | }, 35 | "with args, no intercept": { 36 | args: ["a", "b", "c"], 37 | taps: [ 38 | { 39 | type: "sync" 40 | }, 41 | { 42 | type: "async" 43 | }, 44 | { 45 | type: "promise" 46 | } 47 | ], 48 | interceptors: [] 49 | }, 50 | "with args, with intercept": { 51 | args: ["a", "b", "c"], 52 | taps: [ 53 | { 54 | type: "sync" 55 | }, 56 | { 57 | type: "async" 58 | }, 59 | { 60 | type: "promise" 61 | } 62 | ], 63 | interceptors: [ 64 | { 65 | call: () => {}, 66 | tap: () => {} 67 | }, 68 | { 69 | tap: () => {} 70 | }, 71 | { 72 | call: () => {} 73 | } 74 | ] 75 | } 76 | }; 77 | 78 | for (const configurationName in factoryConfigurations) { 79 | describe(`(${configurationName})`, () => { 80 | let factory; 81 | 82 | beforeEach(() => { 83 | factory = new HookCodeFactory(); 84 | factory.init(factoryConfigurations[configurationName]); 85 | }); 86 | 87 | it("sync without onResult", () => { 88 | const code = factory.callTap(0, { 89 | onError: (err) => `onError(${err});\n`, 90 | onDone: () => "onDone();\n" 91 | }); 92 | expect(code).toMatchSnapshot(); 93 | expect(pretty(code)).toMatchSnapshot(); 94 | expectNoSyntaxError(code); 95 | }); 96 | 97 | it("sync with onResult", () => { 98 | const code = factory.callTap(0, { 99 | onError: (err) => `onError(${err});\n`, 100 | onResult: (result) => `onResult(${result});\n` 101 | }); 102 | expect(code).toMatchSnapshot(); 103 | expect(pretty(code)).toMatchSnapshot(); 104 | expectNoSyntaxError(code); 105 | }); 106 | 107 | it("async without onResult", () => { 108 | const code = factory.callTap(1, { 109 | onError: (err) => `onError(${err});\n`, 110 | onDone: () => "onDone();\n" 111 | }); 112 | expect(code).toMatchSnapshot(); 113 | expect(pretty(code)).toMatchSnapshot(); 114 | expectNoSyntaxError(code); 115 | }); 116 | 117 | it("async with onResult", () => { 118 | const code = factory.callTap(1, { 119 | onError: (err) => `onError(${err});\n`, 120 | onResult: (result) => `onResult(${result});\n` 121 | }); 122 | expect(code).toMatchSnapshot(); 123 | expect(pretty(code)).toMatchSnapshot(); 124 | expectNoSyntaxError(code); 125 | }); 126 | 127 | it("promise without onResult", () => { 128 | const code = factory.callTap(2, { 129 | onError: (err) => `onError(${err});\n`, 130 | onDone: () => "onDone();\n" 131 | }); 132 | expect(code).toMatchSnapshot(); 133 | expect(pretty(code)).toMatchSnapshot(); 134 | expectNoSyntaxError(code); 135 | }); 136 | 137 | it("promise with onResult", () => { 138 | const code = factory.callTap(2, { 139 | onError: (err) => `onError(${err});\n`, 140 | onResult: (result) => `onResult(${result});\n` 141 | }); 142 | expect(code).toMatchSnapshot(); 143 | expect(pretty(code)).toMatchSnapshot(); 144 | expectNoSyntaxError(code); 145 | }); 146 | }); 147 | } 148 | }); 149 | 150 | describe("taps", () => { 151 | const factoryConfigurations = { 152 | none: { 153 | args: ["a", "b", "c"], 154 | taps: [], 155 | interceptors: [] 156 | }, 157 | "single sync": { 158 | args: ["a", "b", "c"], 159 | taps: [ 160 | { 161 | type: "sync" 162 | } 163 | ], 164 | interceptors: [] 165 | }, 166 | "multiple sync": { 167 | args: ["a", "b", "c"], 168 | taps: [ 169 | { 170 | type: "sync" 171 | }, 172 | { 173 | type: "sync" 174 | }, 175 | { 176 | type: "sync" 177 | } 178 | ], 179 | interceptors: [] 180 | }, 181 | "single async": { 182 | args: ["a", "b", "c"], 183 | taps: [ 184 | { 185 | type: "async" 186 | } 187 | ], 188 | interceptors: [] 189 | }, 190 | "single promise": { 191 | args: ["a", "b", "c"], 192 | taps: [ 193 | { 194 | type: "promise" 195 | } 196 | ], 197 | interceptors: [] 198 | }, 199 | mixed: { 200 | args: ["a", "b", "c"], 201 | taps: [ 202 | { 203 | type: "sync" 204 | }, 205 | { 206 | type: "async" 207 | }, 208 | { 209 | type: "promise" 210 | } 211 | ], 212 | interceptors: [] 213 | }, 214 | mixed2: { 215 | args: ["a", "b", "c"], 216 | taps: [ 217 | { 218 | type: "async" 219 | }, 220 | { 221 | type: "promise" 222 | }, 223 | { 224 | type: "sync" 225 | } 226 | ], 227 | interceptors: [] 228 | } 229 | }; 230 | 231 | for (const configurationName in factoryConfigurations) { 232 | describe(`(${configurationName})`, () => { 233 | let factory; 234 | 235 | beforeEach(() => { 236 | factory = new HookCodeFactory(); 237 | factory.init(factoryConfigurations[configurationName]); 238 | }); 239 | 240 | it("callTapsSeries", () => { 241 | const code = factory.callTapsSeries({ 242 | onError: (i, err) => `onError(${i}, ${err});\n`, 243 | onResult: (i, result, next, doneBreak) => 244 | `onResult(${i}, ${result}, () => {\n${next()}}, () => {\n${doneBreak()}});\n`, 245 | onDone: () => "onDone();\n", 246 | rethrowIfPossible: true 247 | }); 248 | expect(code).toMatchSnapshot(); 249 | expect(pretty(code)).toMatchSnapshot(); 250 | expectNoSyntaxError(code); 251 | }); 252 | 253 | it("callTapsParallel", () => { 254 | const code = factory.callTapsParallel({ 255 | onError: (i, err) => `onError(${i}, ${err});\n`, 256 | onResult: (i, result, done, doneBreak) => 257 | `onResult(${i}, ${result}, () => {\n${done()}}, () => {\n${doneBreak()}});\n`, 258 | onDone: () => "onDone();\n", 259 | rethrowIfPossible: true 260 | }); 261 | expect(code).toMatchSnapshot(); 262 | expect(pretty(code)).toMatchSnapshot(); 263 | expectNoSyntaxError(code); 264 | }); 265 | 266 | it("callTapsLooping", () => { 267 | const code = factory.callTapsLooping({ 268 | onError: (i, err) => `onError(${i}, ${err});\n`, 269 | onDone: () => "onDone();\n", 270 | rethrowIfPossible: true 271 | }); 272 | expect(code).toMatchSnapshot(); 273 | expect(pretty(code)).toMatchSnapshot(); 274 | expectNoSyntaxError(code); 275 | }); 276 | }); 277 | } 278 | }); 279 | }); 280 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Tapable 2 | 3 | The tapable package exposes many Hook classes, which can be used to create hooks for plugins. 4 | 5 | ```javascript 6 | const { 7 | AsyncParallelBailHook, 8 | AsyncParallelHook, 9 | AsyncSeriesBailHook, 10 | AsyncSeriesHook, 11 | AsyncSeriesWaterfallHook, 12 | SyncBailHook, 13 | SyncHook, 14 | SyncLoopHook, 15 | SyncWaterfallHook 16 | } = require("tapable"); 17 | ``` 18 | 19 | ## Installation 20 | 21 | ```shell 22 | npm install --save tapable 23 | ``` 24 | 25 | ## Usage 26 | 27 | All Hook constructors take one optional argument, which is a list of argument names as strings. 28 | 29 | ```js 30 | const hook = new SyncHook(["arg1", "arg2", "arg3"]); 31 | ``` 32 | 33 | The best practice is to expose all hooks of a class in a `hooks` property: 34 | 35 | ```js 36 | class Car { 37 | constructor() { 38 | this.hooks = { 39 | accelerate: new SyncHook(["newSpeed"]), 40 | brake: new SyncHook(), 41 | calculateRoutes: new AsyncParallelHook(["source", "target", "routesList"]) 42 | }; 43 | } 44 | 45 | /* ... */ 46 | } 47 | ``` 48 | 49 | Other people can now use these hooks: 50 | 51 | ```js 52 | const myCar = new Car(); 53 | 54 | // Use the tap method to add a consument 55 | myCar.hooks.brake.tap("WarningLampPlugin", () => warningLamp.on()); 56 | ``` 57 | 58 | It's required to pass a name to identify the plugin/reason. 59 | 60 | You may receive arguments: 61 | 62 | ```js 63 | myCar.hooks.accelerate.tap("LoggerPlugin", (newSpeed) => 64 | console.log(`Accelerating to ${newSpeed}`) 65 | ); 66 | ``` 67 | 68 | For sync hooks, `tap` is the only valid method to add a plugin. Async hooks also support async plugins: 69 | 70 | ```js 71 | myCar.hooks.calculateRoutes.tapPromise( 72 | "GoogleMapsPlugin", 73 | (source, target, routesList) => 74 | // return a promise 75 | google.maps.findRoute(source, target).then((route) => { 76 | routesList.add(route); 77 | }) 78 | ); 79 | myCar.hooks.calculateRoutes.tapAsync( 80 | "BingMapsPlugin", 81 | (source, target, routesList, callback) => { 82 | bing.findRoute(source, target, (err, route) => { 83 | if (err) return callback(err); 84 | routesList.add(route); 85 | // call the callback 86 | callback(); 87 | }); 88 | } 89 | ); 90 | 91 | // You can still use sync plugins 92 | myCar.hooks.calculateRoutes.tap( 93 | "CachedRoutesPlugin", 94 | (source, target, routesList) => { 95 | const cachedRoute = cache.get(source, target); 96 | if (cachedRoute) routesList.add(cachedRoute); 97 | } 98 | ); 99 | ``` 100 | 101 | The class declaring these hooks needs to call them: 102 | 103 | ```js 104 | class Car { 105 | /** 106 | * You won't get returned value from SyncHook or AsyncParallelHook, 107 | * to do that, use SyncWaterfallHook and AsyncSeriesWaterfallHook respectively 108 | */ 109 | 110 | setSpeed(newSpeed) { 111 | // following call returns undefined even when you returned values 112 | this.hooks.accelerate.call(newSpeed); 113 | } 114 | 115 | useNavigationSystemPromise(source, target) { 116 | const routesList = new List(); 117 | return this.hooks.calculateRoutes 118 | .promise(source, target, routesList) 119 | .then((res) => 120 | // res is undefined for AsyncParallelHook 121 | routesList.getRoutes() 122 | ); 123 | } 124 | 125 | useNavigationSystemAsync(source, target, callback) { 126 | const routesList = new List(); 127 | this.hooks.calculateRoutes.callAsync(source, target, routesList, (err) => { 128 | if (err) return callback(err); 129 | callback(null, routesList.getRoutes()); 130 | }); 131 | } 132 | } 133 | ``` 134 | 135 | The Hook will compile a method with the most efficient way of running your plugins. It generates code depending on: 136 | 137 | - The number of registered plugins (none, one, many) 138 | - The kind of registered plugins (sync, async, promise) 139 | - The used call method (sync, async, promise) 140 | - The number of arguments 141 | - Whether interception is used 142 | 143 | This ensures fastest possible execution. 144 | 145 | ## Hook types 146 | 147 | Each hook can be tapped with one or several functions. How they are executed depends on the hook type: 148 | 149 | - Basic hook (without “Waterfall”, “Bail” or “Loop” in its name). This hook simply calls every function it tapped in a row. 150 | 151 | - **Waterfall**. A waterfall hook also calls each tapped function in a row. Unlike the basic hook, it passes a return value from each function to the next function. 152 | 153 | - **Bail**. A bail hook allows exiting early. When any of the tapped function returns anything, the bail hook will stop executing the remaining ones. 154 | 155 | - **Loop**. When a plugin in a loop hook returns a non-undefined value the hook will restart from the first plugin. It will loop until all plugins return undefined. 156 | 157 | Additionally, hooks can be synchronous or asynchronous. To reflect this, there’re “Sync”, “AsyncSeries”, and “AsyncParallel” hook classes: 158 | 159 | - **Sync**. A sync hook can only be tapped with synchronous functions (using `myHook.tap()`). 160 | 161 | - **AsyncSeries**. An async-series hook can be tapped with synchronous, callback-based and promise-based functions (using `myHook.tap()`, `myHook.tapAsync()` and `myHook.tapPromise()`). They call each async method in a row. 162 | 163 | - **AsyncParallel**. An async-parallel hook can also be tapped with synchronous, callback-based and promise-based functions (using `myHook.tap()`, `myHook.tapAsync()` and `myHook.tapPromise()`). However, they run each async method in parallel. 164 | 165 | The hook type is reflected in its class name. E.g., `AsyncSeriesWaterfallHook` allows asynchronous functions and runs them in series, passing each function’s return value into the next function. 166 | 167 | ## Interception 168 | 169 | All Hooks offer an additional interception API: 170 | 171 | ```js 172 | myCar.hooks.calculateRoutes.intercept({ 173 | call: (source, target, routesList) => { 174 | console.log("Starting to calculate routes"); 175 | }, 176 | register: (tapInfo) => { 177 | // tapInfo = { type: "promise", name: "GoogleMapsPlugin", fn: ... } 178 | console.log(`${tapInfo.name} is doing its job`); 179 | return tapInfo; // may return a new tapInfo object 180 | } 181 | }); 182 | ``` 183 | 184 | **call**: `(...args) => void` Adding `call` to your interceptor will trigger when hooks are triggered. You have access to the hooks arguments. 185 | 186 | **tap**: `(tap: Tap) => void` Adding `tap` to your interceptor will trigger when a plugin taps into a hook. Provided is the `Tap` object. `Tap` object can't be changed. 187 | 188 | **loop**: `(...args) => void` Adding `loop` to your interceptor will trigger for each loop of a looping hook. 189 | 190 | **register**: `(tap: Tap) => Tap | undefined` Adding `register` to your interceptor will trigger for each added `Tap` and allows to modify it. 191 | 192 | ## Context 193 | 194 | Plugins and interceptors can opt-in to access an optional `context` object, which can be used to pass arbitrary values to subsequent plugins and interceptors. 195 | 196 | ```js 197 | myCar.hooks.accelerate.intercept({ 198 | context: true, 199 | tap: (context, tapInfo) => { 200 | // tapInfo = { type: "sync", name: "NoisePlugin", fn: ... } 201 | console.log(`${tapInfo.name} is doing it's job`); 202 | 203 | // `context` starts as an empty object if at least one plugin uses `context: true`. 204 | // If no plugins use `context: true`, then `context` is undefined. 205 | if (context) { 206 | // Arbitrary properties can be added to `context`, which plugins can then access. 207 | context.hasMuffler = true; 208 | } 209 | } 210 | }); 211 | 212 | myCar.hooks.accelerate.tap( 213 | { 214 | name: "NoisePlugin", 215 | context: true 216 | }, 217 | (context, newSpeed) => { 218 | if (context && context.hasMuffler) { 219 | console.log("Silence..."); 220 | } else { 221 | console.log("Vroom!"); 222 | } 223 | } 224 | ); 225 | ``` 226 | 227 | ## HookMap 228 | 229 | A HookMap is a helper class for a Map with Hooks 230 | 231 | ```js 232 | const keyedHook = new HookMap((key) => new SyncHook(["arg"])); 233 | ``` 234 | 235 | ```js 236 | keyedHook.for("some-key").tap("MyPlugin", (arg) => { 237 | /* ... */ 238 | }); 239 | keyedHook.for("some-key").tapAsync("MyPlugin", (arg, callback) => { 240 | /* ... */ 241 | }); 242 | keyedHook.for("some-key").tapPromise("MyPlugin", (arg) => { 243 | /* ... */ 244 | }); 245 | ``` 246 | 247 | ```js 248 | const hook = keyedHook.get("some-key"); 249 | if (hook !== undefined) { 250 | hook.callAsync("arg", (err) => { 251 | /* ... */ 252 | }); 253 | } 254 | ``` 255 | 256 | ## Hook/HookMap interface 257 | 258 | Public: 259 | 260 | ```ts 261 | interface Hook { 262 | tap: (name: string | Tap, fn: (context?, ...args) => Result) => void; 263 | tapAsync: ( 264 | name: string | Tap, 265 | fn: ( 266 | context?, 267 | ...args, 268 | callback: (err: Error | null, result: Result) => void 269 | ) => void 270 | ) => void; 271 | tapPromise: ( 272 | name: string | Tap, 273 | fn: (context?, ...args) => Promise 274 | ) => void; 275 | intercept: (interceptor: HookInterceptor) => void; 276 | } 277 | 278 | interface HookInterceptor { 279 | call: (context?, ...args) => void; 280 | loop: (context?, ...args) => void; 281 | tap: (context?, tap: Tap) => void; 282 | register: (tap: Tap) => Tap; 283 | context: boolean; 284 | } 285 | 286 | interface HookMap { 287 | for: (key: any) => Hook; 288 | intercept: (interceptor: HookMapInterceptor) => void; 289 | } 290 | 291 | interface HookMapInterceptor { 292 | factory: (key: any, hook: Hook) => Hook; 293 | } 294 | 295 | interface Tap { 296 | name: string; 297 | type: string; 298 | fn: Function; 299 | stage: number; 300 | context: boolean; 301 | before?: string | Array; 302 | } 303 | ``` 304 | 305 | Protected (only for the class containing the hook): 306 | 307 | ```ts 308 | interface Hook { 309 | isUsed: () => boolean; 310 | call: (...args) => Result; 311 | promise: (...args) => Promise; 312 | callAsync: ( 313 | ...args, 314 | callback: (err: Error | null, result: Result) => void 315 | ) => void; 316 | } 317 | 318 | interface HookMap { 319 | get: (key: any) => Hook | undefined; 320 | for: (key: any) => Hook; 321 | } 322 | ``` 323 | 324 | ## MultiHook 325 | 326 | A helper Hook-like class to redirect taps to multiple other hooks: 327 | 328 | ```js 329 | const { MultiHook } = require("tapable"); 330 | 331 | this.hooks.allHooks = new MultiHook([this.hooks.hookA, this.hooks.hookB]); 332 | ``` 333 | -------------------------------------------------------------------------------- /lib/HookCodeFactory.js: -------------------------------------------------------------------------------- 1 | /* 2 | MIT License http://www.opensource.org/licenses/mit-license.php 3 | Author Tobias Koppers @sokra 4 | */ 5 | "use strict"; 6 | 7 | class HookCodeFactory { 8 | constructor(config) { 9 | this.config = config; 10 | this.options = undefined; 11 | this._args = undefined; 12 | } 13 | 14 | create(options) { 15 | this.init(options); 16 | let fn; 17 | switch (this.options.type) { 18 | case "sync": 19 | fn = new Function( 20 | this.args(), 21 | `"use strict";\n${this.header()}${this.contentWithInterceptors({ 22 | onError: (err) => `throw ${err};\n`, 23 | onResult: (result) => `return ${result};\n`, 24 | resultReturns: true, 25 | onDone: () => "", 26 | rethrowIfPossible: true 27 | })}` 28 | ); 29 | break; 30 | case "async": 31 | fn = new Function( 32 | this.args({ 33 | after: "_callback" 34 | }), 35 | `"use strict";\n${this.header()}${this.contentWithInterceptors({ 36 | onError: (err) => `_callback(${err});\n`, 37 | onResult: (result) => `_callback(null, ${result});\n`, 38 | onDone: () => "_callback();\n" 39 | })}` 40 | ); 41 | break; 42 | case "promise": { 43 | let errorHelperUsed = false; 44 | const content = this.contentWithInterceptors({ 45 | onError: (err) => { 46 | errorHelperUsed = true; 47 | return `_error(${err});\n`; 48 | }, 49 | onResult: (result) => `_resolve(${result});\n`, 50 | onDone: () => "_resolve();\n" 51 | }); 52 | let code = ""; 53 | code += '"use strict";\n'; 54 | code += this.header(); 55 | code += "return new Promise((function(_resolve, _reject) {\n"; 56 | if (errorHelperUsed) { 57 | code += "var _sync = true;\n"; 58 | code += "function _error(_err) {\n"; 59 | code += "if(_sync)\n"; 60 | code += 61 | "_resolve(Promise.resolve().then((function() { throw _err; })));\n"; 62 | code += "else\n"; 63 | code += "_reject(_err);\n"; 64 | code += "};\n"; 65 | } 66 | code += content; 67 | if (errorHelperUsed) { 68 | code += "_sync = false;\n"; 69 | } 70 | code += "}));\n"; 71 | fn = new Function(this.args(), code); 72 | break; 73 | } 74 | } 75 | this.deinit(); 76 | return fn; 77 | } 78 | 79 | setup(instance, options) { 80 | instance._x = options.taps.map((t) => t.fn); 81 | } 82 | 83 | /** 84 | * @param {{ type: "sync" | "promise" | "async", taps: Array, interceptors: Array }} options 85 | */ 86 | init(options) { 87 | this.options = options; 88 | this._args = [...options.args]; 89 | } 90 | 91 | deinit() { 92 | this.options = undefined; 93 | this._args = undefined; 94 | } 95 | 96 | contentWithInterceptors(options) { 97 | if (this.options.interceptors.length > 0) { 98 | const { onError, onResult, onDone } = options; 99 | let code = ""; 100 | for (let i = 0; i < this.options.interceptors.length; i++) { 101 | const interceptor = this.options.interceptors[i]; 102 | if (interceptor.call) { 103 | code += `${this.getInterceptor(i)}.call(${this.args({ 104 | before: interceptor.context ? "_context" : undefined 105 | })});\n`; 106 | } 107 | } 108 | code += this.content( 109 | Object.assign(options, { 110 | onError: 111 | onError && 112 | ((err) => { 113 | let code = ""; 114 | for (let i = 0; i < this.options.interceptors.length; i++) { 115 | const interceptor = this.options.interceptors[i]; 116 | if (interceptor.error) { 117 | code += `${this.getInterceptor(i)}.error(${err});\n`; 118 | } 119 | } 120 | code += onError(err); 121 | return code; 122 | }), 123 | onResult: 124 | onResult && 125 | ((result) => { 126 | let code = ""; 127 | for (let i = 0; i < this.options.interceptors.length; i++) { 128 | const interceptor = this.options.interceptors[i]; 129 | if (interceptor.result) { 130 | code += `${this.getInterceptor(i)}.result(${result});\n`; 131 | } 132 | } 133 | code += onResult(result); 134 | return code; 135 | }), 136 | onDone: 137 | onDone && 138 | (() => { 139 | let code = ""; 140 | for (let i = 0; i < this.options.interceptors.length; i++) { 141 | const interceptor = this.options.interceptors[i]; 142 | if (interceptor.done) { 143 | code += `${this.getInterceptor(i)}.done();\n`; 144 | } 145 | } 146 | code += onDone(); 147 | return code; 148 | }) 149 | }) 150 | ); 151 | return code; 152 | } 153 | return this.content(options); 154 | } 155 | 156 | header() { 157 | let code = ""; 158 | code += this.needContext() ? "var _context = {};\n" : "var _context;\n"; 159 | code += "var _x = this._x;\n"; 160 | if (this.options.interceptors.length > 0) { 161 | code += "var _taps = this.taps;\n"; 162 | code += "var _interceptors = this.interceptors;\n"; 163 | } 164 | return code; 165 | } 166 | 167 | needContext() { 168 | for (const tap of this.options.taps) if (tap.context) return true; 169 | return false; 170 | } 171 | 172 | callTap(tapIndex, { onError, onResult, onDone, rethrowIfPossible }) { 173 | let code = ""; 174 | let hasTapCached = false; 175 | for (let i = 0; i < this.options.interceptors.length; i++) { 176 | const interceptor = this.options.interceptors[i]; 177 | if (interceptor.tap) { 178 | if (!hasTapCached) { 179 | code += `var _tap${tapIndex} = ${this.getTap(tapIndex)};\n`; 180 | hasTapCached = true; 181 | } 182 | code += `${this.getInterceptor(i)}.tap(${ 183 | interceptor.context ? "_context, " : "" 184 | }_tap${tapIndex});\n`; 185 | } 186 | } 187 | code += `var _fn${tapIndex} = ${this.getTapFn(tapIndex)};\n`; 188 | const tap = this.options.taps[tapIndex]; 189 | switch (tap.type) { 190 | case "sync": 191 | if (!rethrowIfPossible) { 192 | code += `var _hasError${tapIndex} = false;\n`; 193 | code += "try {\n"; 194 | } 195 | if (onResult) { 196 | code += `var _result${tapIndex} = _fn${tapIndex}(${this.args({ 197 | before: tap.context ? "_context" : undefined 198 | })});\n`; 199 | } else { 200 | code += `_fn${tapIndex}(${this.args({ 201 | before: tap.context ? "_context" : undefined 202 | })});\n`; 203 | } 204 | if (!rethrowIfPossible) { 205 | code += "} catch(_err) {\n"; 206 | code += `_hasError${tapIndex} = true;\n`; 207 | code += onError("_err"); 208 | code += "}\n"; 209 | code += `if(!_hasError${tapIndex}) {\n`; 210 | } 211 | if (onResult) { 212 | code += onResult(`_result${tapIndex}`); 213 | } 214 | if (onDone) { 215 | code += onDone(); 216 | } 217 | if (!rethrowIfPossible) { 218 | code += "}\n"; 219 | } 220 | break; 221 | case "async": { 222 | let cbCode = ""; 223 | cbCode += onResult 224 | ? `(function(_err${tapIndex}, _result${tapIndex}) {\n` 225 | : `(function(_err${tapIndex}) {\n`; 226 | cbCode += `if(_err${tapIndex}) {\n`; 227 | cbCode += onError(`_err${tapIndex}`); 228 | cbCode += "} else {\n"; 229 | if (onResult) { 230 | cbCode += onResult(`_result${tapIndex}`); 231 | } 232 | if (onDone) { 233 | cbCode += onDone(); 234 | } 235 | cbCode += "}\n"; 236 | cbCode += "})"; 237 | code += `_fn${tapIndex}(${this.args({ 238 | before: tap.context ? "_context" : undefined, 239 | after: cbCode 240 | })});\n`; 241 | break; 242 | } 243 | case "promise": 244 | code += `var _hasResult${tapIndex} = false;\n`; 245 | code += `var _promise${tapIndex} = _fn${tapIndex}(${this.args({ 246 | before: tap.context ? "_context" : undefined 247 | })});\n`; 248 | code += `if (!_promise${tapIndex} || !_promise${tapIndex}.then)\n`; 249 | code += ` throw new Error('Tap function (tapPromise) did not return promise (returned ' + _promise${tapIndex} + ')');\n`; 250 | code += `_promise${tapIndex}.then((function(_result${tapIndex}) {\n`; 251 | code += `_hasResult${tapIndex} = true;\n`; 252 | if (onResult) { 253 | code += onResult(`_result${tapIndex}`); 254 | } 255 | if (onDone) { 256 | code += onDone(); 257 | } 258 | code += `}), function(_err${tapIndex}) {\n`; 259 | code += `if(_hasResult${tapIndex}) throw _err${tapIndex};\n`; 260 | code += onError( 261 | `!_err${tapIndex} ? new Error('Tap function (tapPromise) rejects "' + _err${tapIndex} + '" value') : _err${tapIndex}` 262 | ); 263 | code += "});\n"; 264 | break; 265 | } 266 | return code; 267 | } 268 | 269 | callTapsSeries({ 270 | onError, 271 | onResult, 272 | resultReturns, 273 | onDone, 274 | doneReturns, 275 | rethrowIfPossible 276 | }) { 277 | if (this.options.taps.length === 0) return onDone(); 278 | const firstAsync = this.options.taps.findIndex((t) => t.type !== "sync"); 279 | const somethingReturns = resultReturns || doneReturns; 280 | let code = ""; 281 | let current = onDone; 282 | let unrollCounter = 0; 283 | for (let j = this.options.taps.length - 1; j >= 0; j--) { 284 | const i = j; 285 | const unroll = 286 | current !== onDone && 287 | (this.options.taps[i].type !== "sync" || unrollCounter++ > 20); 288 | if (unroll) { 289 | unrollCounter = 0; 290 | code += `function _next${i}() {\n`; 291 | code += current(); 292 | code += "}\n"; 293 | current = () => `${somethingReturns ? "return " : ""}_next${i}();\n`; 294 | } 295 | const done = current; 296 | const doneBreak = (skipDone) => { 297 | if (skipDone) return ""; 298 | return onDone(); 299 | }; 300 | const content = this.callTap(i, { 301 | onError: (error) => onError(i, error, done, doneBreak), 302 | onResult: 303 | onResult && ((result) => onResult(i, result, done, doneBreak)), 304 | onDone: !onResult && done, 305 | rethrowIfPossible: 306 | rethrowIfPossible && (firstAsync < 0 || i < firstAsync) 307 | }); 308 | current = () => content; 309 | } 310 | code += current(); 311 | return code; 312 | } 313 | 314 | callTapsLooping({ onError, onDone, rethrowIfPossible }) { 315 | if (this.options.taps.length === 0) return onDone(); 316 | const syncOnly = this.options.taps.every((t) => t.type === "sync"); 317 | let code = ""; 318 | if (!syncOnly) { 319 | code += "var _looper = (function() {\n"; 320 | code += "var _loopAsync = false;\n"; 321 | } 322 | code += "var _loop;\n"; 323 | code += "do {\n"; 324 | code += "_loop = false;\n"; 325 | for (let i = 0; i < this.options.interceptors.length; i++) { 326 | const interceptor = this.options.interceptors[i]; 327 | if (interceptor.loop) { 328 | code += `${this.getInterceptor(i)}.loop(${this.args({ 329 | before: interceptor.context ? "_context" : undefined 330 | })});\n`; 331 | } 332 | } 333 | code += this.callTapsSeries({ 334 | onError, 335 | onResult: (i, result, next, doneBreak) => { 336 | let code = ""; 337 | code += `if(${result} !== undefined) {\n`; 338 | code += "_loop = true;\n"; 339 | if (!syncOnly) code += "if(_loopAsync) _looper();\n"; 340 | code += doneBreak(true); 341 | code += "} else {\n"; 342 | code += next(); 343 | code += "}\n"; 344 | return code; 345 | }, 346 | onDone: 347 | onDone && 348 | (() => { 349 | let code = ""; 350 | code += "if(!_loop) {\n"; 351 | code += onDone(); 352 | code += "}\n"; 353 | return code; 354 | }), 355 | rethrowIfPossible: rethrowIfPossible && syncOnly 356 | }); 357 | code += "} while(_loop);\n"; 358 | if (!syncOnly) { 359 | code += "_loopAsync = true;\n"; 360 | code += "});\n"; 361 | code += "_looper();\n"; 362 | } 363 | return code; 364 | } 365 | 366 | callTapsParallel({ 367 | onError, 368 | onResult, 369 | onDone, 370 | rethrowIfPossible, 371 | onTap = (i, run) => run() 372 | }) { 373 | if (this.options.taps.length <= 1) { 374 | return this.callTapsSeries({ 375 | onError, 376 | onResult, 377 | onDone, 378 | rethrowIfPossible 379 | }); 380 | } 381 | let code = ""; 382 | code += "do {\n"; 383 | code += `var _counter = ${this.options.taps.length};\n`; 384 | if (onDone) { 385 | code += "var _done = (function() {\n"; 386 | code += onDone(); 387 | code += "});\n"; 388 | } 389 | for (let i = 0; i < this.options.taps.length; i++) { 390 | const done = () => { 391 | if (onDone) return "if(--_counter === 0) _done();\n"; 392 | return "--_counter;"; 393 | }; 394 | const doneBreak = (skipDone) => { 395 | if (skipDone || !onDone) return "_counter = 0;\n"; 396 | return "_counter = 0;\n_done();\n"; 397 | }; 398 | code += "if(_counter <= 0) break;\n"; 399 | code += onTap( 400 | i, 401 | () => 402 | this.callTap(i, { 403 | onError: (error) => { 404 | let code = ""; 405 | code += "if(_counter > 0) {\n"; 406 | code += onError(i, error, done, doneBreak); 407 | code += "}\n"; 408 | return code; 409 | }, 410 | onResult: 411 | onResult && 412 | ((result) => { 413 | let code = ""; 414 | code += "if(_counter > 0) {\n"; 415 | code += onResult(i, result, done, doneBreak); 416 | code += "}\n"; 417 | return code; 418 | }), 419 | onDone: !onResult && (() => done()), 420 | rethrowIfPossible 421 | }), 422 | done, 423 | doneBreak 424 | ); 425 | } 426 | code += "} while(false);\n"; 427 | return code; 428 | } 429 | 430 | args({ before, after } = {}) { 431 | let allArgs = this._args; 432 | if (before) allArgs = [before, ...allArgs]; 433 | if (after) allArgs = [...allArgs, after]; 434 | if (allArgs.length === 0) { 435 | return ""; 436 | } 437 | 438 | return allArgs.join(", "); 439 | } 440 | 441 | getTapFn(idx) { 442 | return `_x[${idx}]`; 443 | } 444 | 445 | getTap(idx) { 446 | return `_taps[${idx}]`; 447 | } 448 | 449 | getInterceptor(idx) { 450 | return `_interceptors[${idx}]`; 451 | } 452 | } 453 | 454 | module.exports = HookCodeFactory; 455 | -------------------------------------------------------------------------------- /lib/__tests__/__snapshots__/SyncHooks.js.snap: -------------------------------------------------------------------------------- 1 | // Jest Snapshot v1, https://goo.gl/fbAQLP 2 | 3 | exports[`SyncBailHook should have to correct behavior 1`] = ` 4 | Object { 5 | "async": Object {}, 6 | "intercept": Object {}, 7 | "sync": Object { 8 | "callAsyncIntercepted": Object { 9 | "type": "async", 10 | "value": 6, 11 | }, 12 | "callAsyncInterceptedCall1": Array [ 13 | 1, 14 | 2, 15 | 3, 16 | ], 17 | "callAsyncInterceptedCall2": Array [ 18 | 1, 19 | 2, 20 | 3, 21 | ], 22 | "callAsyncInterceptedTap1": Object { 23 | "fn": 3, 24 | "name": "sync1", 25 | "type": "sync", 26 | }, 27 | "callAsyncInterceptedTap2": Object { 28 | "fn": 3, 29 | "name": "sync1", 30 | "type": "sync", 31 | }, 32 | "callAsyncMultipleSync": Object { 33 | "type": "async", 34 | "value": 42, 35 | }, 36 | "callAsyncMultipleSyncCalled1": true, 37 | "callAsyncMultipleSyncError": Object { 38 | "error": "Error in sync2", 39 | "type": "async", 40 | }, 41 | "callAsyncMultipleSyncErrorCalled1": true, 42 | "callAsyncMultipleSyncErrorCalled2": true, 43 | "callAsyncMultipleSyncWithArg": Object { 44 | "type": "async", 45 | "value": 84, 46 | }, 47 | "callAsyncMultipleSyncWithArgCalled1": 42, 48 | "callAsyncMultipleSyncWithArgFirstReturn": Object { 49 | "type": "async", 50 | "value": 84, 51 | }, 52 | "callAsyncMultipleSyncWithArgFirstReturnCalled1": 42, 53 | "callAsyncMultipleSyncWithArgLastReturn": Object { 54 | "type": "async", 55 | "value": 85, 56 | }, 57 | "callAsyncMultipleSyncWithArgLastReturnCalled1": 42, 58 | "callAsyncMultipleSyncWithArgLastReturnCalled2": 42, 59 | "callAsyncMultipleSyncWithArgNoReturn": Object { 60 | "type": "async", 61 | "value": undefined, 62 | }, 63 | "callAsyncMultipleSyncWithArgNoReturnCalled1": 42, 64 | "callAsyncMultipleSyncWithArgNoReturnCalled2": 42, 65 | "callAsyncMultipleSyncWithArgs": Object { 66 | "type": "async", 67 | "value": 129, 68 | }, 69 | "callAsyncMultipleSyncWithArgsCalled1": Array [ 70 | 42, 71 | 43, 72 | 44, 73 | ], 74 | "callAsyncNone": Object { 75 | "type": "async", 76 | "value": undefined, 77 | }, 78 | "callAsyncNoneWithArg": Object { 79 | "type": "async", 80 | "value": undefined, 81 | }, 82 | "callAsyncSingleSync": Object { 83 | "type": "async", 84 | "value": 42, 85 | }, 86 | "callAsyncSingleSyncCalled": true, 87 | "callAsyncSingleSyncWithArg": Object { 88 | "type": "async", 89 | "value": 42, 90 | }, 91 | "callAsyncSingleSyncWithArgCalled": 42, 92 | "callIntercepted": Object { 93 | "type": "return", 94 | "value": 6, 95 | }, 96 | "callInterceptedCall1": Array [ 97 | 1, 98 | 2, 99 | 3, 100 | ], 101 | "callInterceptedCall2": Array [ 102 | 1, 103 | 2, 104 | 3, 105 | ], 106 | "callInterceptedTap1": Object { 107 | "fn": 3, 108 | "name": "sync1", 109 | "type": "sync", 110 | }, 111 | "callInterceptedTap2": Object { 112 | "fn": 3, 113 | "name": "sync1", 114 | "type": "sync", 115 | }, 116 | "callMultipleSync": Object { 117 | "type": "return", 118 | "value": 42, 119 | }, 120 | "callMultipleSyncCalled1": true, 121 | "callMultipleSyncError": Object { 122 | "error": "Error in sync2", 123 | }, 124 | "callMultipleSyncErrorCalled1": true, 125 | "callMultipleSyncErrorCalled2": true, 126 | "callMultipleSyncWithArg": Object { 127 | "type": "return", 128 | "value": 84, 129 | }, 130 | "callMultipleSyncWithArgCalled1": 42, 131 | "callMultipleSyncWithArgFirstReturn": Object { 132 | "type": "return", 133 | "value": 84, 134 | }, 135 | "callMultipleSyncWithArgFirstReturnCalled1": 42, 136 | "callMultipleSyncWithArgLastReturn": Object { 137 | "type": "return", 138 | "value": 85, 139 | }, 140 | "callMultipleSyncWithArgLastReturnCalled1": 42, 141 | "callMultipleSyncWithArgLastReturnCalled2": 42, 142 | "callMultipleSyncWithArgNoReturn": Object { 143 | "type": "no result", 144 | }, 145 | "callMultipleSyncWithArgNoReturnCalled1": 42, 146 | "callMultipleSyncWithArgNoReturnCalled2": 42, 147 | "callMultipleSyncWithArgs": Object { 148 | "type": "return", 149 | "value": 129, 150 | }, 151 | "callMultipleSyncWithArgsCalled1": Array [ 152 | 42, 153 | 43, 154 | 44, 155 | ], 156 | "callNone": Object { 157 | "type": "no result", 158 | }, 159 | "callNoneWithArg": Object { 160 | "type": "no result", 161 | }, 162 | "callSingleSync": Object { 163 | "type": "return", 164 | "value": 42, 165 | }, 166 | "callSingleSyncCalled": true, 167 | "callSingleSyncWithArg": Object { 168 | "type": "return", 169 | "value": 42, 170 | }, 171 | "callSingleSyncWithArgCalled": 42, 172 | "promiseIntercepted": Object { 173 | "type": "promise", 174 | "value": 6, 175 | }, 176 | "promiseInterceptedCall1": Array [ 177 | 1, 178 | 2, 179 | 3, 180 | ], 181 | "promiseInterceptedCall2": Array [ 182 | 1, 183 | 2, 184 | 3, 185 | ], 186 | "promiseInterceptedTap1": Object { 187 | "fn": 3, 188 | "name": "sync1", 189 | "type": "sync", 190 | }, 191 | "promiseInterceptedTap2": Object { 192 | "fn": 3, 193 | "name": "sync1", 194 | "type": "sync", 195 | }, 196 | "promiseMultipleSync": Object { 197 | "type": "promise", 198 | "value": 42, 199 | }, 200 | "promiseMultipleSyncCalled1": true, 201 | "promiseMultipleSyncError": Object { 202 | "error": "Error in sync2", 203 | "type": "promise", 204 | }, 205 | "promiseMultipleSyncErrorCalled1": true, 206 | "promiseMultipleSyncErrorCalled2": true, 207 | "promiseMultipleSyncWithArg": Object { 208 | "type": "promise", 209 | "value": 84, 210 | }, 211 | "promiseMultipleSyncWithArgCalled1": 42, 212 | "promiseMultipleSyncWithArgFirstReturn": Object { 213 | "type": "promise", 214 | "value": 84, 215 | }, 216 | "promiseMultipleSyncWithArgFirstReturnCalled1": 42, 217 | "promiseMultipleSyncWithArgLastReturn": Object { 218 | "type": "promise", 219 | "value": 85, 220 | }, 221 | "promiseMultipleSyncWithArgLastReturnCalled1": 42, 222 | "promiseMultipleSyncWithArgLastReturnCalled2": 42, 223 | "promiseMultipleSyncWithArgNoReturn": Object { 224 | "type": "promise", 225 | "value": undefined, 226 | }, 227 | "promiseMultipleSyncWithArgNoReturnCalled1": 42, 228 | "promiseMultipleSyncWithArgNoReturnCalled2": 42, 229 | "promiseMultipleSyncWithArgs": Object { 230 | "type": "promise", 231 | "value": 129, 232 | }, 233 | "promiseMultipleSyncWithArgsCalled1": Array [ 234 | 42, 235 | 43, 236 | 44, 237 | ], 238 | "promiseNone": Object { 239 | "type": "promise", 240 | "value": undefined, 241 | }, 242 | "promiseNoneWithArg": Object { 243 | "type": "promise", 244 | "value": undefined, 245 | }, 246 | "promiseSingleSync": Object { 247 | "type": "promise", 248 | "value": 42, 249 | }, 250 | "promiseSingleSyncCalled": true, 251 | "promiseSingleSyncWithArg": Object { 252 | "type": "promise", 253 | "value": 42, 254 | }, 255 | "promiseSingleSyncWithArgCalled": 42, 256 | }, 257 | } 258 | `; 259 | 260 | exports[`SyncHook should have to correct behavior 1`] = ` 261 | Object { 262 | "async": Object {}, 263 | "intercept": Object {}, 264 | "sync": Object { 265 | "callAsyncIntercepted": Object { 266 | "type": "async", 267 | "value": undefined, 268 | }, 269 | "callAsyncInterceptedCall1": Array [ 270 | 1, 271 | 2, 272 | 3, 273 | ], 274 | "callAsyncInterceptedCall2": Array [ 275 | 1, 276 | 2, 277 | 3, 278 | ], 279 | "callAsyncInterceptedTap1": Object { 280 | "fn": 2, 281 | "name": "sync2", 282 | "type": "sync", 283 | }, 284 | "callAsyncInterceptedTap2": Object { 285 | "fn": 3, 286 | "name": "sync1", 287 | "type": "sync", 288 | }, 289 | "callAsyncMultipleSync": Object { 290 | "type": "async", 291 | "value": undefined, 292 | }, 293 | "callAsyncMultipleSyncCalled1": true, 294 | "callAsyncMultipleSyncCalled2": true, 295 | "callAsyncMultipleSyncError": Object { 296 | "error": "Error in sync2", 297 | "type": "async", 298 | }, 299 | "callAsyncMultipleSyncErrorCalled1": true, 300 | "callAsyncMultipleSyncErrorCalled2": true, 301 | "callAsyncMultipleSyncWithArg": Object { 302 | "type": "async", 303 | "value": undefined, 304 | }, 305 | "callAsyncMultipleSyncWithArgCalled1": 42, 306 | "callAsyncMultipleSyncWithArgCalled2": 42, 307 | "callAsyncMultipleSyncWithArgFirstReturn": Object { 308 | "type": "async", 309 | "value": undefined, 310 | }, 311 | "callAsyncMultipleSyncWithArgFirstReturnCalled1": 42, 312 | "callAsyncMultipleSyncWithArgFirstReturnCalled2": 42, 313 | "callAsyncMultipleSyncWithArgLastReturn": Object { 314 | "type": "async", 315 | "value": undefined, 316 | }, 317 | "callAsyncMultipleSyncWithArgLastReturnCalled1": 42, 318 | "callAsyncMultipleSyncWithArgLastReturnCalled2": 42, 319 | "callAsyncMultipleSyncWithArgNoReturn": Object { 320 | "type": "async", 321 | "value": undefined, 322 | }, 323 | "callAsyncMultipleSyncWithArgNoReturnCalled1": 42, 324 | "callAsyncMultipleSyncWithArgNoReturnCalled2": 42, 325 | "callAsyncMultipleSyncWithArgs": Object { 326 | "type": "async", 327 | "value": undefined, 328 | }, 329 | "callAsyncMultipleSyncWithArgsCalled1": Array [ 330 | 42, 331 | 43, 332 | 44, 333 | ], 334 | "callAsyncMultipleSyncWithArgsCalled2": Array [ 335 | 42, 336 | 43, 337 | 44, 338 | ], 339 | "callAsyncNone": Object { 340 | "type": "async", 341 | "value": undefined, 342 | }, 343 | "callAsyncNoneWithArg": Object { 344 | "type": "async", 345 | "value": undefined, 346 | }, 347 | "callAsyncSingleSync": Object { 348 | "type": "async", 349 | "value": undefined, 350 | }, 351 | "callAsyncSingleSyncCalled": true, 352 | "callAsyncSingleSyncWithArg": Object { 353 | "type": "async", 354 | "value": undefined, 355 | }, 356 | "callAsyncSingleSyncWithArgCalled": 42, 357 | "callIntercepted": Object { 358 | "type": "no result", 359 | }, 360 | "callInterceptedCall1": Array [ 361 | 1, 362 | 2, 363 | 3, 364 | ], 365 | "callInterceptedCall2": Array [ 366 | 1, 367 | 2, 368 | 3, 369 | ], 370 | "callInterceptedTap1": Object { 371 | "fn": 2, 372 | "name": "sync2", 373 | "type": "sync", 374 | }, 375 | "callInterceptedTap2": Object { 376 | "fn": 3, 377 | "name": "sync1", 378 | "type": "sync", 379 | }, 380 | "callMultipleSync": Object { 381 | "type": "no result", 382 | }, 383 | "callMultipleSyncCalled1": true, 384 | "callMultipleSyncCalled2": true, 385 | "callMultipleSyncError": Object { 386 | "error": "Error in sync2", 387 | }, 388 | "callMultipleSyncErrorCalled1": true, 389 | "callMultipleSyncErrorCalled2": true, 390 | "callMultipleSyncWithArg": Object { 391 | "type": "no result", 392 | }, 393 | "callMultipleSyncWithArgCalled1": 42, 394 | "callMultipleSyncWithArgCalled2": 42, 395 | "callMultipleSyncWithArgFirstReturn": Object { 396 | "type": "no result", 397 | }, 398 | "callMultipleSyncWithArgFirstReturnCalled1": 42, 399 | "callMultipleSyncWithArgFirstReturnCalled2": 42, 400 | "callMultipleSyncWithArgLastReturn": Object { 401 | "type": "no result", 402 | }, 403 | "callMultipleSyncWithArgLastReturnCalled1": 42, 404 | "callMultipleSyncWithArgLastReturnCalled2": 42, 405 | "callMultipleSyncWithArgNoReturn": Object { 406 | "type": "no result", 407 | }, 408 | "callMultipleSyncWithArgNoReturnCalled1": 42, 409 | "callMultipleSyncWithArgNoReturnCalled2": 42, 410 | "callMultipleSyncWithArgs": Object { 411 | "type": "no result", 412 | }, 413 | "callMultipleSyncWithArgsCalled1": Array [ 414 | 42, 415 | 43, 416 | 44, 417 | ], 418 | "callMultipleSyncWithArgsCalled2": Array [ 419 | 42, 420 | 43, 421 | 44, 422 | ], 423 | "callNone": Object { 424 | "type": "no result", 425 | }, 426 | "callNoneWithArg": Object { 427 | "type": "no result", 428 | }, 429 | "callSingleSync": Object { 430 | "type": "no result", 431 | }, 432 | "callSingleSyncCalled": true, 433 | "callSingleSyncWithArg": Object { 434 | "type": "no result", 435 | }, 436 | "callSingleSyncWithArgCalled": 42, 437 | "promiseIntercepted": Object { 438 | "type": "promise", 439 | "value": undefined, 440 | }, 441 | "promiseInterceptedCall1": Array [ 442 | 1, 443 | 2, 444 | 3, 445 | ], 446 | "promiseInterceptedCall2": Array [ 447 | 1, 448 | 2, 449 | 3, 450 | ], 451 | "promiseInterceptedTap1": Object { 452 | "fn": 2, 453 | "name": "sync2", 454 | "type": "sync", 455 | }, 456 | "promiseInterceptedTap2": Object { 457 | "fn": 3, 458 | "name": "sync1", 459 | "type": "sync", 460 | }, 461 | "promiseMultipleSync": Object { 462 | "type": "promise", 463 | "value": undefined, 464 | }, 465 | "promiseMultipleSyncCalled1": true, 466 | "promiseMultipleSyncCalled2": true, 467 | "promiseMultipleSyncError": Object { 468 | "error": "Error in sync2", 469 | "type": "promise", 470 | }, 471 | "promiseMultipleSyncErrorCalled1": true, 472 | "promiseMultipleSyncErrorCalled2": true, 473 | "promiseMultipleSyncWithArg": Object { 474 | "type": "promise", 475 | "value": undefined, 476 | }, 477 | "promiseMultipleSyncWithArgCalled1": 42, 478 | "promiseMultipleSyncWithArgCalled2": 42, 479 | "promiseMultipleSyncWithArgFirstReturn": Object { 480 | "type": "promise", 481 | "value": undefined, 482 | }, 483 | "promiseMultipleSyncWithArgFirstReturnCalled1": 42, 484 | "promiseMultipleSyncWithArgFirstReturnCalled2": 42, 485 | "promiseMultipleSyncWithArgLastReturn": Object { 486 | "type": "promise", 487 | "value": undefined, 488 | }, 489 | "promiseMultipleSyncWithArgLastReturnCalled1": 42, 490 | "promiseMultipleSyncWithArgLastReturnCalled2": 42, 491 | "promiseMultipleSyncWithArgNoReturn": Object { 492 | "type": "promise", 493 | "value": undefined, 494 | }, 495 | "promiseMultipleSyncWithArgNoReturnCalled1": 42, 496 | "promiseMultipleSyncWithArgNoReturnCalled2": 42, 497 | "promiseMultipleSyncWithArgs": Object { 498 | "type": "promise", 499 | "value": undefined, 500 | }, 501 | "promiseMultipleSyncWithArgsCalled1": Array [ 502 | 42, 503 | 43, 504 | 44, 505 | ], 506 | "promiseMultipleSyncWithArgsCalled2": Array [ 507 | 42, 508 | 43, 509 | 44, 510 | ], 511 | "promiseNone": Object { 512 | "type": "promise", 513 | "value": undefined, 514 | }, 515 | "promiseNoneWithArg": Object { 516 | "type": "promise", 517 | "value": undefined, 518 | }, 519 | "promiseSingleSync": Object { 520 | "type": "promise", 521 | "value": undefined, 522 | }, 523 | "promiseSingleSyncCalled": true, 524 | "promiseSingleSyncWithArg": Object { 525 | "type": "promise", 526 | "value": undefined, 527 | }, 528 | "promiseSingleSyncWithArgCalled": 42, 529 | }, 530 | } 531 | `; 532 | 533 | exports[`SyncLoopHook should have to correct behavior 1`] = ` 534 | Object { 535 | "async": Object {}, 536 | "sync": Object { 537 | "callAsyncInterceptedSync": Object { 538 | "type": "async", 539 | "value": undefined, 540 | }, 541 | "callAsyncInterceptedSyncCalled1": 83, 542 | "callAsyncInterceptedSyncCalled2": 42, 543 | "callAsyncInterceptedSyncCalledCall": 1, 544 | "callAsyncInterceptedSyncCalledLoop": 83, 545 | "callAsyncInterceptedSyncCalledTap": 125, 546 | "callAsyncMultipleSync": Object { 547 | "type": "async", 548 | "value": undefined, 549 | }, 550 | "callAsyncMultipleSyncCalled1": 83, 551 | "callAsyncMultipleSyncCalled2": 42, 552 | "callAsyncNone": Object { 553 | "type": "async", 554 | "value": undefined, 555 | }, 556 | "callAsyncNoneWithArg": Object { 557 | "type": "async", 558 | "value": undefined, 559 | }, 560 | "callAsyncSingleSync": Object { 561 | "type": "async", 562 | "value": undefined, 563 | }, 564 | "callAsyncSingleSyncCalled": 42, 565 | "callInterceptedSync": Object { 566 | "type": "no result", 567 | }, 568 | "callInterceptedSyncCalled1": 83, 569 | "callInterceptedSyncCalled2": 42, 570 | "callInterceptedSyncCalledCall": 1, 571 | "callInterceptedSyncCalledLoop": 83, 572 | "callInterceptedSyncCalledTap": 125, 573 | "callMultipleSync": Object { 574 | "type": "no result", 575 | }, 576 | "callMultipleSyncCalled1": 83, 577 | "callMultipleSyncCalled2": 42, 578 | "callNone": Object { 579 | "type": "no result", 580 | }, 581 | "callNoneWithArg": Object { 582 | "type": "no result", 583 | }, 584 | "callSingleSync": Object { 585 | "type": "no result", 586 | }, 587 | "callSingleSyncCalled": 42, 588 | "promiseInterceptedSync": Object { 589 | "type": "promise", 590 | "value": undefined, 591 | }, 592 | "promiseInterceptedSyncCalled1": 83, 593 | "promiseInterceptedSyncCalled2": 42, 594 | "promiseInterceptedSyncCalledCall": 1, 595 | "promiseInterceptedSyncCalledLoop": 83, 596 | "promiseInterceptedSyncCalledTap": 125, 597 | "promiseMultipleSync": Object { 598 | "type": "promise", 599 | "value": undefined, 600 | }, 601 | "promiseMultipleSyncCalled1": 83, 602 | "promiseMultipleSyncCalled2": 42, 603 | "promiseNone": Object { 604 | "type": "promise", 605 | "value": undefined, 606 | }, 607 | "promiseNoneWithArg": Object { 608 | "type": "promise", 609 | "value": undefined, 610 | }, 611 | "promiseSingleSync": Object { 612 | "type": "promise", 613 | "value": undefined, 614 | }, 615 | "promiseSingleSyncCalled": 42, 616 | }, 617 | } 618 | `; 619 | 620 | exports[`SyncWaterfallHook should have to correct behavior 1`] = ` 621 | Object { 622 | "async": Object {}, 623 | "intercept": Object {}, 624 | "sync": Object { 625 | "callAsyncIntercepted": Object { 626 | "type": "async", 627 | "value": 9, 628 | }, 629 | "callAsyncInterceptedCall1": Array [ 630 | 1, 631 | 2, 632 | 3, 633 | ], 634 | "callAsyncInterceptedCall2": Array [ 635 | 1, 636 | 2, 637 | 3, 638 | ], 639 | "callAsyncInterceptedTap1": Object { 640 | "fn": 2, 641 | "name": "sync2", 642 | "type": "sync", 643 | }, 644 | "callAsyncInterceptedTap2": Object { 645 | "fn": 3, 646 | "name": "sync1", 647 | "type": "sync", 648 | }, 649 | "callAsyncMultipleSync": Object { 650 | "error": "Waterfall hooks must have at least one argument", 651 | }, 652 | "callAsyncMultipleSyncError": Object { 653 | "error": "Waterfall hooks must have at least one argument", 654 | }, 655 | "callAsyncMultipleSyncWithArg": Object { 656 | "type": "async", 657 | "value": 127, 658 | }, 659 | "callAsyncMultipleSyncWithArgCalled1": 42, 660 | "callAsyncMultipleSyncWithArgCalled2": 84, 661 | "callAsyncMultipleSyncWithArgFirstReturn": Object { 662 | "type": "async", 663 | "value": 84, 664 | }, 665 | "callAsyncMultipleSyncWithArgFirstReturnCalled1": 42, 666 | "callAsyncMultipleSyncWithArgFirstReturnCalled2": 84, 667 | "callAsyncMultipleSyncWithArgLastReturn": Object { 668 | "type": "async", 669 | "value": 85, 670 | }, 671 | "callAsyncMultipleSyncWithArgLastReturnCalled1": 42, 672 | "callAsyncMultipleSyncWithArgLastReturnCalled2": 42, 673 | "callAsyncMultipleSyncWithArgNoReturn": Object { 674 | "type": "async", 675 | "value": 42, 676 | }, 677 | "callAsyncMultipleSyncWithArgNoReturnCalled1": 42, 678 | "callAsyncMultipleSyncWithArgNoReturnCalled2": 42, 679 | "callAsyncMultipleSyncWithArgs": Object { 680 | "type": "async", 681 | "value": 217, 682 | }, 683 | "callAsyncMultipleSyncWithArgsCalled1": Array [ 684 | 42, 685 | 43, 686 | 44, 687 | ], 688 | "callAsyncMultipleSyncWithArgsCalled2": Array [ 689 | 129, 690 | 43, 691 | 44, 692 | ], 693 | "callAsyncNone": Object { 694 | "error": "Waterfall hooks must have at least one argument", 695 | }, 696 | "callAsyncNoneWithArg": Object { 697 | "type": "async", 698 | "value": 42, 699 | }, 700 | "callAsyncSingleSync": Object { 701 | "error": "Waterfall hooks must have at least one argument", 702 | }, 703 | "callAsyncSingleSyncWithArg": Object { 704 | "type": "async", 705 | "value": 42, 706 | }, 707 | "callAsyncSingleSyncWithArgCalled": 42, 708 | "callIntercepted": Object { 709 | "type": "return", 710 | "value": 9, 711 | }, 712 | "callInterceptedCall1": Array [ 713 | 1, 714 | 2, 715 | 3, 716 | ], 717 | "callInterceptedCall2": Array [ 718 | 1, 719 | 2, 720 | 3, 721 | ], 722 | "callInterceptedTap1": Object { 723 | "fn": 2, 724 | "name": "sync2", 725 | "type": "sync", 726 | }, 727 | "callInterceptedTap2": Object { 728 | "fn": 3, 729 | "name": "sync1", 730 | "type": "sync", 731 | }, 732 | "callMultipleSync": Object { 733 | "error": "Waterfall hooks must have at least one argument", 734 | }, 735 | "callMultipleSyncError": Object { 736 | "error": "Waterfall hooks must have at least one argument", 737 | }, 738 | "callMultipleSyncWithArg": Object { 739 | "type": "return", 740 | "value": 127, 741 | }, 742 | "callMultipleSyncWithArgCalled1": 42, 743 | "callMultipleSyncWithArgCalled2": 84, 744 | "callMultipleSyncWithArgFirstReturn": Object { 745 | "type": "return", 746 | "value": 84, 747 | }, 748 | "callMultipleSyncWithArgFirstReturnCalled1": 42, 749 | "callMultipleSyncWithArgFirstReturnCalled2": 84, 750 | "callMultipleSyncWithArgLastReturn": Object { 751 | "type": "return", 752 | "value": 85, 753 | }, 754 | "callMultipleSyncWithArgLastReturnCalled1": 42, 755 | "callMultipleSyncWithArgLastReturnCalled2": 42, 756 | "callMultipleSyncWithArgNoReturn": Object { 757 | "type": "return", 758 | "value": 42, 759 | }, 760 | "callMultipleSyncWithArgNoReturnCalled1": 42, 761 | "callMultipleSyncWithArgNoReturnCalled2": 42, 762 | "callMultipleSyncWithArgs": Object { 763 | "type": "return", 764 | "value": 217, 765 | }, 766 | "callMultipleSyncWithArgsCalled1": Array [ 767 | 42, 768 | 43, 769 | 44, 770 | ], 771 | "callMultipleSyncWithArgsCalled2": Array [ 772 | 129, 773 | 43, 774 | 44, 775 | ], 776 | "callNone": Object { 777 | "error": "Waterfall hooks must have at least one argument", 778 | }, 779 | "callNoneWithArg": Object { 780 | "type": "return", 781 | "value": 42, 782 | }, 783 | "callSingleSync": Object { 784 | "error": "Waterfall hooks must have at least one argument", 785 | }, 786 | "callSingleSyncWithArg": Object { 787 | "type": "return", 788 | "value": 42, 789 | }, 790 | "callSingleSyncWithArgCalled": 42, 791 | "promiseIntercepted": Object { 792 | "type": "promise", 793 | "value": 9, 794 | }, 795 | "promiseInterceptedCall1": Array [ 796 | 1, 797 | 2, 798 | 3, 799 | ], 800 | "promiseInterceptedCall2": Array [ 801 | 1, 802 | 2, 803 | 3, 804 | ], 805 | "promiseInterceptedTap1": Object { 806 | "fn": 2, 807 | "name": "sync2", 808 | "type": "sync", 809 | }, 810 | "promiseInterceptedTap2": Object { 811 | "fn": 3, 812 | "name": "sync1", 813 | "type": "sync", 814 | }, 815 | "promiseMultipleSync": Object { 816 | "error": "Waterfall hooks must have at least one argument", 817 | }, 818 | "promiseMultipleSyncError": Object { 819 | "error": "Waterfall hooks must have at least one argument", 820 | }, 821 | "promiseMultipleSyncWithArg": Object { 822 | "type": "promise", 823 | "value": 127, 824 | }, 825 | "promiseMultipleSyncWithArgCalled1": 42, 826 | "promiseMultipleSyncWithArgCalled2": 84, 827 | "promiseMultipleSyncWithArgFirstReturn": Object { 828 | "type": "promise", 829 | "value": 84, 830 | }, 831 | "promiseMultipleSyncWithArgFirstReturnCalled1": 42, 832 | "promiseMultipleSyncWithArgFirstReturnCalled2": 84, 833 | "promiseMultipleSyncWithArgLastReturn": Object { 834 | "type": "promise", 835 | "value": 85, 836 | }, 837 | "promiseMultipleSyncWithArgLastReturnCalled1": 42, 838 | "promiseMultipleSyncWithArgLastReturnCalled2": 42, 839 | "promiseMultipleSyncWithArgNoReturn": Object { 840 | "type": "promise", 841 | "value": 42, 842 | }, 843 | "promiseMultipleSyncWithArgNoReturnCalled1": 42, 844 | "promiseMultipleSyncWithArgNoReturnCalled2": 42, 845 | "promiseMultipleSyncWithArgs": Object { 846 | "type": "promise", 847 | "value": 217, 848 | }, 849 | "promiseMultipleSyncWithArgsCalled1": Array [ 850 | 42, 851 | 43, 852 | 44, 853 | ], 854 | "promiseMultipleSyncWithArgsCalled2": Array [ 855 | 129, 856 | 43, 857 | 44, 858 | ], 859 | "promiseNone": Object { 860 | "error": "Waterfall hooks must have at least one argument", 861 | }, 862 | "promiseNoneWithArg": Object { 863 | "type": "promise", 864 | "value": 42, 865 | }, 866 | "promiseSingleSync": Object { 867 | "error": "Waterfall hooks must have at least one argument", 868 | }, 869 | "promiseSingleSyncWithArg": Object { 870 | "type": "promise", 871 | "value": 42, 872 | }, 873 | "promiseSingleSyncWithArgCalled": 42, 874 | }, 875 | } 876 | `; 877 | -------------------------------------------------------------------------------- /lib/__tests__/HookTester.js: -------------------------------------------------------------------------------- 1 | /* 2 | MIT License http://www.opensource.org/licenses/mit-license.php 3 | Author Tobias Koppers @sokra 4 | */ 5 | "use strict"; 6 | 7 | describe("HookTester", () => { 8 | // eslint-disable-next-line jest/expect-expect 9 | it("should run", () => {}); 10 | }); 11 | 12 | process.on("unhandledRejection", (err) => console.error(err.stack)); 13 | 14 | class HookTester { 15 | constructor(hookCreator, sync) { 16 | this.hookCreator = hookCreator; 17 | this.sync = sync; 18 | } 19 | 20 | async run(syncOnly) { 21 | const result = { 22 | sync: {}, 23 | async: {}, 24 | intercept: {} 25 | }; 26 | 27 | if (syncOnly) { 28 | await this.runSync(result.sync, "call"); 29 | } else { 30 | await this.runAsync(result.async, "callAsync"); 31 | await this.runAsync(result.async, "promise"); 32 | 33 | await this.runIntercept(result.intercept, "callAsync"); 34 | await this.runIntercept(result.intercept, "promise"); 35 | } 36 | 37 | await this.runSync(result.sync, "callAsync"); 38 | await this.runSync(result.sync, "promise"); 39 | 40 | return result; 41 | } 42 | 43 | async runForLoop(syncOnly) { 44 | const result = { 45 | sync: {}, 46 | async: {} 47 | }; 48 | 49 | if (syncOnly) { 50 | await this.runForLoopSync(result.sync, "call"); 51 | } else { 52 | await this.runForLoopAsync(result.async, "callAsync"); 53 | await this.runForLoopAsync(result.async, "promise"); 54 | } 55 | 56 | await this.runForLoopSync(result.sync, "callAsync"); 57 | await this.runForLoopSync(result.sync, "promise"); 58 | 59 | return result; 60 | } 61 | 62 | async runForLoopAsync(result, method) { 63 | { 64 | const hook = this.createHook([], `${method}BrokenPromise`); 65 | hook.tapPromise("promise", () => "this is not a promise"); 66 | result[`${method}BrokenPromise`] = await this.gainResult((cb) => 67 | hook[method](cb) 68 | ); 69 | } 70 | { 71 | const hook = this.createHook([], `${method}SinglePromise`); 72 | hook.tapPromise("promise", () => { 73 | result[`${method}SinglePromiseCalled`] = 74 | (result[`${method}SinglePromiseCalled`] || 0) + 1; 75 | if (result[`${method}SinglePromiseCalled`] < 42) { 76 | return Promise.resolve().then(() => true); 77 | } 78 | return Promise.resolve().then(() => {}); 79 | }); 80 | result[`${method}SinglePromise`] = await this.gainResult((cb) => 81 | hook[method](cb) 82 | ); 83 | } 84 | 85 | { 86 | const hook = this.createHook([], `${method}MultiplePromise`); 87 | hook.tapPromise("promise1", () => { 88 | result[`${method}MultiplePromiseCalled1`] = 89 | (result[`${method}MultiplePromiseCalled1`] || 0) + 1; 90 | if (result[`${method}MultiplePromiseCalled1`] < 42) { 91 | return Promise.resolve().then(() => true); 92 | } 93 | return Promise.resolve().then(() => {}); 94 | }); 95 | hook.tapPromise("promise2", () => { 96 | result[`${method}MultiplePromiseCalled2`] = 97 | (result[`${method}MultiplePromiseCalled2`] || 0) + 1; 98 | if (result[`${method}MultiplePromiseCalled2`] < 42) { 99 | return Promise.resolve().then(() => true); 100 | } 101 | return Promise.resolve().then(() => {}); 102 | }); 103 | result[`${method}MultiplePromise`] = await this.gainResult((cb) => 104 | hook[method](cb) 105 | ); 106 | } 107 | 108 | { 109 | const hook = this.createHook([], `${method}SingleAsync`); 110 | hook.tapAsync("async", (callback) => { 111 | result[`${method}SingleAsyncCalled`] = 112 | (result[`${method}SingleAsyncCalled`] || 0) + 1; 113 | if (result[`${method}SingleAsyncCalled`] < 42) { 114 | return Promise.resolve().then(() => callback(null, true)); 115 | } 116 | return Promise.resolve().then(() => callback()); 117 | }); 118 | result[`${method}SingleAsync`] = await this.gainResult((cb) => 119 | hook[method](cb) 120 | ); 121 | } 122 | 123 | { 124 | const hook = this.createHook([], `${method}MultipleAsync`); 125 | hook.tapAsync("async1", (callback) => { 126 | result[`${method}MultipleAsyncCalled1`] = 127 | (result[`${method}MultipleAsyncCalled1`] || 0) + 1; 128 | if (result[`${method}MultipleAsyncCalled1`] < 42) { 129 | return Promise.resolve().then(() => callback(null, true)); 130 | } 131 | return Promise.resolve().then(() => callback()); 132 | }); 133 | hook.tapAsync("async2", (callback) => { 134 | result[`${method}MultipleAsyncCalled2`] = 135 | (result[`${method}MultipleAsyncCalled2`] || 0) + 1; 136 | if (result[`${method}MultipleAsyncCalled2`] < 42) { 137 | return Promise.resolve().then(() => callback(null, true)); 138 | } 139 | return Promise.resolve().then(() => callback()); 140 | }); 141 | result[`${method}MultipleAsync`] = await this.gainResult((cb) => 142 | hook[method](cb) 143 | ); 144 | } 145 | 146 | { 147 | const hook = this.createHook([], `${method}Mixed`); 148 | hook.tapAsync("async1", (callback) => { 149 | result[`${method}MixedCalled1`] = 150 | (result[`${method}MixedCalled1`] || 0) + 1; 151 | if (result[`${method}MixedCalled1`] < 42) { 152 | return Promise.resolve().then(() => callback(null, true)); 153 | } 154 | return Promise.resolve().then(() => callback()); 155 | }); 156 | hook.tap("sync2", () => { 157 | result[`${method}MixedCalled2`] = 158 | (result[`${method}MixedCalled2`] || 0) + 1; 159 | if (result[`${method}MixedCalled2`] < 42) return true; 160 | }); 161 | hook.tapPromise("promise3", () => { 162 | result[`${method}MixedCalled3`] = 163 | (result[`${method}MixedCalled3`] || 0) + 1; 164 | if (result[`${method}MixedCalled3`] < 42) { 165 | return Promise.resolve().then(() => true); 166 | } 167 | return Promise.resolve().then(() => {}); 168 | }); 169 | result[`${method}Mixed`] = await this.gainResult((cb) => 170 | hook[method](cb) 171 | ); 172 | } 173 | } 174 | 175 | async runForLoopSync(result, method) { 176 | { 177 | const hook = this.createHook([], `${method}None`); 178 | result[`${method}None`] = await this.gainResult((cb) => hook[method](cb)); 179 | } 180 | 181 | { 182 | const hook = this.createHook(["arg"], `${method}NoneWithArg`); 183 | result[`${method}NoneWithArg`] = await this.gainResult((cb) => 184 | hook[method](42, cb) 185 | ); 186 | } 187 | 188 | { 189 | const hook = this.createHook([], `${method}SingleSync`); 190 | hook.tap("sync", () => { 191 | result[`${method}SingleSyncCalled`] = 192 | (result[`${method}SingleSyncCalled`] || 0) + 1; 193 | if (result[`${method}SingleSyncCalled`] < 42) return true; 194 | }); 195 | result[`${method}SingleSync`] = await this.gainResult((cb) => 196 | hook[method](cb) 197 | ); 198 | } 199 | 200 | { 201 | const hook = this.createHook([], `${method}MultipleSync`); 202 | hook.tap("sync1", () => { 203 | result[`${method}MultipleSyncCalled1`] = 204 | (result[`${method}MultipleSyncCalled1`] || 0) + 1; 205 | if (result[`${method}MultipleSyncCalled1`] < 42) return true; 206 | }); 207 | hook.tap("sync2", () => { 208 | result[`${method}MultipleSyncCalled2`] = 209 | (result[`${method}MultipleSyncCalled2`] || 0) + 1; 210 | if (result[`${method}MultipleSyncCalled2`] < 42) return true; 211 | }); 212 | result[`${method}MultipleSync`] = await this.gainResult((cb) => 213 | hook[method](cb) 214 | ); 215 | } 216 | 217 | { 218 | const hook = this.createHook([], `${method}InterceptedSync`); 219 | hook.tap("sync1", () => { 220 | result[`${method}InterceptedSyncCalled1`] = 221 | (result[`${method}InterceptedSyncCalled1`] || 0) + 1; 222 | if (result[`${method}InterceptedSyncCalled1`] < 42) return true; 223 | }); 224 | hook.tap("sync2", () => { 225 | result[`${method}InterceptedSyncCalled2`] = 226 | (result[`${method}InterceptedSyncCalled2`] || 0) + 1; 227 | if (result[`${method}InterceptedSyncCalled2`] < 42) return true; 228 | }); 229 | hook.intercept({ 230 | call: (_a) => 231 | (result[`${method}InterceptedSyncCalledCall`] = 232 | (result[`${method}InterceptedSyncCalledCall`] || 0) + 1), 233 | loop: (_a) => 234 | (result[`${method}InterceptedSyncCalledLoop`] = 235 | (result[`${method}InterceptedSyncCalledLoop`] || 0) + 1), 236 | tap: (_tap) => { 237 | result[`${method}InterceptedSyncCalledTap`] = 238 | (result[`${method}InterceptedSyncCalledTap`] || 0) + 1; 239 | } 240 | }); 241 | result[`${method}InterceptedSync`] = await this.gainResult((cb) => 242 | hook[method](cb) 243 | ); 244 | } 245 | } 246 | 247 | async runSync(result, method) { 248 | { 249 | const hook = this.createHook([], `${method}None`); 250 | result[`${method}None`] = await this.gainResult((cb) => hook[method](cb)); 251 | } 252 | 253 | { 254 | const hook = this.createHook(["arg"], `${method}NoneWithArg`); 255 | result[`${method}NoneWithArg`] = await this.gainResult((cb) => 256 | hook[method](42, cb) 257 | ); 258 | } 259 | 260 | { 261 | const hook = this.createHook([], `${method}SingleSync`); 262 | hook.tap("sync", () => { 263 | result[`${method}SingleSyncCalled`] = true; 264 | return 42; 265 | }); 266 | result[`${method}SingleSync`] = await this.gainResult((cb) => 267 | hook[method](cb) 268 | ); 269 | } 270 | 271 | { 272 | const hook = this.createHook(["myArg"], `${method}SingleSyncWithArg`); 273 | hook.tap("sync", (nr) => { 274 | result[`${method}SingleSyncWithArgCalled`] = nr; 275 | return nr; 276 | }); 277 | result[`${method}SingleSyncWithArg`] = await this.gainResult((cb) => 278 | hook[method](42, cb) 279 | ); 280 | } 281 | 282 | { 283 | const hook = this.createHook([], `${method}MultipleSync`); 284 | hook.tap("sync1", () => { 285 | result[`${method}MultipleSyncCalled1`] = true; 286 | return 42; 287 | }); 288 | hook.tap("sync2", () => { 289 | result[`${method}MultipleSyncCalled2`] = true; 290 | return 43; 291 | }); 292 | result[`${method}MultipleSync`] = await this.gainResult((cb) => 293 | hook[method](cb) 294 | ); 295 | } 296 | 297 | { 298 | const hook = this.createHook(["a"], `${method}MultipleSyncWithArg`); 299 | hook.tap("sync1", (a) => { 300 | result[`${method}MultipleSyncWithArgCalled1`] = a; 301 | return 42 + a; 302 | }); 303 | hook.tap("sync2", (a) => { 304 | result[`${method}MultipleSyncWithArgCalled2`] = a; 305 | return 43 + a; 306 | }); 307 | result[`${method}MultipleSyncWithArg`] = await this.gainResult((cb) => 308 | hook[method](42, cb) 309 | ); 310 | } 311 | 312 | { 313 | const hook = this.createHook( 314 | ["a"], 315 | `${method}MultipleSyncWithArgNoReturn` 316 | ); 317 | hook.tap("sync1", (a) => { 318 | result[`${method}MultipleSyncWithArgNoReturnCalled1`] = a; 319 | }); 320 | hook.tap("sync2", (a) => { 321 | result[`${method}MultipleSyncWithArgNoReturnCalled2`] = a; 322 | }); 323 | result[`${method}MultipleSyncWithArgNoReturn`] = await this.gainResult( 324 | (cb) => hook[method](42, cb) 325 | ); 326 | } 327 | 328 | { 329 | const hook = this.createHook( 330 | ["a"], 331 | `${method}MultipleSyncWithArgFirstReturn` 332 | ); 333 | hook.tap("sync1", (a) => { 334 | result[`${method}MultipleSyncWithArgFirstReturnCalled1`] = a; 335 | return 42 + a; 336 | }); 337 | hook.tap("sync2", (a) => { 338 | result[`${method}MultipleSyncWithArgFirstReturnCalled2`] = a; 339 | }); 340 | result[`${method}MultipleSyncWithArgFirstReturn`] = await this.gainResult( 341 | (cb) => hook[method](42, cb) 342 | ); 343 | } 344 | 345 | { 346 | const hook = this.createHook( 347 | ["a"], 348 | `${method}MultipleSyncWithArgLastReturn` 349 | ); 350 | hook.tap("sync1", (a) => { 351 | result[`${method}MultipleSyncWithArgLastReturnCalled1`] = a; 352 | }); 353 | hook.tap("sync2", (a) => { 354 | result[`${method}MultipleSyncWithArgLastReturnCalled2`] = a; 355 | return 43 + a; 356 | }); 357 | result[`${method}MultipleSyncWithArgLastReturn`] = await this.gainResult( 358 | (cb) => hook[method](42, cb) 359 | ); 360 | } 361 | 362 | { 363 | const hook = this.createHook( 364 | ["a", "b", "c"], 365 | `${method}MultipleSyncWithArgs` 366 | ); 367 | hook.tap("sync1", (a, b, c) => { 368 | result[`${method}MultipleSyncWithArgsCalled1`] = [a, b, c]; 369 | return a + b + c; 370 | }); 371 | hook.tap("sync2", (a, b, c) => { 372 | result[`${method}MultipleSyncWithArgsCalled2`] = [a, b, c]; 373 | return a + b + c + 1; 374 | }); 375 | result[`${method}MultipleSyncWithArgs`] = await this.gainResult((cb) => 376 | hook[method](42, 43, 44, cb) 377 | ); 378 | } 379 | 380 | { 381 | const hook = this.createHook([], `${method}MultipleSyncError`); 382 | hook.tap("sync1", () => { 383 | result[`${method}MultipleSyncErrorCalled1`] = true; 384 | }); 385 | hook.tap("sync2", () => { 386 | result[`${method}MultipleSyncErrorCalled2`] = true; 387 | throw new Error("Error in sync2"); 388 | }); 389 | hook.tap("sync3", () => { 390 | result[`${method}MultipleSyncErrorCalled3`] = true; 391 | }); 392 | result[`${method}MultipleSyncError`] = await this.gainResult((cb) => 393 | hook[method](cb) 394 | ); 395 | } 396 | 397 | { 398 | const hook = this.createHook(["a", "b", "c"], `${method}Intercepted`); 399 | hook.intercept({ 400 | call: (a, b, c) => { 401 | result[`${method}InterceptedCall1`] = [a, b, c]; 402 | }, 403 | tap: (tap) => { 404 | result[`${method}InterceptedTap1`] = { ...tap, fn: tap.fn.length }; 405 | } 406 | }); 407 | hook.intercept({ 408 | call: (a, b, c) => { 409 | result[`${method}InterceptedCall2`] = [a, b, c]; 410 | }, 411 | tap: (tap) => { 412 | if (!result[`${method}InterceptedTap2`]) { 413 | result[`${method}InterceptedTap2`] = { ...tap, fn: tap.fn.length }; 414 | } 415 | } 416 | }); 417 | hook.tap("sync1", (a, b, c) => a + b + c); 418 | hook.tap("sync2", (a, b) => a + b + 1); 419 | result[`${method}Intercepted`] = await this.gainResult((cb) => 420 | hook[method](1, 2, 3, cb) 421 | ); 422 | } 423 | } 424 | 425 | async runAsync(result, type) { 426 | { 427 | const hook = this.createHook([], `${type}None`); 428 | result[`${type}None`] = await this.gainResult((cb) => hook[type](cb)); 429 | } 430 | 431 | { 432 | const hook = this.createHook(["arg"], `${type}NoneWithArg`); 433 | result[`${type}NoneWithArg`] = await this.gainResult((cb) => 434 | hook[type](42, cb) 435 | ); 436 | } 437 | 438 | { 439 | const hook = this.createHook([], `${type}SingleSync`); 440 | hook.tap("sync", () => { 441 | result[`${type}SingleSyncCalled1`] = true; 442 | return 42; 443 | }); 444 | result[`${type}SingleSync`] = await this.gainResult((cb) => 445 | hook[type](cb) 446 | ); 447 | } 448 | 449 | { 450 | const hook = this.createHook(["x"], `${type}SingleSyncWithArg`); 451 | hook.tap("sync", (arg) => { 452 | result[`${type}SingleSyncWithArgCalled1`] = arg; 453 | return arg + 1; 454 | }); 455 | result[`${type}SingleSyncWithArg`] = await this.gainResult((cb) => 456 | hook[type](42, cb) 457 | ); 458 | } 459 | 460 | { 461 | const hook = this.createHook(["x"], `${type}SingleSyncWithArgNoReturn`); 462 | hook.tap("sync", (arg) => { 463 | result[`${type}SingleSyncWithArgNoReturnCalled1`] = arg; 464 | }); 465 | result[`${type}SingleSyncWithArgNoReturn`] = await this.gainResult((cb) => 466 | hook[type](42, cb) 467 | ); 468 | } 469 | 470 | { 471 | const hook = this.createHook([], `${type}MultipleSync`); 472 | hook.tap("sync1", () => { 473 | result[`${type}MultipleSyncCalled1`] = true; 474 | return 42; 475 | }); 476 | hook.tap("sync2", () => { 477 | result[`${type}MultipleSyncCalled2`] = true; 478 | return 43; 479 | }); 480 | result[`${type}MultipleSync`] = await this.gainResult((cb) => 481 | hook[type](cb) 482 | ); 483 | } 484 | 485 | { 486 | const hook = this.createHook([], `${type}MultipleSyncLastReturn`); 487 | hook.tap("sync1", () => { 488 | result[`${type}MultipleSyncLastReturnCalled1`] = true; 489 | }); 490 | hook.tap("sync2", () => { 491 | result[`${type}MultipleSyncLastReturnCalled2`] = true; 492 | return 43; 493 | }); 494 | result[`${type}MultipleSyncLastReturn`] = await this.gainResult((cb) => 495 | hook[type](cb) 496 | ); 497 | } 498 | 499 | { 500 | const hook = this.createHook([], `${type}MultipleSyncNoReturn`); 501 | hook.tap("sync1", () => { 502 | result[`${type}MultipleSyncNoReturnCalled1`] = true; 503 | }); 504 | hook.tap("sync2", () => { 505 | result[`${type}MultipleSyncNoReturnCalled2`] = true; 506 | }); 507 | result[`${type}MultipleSyncNoReturn`] = await this.gainResult((cb) => 508 | hook[type](cb) 509 | ); 510 | } 511 | 512 | { 513 | const hook = this.createHook(["arg"], `${type}MultipleSyncWithArg`); 514 | hook.tap("sync1", (arg) => { 515 | result[`${type}MultipleSyncWithArgCalled1`] = arg; 516 | return arg + 1; 517 | }); 518 | hook.tap("sync2", (arg) => { 519 | result[`${type}MultipleSyncWithArgCalled2`] = arg; 520 | return arg + 2; 521 | }); 522 | result[`${type}MultipleSyncWithArg`] = await this.gainResult((cb) => 523 | hook[type](42, cb) 524 | ); 525 | } 526 | 527 | { 528 | const hook = this.createHook( 529 | ["arg"], 530 | `${type}MultipleSyncWithArgNoReturn` 531 | ); 532 | hook.tap("sync1", (arg) => { 533 | result[`${type}MultipleSyncWithArgNoReturnCalled1`] = arg; 534 | }); 535 | hook.tap("sync2", (arg) => { 536 | result[`${type}MultipleSyncWithArgNoReturnCalled2`] = arg; 537 | }); 538 | result[`${type}MultipleSyncWithArgNoReturn`] = await this.gainResult( 539 | (cb) => hook[type](42, cb) 540 | ); 541 | } 542 | 543 | { 544 | const hook = this.createHook( 545 | ["arg"], 546 | `${type}MultipleSyncWithArgLastReturn` 547 | ); 548 | hook.tap("sync1", (arg) => { 549 | result[`${type}MultipleSyncWithArgLastReturnCalled1`] = arg; 550 | }); 551 | hook.tap("sync2", (arg) => { 552 | result[`${type}MultipleSyncWithArgLastReturnCalled2`] = arg; 553 | return arg + 2; 554 | }); 555 | result[`${type}MultipleSyncWithArgLastReturn`] = await this.gainResult( 556 | (cb) => hook[type](42, cb) 557 | ); 558 | } 559 | 560 | { 561 | const hook = this.createHook( 562 | ["arg"], 563 | `${type}MultipleSyncWithArgFirstReturn` 564 | ); 565 | hook.tap("sync1", (arg) => { 566 | result[`${type}MultipleSyncWithArgFirstReturnCalled1`] = arg; 567 | return arg + 1; 568 | }); 569 | hook.tap("sync2", (arg) => { 570 | result[`${type}MultipleSyncWithArgFirstReturnCalled2`] = arg; 571 | }); 572 | result[`${type}MultipleSyncWithArgFirstReturn`] = await this.gainResult( 573 | (cb) => hook[type](42, cb) 574 | ); 575 | } 576 | 577 | { 578 | const hook = this.createHook(["x"], `${type}SingleAsyncWithArg`); 579 | hook.tapAsync("async", (arg, callback) => { 580 | result[`${type}SingleAsyncWithArgCalled1`] = arg; 581 | callback(null, arg); 582 | }); 583 | result[`${type}SingleAsyncWithArg`] = await this.gainResult((cb) => 584 | hook[type](42, cb) 585 | ); 586 | } 587 | 588 | { 589 | const hook = this.createHook(["x"], `${type}MultipleAsyncWithArg`); 590 | hook.tapAsync("async1", (arg, callback) => { 591 | result[`${type}MultipleAsyncWithArgCalled1`] = arg; 592 | callback(null, arg + 1); 593 | }); 594 | hook.tapAsync("async2", (arg, callback) => { 595 | result[`${type}MultipleAsyncWithArgCalled2`] = arg; 596 | callback(null, arg + 2); 597 | }); 598 | result[`${type}MultipleAsyncWithArg`] = await this.gainResult((cb) => 599 | hook[type](42, cb) 600 | ); 601 | } 602 | 603 | { 604 | const hook = this.createHook( 605 | ["x"], 606 | `${type}MultipleAsyncWithArgNoReturn` 607 | ); 608 | hook.tapAsync("async1", (arg, callback) => { 609 | result[`${type}MultipleAsyncWithArgNoReturnCalled1`] = arg; 610 | callback(); 611 | }); 612 | hook.tapAsync("async2", (arg, callback) => { 613 | result[`${type}MultipleAsyncWithArgNoReturnCalled2`] = arg; 614 | callback(); 615 | }); 616 | result[`${type}MultipleAsyncWithArgNoReturn`] = await this.gainResult( 617 | (cb) => hook[type](42, cb) 618 | ); 619 | } 620 | 621 | { 622 | const hook = this.createHook( 623 | ["x"], 624 | `${type}MultipleAsyncWithArgFirstReturn` 625 | ); 626 | hook.tapAsync("async1", (arg, callback) => { 627 | result[`${type}MultipleAsyncWithArgFirstReturnCalled1`] = arg; 628 | callback(null, arg + 1); 629 | }); 630 | hook.tapAsync("async2", (arg, callback) => { 631 | result[`${type}MultipleAsyncWithArgFirstReturnCalled2`] = arg; 632 | callback(); 633 | }); 634 | result[`${type}MultipleAsyncWithArgFirstReturn`] = await this.gainResult( 635 | (cb) => hook[type](42, cb) 636 | ); 637 | } 638 | 639 | { 640 | const hook = this.createHook( 641 | ["x"], 642 | `${type}MultipleAsyncWithArgLastReturn` 643 | ); 644 | hook.tapAsync("async1", (arg, callback) => { 645 | result[`${type}MultipleAsyncWithArgLastReturnCalled1`] = arg; 646 | callback(); 647 | }); 648 | hook.tapAsync("async2", (arg, callback) => { 649 | result[`${type}MultipleAsyncWithArgLastReturnCalled2`] = arg; 650 | callback(null, arg + 2); 651 | }); 652 | result[`${type}MultipleAsyncWithArgLastReturn`] = await this.gainResult( 653 | (cb) => hook[type](42, cb) 654 | ); 655 | } 656 | 657 | { 658 | const hook = this.createHook(["x"], `${type}SinglePromiseWithArg`); 659 | hook.tapPromise("promise", (arg) => { 660 | result[`${type}SinglePromiseWithArgCalled1`] = arg; 661 | return Promise.resolve(arg + 1); 662 | }); 663 | result[`${type}SinglePromiseWithArg`] = await this.gainResult((cb) => 664 | hook[type](42, cb) 665 | ); 666 | } 667 | 668 | { 669 | const hook = this.createHook( 670 | ["x"], 671 | `${type}SinglePromiseWithUndefinedArg` 672 | ); 673 | hook.tapPromise("promise", (arg) => { 674 | result[`${type}SinglePromiseWithUndefinedCalled1`] = arg; 675 | return Promise.resolve(undefined); 676 | }); 677 | result[`${type}SinglePromiseWithUndefined`] = await this.gainResult( 678 | (cb) => hook[type](42, cb) 679 | ); 680 | } 681 | 682 | { 683 | const hook = this.createHook(["x"], `${type}SinglePromiseWithNullArg`); 684 | hook.tapPromise("promise", (arg) => { 685 | result[`${type}SinglePromiseWithNullArgCalled1`] = arg; 686 | return Promise.resolve(null); 687 | }); 688 | result[`${type}SinglePromiseWithNullArg`] = await this.gainResult((cb) => 689 | hook[type](42, cb) 690 | ); 691 | } 692 | 693 | { 694 | const hook = this.createHook(["x"], `${type}SinglePromiseWithZeroArg`); 695 | hook.tapPromise("promise", (arg) => { 696 | result[`${type}SinglePromiseWithZeroArgCalled1`] = arg; 697 | return Promise.resolve(0); 698 | }); 699 | result[`${type}SinglePromiseWithZeroArg`] = await this.gainResult((cb) => 700 | hook[type](42, cb) 701 | ); 702 | } 703 | 704 | { 705 | const hook = this.createHook( 706 | ["x"], 707 | `${type}SinglePromiseWithEmptyStringArg` 708 | ); 709 | hook.tapPromise("promise", (arg) => { 710 | result[`${type}SinglePromiseWithEmptyStringArgCalled1`] = arg; 711 | return Promise.resolve(""); 712 | }); 713 | result[`${type}SinglePromiseWithEmptyStringArg`] = await this.gainResult( 714 | (cb) => hook[type](42, cb) 715 | ); 716 | } 717 | 718 | { 719 | const hook = this.createHook(["x"], `${type}SinglePromiseWithNullReject`); 720 | // eslint-disable-next-line prefer-promise-reject-errors 721 | hook.tapPromise("promise", (_arg) => Promise.reject(null)); 722 | result[`${type}SinglePromiseWithNullReject`] = await this.gainResult( 723 | (cb) => hook[type](null, cb) 724 | ); 725 | } 726 | 727 | { 728 | const hook = this.createHook( 729 | ["x"], 730 | `${type}SinglePromiseWithUndefinedReject` 731 | ); 732 | // eslint-disable-next-line prefer-promise-reject-errors 733 | hook.tapPromise("promise", (_arg) => Promise.reject(undefined)); 734 | result[`${type}SinglePromiseWithUndefinedReject`] = await this.gainResult( 735 | (cb) => hook[type](null, cb) 736 | ); 737 | } 738 | 739 | { 740 | const hook = this.createHook(["x"], `${type}SinglePromiseWithZeroReject`); 741 | // eslint-disable-next-line prefer-promise-reject-errors 742 | hook.tapPromise("promise", (_arg) => Promise.reject(0)); 743 | result[`${type}SinglePromiseWithZeroReject`] = await this.gainResult( 744 | (cb) => hook[type](null, cb) 745 | ); 746 | } 747 | 748 | { 749 | const hook = this.createHook( 750 | ["x"], 751 | `${type}SinglePromiseWithFalseReject` 752 | ); 753 | // eslint-disable-next-line prefer-promise-reject-errors 754 | hook.tapPromise("promise", (_arg) => Promise.reject(false)); 755 | result[`${type}SinglePromiseWithFalseReject`] = await this.gainResult( 756 | (cb) => hook[type](null, cb) 757 | ); 758 | } 759 | 760 | { 761 | const hook = this.createHook( 762 | ["x"], 763 | `${type}SinglePromiseWithEmptyReject` 764 | ); 765 | // eslint-disable-next-line prefer-promise-reject-errors 766 | hook.tapPromise("promise", (_arg) => Promise.reject("")); 767 | result[`${type}SinglePromiseWithEmptyReject`] = await this.gainResult( 768 | (cb) => hook[type](null, cb) 769 | ); 770 | } 771 | 772 | { 773 | const hook = this.createHook(["x"], `${type}MultiplePromiseWithArg`); 774 | hook.tapPromise("promise1", (arg) => { 775 | result[`${type}MultiplePromiseWithArgCalled1`] = arg; 776 | return Promise.resolve(arg + 1); 777 | }); 778 | hook.tapPromise("promise2", (arg) => { 779 | result[`${type}MultiplePromiseWithArgCalled2`] = arg; 780 | return Promise.resolve(arg + 2); 781 | }); 782 | result[`${type}MultiplePromiseWithArg`] = await this.gainResult((cb) => 783 | hook[type](42, cb) 784 | ); 785 | } 786 | 787 | { 788 | const hook = this.createHook( 789 | ["x"], 790 | `${type}MultiplePromiseWithArgNoReturn` 791 | ); 792 | hook.tapPromise("promise1", (arg) => { 793 | result[`${type}MultiplePromiseWithArgNoReturnCalled1`] = arg; 794 | return Promise.resolve(); 795 | }); 796 | hook.tapPromise("promise2", (arg) => { 797 | result[`${type}MultiplePromiseWithArgNoReturnCalled2`] = arg; 798 | return Promise.resolve(); 799 | }); 800 | result[`${type}MultiplePromiseWithArgNoReturn`] = await this.gainResult( 801 | (cb) => hook[type](42, cb) 802 | ); 803 | } 804 | 805 | { 806 | const hook = this.createHook( 807 | ["x"], 808 | `${type}MultiplePromiseWithArgFirstReturn` 809 | ); 810 | hook.tapPromise("promise1", (arg) => { 811 | result[`${type}MultiplePromiseWithArgFirstReturnCalled1`] = arg; 812 | return Promise.resolve(arg + 1); 813 | }); 814 | hook.tapPromise("promise2", (arg) => { 815 | result[`${type}MultiplePromiseWithArgFirstReturnCalled2`] = arg; 816 | return Promise.resolve(); 817 | }); 818 | result[`${type}MultiplePromiseWithArgFirstReturn`] = 819 | await this.gainResult((cb) => hook[type](42, cb)); 820 | } 821 | 822 | { 823 | const hook = this.createHook( 824 | ["x"], 825 | `${type}MultiplePromiseWithArgLastReturn` 826 | ); 827 | hook.tapPromise("promise1", (arg) => { 828 | result[`${type}MultiplePromiseWithArgLastReturnCalled1`] = arg; 829 | return Promise.resolve(); 830 | }); 831 | hook.tapPromise("promise2", (arg) => { 832 | result[`${type}MultiplePromiseWithArgLastReturnCalled2`] = arg; 833 | return Promise.resolve(arg + 2); 834 | }); 835 | result[`${type}MultiplePromiseWithArgLastReturn`] = await this.gainResult( 836 | (cb) => hook[type](42, cb) 837 | ); 838 | } 839 | 840 | { 841 | const hook = this.createHook(["x"], `${type}MultipleMixed1WithArg`); 842 | hook.tapAsync("async", (arg, callback) => { 843 | result[`${type}MultipleMixed1WithArgCalled1`] = arg; 844 | callback(null, arg + 1); 845 | }); 846 | hook.tapPromise("promise", (arg) => { 847 | result[`${type}MultipleMixed1WithArgCalled2`] = arg; 848 | return Promise.resolve(arg + 2); 849 | }); 850 | hook.tap("sync", (arg) => { 851 | result[`${type}MultipleMixed1WithArgCalled3`] = arg; 852 | return arg + 3; 853 | }); 854 | result[`${type}MultipleMixed1WithArg`] = await this.gainResult((cb) => 855 | hook[type](42, cb) 856 | ); 857 | } 858 | 859 | { 860 | const hook = this.createHook(["x"], `${type}MultipleMixed2WithArg`); 861 | hook.tapAsync("async", (arg, callback) => { 862 | result[`${type}MultipleMixed2WithArgCalled1`] = arg; 863 | setTimeout(() => callback(null, arg + 1), 100); 864 | }); 865 | hook.tapPromise("promise", (arg) => { 866 | result[`${type}MultipleMixed2WithArgCalled2`] = arg; 867 | return Promise.resolve(arg + 2); 868 | }); 869 | result[`${type}MultipleMixed2WithArg`] = await this.gainResult((cb) => 870 | hook[type](42, cb) 871 | ); 872 | } 873 | 874 | { 875 | const hook = this.createHook(["x"], `${type}MultipleMixed3WithArg`); 876 | hook.tapAsync("async1", (arg, callback) => { 877 | result[`${type}MultipleMixed3WithArgCalled1`] = arg; 878 | callback(null, arg + 1); 879 | }); 880 | hook.tapPromise("promise", (arg) => { 881 | result[`${type}MultipleMixed3WithArgCalled2`] = arg; 882 | return Promise.resolve(arg + 2); 883 | }); 884 | hook.tapAsync("async2", (arg, callback) => { 885 | result[`${type}MultipleMixed3WithArgCalled3`] = arg; 886 | setTimeout(() => callback(null, arg + 3), 100); 887 | }); 888 | result[`${type}MultipleMixed3WithArg`] = await this.gainResult((cb) => 889 | hook[type](42, cb) 890 | ); 891 | } 892 | 893 | { 894 | const hook = this.createHook([], `${type}MultipleSyncError`); 895 | hook.tap("sync1", () => { 896 | result[`${type}MultipleSyncErrorCalled1`] = true; 897 | }); 898 | hook.tap("sync2", () => { 899 | throw new Error("Error in sync2"); 900 | }); 901 | hook.tap("sync3", () => { 902 | result[`${type}MultipleSyncErrorCalled3`] = true; 903 | }); 904 | result[`${type}MultipleSyncError`] = await this.gainResult((cb) => 905 | hook[type](cb) 906 | ); 907 | } 908 | 909 | { 910 | const hook = this.createHook([], `${type}MultipleAsyncError`); 911 | hook.tapAsync("async1", (callback) => { 912 | result[`${type}MultipleAsyncErrorCalled1`] = true; 913 | callback(); 914 | }); 915 | hook.tapAsync("async2", (callback) => { 916 | callback(new Error("Error in async2")); 917 | }); 918 | hook.tapAsync("async3", (callback) => { 919 | result[`${type}MultipleAsyncErrorCalled3`] = true; 920 | callback(); 921 | }); 922 | result[`${type}MultipleAsyncError`] = await this.gainResult((cb) => 923 | hook[type](cb) 924 | ); 925 | } 926 | 927 | { 928 | const hook = this.createHook([], `${type}MultipleAsyncLateError`); 929 | hook.tapAsync("async1", (callback) => { 930 | result[`${type}MultipleAsyncLateErrorCalled1`] = true; 931 | callback(); 932 | }); 933 | hook.tapAsync("async2", (callback) => { 934 | setTimeout(() => callback(new Error("Error in async2")), 100); 935 | }); 936 | hook.tapAsync("async3", (callback) => { 937 | result[`${type}MultipleAsyncLateErrorCalled3`] = true; 938 | callback(); 939 | }); 940 | result[`${type}MultipleAsyncLateError`] = await this.gainResult((cb) => 941 | hook[type](cb) 942 | ); 943 | } 944 | 945 | { 946 | const hook = this.createHook( 947 | [], 948 | `${type}MultipleAsyncLateErrorEarlyResult1` 949 | ); 950 | hook.tapAsync("async1", (callback) => { 951 | result[`${type}MultipleAsyncLateErrorEarlyResult1Called1`] = true; 952 | callback(); 953 | }); 954 | hook.tapAsync("async2", (callback) => { 955 | setTimeout(() => callback(new Error("Error in async2")), 100); 956 | }); 957 | hook.tapAsync("async3", (callback) => { 958 | result[`${type}MultipleAsyncLateErrorEarlyResult1Called3`] = true; 959 | callback(null, 7); 960 | }); 961 | result[`${type}MultipleAsyncLateErrorEarlyResult1`] = 962 | await this.gainResult((cb) => hook[type](cb)); 963 | } 964 | 965 | { 966 | const hook = this.createHook( 967 | [], 968 | `${type}MultipleAsyncLateErrorEarlyResult2` 969 | ); 970 | hook.tapAsync("async1", (callback) => { 971 | result[`${type}MultipleAsyncLateErrorEarlyResult2Called1`] = true; 972 | setTimeout(() => callback(null, 42), 200); 973 | }); 974 | hook.tapAsync("async2", (callback) => { 975 | setTimeout(() => callback(new Error("Error in async2")), 100); 976 | }); 977 | hook.tapAsync("async3", (callback) => { 978 | result[`${type}MultipleAsyncLateErrorEarlyResult2Called3`] = true; 979 | callback(null, 7); 980 | }); 981 | result[`${type}MultipleAsyncLateErrorEarlyResult2`] = 982 | await this.gainResult((cb) => hook[type](cb)); 983 | } 984 | 985 | { 986 | const hook = this.createHook([], `${type}MultipleAsyncEarlyError`); 987 | hook.tapAsync("async1", (callback) => { 988 | result[`${type}MultipleAsyncEarlyErrorCalled1`] = true; 989 | setTimeout(() => callback(), 100); 990 | }); 991 | hook.tapAsync("async2", (callback) => { 992 | callback(new Error("Error in async2")); 993 | }); 994 | hook.tapAsync("async3", (callback) => { 995 | result[`${type}MultipleAsyncEarlyErrorCalled3`] = true; 996 | setTimeout(() => callback(), 100); 997 | }); 998 | result[`${type}MultipleAsyncEarlyError`] = await this.gainResult((cb) => 999 | hook[type](cb) 1000 | ); 1001 | } 1002 | 1003 | { 1004 | const hook = this.createHook([], `${type}MultiplePromiseError`); 1005 | hook.tapPromise("promise1", () => { 1006 | result[`${type}MultiplePromiseErrorCalled1`] = true; 1007 | return Promise.resolve(); 1008 | }); 1009 | hook.tapPromise("promise2", () => 1010 | Promise.resolve().then(() => { 1011 | throw new Error("Error in async2"); 1012 | }) 1013 | ); 1014 | hook.tapPromise("promise3", () => { 1015 | result[`${type}MultiplePromiseErrorCalled3`] = true; 1016 | return Promise.resolve(); 1017 | }); 1018 | result[`${type}MultiplePromiseError`] = await this.gainResult((cb) => 1019 | hook[type](cb) 1020 | ); 1021 | } 1022 | 1023 | { 1024 | const hook = this.createHook([], `${type}MultiplePromiseLateError`); 1025 | hook.tapPromise("promise1", () => { 1026 | result[`${type}MultiplePromiseLateErrorCalled1`] = true; 1027 | return Promise.resolve(); 1028 | }); 1029 | hook.tapPromise( 1030 | "promise2", 1031 | () => 1032 | new Promise((resolve, reject) => { 1033 | setTimeout(() => reject(new Error("Error in async2")), 100); 1034 | }) 1035 | ); 1036 | hook.tapPromise("promise3", () => { 1037 | result[`${type}MultiplePromiseLateErrorCalled3`] = true; 1038 | return Promise.resolve(); 1039 | }); 1040 | result[`${type}MultiplePromiseLateError`] = await this.gainResult((cb) => 1041 | hook[type](cb) 1042 | ); 1043 | } 1044 | 1045 | { 1046 | const hook = this.createHook([], `${type}MultiplePromiseEarlyError`); 1047 | hook.tapPromise("promise1", () => { 1048 | result[`${type}MultiplePromiseEarlyErrorCalled1`] = true; 1049 | return new Promise((resolve) => { 1050 | setTimeout(() => resolve(), 100); 1051 | }); 1052 | }); 1053 | hook.tapPromise("promise2", () => 1054 | Promise.resolve().then(() => { 1055 | throw new Error("Error in async2"); 1056 | }) 1057 | ); 1058 | hook.tapPromise("promise3", () => { 1059 | result[`${type}MultiplePromiseEarlyErrorCalled3`] = true; 1060 | return new Promise((resolve) => { 1061 | setTimeout(() => resolve(), 100); 1062 | }); 1063 | }); 1064 | result[`${type}MultiplePromiseEarlyError`] = await this.gainResult((cb) => 1065 | hook[type](cb) 1066 | ); 1067 | } 1068 | 1069 | { 1070 | const hook = this.createHook(["x"], `${type}MultipleMixedError1WithArg`); 1071 | hook.tapAsync("async", (arg, callback) => { 1072 | result[`${type}MultipleMixedError1WithArgCalled1`] = arg; 1073 | callback(null, arg); 1074 | }); 1075 | hook.tapPromise("promise", (arg) => { 1076 | result[`${type}MultipleMixedError1WithArgCalled2`] = arg; 1077 | return Promise.resolve(arg + 1); 1078 | }); 1079 | hook.tap("sync", (arg) => { 1080 | result[`${type}MultipleMixedError1WithArgCalled3`] = arg; 1081 | throw new Error("Error in sync"); 1082 | }); 1083 | result[`${type}MultipleMixedError1WithArg`] = await this.gainResult( 1084 | (cb) => hook[type](42, cb) 1085 | ); 1086 | } 1087 | 1088 | { 1089 | const hook = this.createHook(["x"], `${type}MultipleMixedError2WithArg`); 1090 | hook.tapAsync("async", (arg, callback) => { 1091 | result[`${type}MultipleMixedError2WithArgCalled1`] = arg; 1092 | callback(null, arg); 1093 | }); 1094 | hook.tapPromise("promise", (arg) => { 1095 | result[`${type}MultipleMixedError2WithArgCalled2`] = arg; 1096 | return Promise.resolve().then(() => { 1097 | throw new Error("Error in promise"); 1098 | }); 1099 | }); 1100 | hook.tap("sync", (arg) => { 1101 | result[`${type}MultipleMixedError2WithArgCalled3`] = arg; 1102 | return arg + 2; 1103 | }); 1104 | result[`${type}MultipleMixedError2WithArg`] = await this.gainResult( 1105 | (cb) => hook[type](42, cb) 1106 | ); 1107 | } 1108 | 1109 | { 1110 | const hook = this.createHook(["x"], `${type}MultipleMixedError3WithArg`); 1111 | hook.tapAsync("async", (arg, callback) => { 1112 | result[`${type}MultipleMixedError3WithArgCalled1`] = arg; 1113 | callback(new Error("Error in async")); 1114 | }); 1115 | hook.tapPromise("promise", (arg) => { 1116 | result[`${type}MultipleMixedError3WithArgCalled2`] = arg; 1117 | return Promise.resolve(arg + 1); 1118 | }); 1119 | hook.tap("sync", (arg) => { 1120 | result[`${type}MultipleMixedError3WithArgCalled3`] = arg; 1121 | return arg + 2; 1122 | }); 1123 | result[`${type}MultipleMixedError3WithArg`] = await this.gainResult( 1124 | (cb) => hook[type](42, cb) 1125 | ); 1126 | } 1127 | 1128 | { 1129 | const hook = this.createHook([], `${type}MultipleMixedLateError`); 1130 | hook.tapAsync("async", (callback) => { 1131 | result[`${type}MultipleMixedLateErrorCalled1`] = true; 1132 | setTimeout(() => callback(new Error("Error in async")), 100); 1133 | }); 1134 | hook.tapPromise("promise", () => { 1135 | result[`${type}MultipleMixedLateErrorCalled2`] = true; 1136 | return Promise.resolve(42); 1137 | }); 1138 | hook.tap("sync", () => { 1139 | result[`${type}MultipleMixedLateErrorCalled3`] = true; 1140 | return 43; 1141 | }); 1142 | result[`${type}MultipleMixedLateError`] = await this.gainResult((cb) => 1143 | hook[type](cb) 1144 | ); 1145 | } 1146 | } 1147 | 1148 | async runIntercept(result, type) { 1149 | { 1150 | const hook = this.createHook(["a", "b", "c"], `${type}Intercepted`); 1151 | hook.intercept({ 1152 | call: (a, b, c) => { 1153 | result[`${type}InterceptedCall1`] = [a, b, c]; 1154 | }, 1155 | 1156 | done: () => { 1157 | result[`${type}InterceptedDone1`] = true; 1158 | }, 1159 | 1160 | result: (r) => { 1161 | result[`${type}InterceptedResult1`] = r; 1162 | }, 1163 | 1164 | error: (err) => { 1165 | result[`${type}InterceptedError1`] = err; 1166 | }, 1167 | 1168 | tap: (tap) => { 1169 | result[`${type}InterceptedTap1`] = { ...tap, fn: tap.fn.length }; 1170 | } 1171 | }); 1172 | hook.intercept({ 1173 | call: (a, b, c) => { 1174 | result[`${type}InterceptedCall2`] = [a, b, c]; 1175 | }, 1176 | 1177 | done: () => { 1178 | result[`${type}InterceptedDone2`] = true; 1179 | }, 1180 | 1181 | result: (r) => { 1182 | result[`${type}InterceptedResult2`] = r; 1183 | }, 1184 | 1185 | error: (err) => { 1186 | result[`${type}InterceptedError2`] = err; 1187 | }, 1188 | 1189 | tap: (tap) => { 1190 | if (!result[`${type}InterceptedTap2`]) { 1191 | result[`${type}InterceptedTap2`] = { ...tap, fn: tap.fn.length }; 1192 | } 1193 | } 1194 | }); 1195 | hook.tap("sync", (a, b, c) => a + b + c); 1196 | hook.tapPromise("promise", (a, b) => Promise.resolve(a + b + 1)); 1197 | result[`${type}Intercepted`] = await this.gainResult((cb) => 1198 | hook[type](1, 2, 3, cb) 1199 | ); 1200 | } 1201 | 1202 | { 1203 | const hook = this.createHook( 1204 | ["a", "b", "c"], 1205 | `${type}ContextIntercepted` 1206 | ); 1207 | hook.intercept({ 1208 | call: (context, a, b, c) => { 1209 | context.number = 42; 1210 | result[`${type}ContextInterceptedCall1`] = [context, a, b, c]; 1211 | }, 1212 | loop: (context, a, b, c) => { 1213 | context.number2 = 88; 1214 | result[`${type}ContextInterceptedLoop1`] = [context, a, b, c]; 1215 | }, 1216 | tap: (context, _tap) => { 1217 | result[`${type}ContextInterceptedTap1`] = context; 1218 | }, 1219 | context: true 1220 | }); 1221 | hook.intercept({ 1222 | call: (a, b, c) => { 1223 | result[`${type}ContextInterceptedCall2`] = [a, b, c]; 1224 | } 1225 | }); 1226 | hook.tap( 1227 | { 1228 | name: "sync", 1229 | context: true 1230 | }, 1231 | (context, a, b, c) => context.number + a + b + c 1232 | ); 1233 | result[`${type}ContextIntercepted`] = await this.gainResult((cb) => 1234 | hook[type](1, 2, 3, cb) 1235 | ); 1236 | } 1237 | 1238 | { 1239 | const hook = this.createHook( 1240 | ["a", "b", "c"], 1241 | `${type}UnusedContextIntercepted` 1242 | ); 1243 | hook.intercept({ 1244 | call: (context, a, b, c) => { 1245 | result[`${type}UnusedContextInterceptedCall1`] = [context, a, b, c]; 1246 | }, 1247 | tap: (context, _tap) => { 1248 | result[`${type}UnusedContextInterceptedTap1`] = context; 1249 | }, 1250 | context: true 1251 | }); 1252 | hook.intercept({ 1253 | call: (a, b, c) => { 1254 | result[`${type}UnusedContextInterceptedCall2`] = [a, b, c]; 1255 | } 1256 | }); 1257 | hook.tap("sync", (a, b, c) => a + b + c); 1258 | result[`${type}UnusedContextIntercepted`] = await this.gainResult((cb) => 1259 | hook[type](1, 2, 3, cb) 1260 | ); 1261 | } 1262 | } 1263 | 1264 | gainResult(fn) { 1265 | return Promise.race([ 1266 | new Promise((resolve) => { 1267 | try { 1268 | const ret = fn((err, result) => { 1269 | if (err) { 1270 | resolve({ 1271 | type: "async", 1272 | error: err.message 1273 | }); 1274 | } else { 1275 | resolve({ 1276 | type: "async", 1277 | value: result 1278 | }); 1279 | } 1280 | }); 1281 | if (ret instanceof Promise) { 1282 | resolve( 1283 | ret.then( 1284 | (res) => ({ 1285 | type: "promise", 1286 | value: res 1287 | }), 1288 | (err) => ({ 1289 | type: "promise", 1290 | error: err.message 1291 | }) 1292 | ) 1293 | ); 1294 | } else if (ret !== undefined) { 1295 | resolve({ 1296 | type: "return", 1297 | value: ret 1298 | }); 1299 | } 1300 | } catch (err) { 1301 | resolve({ 1302 | error: err.message 1303 | }); 1304 | } 1305 | }), 1306 | new Promise((resolve) => { 1307 | setTimeout( 1308 | () => 1309 | resolve({ 1310 | type: "no result" 1311 | }), 1312 | 1000 1313 | ); 1314 | }) 1315 | ]); 1316 | } 1317 | 1318 | createHook(args, name) { 1319 | try { 1320 | return this.hookCreator(args, name); 1321 | } catch (err) { 1322 | return { 1323 | tap: () => {}, 1324 | tapPromise: () => {}, 1325 | tapAsync: () => {}, 1326 | intercept: () => {}, 1327 | call: () => { 1328 | throw err; 1329 | }, 1330 | callAsync: () => { 1331 | throw err; 1332 | }, 1333 | promise: () => { 1334 | throw err; 1335 | } 1336 | }; 1337 | } 1338 | } 1339 | } 1340 | 1341 | // eslint-disable-next-line jest/no-export 1342 | module.exports = HookTester; 1343 | -------------------------------------------------------------------------------- /lib/__tests__/__snapshots__/HookCodeFactory.js.snap: -------------------------------------------------------------------------------- 1 | // Jest Snapshot v1, https://goo.gl/fbAQLP 2 | 3 | exports[`HookCodeFactory callTap (no args, no intercept) async with onResult 1`] = ` 4 | "var _fn1 = _x[1]; 5 | _fn1((function(_err1, _result1) { 6 | if(_err1) { 7 | onError(_err1); 8 | } else { 9 | onResult(_result1); 10 | } 11 | })); 12 | " 13 | `; 14 | 15 | exports[`HookCodeFactory callTap (no args, no intercept) async with onResult 2`] = ` 16 | "var _fn1 = _x[1]; 17 | _fn1(function(_err1, _result1) { 18 | if (_err1) { 19 | onError(_err1); 20 | } else { 21 | onResult(_result1); 22 | } 23 | }); 24 | " 25 | `; 26 | 27 | exports[`HookCodeFactory callTap (no args, no intercept) async without onResult 1`] = ` 28 | "var _fn1 = _x[1]; 29 | _fn1((function(_err1) { 30 | if(_err1) { 31 | onError(_err1); 32 | } else { 33 | onDone(); 34 | } 35 | })); 36 | " 37 | `; 38 | 39 | exports[`HookCodeFactory callTap (no args, no intercept) async without onResult 2`] = ` 40 | "var _fn1 = _x[1]; 41 | _fn1(function(_err1) { 42 | if (_err1) { 43 | onError(_err1); 44 | } else { 45 | onDone(); 46 | } 47 | }); 48 | " 49 | `; 50 | 51 | exports[`HookCodeFactory callTap (no args, no intercept) promise with onResult 1`] = ` 52 | "var _fn2 = _x[2]; 53 | var _hasResult2 = false; 54 | var _promise2 = _fn2(); 55 | if (!_promise2 || !_promise2.then) 56 | throw new Error('Tap function (tapPromise) did not return promise (returned ' + _promise2 + ')'); 57 | _promise2.then((function(_result2) { 58 | _hasResult2 = true; 59 | onResult(_result2); 60 | }), function(_err2) { 61 | if(_hasResult2) throw _err2; 62 | onError(!_err2 ? new Error('Tap function (tapPromise) rejects \\"' + _err2 + '\\" value') : _err2); 63 | }); 64 | " 65 | `; 66 | 67 | exports[`HookCodeFactory callTap (no args, no intercept) promise with onResult 2`] = ` 68 | "var _fn2 = _x[2]; 69 | var _hasResult2 = false; 70 | var _promise2 = _fn2(); 71 | if (!_promise2 || !_promise2.then) 72 | throw new Error( 73 | \\"Tap function (tapPromise) did not return promise (returned \\" + 74 | _promise2 + 75 | \\")\\" 76 | ); 77 | _promise2.then( 78 | function(_result2) { 79 | _hasResult2 = true; 80 | onResult(_result2); 81 | }, 82 | function(_err2) { 83 | if (_hasResult2) throw _err2; 84 | onError( 85 | !_err2 86 | ? new Error('Tap function (tapPromise) rejects \\"' + _err2 + '\\" value') 87 | : _err2 88 | ); 89 | } 90 | ); 91 | " 92 | `; 93 | 94 | exports[`HookCodeFactory callTap (no args, no intercept) promise without onResult 1`] = ` 95 | "var _fn2 = _x[2]; 96 | var _hasResult2 = false; 97 | var _promise2 = _fn2(); 98 | if (!_promise2 || !_promise2.then) 99 | throw new Error('Tap function (tapPromise) did not return promise (returned ' + _promise2 + ')'); 100 | _promise2.then((function(_result2) { 101 | _hasResult2 = true; 102 | onDone(); 103 | }), function(_err2) { 104 | if(_hasResult2) throw _err2; 105 | onError(!_err2 ? new Error('Tap function (tapPromise) rejects \\"' + _err2 + '\\" value') : _err2); 106 | }); 107 | " 108 | `; 109 | 110 | exports[`HookCodeFactory callTap (no args, no intercept) promise without onResult 2`] = ` 111 | "var _fn2 = _x[2]; 112 | var _hasResult2 = false; 113 | var _promise2 = _fn2(); 114 | if (!_promise2 || !_promise2.then) 115 | throw new Error( 116 | \\"Tap function (tapPromise) did not return promise (returned \\" + 117 | _promise2 + 118 | \\")\\" 119 | ); 120 | _promise2.then( 121 | function(_result2) { 122 | _hasResult2 = true; 123 | onDone(); 124 | }, 125 | function(_err2) { 126 | if (_hasResult2) throw _err2; 127 | onError( 128 | !_err2 129 | ? new Error('Tap function (tapPromise) rejects \\"' + _err2 + '\\" value') 130 | : _err2 131 | ); 132 | } 133 | ); 134 | " 135 | `; 136 | 137 | exports[`HookCodeFactory callTap (no args, no intercept) sync with onResult 1`] = ` 138 | "var _fn0 = _x[0]; 139 | var _hasError0 = false; 140 | try { 141 | var _result0 = _fn0(); 142 | } catch(_err) { 143 | _hasError0 = true; 144 | onError(_err); 145 | } 146 | if(!_hasError0) { 147 | onResult(_result0); 148 | } 149 | " 150 | `; 151 | 152 | exports[`HookCodeFactory callTap (no args, no intercept) sync with onResult 2`] = ` 153 | "var _fn0 = _x[0]; 154 | var _hasError0 = false; 155 | try { 156 | var _result0 = _fn0(); 157 | } catch (_err) { 158 | _hasError0 = true; 159 | onError(_err); 160 | } 161 | if (!_hasError0) { 162 | onResult(_result0); 163 | } 164 | " 165 | `; 166 | 167 | exports[`HookCodeFactory callTap (no args, no intercept) sync without onResult 1`] = ` 168 | "var _fn0 = _x[0]; 169 | var _hasError0 = false; 170 | try { 171 | _fn0(); 172 | } catch(_err) { 173 | _hasError0 = true; 174 | onError(_err); 175 | } 176 | if(!_hasError0) { 177 | onDone(); 178 | } 179 | " 180 | `; 181 | 182 | exports[`HookCodeFactory callTap (no args, no intercept) sync without onResult 2`] = ` 183 | "var _fn0 = _x[0]; 184 | var _hasError0 = false; 185 | try { 186 | _fn0(); 187 | } catch (_err) { 188 | _hasError0 = true; 189 | onError(_err); 190 | } 191 | if (!_hasError0) { 192 | onDone(); 193 | } 194 | " 195 | `; 196 | 197 | exports[`HookCodeFactory callTap (with args, no intercept) async with onResult 1`] = ` 198 | "var _fn1 = _x[1]; 199 | _fn1(a, b, c, (function(_err1, _result1) { 200 | if(_err1) { 201 | onError(_err1); 202 | } else { 203 | onResult(_result1); 204 | } 205 | })); 206 | " 207 | `; 208 | 209 | exports[`HookCodeFactory callTap (with args, no intercept) async with onResult 2`] = ` 210 | "var _fn1 = _x[1]; 211 | _fn1(a, b, c, function(_err1, _result1) { 212 | if (_err1) { 213 | onError(_err1); 214 | } else { 215 | onResult(_result1); 216 | } 217 | }); 218 | " 219 | `; 220 | 221 | exports[`HookCodeFactory callTap (with args, no intercept) async without onResult 1`] = ` 222 | "var _fn1 = _x[1]; 223 | _fn1(a, b, c, (function(_err1) { 224 | if(_err1) { 225 | onError(_err1); 226 | } else { 227 | onDone(); 228 | } 229 | })); 230 | " 231 | `; 232 | 233 | exports[`HookCodeFactory callTap (with args, no intercept) async without onResult 2`] = ` 234 | "var _fn1 = _x[1]; 235 | _fn1(a, b, c, function(_err1) { 236 | if (_err1) { 237 | onError(_err1); 238 | } else { 239 | onDone(); 240 | } 241 | }); 242 | " 243 | `; 244 | 245 | exports[`HookCodeFactory callTap (with args, no intercept) promise with onResult 1`] = ` 246 | "var _fn2 = _x[2]; 247 | var _hasResult2 = false; 248 | var _promise2 = _fn2(a, b, c); 249 | if (!_promise2 || !_promise2.then) 250 | throw new Error('Tap function (tapPromise) did not return promise (returned ' + _promise2 + ')'); 251 | _promise2.then((function(_result2) { 252 | _hasResult2 = true; 253 | onResult(_result2); 254 | }), function(_err2) { 255 | if(_hasResult2) throw _err2; 256 | onError(!_err2 ? new Error('Tap function (tapPromise) rejects \\"' + _err2 + '\\" value') : _err2); 257 | }); 258 | " 259 | `; 260 | 261 | exports[`HookCodeFactory callTap (with args, no intercept) promise with onResult 2`] = ` 262 | "var _fn2 = _x[2]; 263 | var _hasResult2 = false; 264 | var _promise2 = _fn2(a, b, c); 265 | if (!_promise2 || !_promise2.then) 266 | throw new Error( 267 | \\"Tap function (tapPromise) did not return promise (returned \\" + 268 | _promise2 + 269 | \\")\\" 270 | ); 271 | _promise2.then( 272 | function(_result2) { 273 | _hasResult2 = true; 274 | onResult(_result2); 275 | }, 276 | function(_err2) { 277 | if (_hasResult2) throw _err2; 278 | onError( 279 | !_err2 280 | ? new Error('Tap function (tapPromise) rejects \\"' + _err2 + '\\" value') 281 | : _err2 282 | ); 283 | } 284 | ); 285 | " 286 | `; 287 | 288 | exports[`HookCodeFactory callTap (with args, no intercept) promise without onResult 1`] = ` 289 | "var _fn2 = _x[2]; 290 | var _hasResult2 = false; 291 | var _promise2 = _fn2(a, b, c); 292 | if (!_promise2 || !_promise2.then) 293 | throw new Error('Tap function (tapPromise) did not return promise (returned ' + _promise2 + ')'); 294 | _promise2.then((function(_result2) { 295 | _hasResult2 = true; 296 | onDone(); 297 | }), function(_err2) { 298 | if(_hasResult2) throw _err2; 299 | onError(!_err2 ? new Error('Tap function (tapPromise) rejects \\"' + _err2 + '\\" value') : _err2); 300 | }); 301 | " 302 | `; 303 | 304 | exports[`HookCodeFactory callTap (with args, no intercept) promise without onResult 2`] = ` 305 | "var _fn2 = _x[2]; 306 | var _hasResult2 = false; 307 | var _promise2 = _fn2(a, b, c); 308 | if (!_promise2 || !_promise2.then) 309 | throw new Error( 310 | \\"Tap function (tapPromise) did not return promise (returned \\" + 311 | _promise2 + 312 | \\")\\" 313 | ); 314 | _promise2.then( 315 | function(_result2) { 316 | _hasResult2 = true; 317 | onDone(); 318 | }, 319 | function(_err2) { 320 | if (_hasResult2) throw _err2; 321 | onError( 322 | !_err2 323 | ? new Error('Tap function (tapPromise) rejects \\"' + _err2 + '\\" value') 324 | : _err2 325 | ); 326 | } 327 | ); 328 | " 329 | `; 330 | 331 | exports[`HookCodeFactory callTap (with args, no intercept) sync with onResult 1`] = ` 332 | "var _fn0 = _x[0]; 333 | var _hasError0 = false; 334 | try { 335 | var _result0 = _fn0(a, b, c); 336 | } catch(_err) { 337 | _hasError0 = true; 338 | onError(_err); 339 | } 340 | if(!_hasError0) { 341 | onResult(_result0); 342 | } 343 | " 344 | `; 345 | 346 | exports[`HookCodeFactory callTap (with args, no intercept) sync with onResult 2`] = ` 347 | "var _fn0 = _x[0]; 348 | var _hasError0 = false; 349 | try { 350 | var _result0 = _fn0(a, b, c); 351 | } catch (_err) { 352 | _hasError0 = true; 353 | onError(_err); 354 | } 355 | if (!_hasError0) { 356 | onResult(_result0); 357 | } 358 | " 359 | `; 360 | 361 | exports[`HookCodeFactory callTap (with args, no intercept) sync without onResult 1`] = ` 362 | "var _fn0 = _x[0]; 363 | var _hasError0 = false; 364 | try { 365 | _fn0(a, b, c); 366 | } catch(_err) { 367 | _hasError0 = true; 368 | onError(_err); 369 | } 370 | if(!_hasError0) { 371 | onDone(); 372 | } 373 | " 374 | `; 375 | 376 | exports[`HookCodeFactory callTap (with args, no intercept) sync without onResult 2`] = ` 377 | "var _fn0 = _x[0]; 378 | var _hasError0 = false; 379 | try { 380 | _fn0(a, b, c); 381 | } catch (_err) { 382 | _hasError0 = true; 383 | onError(_err); 384 | } 385 | if (!_hasError0) { 386 | onDone(); 387 | } 388 | " 389 | `; 390 | 391 | exports[`HookCodeFactory callTap (with args, with intercept) async with onResult 1`] = ` 392 | "var _tap1 = _taps[1]; 393 | _interceptors[0].tap(_tap1); 394 | _interceptors[1].tap(_tap1); 395 | var _fn1 = _x[1]; 396 | _fn1(a, b, c, (function(_err1, _result1) { 397 | if(_err1) { 398 | onError(_err1); 399 | } else { 400 | onResult(_result1); 401 | } 402 | })); 403 | " 404 | `; 405 | 406 | exports[`HookCodeFactory callTap (with args, with intercept) async with onResult 2`] = ` 407 | "var _tap1 = _taps[1]; 408 | _interceptors[0].tap(_tap1); 409 | _interceptors[1].tap(_tap1); 410 | var _fn1 = _x[1]; 411 | _fn1(a, b, c, function(_err1, _result1) { 412 | if (_err1) { 413 | onError(_err1); 414 | } else { 415 | onResult(_result1); 416 | } 417 | }); 418 | " 419 | `; 420 | 421 | exports[`HookCodeFactory callTap (with args, with intercept) async without onResult 1`] = ` 422 | "var _tap1 = _taps[1]; 423 | _interceptors[0].tap(_tap1); 424 | _interceptors[1].tap(_tap1); 425 | var _fn1 = _x[1]; 426 | _fn1(a, b, c, (function(_err1) { 427 | if(_err1) { 428 | onError(_err1); 429 | } else { 430 | onDone(); 431 | } 432 | })); 433 | " 434 | `; 435 | 436 | exports[`HookCodeFactory callTap (with args, with intercept) async without onResult 2`] = ` 437 | "var _tap1 = _taps[1]; 438 | _interceptors[0].tap(_tap1); 439 | _interceptors[1].tap(_tap1); 440 | var _fn1 = _x[1]; 441 | _fn1(a, b, c, function(_err1) { 442 | if (_err1) { 443 | onError(_err1); 444 | } else { 445 | onDone(); 446 | } 447 | }); 448 | " 449 | `; 450 | 451 | exports[`HookCodeFactory callTap (with args, with intercept) promise with onResult 1`] = ` 452 | "var _tap2 = _taps[2]; 453 | _interceptors[0].tap(_tap2); 454 | _interceptors[1].tap(_tap2); 455 | var _fn2 = _x[2]; 456 | var _hasResult2 = false; 457 | var _promise2 = _fn2(a, b, c); 458 | if (!_promise2 || !_promise2.then) 459 | throw new Error('Tap function (tapPromise) did not return promise (returned ' + _promise2 + ')'); 460 | _promise2.then((function(_result2) { 461 | _hasResult2 = true; 462 | onResult(_result2); 463 | }), function(_err2) { 464 | if(_hasResult2) throw _err2; 465 | onError(!_err2 ? new Error('Tap function (tapPromise) rejects \\"' + _err2 + '\\" value') : _err2); 466 | }); 467 | " 468 | `; 469 | 470 | exports[`HookCodeFactory callTap (with args, with intercept) promise with onResult 2`] = ` 471 | "var _tap2 = _taps[2]; 472 | _interceptors[0].tap(_tap2); 473 | _interceptors[1].tap(_tap2); 474 | var _fn2 = _x[2]; 475 | var _hasResult2 = false; 476 | var _promise2 = _fn2(a, b, c); 477 | if (!_promise2 || !_promise2.then) 478 | throw new Error( 479 | \\"Tap function (tapPromise) did not return promise (returned \\" + 480 | _promise2 + 481 | \\")\\" 482 | ); 483 | _promise2.then( 484 | function(_result2) { 485 | _hasResult2 = true; 486 | onResult(_result2); 487 | }, 488 | function(_err2) { 489 | if (_hasResult2) throw _err2; 490 | onError( 491 | !_err2 492 | ? new Error('Tap function (tapPromise) rejects \\"' + _err2 + '\\" value') 493 | : _err2 494 | ); 495 | } 496 | ); 497 | " 498 | `; 499 | 500 | exports[`HookCodeFactory callTap (with args, with intercept) promise without onResult 1`] = ` 501 | "var _tap2 = _taps[2]; 502 | _interceptors[0].tap(_tap2); 503 | _interceptors[1].tap(_tap2); 504 | var _fn2 = _x[2]; 505 | var _hasResult2 = false; 506 | var _promise2 = _fn2(a, b, c); 507 | if (!_promise2 || !_promise2.then) 508 | throw new Error('Tap function (tapPromise) did not return promise (returned ' + _promise2 + ')'); 509 | _promise2.then((function(_result2) { 510 | _hasResult2 = true; 511 | onDone(); 512 | }), function(_err2) { 513 | if(_hasResult2) throw _err2; 514 | onError(!_err2 ? new Error('Tap function (tapPromise) rejects \\"' + _err2 + '\\" value') : _err2); 515 | }); 516 | " 517 | `; 518 | 519 | exports[`HookCodeFactory callTap (with args, with intercept) promise without onResult 2`] = ` 520 | "var _tap2 = _taps[2]; 521 | _interceptors[0].tap(_tap2); 522 | _interceptors[1].tap(_tap2); 523 | var _fn2 = _x[2]; 524 | var _hasResult2 = false; 525 | var _promise2 = _fn2(a, b, c); 526 | if (!_promise2 || !_promise2.then) 527 | throw new Error( 528 | \\"Tap function (tapPromise) did not return promise (returned \\" + 529 | _promise2 + 530 | \\")\\" 531 | ); 532 | _promise2.then( 533 | function(_result2) { 534 | _hasResult2 = true; 535 | onDone(); 536 | }, 537 | function(_err2) { 538 | if (_hasResult2) throw _err2; 539 | onError( 540 | !_err2 541 | ? new Error('Tap function (tapPromise) rejects \\"' + _err2 + '\\" value') 542 | : _err2 543 | ); 544 | } 545 | ); 546 | " 547 | `; 548 | 549 | exports[`HookCodeFactory callTap (with args, with intercept) sync with onResult 1`] = ` 550 | "var _tap0 = _taps[0]; 551 | _interceptors[0].tap(_tap0); 552 | _interceptors[1].tap(_tap0); 553 | var _fn0 = _x[0]; 554 | var _hasError0 = false; 555 | try { 556 | var _result0 = _fn0(a, b, c); 557 | } catch(_err) { 558 | _hasError0 = true; 559 | onError(_err); 560 | } 561 | if(!_hasError0) { 562 | onResult(_result0); 563 | } 564 | " 565 | `; 566 | 567 | exports[`HookCodeFactory callTap (with args, with intercept) sync with onResult 2`] = ` 568 | "var _tap0 = _taps[0]; 569 | _interceptors[0].tap(_tap0); 570 | _interceptors[1].tap(_tap0); 571 | var _fn0 = _x[0]; 572 | var _hasError0 = false; 573 | try { 574 | var _result0 = _fn0(a, b, c); 575 | } catch (_err) { 576 | _hasError0 = true; 577 | onError(_err); 578 | } 579 | if (!_hasError0) { 580 | onResult(_result0); 581 | } 582 | " 583 | `; 584 | 585 | exports[`HookCodeFactory callTap (with args, with intercept) sync without onResult 1`] = ` 586 | "var _tap0 = _taps[0]; 587 | _interceptors[0].tap(_tap0); 588 | _interceptors[1].tap(_tap0); 589 | var _fn0 = _x[0]; 590 | var _hasError0 = false; 591 | try { 592 | _fn0(a, b, c); 593 | } catch(_err) { 594 | _hasError0 = true; 595 | onError(_err); 596 | } 597 | if(!_hasError0) { 598 | onDone(); 599 | } 600 | " 601 | `; 602 | 603 | exports[`HookCodeFactory callTap (with args, with intercept) sync without onResult 2`] = ` 604 | "var _tap0 = _taps[0]; 605 | _interceptors[0].tap(_tap0); 606 | _interceptors[1].tap(_tap0); 607 | var _fn0 = _x[0]; 608 | var _hasError0 = false; 609 | try { 610 | _fn0(a, b, c); 611 | } catch (_err) { 612 | _hasError0 = true; 613 | onError(_err); 614 | } 615 | if (!_hasError0) { 616 | onDone(); 617 | } 618 | " 619 | `; 620 | 621 | exports[`HookCodeFactory taps (mixed) callTapsLooping 1`] = ` 622 | "var _looper = (function() { 623 | var _loopAsync = false; 624 | var _loop; 625 | do { 626 | _loop = false; 627 | function _next1() { 628 | var _fn2 = _x[2]; 629 | var _hasResult2 = false; 630 | var _promise2 = _fn2(a, b, c); 631 | if (!_promise2 || !_promise2.then) 632 | throw new Error('Tap function (tapPromise) did not return promise (returned ' + _promise2 + ')'); 633 | _promise2.then((function(_result2) { 634 | _hasResult2 = true; 635 | if(_result2 !== undefined) { 636 | _loop = true; 637 | if(_loopAsync) _looper(); 638 | } else { 639 | if(!_loop) { 640 | onDone(); 641 | } 642 | } 643 | }), function(_err2) { 644 | if(_hasResult2) throw _err2; 645 | onError(2, !_err2 ? new Error('Tap function (tapPromise) rejects \\"' + _err2 + '\\" value') : _err2); 646 | }); 647 | } 648 | var _fn0 = _x[0]; 649 | var _hasError0 = false; 650 | try { 651 | var _result0 = _fn0(a, b, c); 652 | } catch(_err) { 653 | _hasError0 = true; 654 | onError(0, _err); 655 | } 656 | if(!_hasError0) { 657 | if(_result0 !== undefined) { 658 | _loop = true; 659 | if(_loopAsync) _looper(); 660 | } else { 661 | var _fn1 = _x[1]; 662 | _fn1(a, b, c, (function(_err1, _result1) { 663 | if(_err1) { 664 | onError(1, _err1); 665 | } else { 666 | if(_result1 !== undefined) { 667 | _loop = true; 668 | if(_loopAsync) _looper(); 669 | } else { 670 | _next1(); 671 | } 672 | } 673 | })); 674 | } 675 | } 676 | } while(_loop); 677 | _loopAsync = true; 678 | }); 679 | _looper(); 680 | " 681 | `; 682 | 683 | exports[`HookCodeFactory taps (mixed) callTapsLooping 2`] = ` 684 | "var _looper = function() { 685 | var _loopAsync = false; 686 | var _loop; 687 | do { 688 | _loop = false; 689 | function _next1() { 690 | var _fn2 = _x[2]; 691 | var _hasResult2 = false; 692 | var _promise2 = _fn2(a, b, c); 693 | if (!_promise2 || !_promise2.then) 694 | throw new Error( 695 | \\"Tap function (tapPromise) did not return promise (returned \\" + 696 | _promise2 + 697 | \\")\\" 698 | ); 699 | _promise2.then( 700 | function(_result2) { 701 | _hasResult2 = true; 702 | if (_result2 !== undefined) { 703 | _loop = true; 704 | if (_loopAsync) _looper(); 705 | } else { 706 | if (!_loop) { 707 | onDone(); 708 | } 709 | } 710 | }, 711 | function(_err2) { 712 | if (_hasResult2) throw _err2; 713 | onError( 714 | 2, 715 | !_err2 716 | ? new Error( 717 | 'Tap function (tapPromise) rejects \\"' + _err2 + '\\" value' 718 | ) 719 | : _err2 720 | ); 721 | } 722 | ); 723 | } 724 | var _fn0 = _x[0]; 725 | var _hasError0 = false; 726 | try { 727 | var _result0 = _fn0(a, b, c); 728 | } catch (_err) { 729 | _hasError0 = true; 730 | onError(0, _err); 731 | } 732 | if (!_hasError0) { 733 | if (_result0 !== undefined) { 734 | _loop = true; 735 | if (_loopAsync) _looper(); 736 | } else { 737 | var _fn1 = _x[1]; 738 | _fn1(a, b, c, function(_err1, _result1) { 739 | if (_err1) { 740 | onError(1, _err1); 741 | } else { 742 | if (_result1 !== undefined) { 743 | _loop = true; 744 | if (_loopAsync) _looper(); 745 | } else { 746 | _next1(); 747 | } 748 | } 749 | }); 750 | } 751 | } 752 | } while (_loop); 753 | _loopAsync = true; 754 | }; 755 | _looper(); 756 | " 757 | `; 758 | 759 | exports[`HookCodeFactory taps (mixed) callTapsParallel 1`] = ` 760 | "do { 761 | var _counter = 3; 762 | var _done = (function() { 763 | onDone(); 764 | }); 765 | if(_counter <= 0) break; 766 | var _fn0 = _x[0]; 767 | var _result0 = _fn0(a, b, c); 768 | if(_counter > 0) { 769 | onResult(0, _result0, () => { 770 | if(--_counter === 0) _done(); 771 | }, () => { 772 | _counter = 0; 773 | _done(); 774 | }); 775 | } 776 | if(_counter <= 0) break; 777 | var _fn1 = _x[1]; 778 | _fn1(a, b, c, (function(_err1, _result1) { 779 | if(_err1) { 780 | if(_counter > 0) { 781 | onError(1, _err1); 782 | } 783 | } else { 784 | if(_counter > 0) { 785 | onResult(1, _result1, () => { 786 | if(--_counter === 0) _done(); 787 | }, () => { 788 | _counter = 0; 789 | _done(); 790 | }); 791 | } 792 | } 793 | })); 794 | if(_counter <= 0) break; 795 | var _fn2 = _x[2]; 796 | var _hasResult2 = false; 797 | var _promise2 = _fn2(a, b, c); 798 | if (!_promise2 || !_promise2.then) 799 | throw new Error('Tap function (tapPromise) did not return promise (returned ' + _promise2 + ')'); 800 | _promise2.then((function(_result2) { 801 | _hasResult2 = true; 802 | if(_counter > 0) { 803 | onResult(2, _result2, () => { 804 | if(--_counter === 0) _done(); 805 | }, () => { 806 | _counter = 0; 807 | _done(); 808 | }); 809 | } 810 | }), function(_err2) { 811 | if(_hasResult2) throw _err2; 812 | if(_counter > 0) { 813 | onError(2, !_err2 ? new Error('Tap function (tapPromise) rejects \\"' + _err2 + '\\" value') : _err2); 814 | } 815 | }); 816 | } while(false); 817 | " 818 | `; 819 | 820 | exports[`HookCodeFactory taps (mixed) callTapsParallel 2`] = ` 821 | "do { 822 | var _counter = 3; 823 | var _done = function() { 824 | onDone(); 825 | }; 826 | if (_counter <= 0) break; 827 | var _fn0 = _x[0]; 828 | var _result0 = _fn0(a, b, c); 829 | if (_counter > 0) { 830 | onResult( 831 | 0, 832 | _result0, 833 | () => { 834 | if (--_counter === 0) _done(); 835 | }, 836 | () => { 837 | _counter = 0; 838 | _done(); 839 | } 840 | ); 841 | } 842 | if (_counter <= 0) break; 843 | var _fn1 = _x[1]; 844 | _fn1(a, b, c, function(_err1, _result1) { 845 | if (_err1) { 846 | if (_counter > 0) { 847 | onError(1, _err1); 848 | } 849 | } else { 850 | if (_counter > 0) { 851 | onResult( 852 | 1, 853 | _result1, 854 | () => { 855 | if (--_counter === 0) _done(); 856 | }, 857 | () => { 858 | _counter = 0; 859 | _done(); 860 | } 861 | ); 862 | } 863 | } 864 | }); 865 | if (_counter <= 0) break; 866 | var _fn2 = _x[2]; 867 | var _hasResult2 = false; 868 | var _promise2 = _fn2(a, b, c); 869 | if (!_promise2 || !_promise2.then) 870 | throw new Error( 871 | \\"Tap function (tapPromise) did not return promise (returned \\" + 872 | _promise2 + 873 | \\")\\" 874 | ); 875 | _promise2.then( 876 | function(_result2) { 877 | _hasResult2 = true; 878 | if (_counter > 0) { 879 | onResult( 880 | 2, 881 | _result2, 882 | () => { 883 | if (--_counter === 0) _done(); 884 | }, 885 | () => { 886 | _counter = 0; 887 | _done(); 888 | } 889 | ); 890 | } 891 | }, 892 | function(_err2) { 893 | if (_hasResult2) throw _err2; 894 | if (_counter > 0) { 895 | onError( 896 | 2, 897 | !_err2 898 | ? new Error( 899 | 'Tap function (tapPromise) rejects \\"' + _err2 + '\\" value' 900 | ) 901 | : _err2 902 | ); 903 | } 904 | } 905 | ); 906 | } while (false); 907 | " 908 | `; 909 | 910 | exports[`HookCodeFactory taps (mixed) callTapsSeries 1`] = ` 911 | "function _next1() { 912 | var _fn2 = _x[2]; 913 | var _hasResult2 = false; 914 | var _promise2 = _fn2(a, b, c); 915 | if (!_promise2 || !_promise2.then) 916 | throw new Error('Tap function (tapPromise) did not return promise (returned ' + _promise2 + ')'); 917 | _promise2.then((function(_result2) { 918 | _hasResult2 = true; 919 | onResult(2, _result2, () => { 920 | onDone(); 921 | }, () => { 922 | onDone(); 923 | }); 924 | }), function(_err2) { 925 | if(_hasResult2) throw _err2; 926 | onError(2, !_err2 ? new Error('Tap function (tapPromise) rejects \\"' + _err2 + '\\" value') : _err2); 927 | }); 928 | } 929 | var _fn0 = _x[0]; 930 | var _result0 = _fn0(a, b, c); 931 | onResult(0, _result0, () => { 932 | var _fn1 = _x[1]; 933 | _fn1(a, b, c, (function(_err1, _result1) { 934 | if(_err1) { 935 | onError(1, _err1); 936 | } else { 937 | onResult(1, _result1, () => { 938 | _next1(); 939 | }, () => { 940 | onDone(); 941 | }); 942 | } 943 | })); 944 | }, () => { 945 | onDone(); 946 | }); 947 | " 948 | `; 949 | 950 | exports[`HookCodeFactory taps (mixed) callTapsSeries 2`] = ` 951 | "function _next1() { 952 | var _fn2 = _x[2]; 953 | var _hasResult2 = false; 954 | var _promise2 = _fn2(a, b, c); 955 | if (!_promise2 || !_promise2.then) 956 | throw new Error( 957 | \\"Tap function (tapPromise) did not return promise (returned \\" + 958 | _promise2 + 959 | \\")\\" 960 | ); 961 | _promise2.then( 962 | function(_result2) { 963 | _hasResult2 = true; 964 | onResult( 965 | 2, 966 | _result2, 967 | () => { 968 | onDone(); 969 | }, 970 | () => { 971 | onDone(); 972 | } 973 | ); 974 | }, 975 | function(_err2) { 976 | if (_hasResult2) throw _err2; 977 | onError( 978 | 2, 979 | !_err2 980 | ? new Error('Tap function (tapPromise) rejects \\"' + _err2 + '\\" value') 981 | : _err2 982 | ); 983 | } 984 | ); 985 | } 986 | var _fn0 = _x[0]; 987 | var _result0 = _fn0(a, b, c); 988 | onResult( 989 | 0, 990 | _result0, 991 | () => { 992 | var _fn1 = _x[1]; 993 | _fn1(a, b, c, function(_err1, _result1) { 994 | if (_err1) { 995 | onError(1, _err1); 996 | } else { 997 | onResult( 998 | 1, 999 | _result1, 1000 | () => { 1001 | _next1(); 1002 | }, 1003 | () => { 1004 | onDone(); 1005 | } 1006 | ); 1007 | } 1008 | }); 1009 | }, 1010 | () => { 1011 | onDone(); 1012 | } 1013 | ); 1014 | " 1015 | `; 1016 | 1017 | exports[`HookCodeFactory taps (mixed2) callTapsLooping 1`] = ` 1018 | "var _looper = (function() { 1019 | var _loopAsync = false; 1020 | var _loop; 1021 | do { 1022 | _loop = false; 1023 | function _next1() { 1024 | var _fn2 = _x[2]; 1025 | var _hasError2 = false; 1026 | try { 1027 | var _result2 = _fn2(a, b, c); 1028 | } catch(_err) { 1029 | _hasError2 = true; 1030 | onError(2, _err); 1031 | } 1032 | if(!_hasError2) { 1033 | if(_result2 !== undefined) { 1034 | _loop = true; 1035 | if(_loopAsync) _looper(); 1036 | } else { 1037 | if(!_loop) { 1038 | onDone(); 1039 | } 1040 | } 1041 | } 1042 | } 1043 | function _next0() { 1044 | var _fn1 = _x[1]; 1045 | var _hasResult1 = false; 1046 | var _promise1 = _fn1(a, b, c); 1047 | if (!_promise1 || !_promise1.then) 1048 | throw new Error('Tap function (tapPromise) did not return promise (returned ' + _promise1 + ')'); 1049 | _promise1.then((function(_result1) { 1050 | _hasResult1 = true; 1051 | if(_result1 !== undefined) { 1052 | _loop = true; 1053 | if(_loopAsync) _looper(); 1054 | } else { 1055 | _next1(); 1056 | } 1057 | }), function(_err1) { 1058 | if(_hasResult1) throw _err1; 1059 | onError(1, !_err1 ? new Error('Tap function (tapPromise) rejects \\"' + _err1 + '\\" value') : _err1); 1060 | }); 1061 | } 1062 | var _fn0 = _x[0]; 1063 | _fn0(a, b, c, (function(_err0, _result0) { 1064 | if(_err0) { 1065 | onError(0, _err0); 1066 | } else { 1067 | if(_result0 !== undefined) { 1068 | _loop = true; 1069 | if(_loopAsync) _looper(); 1070 | } else { 1071 | _next0(); 1072 | } 1073 | } 1074 | })); 1075 | } while(_loop); 1076 | _loopAsync = true; 1077 | }); 1078 | _looper(); 1079 | " 1080 | `; 1081 | 1082 | exports[`HookCodeFactory taps (mixed2) callTapsLooping 2`] = ` 1083 | "var _looper = function() { 1084 | var _loopAsync = false; 1085 | var _loop; 1086 | do { 1087 | _loop = false; 1088 | function _next1() { 1089 | var _fn2 = _x[2]; 1090 | var _hasError2 = false; 1091 | try { 1092 | var _result2 = _fn2(a, b, c); 1093 | } catch (_err) { 1094 | _hasError2 = true; 1095 | onError(2, _err); 1096 | } 1097 | if (!_hasError2) { 1098 | if (_result2 !== undefined) { 1099 | _loop = true; 1100 | if (_loopAsync) _looper(); 1101 | } else { 1102 | if (!_loop) { 1103 | onDone(); 1104 | } 1105 | } 1106 | } 1107 | } 1108 | function _next0() { 1109 | var _fn1 = _x[1]; 1110 | var _hasResult1 = false; 1111 | var _promise1 = _fn1(a, b, c); 1112 | if (!_promise1 || !_promise1.then) 1113 | throw new Error( 1114 | \\"Tap function (tapPromise) did not return promise (returned \\" + 1115 | _promise1 + 1116 | \\")\\" 1117 | ); 1118 | _promise1.then( 1119 | function(_result1) { 1120 | _hasResult1 = true; 1121 | if (_result1 !== undefined) { 1122 | _loop = true; 1123 | if (_loopAsync) _looper(); 1124 | } else { 1125 | _next1(); 1126 | } 1127 | }, 1128 | function(_err1) { 1129 | if (_hasResult1) throw _err1; 1130 | onError( 1131 | 1, 1132 | !_err1 1133 | ? new Error( 1134 | 'Tap function (tapPromise) rejects \\"' + _err1 + '\\" value' 1135 | ) 1136 | : _err1 1137 | ); 1138 | } 1139 | ); 1140 | } 1141 | var _fn0 = _x[0]; 1142 | _fn0(a, b, c, function(_err0, _result0) { 1143 | if (_err0) { 1144 | onError(0, _err0); 1145 | } else { 1146 | if (_result0 !== undefined) { 1147 | _loop = true; 1148 | if (_loopAsync) _looper(); 1149 | } else { 1150 | _next0(); 1151 | } 1152 | } 1153 | }); 1154 | } while (_loop); 1155 | _loopAsync = true; 1156 | }; 1157 | _looper(); 1158 | " 1159 | `; 1160 | 1161 | exports[`HookCodeFactory taps (mixed2) callTapsParallel 1`] = ` 1162 | "do { 1163 | var _counter = 3; 1164 | var _done = (function() { 1165 | onDone(); 1166 | }); 1167 | if(_counter <= 0) break; 1168 | var _fn0 = _x[0]; 1169 | _fn0(a, b, c, (function(_err0, _result0) { 1170 | if(_err0) { 1171 | if(_counter > 0) { 1172 | onError(0, _err0); 1173 | } 1174 | } else { 1175 | if(_counter > 0) { 1176 | onResult(0, _result0, () => { 1177 | if(--_counter === 0) _done(); 1178 | }, () => { 1179 | _counter = 0; 1180 | _done(); 1181 | }); 1182 | } 1183 | } 1184 | })); 1185 | if(_counter <= 0) break; 1186 | var _fn1 = _x[1]; 1187 | var _hasResult1 = false; 1188 | var _promise1 = _fn1(a, b, c); 1189 | if (!_promise1 || !_promise1.then) 1190 | throw new Error('Tap function (tapPromise) did not return promise (returned ' + _promise1 + ')'); 1191 | _promise1.then((function(_result1) { 1192 | _hasResult1 = true; 1193 | if(_counter > 0) { 1194 | onResult(1, _result1, () => { 1195 | if(--_counter === 0) _done(); 1196 | }, () => { 1197 | _counter = 0; 1198 | _done(); 1199 | }); 1200 | } 1201 | }), function(_err1) { 1202 | if(_hasResult1) throw _err1; 1203 | if(_counter > 0) { 1204 | onError(1, !_err1 ? new Error('Tap function (tapPromise) rejects \\"' + _err1 + '\\" value') : _err1); 1205 | } 1206 | }); 1207 | if(_counter <= 0) break; 1208 | var _fn2 = _x[2]; 1209 | var _result2 = _fn2(a, b, c); 1210 | if(_counter > 0) { 1211 | onResult(2, _result2, () => { 1212 | if(--_counter === 0) _done(); 1213 | }, () => { 1214 | _counter = 0; 1215 | _done(); 1216 | }); 1217 | } 1218 | } while(false); 1219 | " 1220 | `; 1221 | 1222 | exports[`HookCodeFactory taps (mixed2) callTapsParallel 2`] = ` 1223 | "do { 1224 | var _counter = 3; 1225 | var _done = function() { 1226 | onDone(); 1227 | }; 1228 | if (_counter <= 0) break; 1229 | var _fn0 = _x[0]; 1230 | _fn0(a, b, c, function(_err0, _result0) { 1231 | if (_err0) { 1232 | if (_counter > 0) { 1233 | onError(0, _err0); 1234 | } 1235 | } else { 1236 | if (_counter > 0) { 1237 | onResult( 1238 | 0, 1239 | _result0, 1240 | () => { 1241 | if (--_counter === 0) _done(); 1242 | }, 1243 | () => { 1244 | _counter = 0; 1245 | _done(); 1246 | } 1247 | ); 1248 | } 1249 | } 1250 | }); 1251 | if (_counter <= 0) break; 1252 | var _fn1 = _x[1]; 1253 | var _hasResult1 = false; 1254 | var _promise1 = _fn1(a, b, c); 1255 | if (!_promise1 || !_promise1.then) 1256 | throw new Error( 1257 | \\"Tap function (tapPromise) did not return promise (returned \\" + 1258 | _promise1 + 1259 | \\")\\" 1260 | ); 1261 | _promise1.then( 1262 | function(_result1) { 1263 | _hasResult1 = true; 1264 | if (_counter > 0) { 1265 | onResult( 1266 | 1, 1267 | _result1, 1268 | () => { 1269 | if (--_counter === 0) _done(); 1270 | }, 1271 | () => { 1272 | _counter = 0; 1273 | _done(); 1274 | } 1275 | ); 1276 | } 1277 | }, 1278 | function(_err1) { 1279 | if (_hasResult1) throw _err1; 1280 | if (_counter > 0) { 1281 | onError( 1282 | 1, 1283 | !_err1 1284 | ? new Error( 1285 | 'Tap function (tapPromise) rejects \\"' + _err1 + '\\" value' 1286 | ) 1287 | : _err1 1288 | ); 1289 | } 1290 | } 1291 | ); 1292 | if (_counter <= 0) break; 1293 | var _fn2 = _x[2]; 1294 | var _result2 = _fn2(a, b, c); 1295 | if (_counter > 0) { 1296 | onResult( 1297 | 2, 1298 | _result2, 1299 | () => { 1300 | if (--_counter === 0) _done(); 1301 | }, 1302 | () => { 1303 | _counter = 0; 1304 | _done(); 1305 | } 1306 | ); 1307 | } 1308 | } while (false); 1309 | " 1310 | `; 1311 | 1312 | exports[`HookCodeFactory taps (mixed2) callTapsSeries 1`] = ` 1313 | "function _next1() { 1314 | var _fn2 = _x[2]; 1315 | var _hasError2 = false; 1316 | try { 1317 | var _result2 = _fn2(a, b, c); 1318 | } catch(_err) { 1319 | _hasError2 = true; 1320 | onError(2, _err); 1321 | } 1322 | if(!_hasError2) { 1323 | onResult(2, _result2, () => { 1324 | onDone(); 1325 | }, () => { 1326 | onDone(); 1327 | }); 1328 | } 1329 | } 1330 | function _next0() { 1331 | var _fn1 = _x[1]; 1332 | var _hasResult1 = false; 1333 | var _promise1 = _fn1(a, b, c); 1334 | if (!_promise1 || !_promise1.then) 1335 | throw new Error('Tap function (tapPromise) did not return promise (returned ' + _promise1 + ')'); 1336 | _promise1.then((function(_result1) { 1337 | _hasResult1 = true; 1338 | onResult(1, _result1, () => { 1339 | _next1(); 1340 | }, () => { 1341 | onDone(); 1342 | }); 1343 | }), function(_err1) { 1344 | if(_hasResult1) throw _err1; 1345 | onError(1, !_err1 ? new Error('Tap function (tapPromise) rejects \\"' + _err1 + '\\" value') : _err1); 1346 | }); 1347 | } 1348 | var _fn0 = _x[0]; 1349 | _fn0(a, b, c, (function(_err0, _result0) { 1350 | if(_err0) { 1351 | onError(0, _err0); 1352 | } else { 1353 | onResult(0, _result0, () => { 1354 | _next0(); 1355 | }, () => { 1356 | onDone(); 1357 | }); 1358 | } 1359 | })); 1360 | " 1361 | `; 1362 | 1363 | exports[`HookCodeFactory taps (mixed2) callTapsSeries 2`] = ` 1364 | "function _next1() { 1365 | var _fn2 = _x[2]; 1366 | var _hasError2 = false; 1367 | try { 1368 | var _result2 = _fn2(a, b, c); 1369 | } catch (_err) { 1370 | _hasError2 = true; 1371 | onError(2, _err); 1372 | } 1373 | if (!_hasError2) { 1374 | onResult( 1375 | 2, 1376 | _result2, 1377 | () => { 1378 | onDone(); 1379 | }, 1380 | () => { 1381 | onDone(); 1382 | } 1383 | ); 1384 | } 1385 | } 1386 | function _next0() { 1387 | var _fn1 = _x[1]; 1388 | var _hasResult1 = false; 1389 | var _promise1 = _fn1(a, b, c); 1390 | if (!_promise1 || !_promise1.then) 1391 | throw new Error( 1392 | \\"Tap function (tapPromise) did not return promise (returned \\" + 1393 | _promise1 + 1394 | \\")\\" 1395 | ); 1396 | _promise1.then( 1397 | function(_result1) { 1398 | _hasResult1 = true; 1399 | onResult( 1400 | 1, 1401 | _result1, 1402 | () => { 1403 | _next1(); 1404 | }, 1405 | () => { 1406 | onDone(); 1407 | } 1408 | ); 1409 | }, 1410 | function(_err1) { 1411 | if (_hasResult1) throw _err1; 1412 | onError( 1413 | 1, 1414 | !_err1 1415 | ? new Error('Tap function (tapPromise) rejects \\"' + _err1 + '\\" value') 1416 | : _err1 1417 | ); 1418 | } 1419 | ); 1420 | } 1421 | var _fn0 = _x[0]; 1422 | _fn0(a, b, c, function(_err0, _result0) { 1423 | if (_err0) { 1424 | onError(0, _err0); 1425 | } else { 1426 | onResult( 1427 | 0, 1428 | _result0, 1429 | () => { 1430 | _next0(); 1431 | }, 1432 | () => { 1433 | onDone(); 1434 | } 1435 | ); 1436 | } 1437 | }); 1438 | " 1439 | `; 1440 | 1441 | exports[`HookCodeFactory taps (multiple sync) callTapsLooping 1`] = ` 1442 | "var _loop; 1443 | do { 1444 | _loop = false; 1445 | var _fn0 = _x[0]; 1446 | var _result0 = _fn0(a, b, c); 1447 | if(_result0 !== undefined) { 1448 | _loop = true; 1449 | } else { 1450 | var _fn1 = _x[1]; 1451 | var _result1 = _fn1(a, b, c); 1452 | if(_result1 !== undefined) { 1453 | _loop = true; 1454 | } else { 1455 | var _fn2 = _x[2]; 1456 | var _result2 = _fn2(a, b, c); 1457 | if(_result2 !== undefined) { 1458 | _loop = true; 1459 | } else { 1460 | if(!_loop) { 1461 | onDone(); 1462 | } 1463 | } 1464 | } 1465 | } 1466 | } while(_loop); 1467 | " 1468 | `; 1469 | 1470 | exports[`HookCodeFactory taps (multiple sync) callTapsLooping 2`] = ` 1471 | "var _loop; 1472 | do { 1473 | _loop = false; 1474 | var _fn0 = _x[0]; 1475 | var _result0 = _fn0(a, b, c); 1476 | if (_result0 !== undefined) { 1477 | _loop = true; 1478 | } else { 1479 | var _fn1 = _x[1]; 1480 | var _result1 = _fn1(a, b, c); 1481 | if (_result1 !== undefined) { 1482 | _loop = true; 1483 | } else { 1484 | var _fn2 = _x[2]; 1485 | var _result2 = _fn2(a, b, c); 1486 | if (_result2 !== undefined) { 1487 | _loop = true; 1488 | } else { 1489 | if (!_loop) { 1490 | onDone(); 1491 | } 1492 | } 1493 | } 1494 | } 1495 | } while (_loop); 1496 | " 1497 | `; 1498 | 1499 | exports[`HookCodeFactory taps (multiple sync) callTapsParallel 1`] = ` 1500 | "do { 1501 | var _counter = 3; 1502 | var _done = (function() { 1503 | onDone(); 1504 | }); 1505 | if(_counter <= 0) break; 1506 | var _fn0 = _x[0]; 1507 | var _result0 = _fn0(a, b, c); 1508 | if(_counter > 0) { 1509 | onResult(0, _result0, () => { 1510 | if(--_counter === 0) _done(); 1511 | }, () => { 1512 | _counter = 0; 1513 | _done(); 1514 | }); 1515 | } 1516 | if(_counter <= 0) break; 1517 | var _fn1 = _x[1]; 1518 | var _result1 = _fn1(a, b, c); 1519 | if(_counter > 0) { 1520 | onResult(1, _result1, () => { 1521 | if(--_counter === 0) _done(); 1522 | }, () => { 1523 | _counter = 0; 1524 | _done(); 1525 | }); 1526 | } 1527 | if(_counter <= 0) break; 1528 | var _fn2 = _x[2]; 1529 | var _result2 = _fn2(a, b, c); 1530 | if(_counter > 0) { 1531 | onResult(2, _result2, () => { 1532 | if(--_counter === 0) _done(); 1533 | }, () => { 1534 | _counter = 0; 1535 | _done(); 1536 | }); 1537 | } 1538 | } while(false); 1539 | " 1540 | `; 1541 | 1542 | exports[`HookCodeFactory taps (multiple sync) callTapsParallel 2`] = ` 1543 | "do { 1544 | var _counter = 3; 1545 | var _done = function() { 1546 | onDone(); 1547 | }; 1548 | if (_counter <= 0) break; 1549 | var _fn0 = _x[0]; 1550 | var _result0 = _fn0(a, b, c); 1551 | if (_counter > 0) { 1552 | onResult( 1553 | 0, 1554 | _result0, 1555 | () => { 1556 | if (--_counter === 0) _done(); 1557 | }, 1558 | () => { 1559 | _counter = 0; 1560 | _done(); 1561 | } 1562 | ); 1563 | } 1564 | if (_counter <= 0) break; 1565 | var _fn1 = _x[1]; 1566 | var _result1 = _fn1(a, b, c); 1567 | if (_counter > 0) { 1568 | onResult( 1569 | 1, 1570 | _result1, 1571 | () => { 1572 | if (--_counter === 0) _done(); 1573 | }, 1574 | () => { 1575 | _counter = 0; 1576 | _done(); 1577 | } 1578 | ); 1579 | } 1580 | if (_counter <= 0) break; 1581 | var _fn2 = _x[2]; 1582 | var _result2 = _fn2(a, b, c); 1583 | if (_counter > 0) { 1584 | onResult( 1585 | 2, 1586 | _result2, 1587 | () => { 1588 | if (--_counter === 0) _done(); 1589 | }, 1590 | () => { 1591 | _counter = 0; 1592 | _done(); 1593 | } 1594 | ); 1595 | } 1596 | } while (false); 1597 | " 1598 | `; 1599 | 1600 | exports[`HookCodeFactory taps (multiple sync) callTapsSeries 1`] = ` 1601 | "var _fn0 = _x[0]; 1602 | var _result0 = _fn0(a, b, c); 1603 | onResult(0, _result0, () => { 1604 | var _fn1 = _x[1]; 1605 | var _result1 = _fn1(a, b, c); 1606 | onResult(1, _result1, () => { 1607 | var _fn2 = _x[2]; 1608 | var _result2 = _fn2(a, b, c); 1609 | onResult(2, _result2, () => { 1610 | onDone(); 1611 | }, () => { 1612 | onDone(); 1613 | }); 1614 | }, () => { 1615 | onDone(); 1616 | }); 1617 | }, () => { 1618 | onDone(); 1619 | }); 1620 | " 1621 | `; 1622 | 1623 | exports[`HookCodeFactory taps (multiple sync) callTapsSeries 2`] = ` 1624 | "var _fn0 = _x[0]; 1625 | var _result0 = _fn0(a, b, c); 1626 | onResult( 1627 | 0, 1628 | _result0, 1629 | () => { 1630 | var _fn1 = _x[1]; 1631 | var _result1 = _fn1(a, b, c); 1632 | onResult( 1633 | 1, 1634 | _result1, 1635 | () => { 1636 | var _fn2 = _x[2]; 1637 | var _result2 = _fn2(a, b, c); 1638 | onResult( 1639 | 2, 1640 | _result2, 1641 | () => { 1642 | onDone(); 1643 | }, 1644 | () => { 1645 | onDone(); 1646 | } 1647 | ); 1648 | }, 1649 | () => { 1650 | onDone(); 1651 | } 1652 | ); 1653 | }, 1654 | () => { 1655 | onDone(); 1656 | } 1657 | ); 1658 | " 1659 | `; 1660 | 1661 | exports[`HookCodeFactory taps (none) callTapsLooping 1`] = ` 1662 | "onDone(); 1663 | " 1664 | `; 1665 | 1666 | exports[`HookCodeFactory taps (none) callTapsLooping 2`] = ` 1667 | "onDone(); 1668 | " 1669 | `; 1670 | 1671 | exports[`HookCodeFactory taps (none) callTapsParallel 1`] = ` 1672 | "onDone(); 1673 | " 1674 | `; 1675 | 1676 | exports[`HookCodeFactory taps (none) callTapsParallel 2`] = ` 1677 | "onDone(); 1678 | " 1679 | `; 1680 | 1681 | exports[`HookCodeFactory taps (none) callTapsSeries 1`] = ` 1682 | "onDone(); 1683 | " 1684 | `; 1685 | 1686 | exports[`HookCodeFactory taps (none) callTapsSeries 2`] = ` 1687 | "onDone(); 1688 | " 1689 | `; 1690 | 1691 | exports[`HookCodeFactory taps (single async) callTapsLooping 1`] = ` 1692 | "var _looper = (function() { 1693 | var _loopAsync = false; 1694 | var _loop; 1695 | do { 1696 | _loop = false; 1697 | var _fn0 = _x[0]; 1698 | _fn0(a, b, c, (function(_err0, _result0) { 1699 | if(_err0) { 1700 | onError(0, _err0); 1701 | } else { 1702 | if(_result0 !== undefined) { 1703 | _loop = true; 1704 | if(_loopAsync) _looper(); 1705 | } else { 1706 | if(!_loop) { 1707 | onDone(); 1708 | } 1709 | } 1710 | } 1711 | })); 1712 | } while(_loop); 1713 | _loopAsync = true; 1714 | }); 1715 | _looper(); 1716 | " 1717 | `; 1718 | 1719 | exports[`HookCodeFactory taps (single async) callTapsLooping 2`] = ` 1720 | "var _looper = function() { 1721 | var _loopAsync = false; 1722 | var _loop; 1723 | do { 1724 | _loop = false; 1725 | var _fn0 = _x[0]; 1726 | _fn0(a, b, c, function(_err0, _result0) { 1727 | if (_err0) { 1728 | onError(0, _err0); 1729 | } else { 1730 | if (_result0 !== undefined) { 1731 | _loop = true; 1732 | if (_loopAsync) _looper(); 1733 | } else { 1734 | if (!_loop) { 1735 | onDone(); 1736 | } 1737 | } 1738 | } 1739 | }); 1740 | } while (_loop); 1741 | _loopAsync = true; 1742 | }; 1743 | _looper(); 1744 | " 1745 | `; 1746 | 1747 | exports[`HookCodeFactory taps (single async) callTapsParallel 1`] = ` 1748 | "var _fn0 = _x[0]; 1749 | _fn0(a, b, c, (function(_err0, _result0) { 1750 | if(_err0) { 1751 | onError(0, _err0); 1752 | } else { 1753 | onResult(0, _result0, () => { 1754 | onDone(); 1755 | }, () => { 1756 | onDone(); 1757 | }); 1758 | } 1759 | })); 1760 | " 1761 | `; 1762 | 1763 | exports[`HookCodeFactory taps (single async) callTapsParallel 2`] = ` 1764 | "var _fn0 = _x[0]; 1765 | _fn0(a, b, c, function(_err0, _result0) { 1766 | if (_err0) { 1767 | onError(0, _err0); 1768 | } else { 1769 | onResult( 1770 | 0, 1771 | _result0, 1772 | () => { 1773 | onDone(); 1774 | }, 1775 | () => { 1776 | onDone(); 1777 | } 1778 | ); 1779 | } 1780 | }); 1781 | " 1782 | `; 1783 | 1784 | exports[`HookCodeFactory taps (single async) callTapsSeries 1`] = ` 1785 | "var _fn0 = _x[0]; 1786 | _fn0(a, b, c, (function(_err0, _result0) { 1787 | if(_err0) { 1788 | onError(0, _err0); 1789 | } else { 1790 | onResult(0, _result0, () => { 1791 | onDone(); 1792 | }, () => { 1793 | onDone(); 1794 | }); 1795 | } 1796 | })); 1797 | " 1798 | `; 1799 | 1800 | exports[`HookCodeFactory taps (single async) callTapsSeries 2`] = ` 1801 | "var _fn0 = _x[0]; 1802 | _fn0(a, b, c, function(_err0, _result0) { 1803 | if (_err0) { 1804 | onError(0, _err0); 1805 | } else { 1806 | onResult( 1807 | 0, 1808 | _result0, 1809 | () => { 1810 | onDone(); 1811 | }, 1812 | () => { 1813 | onDone(); 1814 | } 1815 | ); 1816 | } 1817 | }); 1818 | " 1819 | `; 1820 | 1821 | exports[`HookCodeFactory taps (single promise) callTapsLooping 1`] = ` 1822 | "var _looper = (function() { 1823 | var _loopAsync = false; 1824 | var _loop; 1825 | do { 1826 | _loop = false; 1827 | var _fn0 = _x[0]; 1828 | var _hasResult0 = false; 1829 | var _promise0 = _fn0(a, b, c); 1830 | if (!_promise0 || !_promise0.then) 1831 | throw new Error('Tap function (tapPromise) did not return promise (returned ' + _promise0 + ')'); 1832 | _promise0.then((function(_result0) { 1833 | _hasResult0 = true; 1834 | if(_result0 !== undefined) { 1835 | _loop = true; 1836 | if(_loopAsync) _looper(); 1837 | } else { 1838 | if(!_loop) { 1839 | onDone(); 1840 | } 1841 | } 1842 | }), function(_err0) { 1843 | if(_hasResult0) throw _err0; 1844 | onError(0, !_err0 ? new Error('Tap function (tapPromise) rejects \\"' + _err0 + '\\" value') : _err0); 1845 | }); 1846 | } while(_loop); 1847 | _loopAsync = true; 1848 | }); 1849 | _looper(); 1850 | " 1851 | `; 1852 | 1853 | exports[`HookCodeFactory taps (single promise) callTapsLooping 2`] = ` 1854 | "var _looper = function() { 1855 | var _loopAsync = false; 1856 | var _loop; 1857 | do { 1858 | _loop = false; 1859 | var _fn0 = _x[0]; 1860 | var _hasResult0 = false; 1861 | var _promise0 = _fn0(a, b, c); 1862 | if (!_promise0 || !_promise0.then) 1863 | throw new Error( 1864 | \\"Tap function (tapPromise) did not return promise (returned \\" + 1865 | _promise0 + 1866 | \\")\\" 1867 | ); 1868 | _promise0.then( 1869 | function(_result0) { 1870 | _hasResult0 = true; 1871 | if (_result0 !== undefined) { 1872 | _loop = true; 1873 | if (_loopAsync) _looper(); 1874 | } else { 1875 | if (!_loop) { 1876 | onDone(); 1877 | } 1878 | } 1879 | }, 1880 | function(_err0) { 1881 | if (_hasResult0) throw _err0; 1882 | onError( 1883 | 0, 1884 | !_err0 1885 | ? new Error( 1886 | 'Tap function (tapPromise) rejects \\"' + _err0 + '\\" value' 1887 | ) 1888 | : _err0 1889 | ); 1890 | } 1891 | ); 1892 | } while (_loop); 1893 | _loopAsync = true; 1894 | }; 1895 | _looper(); 1896 | " 1897 | `; 1898 | 1899 | exports[`HookCodeFactory taps (single promise) callTapsParallel 1`] = ` 1900 | "var _fn0 = _x[0]; 1901 | var _hasResult0 = false; 1902 | var _promise0 = _fn0(a, b, c); 1903 | if (!_promise0 || !_promise0.then) 1904 | throw new Error('Tap function (tapPromise) did not return promise (returned ' + _promise0 + ')'); 1905 | _promise0.then((function(_result0) { 1906 | _hasResult0 = true; 1907 | onResult(0, _result0, () => { 1908 | onDone(); 1909 | }, () => { 1910 | onDone(); 1911 | }); 1912 | }), function(_err0) { 1913 | if(_hasResult0) throw _err0; 1914 | onError(0, !_err0 ? new Error('Tap function (tapPromise) rejects \\"' + _err0 + '\\" value') : _err0); 1915 | }); 1916 | " 1917 | `; 1918 | 1919 | exports[`HookCodeFactory taps (single promise) callTapsParallel 2`] = ` 1920 | "var _fn0 = _x[0]; 1921 | var _hasResult0 = false; 1922 | var _promise0 = _fn0(a, b, c); 1923 | if (!_promise0 || !_promise0.then) 1924 | throw new Error( 1925 | \\"Tap function (tapPromise) did not return promise (returned \\" + 1926 | _promise0 + 1927 | \\")\\" 1928 | ); 1929 | _promise0.then( 1930 | function(_result0) { 1931 | _hasResult0 = true; 1932 | onResult( 1933 | 0, 1934 | _result0, 1935 | () => { 1936 | onDone(); 1937 | }, 1938 | () => { 1939 | onDone(); 1940 | } 1941 | ); 1942 | }, 1943 | function(_err0) { 1944 | if (_hasResult0) throw _err0; 1945 | onError( 1946 | 0, 1947 | !_err0 1948 | ? new Error('Tap function (tapPromise) rejects \\"' + _err0 + '\\" value') 1949 | : _err0 1950 | ); 1951 | } 1952 | ); 1953 | " 1954 | `; 1955 | 1956 | exports[`HookCodeFactory taps (single promise) callTapsSeries 1`] = ` 1957 | "var _fn0 = _x[0]; 1958 | var _hasResult0 = false; 1959 | var _promise0 = _fn0(a, b, c); 1960 | if (!_promise0 || !_promise0.then) 1961 | throw new Error('Tap function (tapPromise) did not return promise (returned ' + _promise0 + ')'); 1962 | _promise0.then((function(_result0) { 1963 | _hasResult0 = true; 1964 | onResult(0, _result0, () => { 1965 | onDone(); 1966 | }, () => { 1967 | onDone(); 1968 | }); 1969 | }), function(_err0) { 1970 | if(_hasResult0) throw _err0; 1971 | onError(0, !_err0 ? new Error('Tap function (tapPromise) rejects \\"' + _err0 + '\\" value') : _err0); 1972 | }); 1973 | " 1974 | `; 1975 | 1976 | exports[`HookCodeFactory taps (single promise) callTapsSeries 2`] = ` 1977 | "var _fn0 = _x[0]; 1978 | var _hasResult0 = false; 1979 | var _promise0 = _fn0(a, b, c); 1980 | if (!_promise0 || !_promise0.then) 1981 | throw new Error( 1982 | \\"Tap function (tapPromise) did not return promise (returned \\" + 1983 | _promise0 + 1984 | \\")\\" 1985 | ); 1986 | _promise0.then( 1987 | function(_result0) { 1988 | _hasResult0 = true; 1989 | onResult( 1990 | 0, 1991 | _result0, 1992 | () => { 1993 | onDone(); 1994 | }, 1995 | () => { 1996 | onDone(); 1997 | } 1998 | ); 1999 | }, 2000 | function(_err0) { 2001 | if (_hasResult0) throw _err0; 2002 | onError( 2003 | 0, 2004 | !_err0 2005 | ? new Error('Tap function (tapPromise) rejects \\"' + _err0 + '\\" value') 2006 | : _err0 2007 | ); 2008 | } 2009 | ); 2010 | " 2011 | `; 2012 | 2013 | exports[`HookCodeFactory taps (single sync) callTapsLooping 1`] = ` 2014 | "var _loop; 2015 | do { 2016 | _loop = false; 2017 | var _fn0 = _x[0]; 2018 | var _result0 = _fn0(a, b, c); 2019 | if(_result0 !== undefined) { 2020 | _loop = true; 2021 | } else { 2022 | if(!_loop) { 2023 | onDone(); 2024 | } 2025 | } 2026 | } while(_loop); 2027 | " 2028 | `; 2029 | 2030 | exports[`HookCodeFactory taps (single sync) callTapsLooping 2`] = ` 2031 | "var _loop; 2032 | do { 2033 | _loop = false; 2034 | var _fn0 = _x[0]; 2035 | var _result0 = _fn0(a, b, c); 2036 | if (_result0 !== undefined) { 2037 | _loop = true; 2038 | } else { 2039 | if (!_loop) { 2040 | onDone(); 2041 | } 2042 | } 2043 | } while (_loop); 2044 | " 2045 | `; 2046 | 2047 | exports[`HookCodeFactory taps (single sync) callTapsParallel 1`] = ` 2048 | "var _fn0 = _x[0]; 2049 | var _result0 = _fn0(a, b, c); 2050 | onResult(0, _result0, () => { 2051 | onDone(); 2052 | }, () => { 2053 | onDone(); 2054 | }); 2055 | " 2056 | `; 2057 | 2058 | exports[`HookCodeFactory taps (single sync) callTapsParallel 2`] = ` 2059 | "var _fn0 = _x[0]; 2060 | var _result0 = _fn0(a, b, c); 2061 | onResult( 2062 | 0, 2063 | _result0, 2064 | () => { 2065 | onDone(); 2066 | }, 2067 | () => { 2068 | onDone(); 2069 | } 2070 | ); 2071 | " 2072 | `; 2073 | 2074 | exports[`HookCodeFactory taps (single sync) callTapsSeries 1`] = ` 2075 | "var _fn0 = _x[0]; 2076 | var _result0 = _fn0(a, b, c); 2077 | onResult(0, _result0, () => { 2078 | onDone(); 2079 | }, () => { 2080 | onDone(); 2081 | }); 2082 | " 2083 | `; 2084 | 2085 | exports[`HookCodeFactory taps (single sync) callTapsSeries 2`] = ` 2086 | "var _fn0 = _x[0]; 2087 | var _result0 = _fn0(a, b, c); 2088 | onResult( 2089 | 0, 2090 | _result0, 2091 | () => { 2092 | onDone(); 2093 | }, 2094 | () => { 2095 | onDone(); 2096 | } 2097 | ); 2098 | " 2099 | `; 2100 | --------------------------------------------------------------------------------