├── .eslintignore
├── .gitignore
├── .travis.yml
├── transforms
├── __tests__
│ ├── GregorianCalendar-to-moment-test.js
│ ├── Popover-overlay-to-content-test.js
│ ├── getFieldProps-to-getFieldDecorator-test.js
│ └── time-related-value-to-moment-test.js
├── __testfixtures__
│ ├── time-related-value-to-moment.with-moment.input.js
│ ├── time-related-value-to-moment.with-moment.output.js
│ ├── Popover-overlay-to-content.input.js
│ ├── Popover-overlay-to-content.output.js
│ ├── time-related-value-to-moment.input.js
│ ├── time-related-value-to-moment.output.js
│ ├── getFieldProps-to-getFieldDecorator.input.js
│ ├── getFieldProps-to-getFieldDecorator.output.js
│ ├── GregorianCalendar-to-moment.output.js
│ └── GregorianCalendar-to-moment.input.js
├── Popover-overlay-to-content.js
├── utils.js
├── getJscodeshiftExtension.js
├── getFieldProps-to-getFieldDecorator.js
├── time-related-value-to-moment.js
└── GregorianCalendar-to-moment.js
├── .eslintrc
├── package.json
├── LICENSE
└── README.md
/.eslintignore:
--------------------------------------------------------------------------------
1 | /transforms/__testfixtures__
2 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | /node_modules
2 | /npm-debug.log
3 |
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | language: node_js
2 | node_js:
3 | - "6"
4 | - "7"
5 |
--------------------------------------------------------------------------------
/transforms/__tests__/GregorianCalendar-to-moment-test.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | jest.autoMockOff(); // eslint-disable-line no-undef
4 |
5 | const defineTest = require('jscodeshift/dist/testUtils').defineTest;
6 | defineTest(__dirname, 'GregorianCalendar-to-moment');
7 |
--------------------------------------------------------------------------------
/transforms/__tests__/Popover-overlay-to-content-test.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | jest.autoMockOff(); // eslint-disable-line no-undef
4 |
5 | const defineTest = require('jscodeshift/dist/testUtils').defineTest;
6 | defineTest(__dirname, 'Popover-overlay-to-content');
7 |
--------------------------------------------------------------------------------
/transforms/__tests__/getFieldProps-to-getFieldDecorator-test.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | jest.autoMockOff(); // eslint-disable-line no-undef
4 |
5 | const defineTest = require('jscodeshift/dist/testUtils').defineTest;
6 | defineTest(__dirname, 'getFieldProps-to-getFieldDecorator');
7 |
--------------------------------------------------------------------------------
/.eslintrc:
--------------------------------------------------------------------------------
1 | {
2 | "extends": ["eslint-config-egg"],
3 | "parser": "babel-eslint",
4 | "parserOptions": {
5 | "sourceType": "module",
6 | "ecmaVersion": 6,
7 | "ecmaFeatures": {
8 | "jsx": true,
9 | "experimentalObjectRestSpread": true
10 | }
11 | },
12 | "rules": {
13 | "strict": 0
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/transforms/__testfixtures__/time-related-value-to-moment.with-moment.input.js:
--------------------------------------------------------------------------------
1 | /* eslint-disable quotes */
2 | import moment from 'moment'; // eslint-disable-line no-unused-vars
3 | function Test(props) { // eslint-disable-line no-unused-vars
4 | return (
5 |
8 | );
9 | }
10 |
--------------------------------------------------------------------------------
/transforms/__tests__/time-related-value-to-moment-test.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | jest.autoMockOff(); // eslint-disable-line no-undef
4 |
5 | const defineTest = require('jscodeshift/dist/testUtils').defineTest;
6 | defineTest(__dirname, 'time-related-value-to-moment');
7 | defineTest(__dirname, 'time-related-value-to-moment', null, 'time-related-value-to-moment.with-moment');
8 |
--------------------------------------------------------------------------------
/transforms/__testfixtures__/time-related-value-to-moment.with-moment.output.js:
--------------------------------------------------------------------------------
1 | /* eslint-disable quotes */
2 | import moment from 'moment'; // eslint-disable-line no-unused-vars
3 | function Test(props) { // eslint-disable-line no-unused-vars
4 | return (
5 |
8 | );
9 | }
10 |
--------------------------------------------------------------------------------
/transforms/__testfixtures__/Popover-overlay-to-content.input.js:
--------------------------------------------------------------------------------
1 | function Test() { // eslint-disable-line no-unused-vars
2 | return (
3 |
6 | Content
7 |
8 | }
9 | title="Title"
10 | >
11 | Trigger
12 |
13 | );
14 | }
15 |
--------------------------------------------------------------------------------
/transforms/__testfixtures__/Popover-overlay-to-content.output.js:
--------------------------------------------------------------------------------
1 | function Test() { // eslint-disable-line no-unused-vars
2 | return (
3 |
6 | Content
7 |
8 | }
9 | title="Title"
10 | >
11 | Trigger
12 |
13 | );
14 | }
15 |
--------------------------------------------------------------------------------
/transforms/Popover-overlay-to-content.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | const getExtension = require('./getJscodeshiftExtension');
4 |
5 | module.exports = function(file, api) {
6 | const j = api.jscodeshift;
7 | j.registerMethods(getExtension(j));
8 | const ast = j(file.source);
9 |
10 | ast.find(j.JSXOpeningElement, {
11 | name: {
12 | type: 'JSXIdentifier',
13 | name: 'Popover',
14 | },
15 | }).map(nodePath => nodePath.get('attributes'))
16 | .children(j.JSXAttribute, {
17 | name: {
18 | type: 'JSXIdentifier',
19 | name: 'overlay',
20 | },
21 | })
22 | .map(nodePath => nodePath.get('name'))
23 | .replaceWith(j.jsxIdentifier('content'));
24 |
25 | return ast.toSource();
26 | };
27 |
--------------------------------------------------------------------------------
/transforms/__testfixtures__/time-related-value-to-moment.input.js:
--------------------------------------------------------------------------------
1 | /* eslint-disable quotes */
2 | function Test(props) { // eslint-disable-line no-unused-vars
3 | const time = '00:00:00';
4 | const dateFormat = 'yyyy-MM-dd HH:mm:ss';
5 | return (
6 |
23 | );
24 | }
25 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "antd-codemod",
3 | "description": "antd codemod scripts.",
4 | "scripts": {
5 | "lint": "eslint ./transforms",
6 | "eslint-fix": "eslint --fix ./transforms",
7 | "test": "jest"
8 | },
9 | "jest": {
10 | "testEnvironment": "node"
11 | },
12 | "repository": {
13 | "type": "git",
14 | "url": "git+https://github.com/ant-design/antd-codemod.git"
15 | },
16 | "keywords": [
17 | "antd",
18 | "codemod",
19 | "scripts",
20 | "update",
21 | "jscodeshift"
22 | ],
23 | "author": "Benjy Cui",
24 | "license": "MIT",
25 | "bugs": {
26 | "url": "https://github.com/ant-design/antd-codemod/issues"
27 | },
28 | "homepage": "https://github.com/ant-design/antd-codemod#readme",
29 | "devDependencies": {
30 | "babel-eslint": "^7.1.1",
31 | "eslint": "^3.10.2",
32 | "eslint-config-egg": "^3.2.0",
33 | "jest": "^17.0.3",
34 | "jscodeshift": "^0.3.30"
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/transforms/__testfixtures__/time-related-value-to-moment.output.js:
--------------------------------------------------------------------------------
1 | import moment from 'moment';
2 | /* eslint-disable quotes */
3 | function Test(props) { // eslint-disable-line no-unused-vars
4 | const time = '00:00:00';
5 | const dateFormat = "YYYY-MM-DD HH:mm:ss";
6 | return (
7 |
24 | );
25 | }
26 |
--------------------------------------------------------------------------------
/transforms/utils.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | exports.isTimeRelatedComponent = function isTimeRelatedComponent(componentName) {
4 | return [
5 | 'DatePicker', 'TimePicker',
6 | 'Calendar', 'MonthPicker',
7 | // 'RangePicker' ignore RangePicker now
8 | ].indexOf(componentName) > -1;
9 | };
10 |
11 | exports.getNameFieldValue = function getNameFieldValue(nodePath) {
12 | return nodePath.get('name').get('name').value;
13 | };
14 |
15 | const hasOwn =
16 | Object.prototype.hasOwnProperty.call.bind(Object.prototype.hasOwnProperty);
17 |
18 | function isNode(value) {
19 | return typeof value === 'object' && value;
20 | }
21 |
22 | exports.matchNode = function matchNode(haystack, needle) {
23 | if (typeof needle === 'function') {
24 | return needle(haystack);
25 | }
26 | if (isNode(needle) && isNode(haystack)) {
27 | return Object.keys(needle).every(function(property) {
28 | return (
29 | hasOwn(haystack, property) &&
30 | matchNode(haystack[property], needle[property])
31 | );
32 | });
33 | }
34 | return haystack === needle;
35 | };
36 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2016
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/transforms/__testfixtures__/getFieldProps-to-getFieldDecorator.input.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { Form } from 'antd';
3 |
4 | const NormalForm = Form.create()(React.createClass({ // eslint-disable-line no-unused-vars
5 | render() {
6 | const { getFieldProps } = this.props.form;
7 | const nameField = (
8 |
11 | );
12 | return (
13 |
17 | );
18 | },
19 | }));
20 |
21 | function StatelessForm(props) { // eslint-disable-line no-unused-vars
22 | const getFieldProps = props.form.getFieldProps;
23 |
24 | const nameProps = getFieldProps('name', {
25 | rules: [{ required: true, message: 'Please input your name!' }],
26 | });
27 | const nameField = ;
28 |
29 | const passwordProps = getFieldProps('password');
30 | return (
31 |
35 | );
36 | }
37 |
--------------------------------------------------------------------------------
/transforms/__testfixtures__/getFieldProps-to-getFieldDecorator.output.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { Form } from 'antd';
3 |
4 | const NormalForm = Form.create()(React.createClass({ // eslint-disable-line no-unused-vars
5 | render() {
6 | const { getFieldDecorator } = this.props.form;
7 | const nameField = (
8 | getFieldDecorator('name', {
9 | rules: [{ required: true, message: 'Please input your name!' }],
10 | })()
11 | );
12 | return (
13 |
17 | );
18 | },
19 | }));
20 |
21 | function StatelessForm(props) { // eslint-disable-line no-unused-vars
22 | const getFieldDecorator = props.form.getFieldDecorator;
23 |
24 | const nameDecorator = getFieldDecorator('name', {
25 | rules: [{ required: true, message: 'Please input your name!' }],
26 | });
27 | const nameField = nameDecorator();
28 |
29 | const passwordDecorator = getFieldDecorator('password');
30 | return (
31 |
35 | );
36 | }
37 |
--------------------------------------------------------------------------------
/transforms/__testfixtures__/GregorianCalendar-to-moment.output.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 |
3 | const handleChange = function handleChange(date) {
4 | console.log(date.valueOf());
5 | };
6 |
7 | const NormalForm = React.createClass({ // eslint-disable-line no-unused-vars
8 | disabledDate(date) {
9 | console.log(date.valueOf());
10 | },
11 | disabledTime: date => {
12 | console.log(date.valueOf());
13 | },
14 | render() {
15 | return (
16 |
23 | );
24 | },
25 | });
26 |
27 | function handlePanelChange(date) {
28 | console.log(date.valueOf());
29 | }
30 |
31 | class ClassForm extends React.Component { // eslint-disable-line no-unused-vars
32 | dateCellRender(date) {
33 | return {date.date()}
;
34 | }
35 |
36 | monthCellRender = date => {
37 | return {date.month()}
;
38 | }
39 |
40 | render() {
41 | return (
42 |
49 | );
50 | }
51 | }
52 |
--------------------------------------------------------------------------------
/transforms/__testfixtures__/GregorianCalendar-to-moment.input.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 |
3 | const handleChange = function handleChange(date) {
4 | console.log(date.getTime());
5 | };
6 |
7 | const NormalForm = React.createClass({ // eslint-disable-line no-unused-vars
8 | disabledDate(date) {
9 | console.log(date.getTime());
10 | },
11 | disabledTime: date => {
12 | console.log(date.getTime());
13 | },
14 | render() {
15 | return (
16 |
23 | );
24 | },
25 | });
26 |
27 | function handlePanelChange(date) {
28 | console.log(date.getTime());
29 | }
30 |
31 | class ClassForm extends React.Component { // eslint-disable-line no-unused-vars
32 | dateCellRender(date) {
33 | return {date.getDayOfMonth()}
;
34 | }
35 |
36 | monthCellRender = date => {
37 | return {date.getMonth()}
;
38 | }
39 |
40 | render() {
41 | return (
42 |
49 | );
50 | }
51 | }
52 |
--------------------------------------------------------------------------------
/transforms/getJscodeshiftExtension.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | let extended = false;
4 |
5 | module.exports = function getExtension(j) {
6 | if (extended) {
7 | return {};
8 | }
9 | extended = true;
10 | return {
11 | // works like `find`, but only get direct nodePaths
12 | children(type, filter) {
13 | return this.map(parentPath => {
14 | const collection = j(parentPath).find(type, filter)
15 | .filter(childPath => childPath.parentPath === parentPath);
16 | return collection.paths();
17 | });
18 | },
19 | siblings(type, filter) {
20 | return this.map(selfPath => {
21 | const collection = j(selfPath.parentPath).children(type, filter);
22 | return collection.paths();
23 | });
24 | },
25 | getFunctionDeclaration(nameGetter) {
26 | /* eslint-disable array-callback-return */
27 | return this.map(varPath => {
28 | const currentScope = varPath.scope;
29 | if (!currentScope) {
30 | return;
31 | }
32 | const functionName = nameGetter(varPath);
33 | if (!functionName) {
34 | return;
35 | }
36 | const targetScope = currentScope.lookup(functionName);
37 | if (!targetScope) {
38 | return;
39 | }
40 | const bindings = targetScope.getBindings()[functionName];
41 | if (!bindings) {
42 | return;
43 | }
44 | const decl = j(bindings).closest(j.FunctionDeclaration);
45 | if (decl.length === 1) {
46 | return decl.paths()[0];
47 | }
48 | });
49 | /* eslint-enable array-callback-return */
50 | },
51 | };
52 | };
53 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # antd-codemod
2 |
3 | [](https://travis-ci.org/ant-design/antd-codemod)
4 | [](https://david-dm.org/ant-design/antd-codemod)
5 |
6 | This repository contains a collection of codemod scripts based for use with [JSCodeshift](https://github.com/facebook/jscodeshift) that help update `antd` APIs.
7 |
8 | ## Setup & Run
9 |
10 | * `npm install -g jscodeshift`
11 | * `git clone https://github.com/ant-design/antd-codemod.git` or download a zip file from `https://github.com/ant-design/antd-codemod/archive/master.zip`
12 | * Run `npm install` in the antd-codemod directory
13 | * `jscodeshift -t `
14 | * Use the `-d` option for a dry-run and use `-p` to print the output for comparison
15 |
16 | ## Included Scripts
17 |
18 | ### 1.x-2.x
19 |
20 | #### `getFieldProps-to-getFieldDecorator`
21 |
22 | Replace deprecated `getFieldProps` with newer `getFieldDecorator`:
23 |
24 | ```diff
25 | -
26 | + {getFieldDecorator('userName', { ... })(
27 | +
28 | + )}
29 | ```
30 |
31 | #### `Popover-overlay-to-content`
32 |
33 | `Popover[overlay]` is removed, so we need to replace it with `Popover[content]`:
34 |
35 | ```diff
36 | -
37 | +
38 | ```
39 |
40 | #### `time-related-value-to-moment`
41 |
42 | Update `value` `defaultValue` and `format` of `DatePicker` `TimePicker` `Calendar` `MonthPicker`(not support `RangePicker` now):
43 |
44 | ```diff
45 | + import moment from 'moment';
46 |
47 |
54 | ```
55 |
56 | #### `GergorianCalendar-to-moment`
57 |
58 | Update GregorianCalendar's APIs to moment's APIs.
59 |
60 | ```diff
61 | function disabledDate(date) {
62 | - console.log(date.getTime());
63 | + console.log(date.valueOf());
64 | }
65 | ```
66 |
67 | ## License
68 |
69 | MIT
70 |
--------------------------------------------------------------------------------
/transforms/getFieldProps-to-getFieldDecorator.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | const getFieldPropsIdentifier = {
4 | type: 'Identifier',
5 | name: 'getFieldProps',
6 | };
7 | const getFieldPropsCallExpression = {
8 | callee: getFieldPropsIdentifier,
9 | };
10 |
11 | function propsToDecorator(name) {
12 | return name.replace('Props', 'Decorator');
13 | }
14 |
15 | function processGetFieldPropsInJSX(j, getFieldPropsNodes) {
16 | // wrap form controls with `getFieldDecorator(...)`
17 | const getFieldPropsInJSXNodes = getFieldPropsNodes
18 | .filter(nodePath => j.JSXSpreadAttribute.check(nodePath.parentPath.value));
19 | getFieldPropsInJSXNodes.closest(j.JSXElement)
20 | .replaceWith(nodePath => {
21 | const getFieldPropsNode = j(nodePath)
22 | .find(j.CallExpression, getFieldPropsCallExpression).nodes()[0];
23 | const callExpression = j.callExpression(
24 | j.callExpression(getFieldPropsNode.callee, getFieldPropsNode.arguments),
25 | [ nodePath.value ]
26 | );
27 | if (j.JSXElement.check(nodePath.parentPath.parentPath.value)) {
28 | return j.jsxExpressionContainer(callExpression);
29 | }
30 | return callExpression;
31 | });
32 | // remove `getFieldProps` in JSX
33 | getFieldPropsInJSXNodes.closest(j.JSXSpreadAttribute).remove();
34 | }
35 |
36 | function processGetFieldPropsWithTmp(j, getFieldPropsNodes) {
37 | const tmpPropsIdentifierNodes = getFieldPropsNodes
38 | .filter(nodePath => j.VariableDeclarator.check(nodePath.parentPath.value))
39 | .closest(j.VariableDeclarator)
40 | .map(nodePath => nodePath.get('id'));
41 | // replace `` with `nameDecorator()`
42 | const spreadPropsInJSXNodes = tmpPropsIdentifierNodes.map(nodePath => {
43 | return j(nodePath).closestScope()
44 | .find(j.JSXSpreadAttribute, {
45 | argument: {
46 | type: 'Identifier',
47 | name: nodePath.value.name,
48 | },
49 | })
50 | .get();
51 | });
52 | spreadPropsInJSXNodes.forEach(spreadProps => {
53 | const relativeJSXElement = j(spreadProps).closest(j.JSXElement);
54 | relativeJSXElement.replaceWith(nodePath => {
55 | const callExpression = j.callExpression(
56 | j.identifier(propsToDecorator(spreadProps.value.argument.name)),
57 | relativeJSXElement.nodes()
58 | );
59 | if (j.JSXElement.check(nodePath.parentPath.parentPath.value)) {
60 | return j.jsxExpressionContainer(callExpression);
61 | }
62 | return callExpression;
63 | });
64 | });
65 | spreadPropsInJSXNodes.remove();
66 | // replace `const nameProps = getFieldProps(...)` with `const nameDecorator = getFieldDecorator`
67 | tmpPropsIdentifierNodes.replaceWith(nodePath => j.identifier(propsToDecorator(nodePath.value.name)));
68 | }
69 |
70 | module.exports = function(file, api) {
71 | const j = api.jscodeshift;
72 | const ast = j(file.source);
73 |
74 | const getFieldPropsNodes = ast.find(j.CallExpression, getFieldPropsCallExpression);
75 | processGetFieldPropsInJSX(j, getFieldPropsNodes);
76 | processGetFieldPropsWithTmp(j, getFieldPropsNodes);
77 |
78 | // rename `getFieldProps` to `getFieldDecorator`
79 | ast.find(j.Identifier, getFieldPropsIdentifier)
80 | .replaceWith(j.identifier('getFieldDecorator'));
81 |
82 | return ast.toSource();
83 | };
84 |
--------------------------------------------------------------------------------
/transforms/time-related-value-to-moment.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | const getExtension = require('./getJscodeshiftExtension');
4 | const utils = require('./utils');
5 |
6 | function oldFormatToNewFormat(format) {
7 | return format.split('').map(c => {
8 | if (c === 'y') {
9 | return 'Y';
10 | } else if (c === 'd') {
11 | return 'D';
12 | }
13 | return c;
14 | }).join('');
15 | }
16 |
17 | function getDefaultFormat(componentName) {
18 | if (componentName === 'MonthPicker') {
19 | return 'YYYY-MM';
20 | } else if (componentName === 'TimePicker') {
21 | return 'HH:mm:ss';
22 | }
23 | return 'YYYY-MM-DD';
24 | }
25 |
26 | function getComponentNameFromAttr(j, attr) {
27 | return j(attr).closest(j.JSXOpeningElement).nodes()[0].name.name;
28 | }
29 |
30 | module.exports = function(file, api) {
31 | const j = api.jscodeshift;
32 | j.registerMethods(getExtension(j));
33 | const ast = j(file.source);
34 | const template = j.template;
35 |
36 | const timeRelatedOpeningElementsAttrs = ast.find(j.JSXOpeningElement)
37 | .filter(nodePath => utils.isTimeRelatedComponent(utils.getNameFieldValue(nodePath)))
38 | .map(nodePath => nodePath.get('attributes'));
39 |
40 | // update `format` to moment format
41 | timeRelatedOpeningElementsAttrs
42 | .children(j.JSXAttribute, {
43 | name: {
44 | type: 'JSXIdentifier',
45 | name: 'format',
46 | },
47 | })
48 | .forEach(formatAttr => {
49 | const $formatAttr = j(formatAttr);
50 | const $formatLiteral = $formatAttr.find(j.Literal);
51 | if ($formatLiteral.size() > 0) {
52 | $formatLiteral.replaceWith(nodePath => j.literal(oldFormatToNewFormat(nodePath.value.value)));
53 | return;
54 | }
55 | $formatAttr.getVariableDeclarators(nodePath => {
56 | const formatVariable = nodePath.value.value.expression;
57 | return formatVariable.name;
58 | }).forEach(declarator => {
59 | j(declarator.get('init')).replaceWith(nodePath => j.literal(oldFormatToNewFormat(nodePath.value.value)));
60 | });
61 | });
62 |
63 | let hadUsedMoment = false;
64 | // update `value` || `defaultValue` to moment object
65 | timeRelatedOpeningElementsAttrs
66 | .children(j.JSXAttribute, nodePath => {
67 | const attrName = nodePath.name.name;
68 | return attrName === 'value' || attrName === 'defaultValue';
69 | })
70 | .forEach(attrToModify => {
71 | const $attrToModify = j(attrToModify);
72 | const formatAttr = $attrToModify.siblings(j.JSXAttribute, {
73 | name: {
74 | type: 'JSXIdentifier',
75 | name: 'format',
76 | },
77 | });
78 | const formatAttrPath = formatAttr.nodes()[0];
79 | const formatValue = formatAttr.find(j.Literal).nodes()[0] ||
80 | formatAttrPath && formatAttrPath.value.expression ||
81 | j.identifier(JSON.stringify(getDefaultFormat(getComponentNameFromAttr(j, attrToModify))));
82 |
83 | j(attrToModify.get('value'))
84 | .forEach(nodePath => {
85 | const $valueToBeReplaced = j.JSXExpressionContainer.check(nodePath.value) &&
86 | j.ArrayExpression.check(nodePath.value.expression) ?
87 | j(nodePath.get('expression').get('elements')) : j(nodePath);
88 |
89 | $valueToBeReplaced.replaceWith(({ value }) => {
90 | function wrapInMoment(extractedValue) {
91 | hadUsedMoment = true;
92 | const attrValue = j.Literal.check(extractedValue) ? extractedValue : extractedValue.expression;
93 | return j.callExpression(j.identifier('moment'), [ attrValue, formatValue ]);
94 | }
95 | return j.jsxExpressionContainer(wrapInMoment(value));
96 | });
97 | });
98 | });
99 |
100 | if (hadUsedMoment) {
101 | // make sure that `import moment from 'moment'` exist
102 | const importMomentStatement = ast.find(j.ImportDeclaration, {
103 | source: {
104 | type: 'Literal',
105 | value: 'moment',
106 | },
107 | });
108 | if (importMomentStatement.size() === 0) {
109 | const statement = template.statement`import moment from 'moment';\n`;
110 | ast.find(j.Program).get('body').get(0)
111 | .insertBefore(statement);
112 | }
113 | }
114 |
115 | return ast.toSource();
116 | };
117 |
--------------------------------------------------------------------------------
/transforms/GregorianCalendar-to-moment.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | const getExtension = require('./getJscodeshiftExtension');
4 | const utils = require('./utils');
5 |
6 | function isCallbackWithDateObject(attributeName) {
7 | return [
8 | 'disabledDate', 'disabledTime', 'onChange',
9 | 'dateCellRender', 'monthCellRender', 'onPanelChange',
10 | ].indexOf(attributeName) > -1;
11 | }
12 |
13 | const apisMap = {
14 | getTime: 'valueOf',
15 | getDayOfMonth: 'date',
16 | getMonth: 'month',
17 | };
18 | function getMomentAPI(gregorianCalendarAPI) {
19 | return apisMap[gregorianCalendarAPI] || gregorianCalendarAPI;
20 | }
21 |
22 | function getMethodName(expressionPath) {
23 | // this.xxx
24 | const name = expressionPath.get('property').get('name').value;
25 | if (!name) {
26 | // this.xxx.bind(this)
27 | return expressionPath
28 | .get('callee')
29 | .get('object')
30 | .get('property')
31 | .get('name').value;
32 | }
33 | return name;
34 | }
35 |
36 | module.exports = function(file, api) {
37 | const j = api.jscodeshift;
38 | j.registerMethods(getExtension(j));
39 | const ast = j(file.source);
40 |
41 | function replaceGregorianCalendarAPIToMoment(functionExpressionPath) {
42 | const firstArgument = functionExpressionPath.get('params').value[0];
43 | if (!firstArgument) {
44 | return;
45 | }
46 | const firstArgumentName = firstArgument.name;
47 | j(functionExpressionPath.get('body'))
48 | .find(j.CallExpression, {
49 | callee: {
50 | type: 'MemberExpression',
51 | object: {
52 | name: firstArgumentName,
53 | },
54 | },
55 | })
56 | .map(nodePath => nodePath.get('callee').get('property'))
57 | .replaceWith(nodePath => j.identifier(getMomentAPI(nodePath.get('name').value)));
58 | }
59 |
60 | const callbackExpression = ast.find(j.JSXOpeningElement)
61 | .filter(nodePath => utils.isTimeRelatedComponent(utils.getNameFieldValue(nodePath)))
62 | .map(nodePath => nodePath.get('attributes'))
63 | .children(j.JSXAttribute)
64 | .filter(nodePath => isCallbackWithDateObject(utils.getNameFieldValue(nodePath)))
65 | .map(nodePath => nodePath.get('value').get('expression'));
66 |
67 | callbackExpression.filter(nodePath => j.Identifier.check(nodePath.value))
68 | .getVariableDeclarators(nodePath => nodePath.get('name').value)
69 | .map(nodePath => nodePath.get('init'))
70 | .forEach(replaceGregorianCalendarAPIToMoment);
71 | callbackExpression.filter(nodePath => j.Identifier.check(nodePath.value))
72 | .getFunctionDeclaration(nodePath => nodePath.get('name').value)
73 | .forEach(replaceGregorianCalendarAPIToMoment);
74 |
75 | function isFromThis(nodePath) {
76 | // this.xxx
77 | return j.MemberExpression.check(nodePath.value) &&
78 | j.ThisExpression.check(nodePath.get('object').value) ||
79 | // this.xxx.bind(this)
80 | j.CallExpression.check(nodePath.value) &&
81 | utils.matchNode(nodePath.value, {
82 | callee: {
83 | type: 'MemberExpression',
84 | object: {
85 | type: 'MemberExpression',
86 | object: { type: 'ThisExpression' },
87 | },
88 | property: { name: 'bind' },
89 | },
90 | arguments: [{ type: 'ThisExpression' }],
91 | }) && nodePath.value.arguments.length === 1;
92 | }
93 | const callbackFromThis = callbackExpression.filter(isFromThis);
94 | callbackFromThis
95 | .map(expressionPath => {
96 | return j(expressionPath)
97 | .closest(j.CallExpression, {
98 | callee: {
99 | type: 'MemberExpression',
100 | object: {
101 | name: 'React',
102 | },
103 | property: {
104 | name: 'createClass',
105 | },
106 | },
107 | })
108 | .map(nodePath => nodePath.get('arguments'))
109 | .children(j.ObjectExpression)
110 | .map(nodePath => nodePath.get('properties'))
111 | .children(j.Property)
112 | .filter(propertyPath => {
113 | return propertyPath.get('key').get('name').value ===
114 | getMethodName(expressionPath);
115 | })
116 | .map(nodePath => nodePath.get('value'))
117 | .paths();
118 | })
119 | .forEach(replaceGregorianCalendarAPIToMoment);
120 | callbackFromThis
121 | .map(expressionPath => {
122 | const classDefinition = j(expressionPath)
123 | .closest(j.ClassDeclaration)
124 | .map(nodePath => nodePath.get('body').get('body'));
125 | const methodsDefinition = classDefinition.children(j.MethodDefinition).paths()
126 | .concat(classDefinition.children(j.ClassProperty).filter(nodePath => {
127 | const node = nodePath.get('value').value;
128 | return j.ArrowFunctionExpression.check(node) ||
129 | j.FunctionExpression.check(node);
130 | }).paths());
131 | return methodsDefinition.filter(propertyPath => {
132 | return propertyPath.get('key').get('name').value ===
133 | getMethodName(expressionPath);
134 | }).map(nodePath => nodePath.get('value'));
135 | })
136 | .forEach(replaceGregorianCalendarAPIToMoment);
137 |
138 | return ast.toSource();
139 | };
140 |
--------------------------------------------------------------------------------