react-native-web-headroom
Version:
React Native Web Headroom
258 lines (229 loc) • 8.72 kB
JavaScript
;
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
});