Skip to content

Instantly share code, notes, and snippets.

@busticated
Last active February 9, 2022 11:54
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save busticated/7dddf6eebd17c94e9870095ad246daa9 to your computer and use it in GitHub Desktop.
Save busticated/7dddf6eebd17c94e9870095ad246daa9 to your computer and use it in GitHub Desktop.
whitepace + jscodeshift
'use strict';
///////////////////////////////////////////////////////////////////////////////
// USAGE: jscodeshift ./lib/ -t ./scripts/codemod-react-proptypes.js
///////////////////////////////////////////////////////////////////////////////
module.exports = function(file, api){
var js = api.jscodeshift,
root = js(file.source),
shouldFixWhitespace,
collection;
///////////////////////////////////////////////////////////////////////////
// FROM: require('react').PropTypes
// TO: require('prop-types')
///////////////////////////////////////////////////////////////////////////
getReactRequiresWithPropTypesProperty(root, js)
.replaceWith(requireCallExpression(js, 'prop-types'));
///////////////////////////////////////////////////////////////////////////
// FROM: React.PropTypes
// TO: PropTypes
///////////////////////////////////////////////////////////////////////////
collection = getReactPropTypesRefs(root, js)
.replaceWith(js.identifier('PropTypes'));
if (collection.length){
///////////////////////////////////////////////////////////////////////
// FROM: React = require('react'),
// TO: React = require('react'),
// PropTypes = require('prop-types'),
///////////////////////////////////////////////////////////////////////
getVarDeclarationsWhichIncludeReact(root, js)
.forEach(function(path){
var atIndex = findIndexOfFirstDeclaration(path, 'React');
if (atIndex < 0){ return; }
insertVarDeclarationWithRequire(path, js, {
start: atIndex + 1,
name: 'PropTypes',
module: 'prop-types'
});
});
shouldFixWhitespace = true;
}
///////////////////////////////////////////////////////////////////////////
// FROM: React.createClass
// TO: createClass
///////////////////////////////////////////////////////////////////////////
collection = getReactCreateClassRefs(root, js)
.replaceWith(js.identifier('createReactClass'));
if (collection.length){
///////////////////////////////////////////////////////////////////////
// FROM: React = require('react'),
// TO: React = require('react'),
// createReactClass = require('create-react-class'),
///////////////////////////////////////////////////////////////////////
getVarDeclarationsWhichIncludeReact(root, js)
.forEach(function(path){
var atIndex = findIndexOfFirstDeclaration(path, 'PropTypes', 'React');
if (atIndex < 0){ return; }
insertVarDeclarationWithRequire(path, js, {
start: atIndex + 1,
name: 'createReactClass',
module: 'create-react-class'
});
});
shouldFixWhitespace = shouldFixWhitespace || true;
}
if (shouldFixWhitespace){
///////////////////////////////////////////////////////////////////////
// FROM: Foo = require('foo'), Bar = require('bar'), Baz = require('baz');
// TO: Foo = require('foo'),
// Bar = require('bar'),
// Baz = require('baz');
///////////////////////////////////////////////////////////////////////
// TODO (mirande): hack! figure out how to do this properly?
getVarDeclarationsWhichIncludeReact(root, js)
.forEach(function(path){
path.replace(js(path)
.toSource({ quote: 'single' })
.split(',')
.join(',\n '));
});
}
return root.toSource({ quote: 'single' });
};
// utilities //////////////////////////////////////////////////////////////////
function findIndexOfFirstDeclaration(path){
var index = -1,
i = 1;
while (i < arguments.length){
index = findIndexOfDeclaration(path, arguments[i]);
if (index >= 0){ return index; }
i += 1;
}
return index;
}
function findIndexOfDeclaration(path, name){
return path.value.declarations.findIndex(function(arg){
return arg.id.name === name;
});
}
function getVarDeclarationsWhichIncludeReact(root, js){
return root.find(js.VariableDeclaration)
.filter(function(path){
return path.value.declarations.find(function(dec){
return dec.id.name === 'React';
});
});
}
function getReactRequiresWithPropTypesProperty(root, js){
return root.find(js.MemberExpression, {
object: {
type: 'CallExpression',
callee: { type: 'Identifier', name: 'require' },
arguments: [
{ type: 'Literal', value: 'react' }
]
},
property: {
type: 'Identifier',
name: 'PropTypes'
}
});
}
function getReactPropTypesRefs(root, js){
return root.find(js.MemberExpression, {
object: {
type: 'Identifier',
name: 'React'
},
property: {
type: 'Identifier',
name: 'PropTypes'
}
});
}
function getReactCreateClassRefs(root, js){
return root.find(js.MemberExpression, {
object: {
type: 'Identifier',
name: 'React'
},
property: {
type: 'Identifier',
name: 'createClass'
}
});
}
function insertVarDeclarationWithRequire(path, js, options){
path.value.declarations.splice(
options.start,
options.end || 0,
js.variableDeclarator(
js.identifier(options.name),
requireCallExpression(js, options.module)
)
);
}
function requireCallExpression(js, name){
return js.callExpression(
js.identifier('require'),
[js.literal(name)]
);
}
'use strict';
var React = require('react'),
omit = require('lodash/omit'),
glyphBtn = React.createFactory(require('./glyph'));
module.exports = React.createClass({
displayName: 'AppendElementButton',
propTypes: {
className: React.PropTypes.string,
onAppend: React.PropTypes.func.isRequired,
},
getDefaultProps: function(){
return { className: 'btn-append-element' };
},
onAppend: function(event){
event.preventDefault();
this.props.onAppend();
},
render: function(){
var props = omit(this.props, ['onAppend']);
props.onClick = this.onAppend;
props.text = 'append element';
return glyphBtn(props);
}
});
'use strict';
var React = require('react'),
PropTypes = require('prop-types'),
createReactClass = require('create-react-class'),
omit = require('lodash/omit'),
glyphBtn = React.createFactory(require('./glyph'));
module.exports = createReactClass({
displayName: 'AppendElementButton',
propTypes: {
className: PropTypes.string,
onAppend: PropTypes.func.isRequired,
},
getDefaultProps: function(){
return { className: 'btn-append-element' };
},
onAppend: function(event){
event.preventDefault();
this.props.onAppend();
},
render: function(){
var props = omit(this.props, ['onAppend']);
props.onClick = this.onAppend;
props.text = 'append element';
return glyphBtn(props);
}
});
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment