Add wrapPluginVisitorMethod option to allow introspection and metrics tracking of plugins (#3659)
This commit is contained in:
parent
ea69362249
commit
07b3dc18a0
@ -442,7 +442,11 @@ export default class File extends Store {
|
|||||||
const pluginPasses = this.pluginPasses[i];
|
const pluginPasses = this.pluginPasses[i];
|
||||||
this.call("pre", pluginPasses);
|
this.call("pre", pluginPasses);
|
||||||
this.log.debug("Start transform traverse");
|
this.log.debug("Start transform traverse");
|
||||||
traverse(this.ast, traverse.visitors.merge(this.pluginVisitors[i], pluginPasses), this.scope);
|
|
||||||
|
// merge all plugin visitors into a single visitor
|
||||||
|
let visitor = traverse.visitors.merge(this.pluginVisitors[i], pluginPasses, this.opts.wrapPluginVisitorMethod);
|
||||||
|
traverse(this.ast, visitor, this.scope);
|
||||||
|
|
||||||
this.log.debug("End transform traverse");
|
this.log.debug("End transform traverse");
|
||||||
this.call("post", pluginPasses);
|
this.call("post", pluginPasses);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -102,6 +102,11 @@ module.exports = {
|
|||||||
description: "optional callback to control whether a comment should be inserted, when this is used the comments option is ignored"
|
description: "optional callback to control whether a comment should be inserted, when this is used the comments option is ignored"
|
||||||
},
|
},
|
||||||
|
|
||||||
|
wrapPluginVisitorMethod: {
|
||||||
|
hidden: true,
|
||||||
|
description: "optional callback to wrap all visitor methods"
|
||||||
|
},
|
||||||
|
|
||||||
compact: {
|
compact: {
|
||||||
type: "booleanString",
|
type: "booleanString",
|
||||||
default: "auto",
|
default: "auto",
|
||||||
|
|||||||
@ -13,6 +13,8 @@ export default new Plugin({
|
|||||||
* - 3 We want this to be at the **very** top
|
* - 3 We want this to be at the **very** top
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
name: "internal.blockHoist",
|
||||||
|
|
||||||
visitor: {
|
visitor: {
|
||||||
Block: {
|
Block: {
|
||||||
exit({ node }) {
|
exit({ node }) {
|
||||||
|
|||||||
@ -16,6 +16,8 @@ const superVisitor = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export default new Plugin({
|
export default new Plugin({
|
||||||
|
name: "internal.shadowFunctions",
|
||||||
|
|
||||||
visitor: {
|
visitor: {
|
||||||
ThisExpression(path) {
|
ThisExpression(path) {
|
||||||
remap(path, "this");
|
remap(path, "this");
|
||||||
|
|||||||
@ -1,27 +1,21 @@
|
|||||||
import type Plugin from "./plugin";
|
import type Plugin from "./plugin";
|
||||||
import Store from "../store";
|
import Store from "../store";
|
||||||
import traverse from "babel-traverse";
|
|
||||||
import File from "./file";
|
import File from "./file";
|
||||||
|
|
||||||
export default class PluginPass extends Store {
|
export default class PluginPass extends Store {
|
||||||
constructor(file: File, plugin: Plugin, options: Object = {}) {
|
constructor(file: File, plugin: Plugin, options: Object = {}) {
|
||||||
super();
|
super();
|
||||||
this.plugin = plugin;
|
this.plugin = plugin;
|
||||||
|
this.key = plugin.key;
|
||||||
this.file = file;
|
this.file = file;
|
||||||
this.opts = options;
|
this.opts = options;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
key: string;
|
||||||
plugin: Plugin;
|
plugin: Plugin;
|
||||||
file: File;
|
file: File;
|
||||||
opts: Object;
|
opts: Object;
|
||||||
|
|
||||||
transform() {
|
|
||||||
let file = this.file;
|
|
||||||
file.log.debug(`Start transformer ${this.key}`);
|
|
||||||
traverse(file.ast, this.plugin.visitor, file.scope, file);
|
|
||||||
file.log.debug(`Finish transformer ${this.key}`);
|
|
||||||
}
|
|
||||||
|
|
||||||
addHelper(...args) {
|
addHelper(...args) {
|
||||||
return this.file.addHelper(...args);
|
return this.file.addHelper(...args);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -15,7 +15,7 @@ export default class Plugin extends Store {
|
|||||||
|
|
||||||
this.initialized = false;
|
this.initialized = false;
|
||||||
this.raw = assign({}, plugin);
|
this.raw = assign({}, plugin);
|
||||||
this.key = key;
|
this.key = this.take("name") || key;
|
||||||
|
|
||||||
this.manipulateOptions = this.take("manipulateOptions");
|
this.manipulateOptions = this.take("manipulateOptions");
|
||||||
this.post = this.take("post");
|
this.post = this.take("post");
|
||||||
|
|||||||
@ -76,6 +76,38 @@ suite("api", function () {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test("option wrapPluginVisitorMethod", function () {
|
||||||
|
var calledRaw = 0;
|
||||||
|
var calledIntercept = 0;
|
||||||
|
|
||||||
|
babel.transform("function foo() { bar(foobar); }", {
|
||||||
|
wrapPluginVisitorMethod: function (pluginAlias, visitorType, callback) {
|
||||||
|
if (pluginAlias !== "foobar") {
|
||||||
|
return callback;
|
||||||
|
}
|
||||||
|
|
||||||
|
assert.equal(visitorType, "enter");
|
||||||
|
|
||||||
|
return function () {
|
||||||
|
calledIntercept++;
|
||||||
|
return callback.apply(this, arguments);
|
||||||
|
};
|
||||||
|
},
|
||||||
|
|
||||||
|
plugins: [new Plugin({
|
||||||
|
name: "foobar",
|
||||||
|
visitor: {
|
||||||
|
"Program|Identifier": function () {
|
||||||
|
calledRaw++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})]
|
||||||
|
});
|
||||||
|
|
||||||
|
assert.equal(calledRaw, 4);
|
||||||
|
assert.equal(calledIntercept, 4);
|
||||||
|
});
|
||||||
|
|
||||||
test("pass per preset", function () {
|
test("pass per preset", function () {
|
||||||
var aliasBaseType = null;
|
var aliasBaseType = null;
|
||||||
|
|
||||||
|
|||||||
@ -162,7 +162,7 @@ function validateVisitorMethods(path, val) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export function merge(visitors: Array, states: Array = []) {
|
export function merge(visitors: Array, states: Array = [], wrapper?: ?Function) {
|
||||||
let rootVisitor = {};
|
let rootVisitor = {};
|
||||||
|
|
||||||
for (let i = 0; i < visitors.length; i++) {
|
for (let i = 0; i < visitors.length; i++) {
|
||||||
@ -174,8 +174,10 @@ export function merge(visitors: Array, states: Array = []) {
|
|||||||
for (let type in visitor) {
|
for (let type in visitor) {
|
||||||
let visitorType = visitor[type];
|
let visitorType = visitor[type];
|
||||||
|
|
||||||
// if we have state then overload the callbacks to take it
|
// if we have state or wrapper then overload the callbacks to take it
|
||||||
if (state) visitorType = wrapWithState(visitorType, state);
|
if (state || wrapper) {
|
||||||
|
visitorType = wrapWithStateOrWrapper(visitorType, state, wrapper);
|
||||||
|
}
|
||||||
|
|
||||||
let nodeVisitor = rootVisitor[type] = rootVisitor[type] || {};
|
let nodeVisitor = rootVisitor[type] = rootVisitor[type] || {};
|
||||||
mergePair(nodeVisitor, visitorType);
|
mergePair(nodeVisitor, visitorType);
|
||||||
@ -185,7 +187,7 @@ export function merge(visitors: Array, states: Array = []) {
|
|||||||
return rootVisitor;
|
return rootVisitor;
|
||||||
}
|
}
|
||||||
|
|
||||||
function wrapWithState(oldVisitor, state) {
|
function wrapWithStateOrWrapper(oldVisitor, state, wrapper: ?Function) {
|
||||||
let newVisitor = {};
|
let newVisitor = {};
|
||||||
|
|
||||||
for (let key in oldVisitor) {
|
for (let key in oldVisitor) {
|
||||||
@ -195,10 +197,18 @@ function wrapWithState(oldVisitor, state) {
|
|||||||
if (!Array.isArray(fns)) continue;
|
if (!Array.isArray(fns)) continue;
|
||||||
|
|
||||||
fns = fns.map(function (fn) {
|
fns = fns.map(function (fn) {
|
||||||
let newFn = function (path) {
|
let newFn = fn;
|
||||||
return fn.call(state, path, state);
|
|
||||||
};
|
if (state) {
|
||||||
newFn.toString = () => fn.toString();
|
newFn = function (path) {
|
||||||
|
return fn.call(state, path, state);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (wrapper) {
|
||||||
|
newFn = wrapper(state.key, key, newFn);
|
||||||
|
}
|
||||||
|
|
||||||
return newFn;
|
return newFn;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user