|
export default function transformer(file, api, options) { |
|
const j = api.jscodeshift; |
|
const root = j(file.source); |
|
|
|
let hasModifications = false; |
|
const reactDomFactoriesModuleName = 'react-dom-factories'; |
|
|
|
const findReact = (j, root) => { |
|
// Check for import |
|
// i.e. `import React from 'react'` |
|
let hasReactImport = Array.from( |
|
root.find(j.ImportDeclaration, { |
|
source: { |
|
value: 'react' |
|
} |
|
}).__paths |
|
) |
|
.find(({ node }) => { |
|
return node.specifiers[0].local.name; |
|
}); |
|
|
|
hasReactImport = hasReactImport && hasReactImport.node.specifiers[0].local.name; |
|
|
|
// Check for require |
|
// i.e. `const React = require('react')` |
|
let hasReactRequire = Array.from( |
|
root.find(j.VariableDeclarator, { |
|
init: { |
|
type: 'CallExpression', |
|
callee: { |
|
name: 'require' |
|
} |
|
} |
|
}).__paths |
|
) |
|
.find(({ node }) => { |
|
return node.init.arguments[0].value === 'react' && node.id.name; |
|
}); |
|
|
|
hasReactRequire = hasReactRequire && hasReactRequire.node.id.name; |
|
|
|
return hasReactImport || hasReactRequire; |
|
} |
|
|
|
const hasReact = findReact(j, root); |
|
|
|
const replaceDOMFactoriesImport = (j, root) => { |
|
// Check for import |
|
// i.e. `import DOM from 'react-dom-factories'` |
|
// and replace with React if it does not exist |
|
// i.e. `import React from 'react'; |
|
const domFactoriesImport = root |
|
.find(j.ImportDeclaration, { |
|
source: { |
|
value: reactDomFactoriesModuleName |
|
} |
|
}); |
|
|
|
if(!hasReact) { |
|
// import DOM from 'react-dom-factories' -> import React from 'react' |
|
domFactoriesImport.insertBefore( |
|
j.importDeclaration( |
|
[j.importDefaultSpecifier( |
|
j.identifier('React') |
|
)], |
|
j.literal('react') |
|
) |
|
) |
|
} |
|
|
|
domFactoriesImport.remove(); |
|
|
|
return domFactoriesImport.length; |
|
}; |
|
|
|
const replaceDOMFactories = (j, root) => { |
|
let hasModifications = false; |
|
|
|
const replaceElementFactory = (name) => { |
|
return root |
|
.find(j.CallExpression, { |
|
callee: { |
|
name: name |
|
} |
|
}) |
|
.replaceWith(path => { |
|
// Set element as first argument for call expression, i.e. div(... -> 'div' |
|
path.value.arguments.unshift(j.literal(name)); |
|
|
|
// div(...) -> React.createElement('div', ...) |
|
return j.callExpression( |
|
j.memberExpression( |
|
j.identifier(hasReact || 'React'), |
|
j.identifier('createElement') |
|
), |
|
path.value.arguments |
|
); |
|
}); |
|
}; |
|
|
|
// Find DOM identifier from 'react-dom-factories' |
|
let domFactories = root |
|
.find(j.ImportDeclaration, { |
|
source: { |
|
value: reactDomFactoriesModuleName |
|
} |
|
}) |
|
.find(j.Identifier); |
|
let domFactoriesIdentifier = domFactories.length ? domFactories.get(0).node.name : 'DOM'; |
|
|
|
// Check for any destructuring |
|
// i.e. const { div } = DOM |
|
const elementFactories = root |
|
.find(j.VariableDeclarator, { |
|
init: { |
|
name: domFactoriesIdentifier |
|
} |
|
}) |
|
.find(j.Identifier) |
|
.filter(path => |
|
path.parentPath.parentPath.node.type === 'ObjectPattern' && path.name === 'key' |
|
) |
|
.forEach(({ node }) => { |
|
hasModifications = !!replaceElementFactory(node.name).length; |
|
}); |
|
|
|
// Check for destructured element factory from 'react-dom-factories' |
|
// i.e. import { div } from 'react-dom-factories' |
|
root |
|
.find(j.ImportDeclaration, { |
|
source: { |
|
value: reactDomFactoriesModuleName |
|
} |
|
}) |
|
.find(j.Identifier) |
|
.forEach(({ node }) => { |
|
hasModifications = !!replaceElementFactory(node.name).length; |
|
}); |
|
|
|
// Check for DOM factories |
|
// i.e. DOM.div(... |
|
root |
|
.find(j.CallExpression, { |
|
callee: { |
|
object: { |
|
type: 'Identifier', |
|
name: domFactoriesIdentifier |
|
} |
|
} |
|
}) |
|
.replaceWith(path => { |
|
hasModifications = true; |
|
|
|
// Set element as first argument for call expression, i.e. DOM.div(... -> 'div' |
|
path.value.arguments.unshift(j.literal(path.node.callee.property.name)); |
|
|
|
// DOM.div(...) -> React.createElement('div') |
|
return j.callExpression( |
|
j.memberExpression( |
|
j.identifier(hasReact || 'React'), |
|
j.identifier('createElement') |
|
), |
|
path.value.arguments |
|
) |
|
}); |
|
|
|
// Remove destructured DOM |
|
// i.e. const { div } = DOM |
|
root |
|
.find(j.VariableDeclarator, { |
|
init: { |
|
name: domFactoriesIdentifier |
|
} |
|
}).remove(); |
|
|
|
return hasModifications; |
|
}; |
|
|
|
// Only return the transformed source when we actually have modifications |
|
hasModifications = replaceDOMFactories(j, root) |
|
hasModifications = replaceDOMFactoriesImport(j, root) || hasModifications; |
|
|
|
return hasModifications ? root.toSource({ quote: 'single' }) : null; |
|
} |