tapable icon indicating copy to clipboard operation
tapable copied to clipboard

problem about tap method of interceptor

Open warjiang opened this issue 5 years ago β€’ 4 comments

recently i try to use interceptor of tapable, in the Interception section of readme:

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.

it means if i use a intecptor of tap method, when a plugin taps into a hook, the corresponding function will be invoked. However, the following code snippet does not work as expected.

code snippet:

const { SyncBailHook } = require('tapable')
const hook = new SyncBailHook(['name'])
hook.intercept({
    tap:(tapInfo) => {
        console.log(tapInfo)
    }
})
hook.tap('1', function (name) {
    console.log(name, '1')
})
hook.tap('2', function (name) {
    console.log(name, '2')
    return 'wrong'
})
hook.tap('1', function (name) {
    console.log(name, '2')
})

tap of interceptor only be called when it execute hook.call('webpack').

version of tapable: 1.1.0

warjiang avatar Oct 20 '18 01:10 warjiang

It seems like hook.register would work in your situation.

hook.intercept({
    register:(tapInfo) => {
        console.log(tapInfo)
    }
})

Based on the documentation, I would have expected tap and register to fire at the same time.

newyork-anthonyng avatar Oct 20 '18 11:10 newyork-anthonyng

I looked through the code. When a hook is triggered, interceptor.call and interceptor.tap are both called. It seems the only difference is that interceptor.call is called with the arguments that you used when calling your hook, and interceptor.tap is called with the Tapable object.

For example:

const { SyncHook } = require("tapable");
const greetingHook = new SyncHook(["name"]);

greetingHook.intercept({
    register: (tapInfo) => {
        console.log("πŸ‘Ύ new tap was registered", tapInfo);
    },

    call: (name) => {
         console.log("πŸ™„ tapped method was called", name);
    },

    tap: (tapInfo) => {
        console.log("πŸ™Š tapped method was called", tapInfo);
    }

});

greetingHook.tap("EmailPlugin", function (name) {
    console.log("Hi, this is an email for " + name);
});
// πŸ‘Ύ new tap was registered { type: "sync", fn: [Function], name: "EmailPlugin" }

greetingHook("Anthony");
// πŸ™„ tapped method was called Anthony
// πŸ™Š tapped method was called { type: "sync", fn: [Function], name: "EmailPlugin" }

@ooflorent Is this correct? If so, I could update the docs to make this more clear.

newyork-anthonyng avatar Oct 21 '18 20:10 newyork-anthonyng

I looked through the code. When a hook is triggered, interceptor.call and interceptor.tap are both called. It seems the only difference is that interceptor.call is called with the arguments that you used when calling your hook, and interceptor.tap is called with the Tapable object.

For example:

const { SyncHook } = require("tapable");
const greetingHook = new SyncHook(["name"]);

greetingHook.intercept({
    register: (tapInfo) => {
        console.log("πŸ‘Ύ new tap was registered", tapInfo);
    },

    call: (name) => {
         console.log("πŸ™„ tapped method was called", name);
    },

    tap: (tapInfo) => {
        console.log("πŸ™Š tapped method was called", tapInfo);
    }

});

greetingHook.tap("EmailPlugin", function (name) {
    console.log("Hi, this is an email for " + name);
});
// πŸ‘Ύ new tap was registered { type: "sync", fn: [Function], name: "EmailPlugin" }

greetingHook("Anthony");
// πŸ™„ tapped method was called Anthony
// πŸ™Š tapped method was called { type: "sync", fn: [Function], name: "EmailPlugin" }

@ooflorent Is this correct? If so, I could update the docs to make this more clear.

thanks for your reply,from the implementation of tapable, what u described is true. And what makes me confused are the docs. when the developer use intercept api , the interceptor will be saved as the last element of interceptors which is a property of hook object. And when the developer call the hook, tapable will generate function string according to the context, then return function with new Function api, and cached the function at the same time.

const { SyncHook } = require("tapable");
const greetingHook = new SyncHook(["name"]);

greetingHook.intercept({
    register: (tapInfo) => {
        console.log("πŸ‘Ύ new tap was registered", tapInfo);
    },

    call: (name) => {
         console.log("πŸ™„ tapped method was called", name);
    },

    tap: (tapInfo) => {
        console.log("πŸ™Š tapped method was called", tapInfo);
    }

});

