Skip to content

Commit 3240695

Browse files
Merge pull request #31 from stephencookdev/feature/webpack-4#9
Add webpack v4 support
2 parents e78ea2e + 3c012af commit 3240695

22 files changed

Lines changed: 34418 additions & 691 deletions

File tree

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ SMP follows [semver](https://semver.org/). If upgrading a major version, you can
3535

3636
## Requirements
3737

38-
SMP requires at least Node v6.
38+
SMP requires at least **Node v6**. But otherwise, accepts **all webpack** versions (1, 2, 3, and 4).
3939

4040
## Usage
4141

WrappedPlugin/index.js

Lines changed: 170 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -1,40 +1,159 @@
11
let 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+
358
const 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+
38157
const 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);

__tests__/common/jest.config.js

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
module.exports = {
2+
testEnvironment: "node"
3+
};

__tests__/setups/.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,4 +2,5 @@ app.js
22
constants.js
33
styles.css
44
smp.test.js
5+
jest.config.js
56
dist

0 commit comments

Comments
 (0)