manipulative
Version:
React devtool for modifying Emotion styles in browser
92 lines (75 loc) • 3.48 kB
JavaScript
;
var t = require('@babel/types');
function processReferencePaths(referencePaths, state) {
if (referencePaths !== undefined) {
referencePaths.forEach(function (path) {
var callExpressionNode = path.parentPath.node;
if (!t.isCallExpression(callExpressionNode)) {
return;
}
var filename = state.file.opts.filename;
var start = callExpressionNode.start;
if (filename != null && start != null) {
callExpressionNode.arguments = [t.arrayExpression([t.stringLiteral(filename), t.numericLiteral(start)].concat(path.node.loc !== null ? [t.numericLiteral(path.node.loc.start.line), t.stringLiteral(state.file.code.split("\n")[path.node.loc.start.line - 1])] : []))];
}
});
}
}
function babelPlugin() {
return {
visitor: {
Program: function Program(path, state) {
var needsImport = false;
var USECSSPLACEHOLDER_IDENTIFIER_NAME = "useCssPlaceholder__INJECT";
var fileLines = state.file.code.split("\n"); // We're traversing here early before react-refresh does hook extraction.
// https://github.com/facebook/react/blob/e6a0f276307fcb2f1c5bc41d630c5e4c9e95a037/packages/react-refresh/src/ReactFreshBabelPlugin.js#L721
path.traverse({
JSXAttribute: function JSXAttribute(path) {
var propName = path.node.name;
if (!t.isJSXIdentifier(propName) || propName.name !== "css__") {
return;
}
needsImport = true; // TODO: check container for other props named css and warn
propName.name = "css";
var filename = state.file.opts.filename;
var start = path.node.start;
path.node.value = t.jsxExpressionContainer(t.callExpression(t.identifier(USECSSPLACEHOLDER_IDENTIFIER_NAME), [t.arrayExpression([t.stringLiteral(filename), t.numericLiteral(start)].concat(path.node.loc !== null ? [t.numericLiteral(path.node.loc.start.line), t.stringLiteral(fileLines[path.node.loc.start.line - 1])] : []))]));
}
});
if (needsImport) {
path.unshiftContainer("body", [t.importDeclaration([t.importSpecifier(t.identifier(USECSSPLACEHOLDER_IDENTIFIER_NAME), t.identifier("useCssPlaceholder"))], t.stringLiteral("manipulative"))]);
}
},
ImportDeclaration: function ImportDeclaration(path, state) {
if (path.node.source.value !== "manipulative") {
return;
}
var imports = path.node.specifiers.map(function (s) {
return {
localName: s.local.name,
importedName: s.type === "ImportDefaultSpecifier" ? "default" : s.imported.name
};
});
var shouldExit = false;
var hasReferences = false;
var referencePathsByImportName = imports.reduce(function (byName, _ref) {
var importedName = _ref.importedName,
localName = _ref.localName;
var binding = path.scope.getBinding(localName);
if (!binding) {
shouldExit = true;
return byName;
}
byName[importedName] = binding.referencePaths;
hasReferences = hasReferences || Boolean(byName[importedName].length);
return byName;
}, {});
if (!hasReferences || shouldExit) {
return;
}
processReferencePaths(referencePathsByImportName["useCssPlaceholder"], state);
}
}
};
}
module.exports = babelPlugin;