@warp-drive/build-config
Version:
Provides Build Configuration for projects using WarpDrive or EmberData
103 lines (93 loc) • 4.86 kB
JavaScript
;
const babelImportUtil = require('babel-import-util');
function parentIsUnary(node) {
if (node.parent.type === 'UnaryExpression' && node.parent.operator === '!') {
return true;
}
return false;
}
function babelPluginTransformLogging (babel) {
const {
types: t
} = babel;
return {
name: 'ast-transform',
// not required
visitor: {
ImportDeclaration(path, state) {
const importPath = path.node.source.value;
if (state.opts.sources.includes(importPath)) {
const specifiers = path.get('specifiers');
specifiers.forEach(specifier => {
let name = specifier.node.imported.name;
if (!(name in state.opts.flags)) {
throw new Error(`Unexpected flag ${name} imported from ${importPath}`);
}
let localBindingName = specifier.node.local.name;
let binding = specifier.scope.getBinding(localBindingName);
const enableRuntimeActivation = Boolean(state.opts.runtimeKey);
const strippableKey = enableRuntimeActivation ? state.opts.runtimeKey : state.opts.configKey;
binding.referencePaths.forEach(p => {
let negateStatement = false;
let node = p;
if (parentIsUnary(p)) {
negateStatement = true;
node = p.parentPath;
}
let getConfig = t.memberExpression(t.memberExpression(t.memberExpression(t.callExpression(state.importer.import(p, '@embroider/macros', 'getGlobalConfig'), []), t.identifier('WarpDrive')), t.identifier(strippableKey)), t.identifier(name));
node.replaceWith(
// if (LOG_FOO) {
// // ...
// }
// =>
// if (macroCondition(getGlobalConfig('WarpDrive').debug.LOG_FOO)) {
// // ...
// }
t.callExpression(state.importer.import(p, '@embroider/macros', 'macroCondition'), [negateStatement ? t.unaryExpression('!', getConfig) : getConfig]));
if (enableRuntimeActivation) {
// we do not yet support arbitrary runtime activation locations,
// the only supported locations are `if (LOG)` style statements, no
// ternaries or other more complex expressions
const parentIfStatement = node.parentPath.type === 'IfStatement' ? node.parentPath : null;
if (!parentIfStatement) {
throw new Error(`Runtime activation of logging flags is only supported in if statements, but found node '${node.parentPath.type}'`);
}
// if (LOG_FOO) {
// // ...
// }
// =>
// if (macroCondition(getGlobalConfig('WarpDrive').activeLogging.LOG_FOO)) {
// if (getGlobalConfig('WarpDrive').debug.LOG_FOO || globalThis.getWarpDriveRuntimeConfig().debug.LOG_FOO) {
// // ...
// }
// }
//
// the outer-if is generated by the node-replace above. The inner if is generated here.
const originalBody = parentIfStatement.node.consequent;
// getGlobalConfig('WarpDrive').debug.LOG_FOO
const getActualConfig = t.memberExpression(t.memberExpression(t.memberExpression(t.callExpression(state.importer.import(p, '@embroider/macros', 'getGlobalConfig'), []), t.identifier('WarpDrive')), t.identifier(state.opts.configKey)), t.identifier(name));
// globalThis.getWarpDriveRuntimeConfig().debug.LOG_FOO
const getRuntimeConfig = t.memberExpression(t.memberExpression(t.callExpression(t.memberExpression(t.identifier('globalThis'), t.identifier('getWarpDriveRuntimeConfig')), []), t.identifier(state.opts.configKey)), t.identifier(name));
// <getActualConfig> || <getRuntimeConfig>
const ifExp = t.logicalExpression('||', getActualConfig, getRuntimeConfig);
// if (<negateStatement>(<getActualConfig> || <getRuntimeConfig>)) <originalBody>
const innerIfStatement = t.ifStatement(negateStatement ? t.unaryExpression('!', ifExp) : ifExp, originalBody);
// replace the original body with the new if statement
parentIfStatement.node.consequent = t.blockStatement([innerIfStatement]);
}
});
specifier.scope.removeOwnBinding(localBindingName);
specifier.remove();
});
if (path.get('specifiers').length === 0) {
path.remove();
}
}
},
Program(path, state) {
state.importer = new babelImportUtil.ImportUtil(t, path);
}
}
};
}
module.exports = babelPluginTransformLogging;