11let idInc = 0 ;
22
3+ const genWrappedFunc = ( {
4+ func,
5+ smp,
6+ context,
7+ timeEventName,
8+ pluginName,
9+ endType,
10+ } ) => ( ...args ) => {
11+ const id = idInc ++ ;
12+ // we don't know if there's going to be a callback applied to a particular
13+ // call, so we just set it multiple times, letting each one override the last
14+ const addEndEvent = ( ) =>
15+ smp . addTimeEvent ( "plugins" , timeEventName , "end" , {
16+ id,
17+ // we need to allow failure, since webpack can finish compilation and
18+ // cause our callbacks to fall on deaf ears
19+ allowFailure : true ,
20+ } ) ;
21+
22+ smp . addTimeEvent ( "plugins" , timeEventName , "start" , {
23+ id,
24+ name : pluginName ,
25+ } ) ;
26+ // invoke an end event immediately in case the callback here causes webpack
27+ // to complete compilation. If this gets invoked and not the subsequent
28+ // call, then our data will be inaccurate, sadly
29+ addEndEvent ( ) ;
30+ const normalArgMap = a => wrap ( a , pluginName , smp ) ;
31+ let ret ;
32+ if ( endType === "wrapDone" )
33+ ret = func . apply (
34+ context ,
35+ args . map ( a => wrap ( a , pluginName , smp , addEndEvent ) )
36+ ) ;
37+ else if ( endType === "async" ) {
38+ const argsButLast = args . slice ( 0 , args . length - 1 ) ;
39+ const callback = args [ args . length - 1 ] ;
40+ ret = func . apply (
41+ context ,
42+ argsButLast . map ( normalArgMap ) . concat ( ( ...callbackArgs ) => {
43+ addEndEvent ( ) ;
44+ callback ( ...callbackArgs ) ;
45+ } )
46+ ) ;
47+ } else if ( endType === "promise" )
48+ ret = func . apply ( context , args . map ( normalArgMap ) ) . then ( promiseArg => {
49+ addEndEvent ( ) ;
50+ return promiseArg ;
51+ } ) ;
52+ else ret = func . apply ( context , args . map ( normalArgMap ) ) ;
53+ addEndEvent ( ) ;
54+
55+ return ret ;
56+ } ;
57+
358const genPluginMethod = ( orig , pluginName , smp , type ) =>
459 function ( method , func ) {
560 const timeEventName = pluginName + "/" + type + "/" + method ;
6- const wrappedFunc = ( ...args ) => {
7- const id = idInc ++ ;
8- // we don't know if there's going to be a callback applied to a particular
9- // call, so we just set it multiple times, letting each one override the last
10- const addEndEvent = ( ) =>
11- smp . addTimeEvent ( "plugins" , timeEventName , "end" , {
12- id,
13- // we need to allow failure, since webpack can finish compilation and
14- // cause our callbacks to fall on deaf ears
15- allowFailure : true ,
16- } ) ;
17-
18- smp . addTimeEvent ( "plugins" , timeEventName , "start" , {
19- id,
20- name : pluginName ,
21- } ) ;
22- // invoke an end event immediately in case the callback here causes webpack
23- // to complete compilation. If this gets invoked and not the subsequent
24- // call, then our data will be inaccurate, sadly
25- addEndEvent ( ) ;
26- const ret = func . apply (
27- this ,
28- args . map ( a => wrap ( a , pluginName , smp , addEndEvent ) )
29- ) ;
30- addEndEvent ( ) ;
61+ const wrappedFunc = genWrappedFunc ( {
62+ func,
63+ smp,
64+ context : this ,
65+ timeEventName,
66+ pluginName,
67+ endType : "wrapDone" ,
68+ } ) ;
69+ return orig . plugin ( method , wrappedFunc ) ;
70+ } ;
3171
32- return ret ;
33- } ;
72+ const wrapTap = ( tap , pluginName , smp , type , method ) =>
73+ function ( id , func ) {
74+ const timeEventName = pluginName + "/" + type + "/" + method ;
75+ const wrappedFunc = genWrappedFunc ( {
76+ func,
77+ smp,
78+ context : this ,
79+ timeEventName,
80+ pluginName,
81+ } ) ;
82+ return tap . call ( this , id , wrappedFunc ) ;
83+ } ;
3484
35- return orig . plugin ( method , wrappedFunc ) ;
85+ const wrapTapAsync = ( tapAsync , pluginName , smp , type , method ) =>
86+ function ( id , func ) {
87+ const timeEventName = pluginName + "/" + type + "/" + method ;
88+ const wrappedFunc = genWrappedFunc ( {
89+ func,
90+ smp,
91+ context : this ,
92+ timeEventName,
93+ pluginName,
94+ endType : "async" ,
95+ } ) ;
96+ return tapAsync . call ( this , id , wrappedFunc ) ;
97+ } ;
98+
99+ const wrapTapPromise = ( tapPromise , pluginName , smp , type , method ) =>
100+ function ( id , func ) {
101+ const timeEventName = pluginName + "/" + type + "/" + method ;
102+ const wrappedFunc = genWrappedFunc ( {
103+ func,
104+ smp,
105+ context : this ,
106+ timeEventName,
107+ pluginName,
108+ endType : "promise" ,
109+ } ) ;
110+ return tapPromise . call ( this , id , wrappedFunc ) ;
111+ } ;
112+
113+ const wrappedHooks = [ ] ;
114+ const wrapHooks = ( orig , pluginName , smp , type ) => {
115+ const hooks = orig . hooks ;
116+ if ( ! hooks ) return hooks ;
117+ const prevWrapped = wrappedHooks . find (
118+ w =>
119+ w . pluginName === pluginName && ( w . orig === hooks || w . wrapped === hooks )
120+ ) ;
121+ if ( prevWrapped ) return prevWrapped . wrapped ;
122+
123+ const genProxy = method => {
124+ const proxy = new Proxy ( hooks [ method ] , {
125+ get : ( target , property ) => {
126+ const raw = Reflect . get ( target , property ) ;
127+
128+ if ( property === "tap" && typeof raw === "function" )
129+ return wrapTap ( raw , pluginName , smp , type , method ) . bind ( proxy ) ;
130+ if ( property === "tapAsync" && typeof raw === "function" )
131+ return wrapTapAsync ( raw , pluginName , smp , type , method ) . bind ( proxy ) ;
132+ if ( property === "tapPromise" && typeof raw === "function" )
133+ return wrapTapPromise ( raw , pluginName , smp , type , method ) . bind ( proxy ) ;
134+
135+ return raw ;
136+ } ,
137+ set : ( target , property , value ) => {
138+ return Reflect . set ( target , property , value ) ;
139+ } ,
140+ deleteProperty : ( target , property ) => {
141+ return Reflect . deleteProperty ( target , property ) ;
142+ } ,
143+ } ) ;
144+ return proxy ;
36145 } ;
37146
147+ const wrapped = Object . keys ( hooks ) . reduce ( ( acc , method ) => {
148+ acc [ method ] = genProxy ( method ) ;
149+ return acc ;
150+ } , { } ) ;
151+
152+ wrappedHooks . push ( { orig : hooks , wrapped, pluginName } ) ;
153+
154+ return wrapped ;
155+ } ;
156+
38157const construcNamesToWrap = [
39158 "Compiler" ,
40159 "Compilation" ,
@@ -67,18 +186,21 @@ const wrap = (orig, pluginName, smp, addEndEvent) => {
67186
68187 if ( ! shouldWrap && ! shouldSoftWrap ) {
69188 const vanillaFunc = orig . name === "next" ;
70- wrappedReturn = vanillaFunc
71- ? function ( ) {
72- // do this before calling the callback, since the callback can start
73- // the next plugin step
74- addEndEvent ( ) ;
189+ wrappedReturn =
190+ vanillaFunc && addEndEvent
191+ ? function ( ) {
192+ // do this before calling the callback, since the callback can start
193+ // the next plugin step
194+ addEndEvent ( ) ;
75195
76- return orig . apply ( this , arguments ) ;
77- }
78- : orig ;
196+ return orig . apply ( this , arguments ) ;
197+ }
198+ : orig ;
79199 } else {
80200 const proxy = new Proxy ( orig , {
81201 get : ( target , property ) => {
202+ const raw = Reflect . get ( target , property ) ;
203+
82204 if ( shouldWrap && property === "plugin" )
83205 return genPluginMethod (
84206 target ,
@@ -87,16 +209,24 @@ const wrap = (orig, pluginName, smp, addEndEvent) => {
87209 getOrigConstrucName ( target )
88210 ) . bind ( proxy ) ;
89211
90- if ( typeof target [ property ] === "function" ) {
91- const ret = target [ property ] . bind ( proxy ) ;
212+ if ( shouldWrap && property === "hooks" )
213+ return wrapHooks (
214+ target ,
215+ pluginName ,
216+ smp ,
217+ getOrigConstrucName ( target )
218+ ) ;
219+
220+ if ( typeof raw === "function" ) {
221+ const ret = raw . bind ( proxy ) ;
92222 if ( property === "constructor" )
93223 Object . defineProperty ( ret , "name" , {
94- value : target . constructor . name ,
224+ value : raw . name ,
95225 } ) ;
96226 return ret ;
97227 }
98228
99- return target [ property ] ;
229+ return raw ;
100230 } ,
101231 set : ( target , property , value ) => {
102232 return Reflect . set ( target , property , value ) ;
0 commit comments