Skip to content

Commit

Permalink
feat(cli): add component codemods (#608)
Browse files Browse the repository at this point in the history
* feat(cli): add codemod to rename components

* feat(cli): add codemod to replace static properties
  • Loading branch information
connor-baer authored Jun 8, 2020
1 parent f14269d commit d3d77f8
Show file tree
Hide file tree
Showing 8 changed files with 301 additions and 5 deletions.
20 changes: 20 additions & 0 deletions src/cli/migrate/__testfixtures__/component-names-v2.input.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import React from 'react';
import styled from '@emotion/styled';
import { ListView, SvgButton as SpecialButton } from '@sumup/circuit-ui';

const BaseListView = () => <ListView />;
const BaseListViewItem = () => <ListView.Item />;

const RedListView = styled(ListView)`
color: red;
`;

const RedListViewItem = styled(ListView.Item)`
color: red;
`;

const BaseSpecialButton = () => <SpecialButton />;

const RedSpecialButton = styled(SpecialButton)`
color: red;
`;
20 changes: 20 additions & 0 deletions src/cli/migrate/__testfixtures__/component-names-v2.output.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import React from 'react';
import styled from '@emotion/styled';
import { CardList, IconButton as SpecialButton } from '@sumup/circuit-ui';

const BaseListView = () => <CardList />;
const BaseListViewItem = () => <CardList.Item />;

const RedListView = styled(CardList)`
color: red;
`;

const RedListViewItem = styled(CardList.Item)`
color: red;
`;

const BaseSpecialButton = () => <SpecialButton />;

const RedSpecialButton = styled(SpecialButton)`
color: red;
`;
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import React from 'react';
import styled from '@emotion/styled';
import { Heading, CardList } from '@sumup/circuit-ui';

const BaseHeading = () => <Heading size={Heading.KILO} />;

const headingSize = Heading.KILO;

const BaseCardListItem = () => <CardList.Item size={CardList.Item.KILO} />;

const cardListHeadingSize = CardList.Item.KILO;
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import React from 'react';
import styled from '@emotion/styled';
import { Heading, CardList } from '@sumup/circuit-ui';

const BaseHeading = () => <Heading size={'kilo'} />;

const headingSize = 'kilo';

const BaseCardListItem = () => <CardList.Item size={'kilo'} />;

const cardListHeadingSize = 'kilo';
2 changes: 2 additions & 0 deletions src/cli/migrate/__tests__/transforms.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,3 +24,5 @@ defineTest(__dirname, 'as-prop');
defineTest(__dirname, 'selector-props');
defineTest(__dirname, 'exit-animations');
defineTest(__dirname, 'input-deepref-prop');
defineTest(__dirname, 'component-names-v2');
defineTest(__dirname, 'component-static-properties');
57 changes: 57 additions & 0 deletions src/cli/migrate/component-names-v2.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
/**
* Copyright 2020, SumUp Ltd.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

import { Transform, JSCodeshift, Collection } from 'jscodeshift';

import { findImportsByPath } from './utils';

function transformFactory(
j: JSCodeshift,
root: Collection,
componentNames: string[]
): void {
const [oldComponentName, newComponentName] = componentNames;
const imports = findImportsByPath(j, root, '@sumup/circuit-ui');

const componentImport = imports.find(i => i.name === oldComponentName);

if (!componentImport) {
return;
}

root
.find(j.Identifier)
.filter(nodePath => nodePath.node.name === oldComponentName)
.replaceWith(j.identifier(newComponentName));
}

const transform: Transform = (file, api) => {
const j = api.jscodeshift;
const root = j(file.source);

[
['ListView', 'CardList'],
['SvgButton', 'IconButton'],
['Message', 'Notification'],
['InlineNotification', 'InlineMessage'],
['GlobalStyles', 'BaseStyles']
].forEach(componentNames => {
transformFactory(j, root, componentNames);
});

return root.toSource();
};

export default transform;
152 changes: 152 additions & 0 deletions src/cli/migrate/component-static-properties.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,152 @@
/**
* Copyright 2020, SumUp Ltd.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

import { Transform, JSCodeshift, Collection } from 'jscodeshift';
import { toLower } from 'lodash/fp';

import { findLocalNames, findProperty } from './utils';

const components = [
{
componentName: 'Badge',
properties: ['NEUTRAL', 'PRIMARY', 'SUCCESS', 'WARNING', 'DANGER']
},
{
componentName: 'Blockquote',
properties: ['KILO', 'MEGA', 'GIGA']
},
{
componentName: 'Button',
properties: ['KILO', 'MEGA', 'GIGA']
},
{
componentName: 'ButtonGroup',
properties: ['LEFT', 'CENTER', 'RIGHT']
},
{
componentName: 'Card',
properties: ['SINGLE', 'DOUBLE', 'TRIPLE', 'MEGA', 'GIGA']
},
{
componentName: 'CardList.Item',
properties: ['KILO', 'MEGA', 'GIGA']
},
{
componentName: 'ListView.Item',
properties: ['KILO', 'MEGA', 'GIGA']
},
{
componentName: 'Heading',
properties: ['KILO', 'MEGA', 'GIGA', 'TERA', 'PETA', 'EXA', 'ZETTA']
},
{
componentName: 'InlineNotification',
properties: ['DANGER', 'SUCCESS', 'WARNING', 'MEGA', 'GIGA']
},
{
componentName: 'InlineMessage',
properties: ['DANGER', 'SUCCESS', 'WARNING', 'MEGA', 'GIGA']
},
{
componentName: 'Input',
properties: ['LEFT', 'RIGHT']
},
{
componentName: 'TextArea',
properties: ['LEFT', 'RIGHT']
},
{
componentName: 'List',
properties: ['KILO', 'MEGA', 'GIGA']
},
{
componentName: 'MessageIcon',
properties: ['SUCCESS', 'ERROR', 'WARNING']
},
{
componentName: 'ModalFooter',
properties: ['LEFT', 'RIGHT']
},
{
componentName: 'CardFooter',
properties: ['LEFT', 'RIGHT']
},
{
componentName: 'NotificationIcon',
properties: ['SUCCESS', 'ERROR', 'WARNING']
},
{
componentName: 'Popover',
properties: ['TOP', 'BOTTOM', 'LEFT', 'RIGHT', 'START', 'END', 'CENTER']
},
{
componentName: 'ProgressBar',
properties: ['KILO', 'MEGA', 'GIGA']
},
{
componentName: 'SubHeading',
properties: ['KILO', 'MEGA']
},
{
componentName: 'TableHeader',
properties: ['LEFT', 'RIGHT', 'CENTER', 'COL', 'ROW']
},
{
componentName: 'TableCell',
properties: ['LEFT', 'RIGHT', 'CENTER']
},
{
componentName: 'Text',
properties: ['KILO', 'MEGA', 'GIGA']
},
{
componentName: 'Tooltip',
properties: ['CENTER', 'TOP', 'RIGHT', 'BOTTOM', 'LEFT']
}
];

function transformFactory(
j: JSCodeshift,
root: Collection,
componentName: string,
properties: string[]
): void {
const localNames = findLocalNames(j, root, componentName);

if (!localNames) {
return;
}

localNames.forEach(localName => {
properties.forEach(property => {
findProperty(j, root, `${localName}.${property}`).replaceWith(
j.stringLiteral(toLower(property))
);
});
});
}

const transform: Transform = (file, api) => {
const j = api.jscodeshift;
const root = j(file.source);

components.forEach(({ componentName, properties }) => {
transformFactory(j, root, componentName, properties);
});

return root.toSource({ quote: 'single' });
};

export default transform;
33 changes: 28 additions & 5 deletions src/cli/migrate/utils/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -100,17 +100,21 @@ export function findLocalNames(
): string[] | null {
const imports = findImportsByPath(j, root, '@sumup/circuit-ui');

const buttonImport = imports.find(i => i.name === componentName);
const [baseName, subName] = componentName.split('.');

if (!buttonImport) {
const componentImport = imports.find(i => i.name === baseName);

if (!componentImport) {
return null;
}

const localName = buttonImport.local;
const localName = subName
? `${componentImport.local}.${subName}`
: componentImport.local;

const styledButtons = findStyledComponentNames(j, root, localName);
const styledComponents = findStyledComponentNames(j, root, localName);

return [localName, ...styledButtons];
return [localName, ...styledComponents];
}

export function renameJSXAttribute(
Expand All @@ -132,3 +136,22 @@ export function renameJSXAttribute(
j.jsxAttribute(j.jsxIdentifier(toName), nodePath.node.value)
);
}

export function findProperty(
j: JSCodeshift,
root: Collection,
path: string
): Collection {
const [parent, ...properties] = path.split('.');
const query = properties.reduce(
(acc, property) => ({
type: 'MemberExpression',
object: acc,
property: {
name: property
}
}),
{ name: parent } as any
);
return root.find(j.MemberExpression, query);
}

0 comments on commit d3d77f8

Please sign in to comment.