Skip to content

Commit

Permalink
Add rename-identifier and rename-property (#139)
Browse files Browse the repository at this point in the history
  • Loading branch information
lemonmade committed May 26, 2016
1 parent 40069fc commit e2f960c
Show file tree
Hide file tree
Showing 13 changed files with 169 additions and 15 deletions.
17 changes: 16 additions & 1 deletion packages/esify/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -53,8 +53,23 @@ module.exports = {
globalIdentifiers: {
_: 'lodash',
$: 'jquery',
jQuery: 'jquery',
moment: 'moment',
},
// A list of identifiers to rename for rename-identifier
renameIdentifiers: {
jQuery: '$',
},
// A list of identifiers and their properties that should be renamed for rename-property
renameProperties: {
_: {
first: 'head',
each: 'forEach',
eachRight: 'forEachRight',
entries: 'toPairs',
entriesIn: 'toPairsIn',
extend: 'assignIn',
extendWith: 'assignInWith',
},
},
}
```
21 changes: 19 additions & 2 deletions packages/esify/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -35,13 +35,17 @@ var TRANSFORMS = [
{path: 'js-codemod/transforms/template-literals'},
{path: 'shopify-codemod/transforms/strip-template-literal-parenthesis'},
{path: 'js-codemod/transforms/object-shorthand'},
// These are run very late to ensure they catch any identifiers/ member expressions
// added in earlier transforms
{path: 'js-codemod/transforms/unquote-properties'},
{path: 'shopify-codemod/transforms/rename-identifier'},
{path: 'shopify-codemod/transforms/rename-property'},
// constant-function-expression-to-statement and global-reference-to-import need
// `const` references, so they must happen after `no-vars`
{path: 'js-codemod/transforms/no-vars'},
{path: 'shopify-codemod/transforms/constant-function-expression-to-statement'},
{path: 'shopify-codemod/transforms/global-reference-to-import'},
{path: 'shopify-codemod/transforms/global-identifier-to-import'},
{path: 'js-codemod/transforms/unquote-properties'},
];

var OPTIONS = loadOptions();
Expand Down Expand Up @@ -71,7 +75,6 @@ function loadOptions() {
globalIdentifiers: {
_: 'lodash',
$: 'jquery',
jQuery: 'jquery',
moment: 'moment',
jstz: 'jstimezonedetect',
mousetrap: 'mousetrap',
Expand All @@ -83,6 +86,20 @@ function loadOptions() {
FastClick: 'shopify-fastclick',
Clipboard: 'clipboard',
},
renameIdentifiers: {
jQuery: '$',
},
renameProperties: {
_: {
first: 'head',
each: 'forEach',
eachRight: 'forEachRight',
entries: 'toPairs',
entriesIn: 'toPairsIn',
extend: 'assignIn',
extendWith: 'assignInWith',
},
},
};
}
}
Expand Down
48 changes: 48 additions & 0 deletions packages/shopify-codemod/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,54 @@ This repository contains a collection of Codemods written with [JSCodeshift](htt

## Included Transforms

### `rename-identifier`

Renames a user-defined list of identifiers. Use the `renameIdentifiers` option to specify the old name/ new name pairs.

```sh
jscodeshift -t shopify-codemods/transforms/rename-identifier <file>
```

#### Example

```js
// with {renameIdentifiers: {jQuery: '$'}}
jQuery('.foo').find('.bar');
jQuery.ajax();
foo.jQuery('.bar');

// BECOMES:

$('.foo').find('.bar');
$.ajax();
foo.jQuery('.bar');
```

### `rename-property`

Renames a user-defined list of object/ property pairs to use new property names. Use the `renameProperties` option to specify the old property name/ new property name pairs.

```sh
jscodeshift -t shopify-codemods/transforms/rename-property <file>
```

#### Example

```js
// with {renameProperties: {_: {first: 'head'}}}
_.first([]);
_.first.bind(_);
foo._.first([]);
_.each([]);

// BECOMES:

_.head([]);
_.head.bind(_);
foo._.first([]);
_.each([]);
```

### `global-identifer-to-import`

Creates import statements for global identifiers. Use the `globalIdentifiers` option to specify identifier/ import path pairs.
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
foo();
foo.baz = 42;
const foo = 'bar';
something.foo();
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
bar();
bar.baz = 42;
const bar = 'bar';
something.foo();
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
_.something();
_.first();
_.head();
_.each.bind();
foo._.each();
foo.each();
_[each]();
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
_.something();
_.head();
_.head();
_.forEach.bind();
foo._.each();
foo.each();
_[each]();
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import 'test-helper';
import transform from 'rename-identifier';

describe('renameIdentifier', () => {
it('renames root identifiers', () => {
expect(transform).to.transform('rename-identifier/basic', {renameIdentifiers: {foo: 'bar'}});
});
});
15 changes: 15 additions & 0 deletions packages/shopify-codemod/test/transforms/rename-property.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import 'test-helper';
import transform from 'rename-property';

describe('renameProperty', () => {
it('transformed object-property pairs to new names', () => {
expect(transform).to.transform('rename-property/basic', {
renameProperties: {
_: {
first: 'head',
each: 'forEach',
},
},
});
});
});
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import {insertAfterDirectives} from './utils';
import {insertAfterDirectives, pathIsFirstMember} from './utils';

export default function globalIdentifierToImport({source}, {jscodeshift: j}, {printOptions = {}, globalIdentifiers = {}}) {
return j(source)
Expand All @@ -11,16 +11,11 @@ export default function globalIdentifierToImport({source}, {jscodeshift: j}, {pr
}

function isWindow(windowPath) {
return windowPath.get('name').value === 'window' && !isNestedInMemberExpression(windowPath);
}

function isNestedInMemberExpression(aPath) {
const parentNode = aPath.parentPath.node;
return j.MemberExpression.check(parentNode) && parentNode.property === aPath.node;
return windowPath.get('name').value === 'window' && pathIsFirstMember(windowPath);
}

function isGlobalIdentifier(identifierPath) {
return !isNestedInMemberExpression(identifierPath) || isWindow(identifierPath.parentPath.get('object'));
return pathIsFirstMember(identifierPath) || isWindow(identifierPath.parentPath.get('object'));
}

j(path)
Expand All @@ -30,7 +25,7 @@ export default function globalIdentifierToImport({source}, {jscodeshift: j}, {pr
imports.add(identifierPath.node.name);

// can only happen for window.globalIdentifier
if (isNestedInMemberExpression(identifierPath)) {
if (!pathIsFirstMember(identifierPath)) {
identifierPath.parentPath.replace(identifierPath.node);
}
});
Expand Down
12 changes: 12 additions & 0 deletions packages/shopify-codemod/transforms/rename-identifier.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import {pathIsFirstMember} from './utils';

export default function renameIdentifier({source}, {jscodeshift: j}, {printOptions = {quote: 'single'}, renameIdentifiers = {}}) {

if (Object.keys(renameIdentifiers).length === 0) { return null; }

return j(source)
.find(j.Identifier, {name: (name) => renameIdentifiers.hasOwnProperty(name)})
.filter(pathIsFirstMember)
.replaceWith(({node: {name}}) => j.identifier(renameIdentifiers[name]))
.toSource(printOptions);
}
18 changes: 18 additions & 0 deletions packages/shopify-codemod/transforms/rename-property.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import {pathIsFirstMember} from './utils';

export default function renameProperty({source}, {jscodeshift: j}, {printOptions = {quote: 'single'}, renameProperties = {}}) {
function hasPropertyThatShouldBeRenamed({node: {computed, object, property}}) {
return !computed && renameProperties[object.name].hasOwnProperty(property.name);
}

if (Object.keys(renameProperties).length === 0) { return null; }

return j(source)
.find(j.MemberExpression, {object: {name: (name) => renameProperties.hasOwnProperty(name)}})
.filter((path) => pathIsFirstMember(path) && hasPropertyThatShouldBeRenamed(path))
.forEach((path) => {
const {node: {object, property}} = path;
path.get('property').replace(j.identifier(renameProperties[object.name][property.name]));
})
.toSource(printOptions);
}
10 changes: 7 additions & 3 deletions packages/shopify-codemod/transforms/utils.js
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
import j from 'jscodeshift';

export function findFirstMember(node) {
if (node.type === 'MemberExpression') {
if (j.MemberExpression.check(node)) {
return findFirstMember(node.object);
}
return node;
}

export function findLastMember(node) {
if (node.type === 'MemberExpression') {
if (j.MemberExpression.check(node)) {
return findLastMember(node.property);
}
return node;
Expand All @@ -21,7 +21,7 @@ export function matchLast(matcher) {
export function insertAfterDirectives(body, newNode) {
let i = 0;
for (;i < body.length; i++) {
if (body[i].type !== 'ExpressionStatement' || body[i].expression.type !== 'Literal') {
if (!j.ExpressionStatement.check(body[i]) || !j.Literal.check(body[i].expression)) {
break;
}
}
Expand All @@ -40,6 +40,10 @@ export function isUndefined(node) {
});
}

export function pathIsFirstMember({node, parentPath: {node: parentNode}}) {
return !j.MemberExpression.check(parentNode) || parentNode.object === node;
}

// from https://github.com/sindresorhus/globals/blob/1e9ebc39828b92bd5c8ec7dc7bb07d62f2fb0153/globals.json#L852
export const MOCHA_FUNCTIONS = new Set([
'after',
Expand Down

0 comments on commit e2f960c

Please sign in to comment.