text: 'use strict';
  
  function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
  
  function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; }
  
  function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; }
  
  var p = require('path');
  // const printAST = require('ast-pretty-print')
  
  var macrosRegex = /[./]macro(\.js)?$/;
  
  // https://stackoverflow.com/a/32749533/971592
  
  var MacroError = function (_Error) {
    _inherits(MacroError, _Error);
  
    function MacroError(message) {
      _classCallCheck(this, MacroError);
  
      var _this = _possibleConstructorReturn(this, (MacroError.__proto__ || Object.getPrototypeOf(MacroError)).call(this, message));
  
      _this.name = 'MacroError';
      /* istanbul ignore else */
      if (typeof Error.captureStackTrace === 'function') {
        Error.captureStackTrace(_this, _this.constructor);
      } else if (!_this.stack) {
        _this.stack = new Error(message).stack;
      }
      return _this;
    }
  
    return MacroError;
  }(Error);
  
  function createMacro(macro) {
    var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
  
    if (options.configName === 'options') {
      throw new Error(`You cannot use the configName "options". It is reserved for babel-plugin-macros.`);
    }
    macroWrapper.isBabelMacro = true;
    macroWrapper.options = options;
    return macroWrapper;
  
    function macroWrapper(args) {
      var source = args.source,
          isBabelMacrosCall = args.isBabelMacrosCall;
  
      if (!isBabelMacrosCall) {
        throw new MacroError(`The macro you imported from "${source}" is being executed outside the context of compilation with babel-plugin-macros. ` + `This indicates that you don't have the babel plugin "babel-plugin-macros" configured correctly. ` + `Please see the documentation for how to configure babel-plugin-macros properly: ` + 'https://github.com/kentcdodds/babel-plugin-macros/blob/master/other/docs/user.md');
      }
      return macro(args);
    }
  }
  
  function macrosPlugin(babel) {
    var _ref = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {},
        _ref$require = _ref.require,
        _require = _ref$require === undefined ? require : _ref$require;
  
    function interopRequire(path) {
      // eslint-disable-next-line import/no-dynamic-require
      var o = _require(path);
      return o && o.__esModule && o.default ? o.default : o;
    }
  
    return {
      name: 'macros',
      visitor: {
        ImportDeclaration(path, state) {
          var isMacros = looksLike(path, {
            node: {
              source: {
                value: function value(v) {
                  return macrosRegex.test(v);
                }
              }
            }
          });
          if (!isMacros) {
            return;
          }
          var imports = path.node.specifiers.map(function (s) {
            return {
              localName: s.local.name,
              importedName: s.type === 'ImportDefaultSpecifier' ? 'default' : s.imported.name
            };
          });
          var source = path.node.source.value;
          applyMacros({
            path,
            imports,
            source,
            state,
            babel,
            interopRequire
          });
          path.remove();
        },
        VariableDeclaration(path, state) {
          var isMacros = function isMacros(child) {
            return looksLike(child, {
              node: {
                init: {
                  callee: {
                    type: 'Identifier',
                    name: 'require'
                  },
                  arguments: function _arguments(args) {
                    return args.length === 1 && macrosRegex.test(args[0].value);
                  }
                }
              }
            });
          };
  
          path.get('declarations').filter(isMacros).forEach(function (child) {
            var imports = child.node.id.name ? [{ localName: child.node.id.name, importedName: 'default' }] : child.node.id.properties.map(function (property) {
              return {
                localName: property.value.name,
                importedName: property.key.name
              };
            });
  
            var call = child.get('init');
            var source = call.node.arguments[0].value;
            applyMacros({
              path: call,
              imports,
              source,
              state,
              babel,
              interopRequire
            });
  
            child.remove();
          });
        }
      }
    };
  }
  
  // eslint-disable-next-line complexity
  function applyMacros(_ref2) {
    var path = _ref2.path,
        imports = _ref2.imports,
        source = _ref2.source,
        state = _ref2.state,
        babel = _ref2.babel,
        interopRequire = _ref2.interopRequire;
    var filename = state.file.opts.filename;
  
    var hasReferences = false;
    var referencePathsByImportName = imports.reduce(function (byName, _ref3) {
      var importedName = _ref3.importedName,
          localName = _ref3.localName;
  
      byName[importedName] = path.scope.getBinding(localName).referencePaths;
      hasReferences = hasReferences || Boolean(byName[importedName].length);
      return byName;
    }, {});
    if (!hasReferences) {
      return;
    }
    var requirePath = source;
    var isRelative = source.indexOf('.') === 0;
    if (isRelative) {
      requirePath = p.join(p.dirname(getFullFilename(filename)), source);
    }
    var macro = interopRequire(requirePath);
    if (!macro.isBabelMacro) {
      throw new Error(
      // eslint-disable-next-line prefer-template
      `The macro imported from "${source}" must be wrapped in "createMacro" ` + `which you can get from "babel-plugin-macros". ` + `Please refer to the documentation to see how to do this properly: https://github.com/kentcdodds/babel-plugin-macros/blob/master/other/docs/author.md#writing-a-macro`);
    }
    var config = getConfig(macro, filename, source);
    try {
      macro({
        references: referencePathsByImportName,
        state,
        babel,
        config,
        isBabelMacrosCall: true
      });
    } catch (error) {
      if (error.name === 'MacroError') {
        throw error;
      }
      error.message = `${source}: ${error.message}`;
      if (!isRelative) {
        error.message = `${error.message} Learn more: https://www.npmjs.com/package/${source.replace(/(\/.*)/g, '')}`;
      }
      throw error;
    }
  }
  
  // eslint-disable-next-line consistent-return
  function getConfig(macro, filename, source) {
    if (macro.options.configName) {
      try {
        // lazy-loading it here to avoid perf issues of loading it up front.
        // No I did not measure. Yes I'm a bad person.
        // FWIW, this thing told me that cosmiconfig is 227.1 kb of minified JS
        // so that's probably significant... https://bundlephobia.com/result?p=cosmiconfig@3.1.0
        // Note that cosmiconfig will cache the babel-plugin-macros config 👍
        var loaded = require('cosmiconfig')('babel-plugin-macros', {
          packageProp: 'babelMacros',
          rc: '.babel-plugin-macrosrc',
          js: 'babel-plugin-macros.config.js',
          rcExtensions: true,
          sync: true
        }).load(filename);
        if (loaded) {
          return loaded.config[macro.options.configName];
        }
      } catch (error) {
        // eslint-disable-next-line no-console
        console.error(`There was an error trying to load the config "${macro.options.configName}" ` + `for the macro imported from "${source}. ` + `Please see the error thrown for more information.`);
        throw error;
      }
    }
  }
  
  /*
   istanbul ignore next
   because this is hard to test
   and not worth it...
   */
  function getFullFilename(filename) {
    if (p.isAbsolute(filename)) {
      return filename;
    }
    return p.join(process.cwd(), filename);
  }
  
  function looksLike(a, b) {
    return a && b && Object.keys(b).every(function (bKey) {
      var bVal = b[bKey];
      var aVal = a[bKey];
      if (typeof bVal === 'function') {
        return bVal(aVal);
      }
      return isPrimitive(bVal) ? bVal === aVal : looksLike(aVal, bVal);
    });
  }
  
  function isPrimitive(val) {
    // eslint-disable-next-line
    return val == null || /^[sbn]/.test(typeof val);
  }
  
  module.exports = macrosPlugin;
  Object.assign(module.exports, {
    createMacro,
    MacroError
  });