Skip to content

Instantly share code, notes, and snippets.

@rafeca
Created September 14, 2018 13:17
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save rafeca/e06e88feed05eaa526bd96744337c29f to your computer and use it in GitHub Desktop.
Save rafeca/e06e88feed05eaa526bd96744337c29f to your computer and use it in GitHub Desktop.
/**
* @format
*/
const t = require('@babel/types');
const babylon = require('@babel/parser');
const invariant = require('invariant');
const isGlobal = binding => !binding;
const isFlowDeclared = binding => t.isDeclareVariable(binding.path);
function isGlobalOrFlowDeclared(binding) {
return isGlobal(binding) || isFlowDeclared(binding);
}
function isReplaceableGlobal(node, parent, scope, opts) {
if (!t.isIdentifier(node)) {
return false;
}
if (!Object.hasOwnProperty.call(opts, node.name)) {
return false;
}
// Check that it's a global variable (not defined in any other place, or an object
// property).
return (
isGlobalOrFlowDeclared(scope.getBinding(node.name)) &&
!t.isMemberExpression(parent) &&
!(t.isObjectProperty(parent) && parent.key === node)
);
}
function getValue(node, opts) {
invariant(
opts.hasOwnProperty(node.name),
`property ${node.name} should be present in plugin options`,
);
return opts[node.name];
}
function getAstFromValue(value) {
switch (typeof value) {
case 'string':
return t.stringLiteral(value);
case 'boolean':
return t.booleanLiteral(value);
case 'number':
return t.numericLiteral(value);
case 'undefined':
return t.unaryExpression('void', t.numericLiteral(0), true);
}
return babylon.parseExpression(JSON.stringify(value));
}
module.exports = function replaceGlobal() {
return {
visitor: {
Identifier(path, state) {
if (
isReplaceableGlobal(path.node, path.parent, path.scope, state.opts)
) {
const value = getValue(path.node, state.opts);
path.replaceWith(getAstFromValue(value));
}
},
},
};
};
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment