This is a version of create-element-to-jsx with modifications needed to convert typescript files with updated versions of various dependencies.
I used yarn to resolve jscodeshift version to 0.13.1
| /** | |
| * Copyright 2015-present, Facebook, Inc. | |
| * | |
| * This source code is licensed under the MIT license found in the | |
| * LICENSE file in the root directory of this source tree. | |
| * | |
| */ | |
| 'use strict'; | |
| module.exports = function(file, api, options) { | |
| const j = api.jscodeshift; | |
| const printOptions = options.printOptions || {}; | |
| const root = j(file.source); | |
| const ReactUtils = require('./utils/ReactUtils')(j); | |
| const encodeJSXTextValue = value => | |
| value.replace(/</g, '<').replace(/>/g, '>'); | |
| const canLiteralBePropString = node =>{ | |
| return (node.raw || node.extra.raw).indexOf('\\') === -1 && node.value.indexOf('"') === -1 | |
| }; | |
| const isLiteralType = type => type === 'Literal' || type ==='StringLiteral' || type === 'NullLiteral' | |
| const convertExpressionToJSXAttributes = expression => { | |
| if (!expression) { | |
| return { | |
| attributes: [], | |
| extraComments: [] | |
| }; | |
| } | |
| const isReactSpread = | |
| expression.type === 'CallExpression' && | |
| expression.callee.type === 'MemberExpression' && | |
| expression.callee.object.name === 'React' && | |
| expression.callee.property.name === '__spread'; | |
| const isObjectAssign = | |
| expression.type === 'CallExpression' && | |
| expression.callee.type === 'MemberExpression' && | |
| expression.callee.object.name === 'Object' && | |
| expression.callee.property.name === 'assign'; | |
| const validSpreadTypes = [ | |
| 'Identifier', | |
| 'MemberExpression', | |
| 'CallExpression' | |
| ]; | |
| if (isReactSpread || isObjectAssign) { | |
| console.log('a') | |
| const resultAttributes = []; | |
| const resultExtraComments = expression.comments || []; | |
| const { callee } = expression; | |
| for (const node of [callee, callee.object, callee.property]) { | |
| resultExtraComments.push(...(node.comments || [])); | |
| } | |
| expression.arguments.forEach(expression => { | |
| const { attributes, extraComments } = convertExpressionToJSXAttributes( | |
| expression | |
| ); | |
| resultAttributes.push(...attributes); | |
| resultExtraComments.push(...extraComments); | |
| }); | |
| return { | |
| attributes: resultAttributes, | |
| extraComments: resultExtraComments | |
| }; | |
| } else if (validSpreadTypes.indexOf(expression.type) != -1) { | |
| console.log('b') | |
| return { | |
| attributes: [j.jsxSpreadAttribute(expression)], | |
| extraComments: [] | |
| }; | |
| } else if (expression.type === 'ObjectExpression') { | |
| console.log('c') | |
| const attributes = expression.properties.map(property => { | |
| if (property.type === 'SpreadProperty') { | |
| const spreadAttribute = j.jsxSpreadAttribute(property.argument); | |
| spreadAttribute.comments = property.comments; | |
| return spreadAttribute; | |
| } else if (property.type === 'Property' || property.type === 'ObjectProperty') { | |
| console.log(property) | |
| const propertyValueType = property.value.type; | |
| let value; | |
| if ( | |
| isLiteralType(propertyValueType) && | |
| typeof property.value.value === 'string' && | |
| canLiteralBePropString(property.value) | |
| ) { | |
| value = j.literal(property.value.value); | |
| value.comments = property.value.comments; | |
| } else { | |
| value = j.jsxExpressionContainer(property.value); | |
| } | |
| let jsxIdentifier; | |
| if (isLiteralType(property.key.type)) { | |
| jsxIdentifier = j.jsxIdentifier(property.key.value); | |
| } else { | |
| jsxIdentifier = j.jsxIdentifier(property.key.name); | |
| } | |
| jsxIdentifier.comments = property.key.comments; | |
| const jsxAttribute = j.jsxAttribute(jsxIdentifier, value); | |
| jsxAttribute.comments = property.comments; | |
| return jsxAttribute; | |
| } | |
| return null; | |
| }); | |
| return { | |
| attributes, | |
| extraComments: expression.comments || [] | |
| }; | |
| } else if (isLiteralType(expression.type) && expression.value === null) { | |
| console.log('d') | |
| return { | |
| attributes: [], | |
| extraComments: expression.comments || [] | |
| }; | |
| } else { | |
| throw new Error(`Unexpected attribute of type "${expression.type}"`); | |
| } | |
| }; | |
| const canConvertToJSXIdentifier = node => | |
| (isLiteralType(node.type) && typeof node.value === 'string') || | |
| node.type === 'Identifier' || | |
| (node.type === 'MemberExpression' && | |
| !node.computed && | |
| canConvertToJSXIdentifier(node.object) && | |
| canConvertToJSXIdentifier(node.property)); | |
| const jsxIdentifierFor = node => { | |
| let identifier; | |
| let comments = node.comments || []; | |
| if (isLiteralType(node.type)) { | |
| identifier = j.jsxIdentifier(node.value); | |
| } else if (node.type === 'MemberExpression') { | |
| let { | |
| identifier: objectIdentifier, | |
| comments: objectComments | |
| } = jsxIdentifierFor(node.object); | |
| let { | |
| identifier: propertyIdentifier, | |
| comments: propertyComments | |
| } = jsxIdentifierFor(node.property); | |
| identifier = j.jsxMemberExpression(objectIdentifier, propertyIdentifier); | |
| comments.push(...objectComments, ...propertyComments); | |
| } else { | |
| identifier = j.jsxIdentifier(node.name); | |
| } | |
| return { identifier, comments }; | |
| }; | |
| const isCapitalizationInvalid = node => | |
| (isLiteralType(node.type) && !/^[a-z]/.test(node.value)) || | |
| (node.type === 'Identifier' && /^[a-z]/.test(node.name)); | |
| const convertNodeToJSX = node => { | |
| const comments = node.value.comments || []; | |
| const { callee } = node.value; | |
| for (const calleeNode of [callee, callee.object, callee.property]) { | |
| for (const comment of calleeNode.comments || []) { | |
| comment.leading = true; | |
| comment.trailing = false; | |
| comments.push(comment); | |
| } | |
| } | |
| const args = node.value.arguments; | |
| if ( | |
| isCapitalizationInvalid(args[0]) || | |
| !canConvertToJSXIdentifier(args[0]) | |
| ) { | |
| return node.value; | |
| } | |
| const { | |
| identifier: jsxIdentifier, | |
| comments: identifierComments | |
| } = jsxIdentifierFor(args[0]); | |
| const props = args[1]; | |
| const { attributes, extraComments } = convertExpressionToJSXAttributes( | |
| props | |
| ); | |
| for (const comment of [...identifierComments, ...extraComments]) { | |
| comment.leading = false; | |
| comment.trailing = true; | |
| comments.push(comment); | |
| } | |
| const children = args.slice(2).map((child, index) => { | |
| if ( | |
| isLiteralType(child.type) && | |
| typeof child.value === 'string' && | |
| !child.comments && | |
| child.value !== '' && | |
| child.value.trim() === child.value | |
| ) { | |
| return j.jsxText(encodeJSXTextValue(child.value)); | |
| } else if ( | |
| child.type === 'CallExpression' && | |
| child.callee.object && | |
| child.callee.object.name === 'React' && | |
| child.callee.property.name === 'createElement' | |
| ) { | |
| const jsxChild = convertNodeToJSX(node.get('arguments', index + 2)); | |
| if ( | |
| jsxChild.type !== 'JSXElement' || | |
| (jsxChild.comments || []).length > 0 | |
| ) { | |
| return j.jsxExpressionContainer(jsxChild); | |
| } else { | |
| return jsxChild; | |
| } | |
| } else if (child.type === 'SpreadElement') { | |
| return j.jsxExpressionContainer(child.argument); | |
| } else { | |
| return j.jsxExpressionContainer(child); | |
| } | |
| }); | |
| console.log(jsxIdentifier, attributes) | |
| const openingElement = j.jsxOpeningElement(jsxIdentifier, attributes); | |
| if (children.length) { | |
| const endIdentifier = Object.assign({}, jsxIdentifier, { comments: [] }); | |
| // Add text newline nodes between elements so recast formats one child per | |
| // line instead of all children on one line. | |
| const paddedChildren = [j.jsxText('\n')]; | |
| for (const child of children) { | |
| paddedChildren.push(child, j.jsxText('\n')); | |
| } | |
| const element = j.jsxElement( | |
| openingElement, | |
| j.jsxClosingElement(endIdentifier), | |
| paddedChildren | |
| ); | |
| element.comments = comments; | |
| return element; | |
| } else { | |
| openingElement.selfClosing = true; | |
| const element = j.jsxElement(openingElement); | |
| element.comments = comments; | |
| return element; | |
| } | |
| }; | |
| console.log(options) | |
| if (options['explicit-require'] === false || ReactUtils.hasReact(root)) { | |
| const mutations = root | |
| .find(j.CallExpression, { | |
| callee: { | |
| object: { | |
| name: 'React' | |
| }, | |
| property: { | |
| name: 'createElement' | |
| } | |
| } | |
| }) | |
| .replaceWith(convertNodeToJSX) | |
| .size(); | |
| if (mutations) { | |
| return root.toSource(printOptions); | |
| } | |
| } | |
| return null; | |
| }; |
This is a version of create-element-to-jsx with modifications needed to convert typescript files with updated versions of various dependencies.
I used yarn to resolve jscodeshift version to 0.13.1