greetingHook.tap("EmailPlugin", function (name) {
    console.log("Hi, this is an email for " + name);
});
// greetingHook("Anthony"); should use call instead of call it directly
greetingHook.call("Anthony");

these code will generate the function as follow.

function anonymous(name /*``*/ ) {
	"use strict";
	var _context;
	var _x = this._x;
	var _taps = this.taps;
	var _interceptors = this.interceptors;
	_interceptors[0].call(name);
	var _tap0 = _taps[0];
	_interceptors[0].tap(_tap0);
	var _fn0 = _x[0];
	_fn0(name);

}

from the generatored anonymous function, we can see that interceptor.call and interceptor.tap will be invoked when invoking of the anonymous which is referred as greetingHook.call. And the difference is that nterceptor.call will be trigger with the arguments, interceptor.tap will be invoked with the tapInfo. The invoking of interceptor.register happened at tap(Hook.js loc:32), in the function of tap, it invokes _runRegisterInterceptors(Hook.js loc:71), at this phase, developer can access the tapInfo and make some change to the tapInfo, the detail of _runRegisterInterceptors is as follow:

_runRegisterInterceptors(options) {
		for (const interceptor of this.interceptors) {
			if (interceptor.register) {
				const newOptions = interceptor.register(options);
				if (newOptions !== undefined) options = newOptions;
			}
		}
		return options;
}

Last but not least, i really really want the official team can improve the quality of this doc πŸ™ πŸ™ πŸ™. I am willing to make some efforts to improve the doc.

warjiang avatar Oct 22 '18 03:10 warjiang

I looked through the code. When a hook is triggered, interceptor.call and interceptor.tap are both called. It seems the only difference is that interceptor.call is called with the arguments that you used when calling your hook, and interceptor.tap is called with the Tapable object.

For example:

const { SyncHook } = require("tapable");
const greetingHook = new SyncHook(["name"]);

greetingHook.intercept({
    register: (tapInfo) => {
        console.log("πŸ‘Ύ new tap was registered", tapInfo);
    },

    call: (name) => {
         console.log("πŸ™„ tapped method was called", name);
    },

    tap: (tapInfo) => {
        console.log("πŸ™Š tapped method was called", tapInfo);
    }

});

greetingHook.tap("EmailPlugin", function (name) {
    console.log("Hi, this is an email for " + name);
});
// πŸ‘Ύ new tap was registered { type: "sync", fn: [Function], name: "EmailPlugin" }

greetingHook("Anthony");
// πŸ™„ tapped method was called Anthony
// πŸ™Š tapped method was called { type: "sync", fn: [Function], name: "EmailPlugin" }

@ooflorent Is this correct? If so, I could update the docs to make this more clear.

first thanks for your reply, i am agree with what you described above. Tapable will generate function until you trigger the hook。the example you mentioned will generate a anonymous function like so:

function anonymous(name /*``*/ ) {
	"use strict";
	var _context;
	var _x = this._x;
	var _taps = this.taps;
	var _interceptors = this.interceptors;
	_interceptors[0].call(name);
	var _tap0 = _taps[0];
	_interceptors[0].tap(_tap0);
	var _fn0 = _x[0];
	_fn0(name);

}

from the anonymous generated above, we can see that the interceptor.call and interceptor.tap will be invoked when the hook be triggered, the difference is that interceptor.call accept the arguments, interceptor.tap accept tapInfo object。 The invoke of interceptor.register in the lib/Hook.js(loc:41), implementation of _runRegisterInterceptors as follows :

_runRegisterInterceptors(options) {
		for (const interceptor of this.interceptors) {
			if (interceptor.register) {
				const newOptions = interceptor.register(options);
				if (newOptions !== undefined) options = newOptions;
			}
		}
		return options;
}

so when developer trigger the function, the interceptor.register will be invoked.

In summary, what you described is right absolutely. Last but not least, i really really want the official team can improve the quality of the doc, and i'm willing to contribute the doc if any need.

warjiang avatar Oct 22 '18 05:10 warjiang