UNPKG

@warp-drive/build-config

Version:

Provides Build Configuration for projects using WarpDrive or EmberData

103 lines (93 loc) 4.86 kB
'use strict'; 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;