@reatom/babel-plugin
Version:
Compile-time preprocessing for reatom
118 lines (109 loc) • 2.82 kB
JavaScript
const definions = {
source: '@reatom/core',
declareAtom: 'declareAtom',
declareAction: 'declareAction',
map: 'map',
combine: 'combine',
names: ['declareAtom', 'declareAction', 'map', 'combine'],
}
/*
Inspired with zerobias/effector
Thanks @zerobias and @goodmind for implementation example
https://www.npmjs.com/package/effector
*/
module.exports = function(babel) {
const { types: t } = babel
const plugin = {
name: '@reatom/babel-plugin',
pre() {
this.importedNames = new Map()
},
visitor: {
ImportDeclaration(path) {
const {
source: { value: source },
specifiers,
} = path.node
if (source !== definions.source) {
return
}
for (let i = 0; i < specifiers.length; i++) {
const s = specifiers[i]
if (!s.imported) continue
const importedName = s.imported.name
const localName = s.local.name
if (definions.names.includes(importedName)) {
this.importedNames.set(localName, importedName)
}
}
},
CallExpression(path) {
if (!t.isIdentifier(path.node.callee)) {
return
}
if (this.importedNames.has(path.node.callee.name)) {
const name = this.importedNames.get(path.node.callee.name)
const id = findCandidateNameForExpression(path)
if (id) {
pushNameToArgs(path, id, t, name)
}
}
},
},
}
return plugin
}
function findCandidateNameForExpression(path) {
let id
path.find(subPath => {
if (subPath.isAssignmentExpression()) {
id = subPath.node.left
} else if (subPath.isObjectProperty()) {
id = subPath.node.key
} else if (subPath.isVariableDeclarator()) {
id = subPath.node.id
} else if (subPath.isStatement()) {
return true
}
return id || false
})
return id
}
function pushNameToArgs(path, nameNodeId, t, name) {
const displayName = nameNodeId.name
if (!displayName) {
return
}
const callExp = path.find(subPath => subPath.isCallExpression())
if (!callExp) {
return
}
const {
node: { arguments: args },
} = callExp
switch (name) {
case definions.map:
case definions.declareAtom:
if (args.length === 2) {
args.unshift(t.stringLiteral(displayName))
}
break
case definions.declareAction:
if (
args.length === 0 ||
!['TemplateLiteral', 'StringLiteral', 'ArrayExpression'].includes(
args[0].type,
)
) {
args.unshift(t.stringLiteral(displayName))
}
break
case definions.combine:
if (args.length === 1) {
args.unshift(t.stringLiteral(displayName))
}
break
default:
break
}
}