├── packages
├── react-refactor-gui
│ ├── .env
│ ├── public
│ │ ├── favicon.ico
│ │ ├── manifest.json
│ │ └── index.html
│ ├── src
│ │ ├── index.css
│ │ ├── index.js
│ │ ├── defaultSource.js
│ │ ├── Header.jsx
│ │ ├── GithubRibbon.css
│ │ ├── App.js
│ │ ├── Footer.jsx
│ │ ├── CodeEditor.jsx
│ │ ├── Introduction.jsx
│ │ ├── GithubRibbon.jsx
│ │ └── Content.jsx
│ └── package.json
├── react-refactor
│ ├── test
│ │ ├── testUtils.js
│ │ ├── __fixtures__
│ │ │ ├── FuncComp.jsx.txt
│ │ │ ├── ClassComp.jsx.txt
│ │ │ ├── refactoredExample.jsx.txt
│ │ │ └── example.jsx.txt
│ │ ├── parser.spec.js
│ │ ├── index.spec.js
│ │ ├── treeUtils.spec.js
│ │ ├── __snapshots__
│ │ │ ├── functionalToClass.spec.js.snap
│ │ │ ├── index.spec.js.snap
│ │ │ ├── classToFunctional.spec.js.snap
│ │ │ └── parser.spec.js.snap
│ │ ├── classToFunctional.spec.js
│ │ ├── stringUtils.spec.js
│ │ └── functionalToClass.spec.js
│ ├── .babelrc
│ ├── src
│ │ ├── errors.js
│ │ ├── treeUtils.js
│ │ ├── parser.js
│ │ ├── stringUtils.js
│ │ ├── functionalToClass.js
│ │ ├── index.js
│ │ └── classToFunctional.js
│ └── package.json
└── react-refactor-cli
│ ├── .babelrc
│ ├── test
│ ├── __fixture__
│ │ └── Func.jsx
│ └── command.spec.js
│ ├── bin
│ └── react-refactor.js
│ ├── src
│ ├── argv.js
│ └── command.js
│ └── package.json
├── lerna.json
├── .gitignore
├── .travis.yml
├── .editorconfig
├── package.json
├── LICENSE
└── README.md
/packages/react-refactor-gui/.env:
--------------------------------------------------------------------------------
1 | PUBLIC_URL=./
2 |
--------------------------------------------------------------------------------
/lerna.json:
--------------------------------------------------------------------------------
1 | {
2 | "lerna": "2.5.1",
3 | "packages": [
4 | "packages/*"
5 | ],
6 | "version": "1.0.0"
7 | }
8 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules
2 | .idea
3 | coverage
4 | *.log
5 | build
6 | *.lerna_backup
7 | packages/*/README.md
8 | package-lock.json
9 |
--------------------------------------------------------------------------------
/packages/react-refactor-gui/public/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chrvadala/react-refactor/HEAD/packages/react-refactor-gui/public/favicon.ico
--------------------------------------------------------------------------------
/packages/react-refactor/test/testUtils.js:
--------------------------------------------------------------------------------
1 | function removeSpaces(string){
2 | return string.replace(/\s/g, '')
3 | }
4 |
5 | module.exports = {
6 | removeSpaces
7 | }
8 |
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | language: node_js
2 |
3 | os:
4 | - linux
5 |
6 | node_js:
7 | - "6"
8 | - "8"
9 | - "lts/*"
10 |
11 | before_script:
12 | - "yarn run bootstrap"
13 |
--------------------------------------------------------------------------------
/packages/react-refactor/.babelrc:
--------------------------------------------------------------------------------
1 | {
2 | "plugins": [
3 | "transform-object-rest-spread"
4 | ],
5 | "presets": [
6 | [
7 | "es2015"
8 | ]
9 | ]
10 | }
11 |
--------------------------------------------------------------------------------
/packages/react-refactor-cli/.babelrc:
--------------------------------------------------------------------------------
1 | {
2 | "plugins": [
3 | "transform-object-rest-spread"
4 | ],
5 | "presets": [
6 | [
7 | "es2015"
8 | ]
9 | ]
10 | }
11 |
--------------------------------------------------------------------------------
/packages/react-refactor/src/errors.js:
--------------------------------------------------------------------------------
1 | export const NotAReactComponent = new Error('This isn\'t a React component')
2 | export const NotAProgram = new Error('This isn\'t a Program script')
3 |
--------------------------------------------------------------------------------
/packages/react-refactor-gui/src/index.css:
--------------------------------------------------------------------------------
1 | body{
2 | margin: 0;
3 | font-family: Roboto, cursive;
4 | background: #f3f5f7;
5 | }
6 |
7 | html, body, #root{
8 | height: 100%;
9 | width: 100%;
10 | }
11 |
--------------------------------------------------------------------------------
/packages/react-refactor-gui/src/index.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import ReactDOM from 'react-dom';
3 | import App from './App';
4 | import "./index.css"
5 |
6 | ReactDOM.render( , document.getElementById('root'));
7 |
--------------------------------------------------------------------------------
/packages/react-refactor-gui/src/defaultSource.js:
--------------------------------------------------------------------------------
1 | export default `
2 | function Welcome(props) {
3 | return
Hello, {props.name} ;
4 | }
5 |
6 | class Welcome extends React.Component {
7 | render() {
8 | return Hello, {this.props.name} ;
9 | }
10 | }
11 |
12 | `.trim()
13 |
--------------------------------------------------------------------------------
/packages/react-refactor-cli/test/__fixture__/Func.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import PropTypes from 'prop-types';
3 |
4 | function Func(props) {
5 | return (
6 |
7 | ciao
8 |
9 | );
10 | }
11 |
12 | Func.propTypes = {};
13 | Func.defaultProps = {};
14 |
15 |
16 | export default Func;
17 |
--------------------------------------------------------------------------------
/packages/react-refactor/test/__fixtures__/FuncComp.jsx.txt:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import PropTypes from 'prop-types';
3 |
4 | function FuncComp(props) {
5 | return (
6 |
7 |
8 |
9 | );
10 | }
11 |
12 | FuncComp.propTypes = {};
13 | FuncComp.defaultProps = {};
14 |
15 |
16 | export default FuncComp;
17 |
--------------------------------------------------------------------------------
/.editorconfig:
--------------------------------------------------------------------------------
1 | # EditorConfig helps developers define and maintain consistent
2 | # coding styles between different editors and IDEs
3 | # http://editorconfig.org
4 |
5 | root = true
6 |
7 | [*]
8 | indent_style = space
9 | indent_size = 2
10 | end_of_line = lf
11 | charset = utf-8
12 | trim_trailing_whitespace = true
13 | insert_final_newline = true
--------------------------------------------------------------------------------
/packages/react-refactor-cli/bin/react-refactor.js:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env node
2 |
3 | //version check
4 | const updateNotifier = require('update-notifier');
5 | const pkg = require('../package.json');
6 | updateNotifier({pkg}).notify();
7 |
8 | //command
9 | const argv = require('../build/argv').default
10 | const command = require('../build/command').default
11 | command(argv._[0], argv)
12 |
--------------------------------------------------------------------------------
/packages/react-refactor-gui/public/manifest.json:
--------------------------------------------------------------------------------
1 | {
2 | "short_name": "React Refactor",
3 | "name": "Create React App Sample",
4 | "icons": [
5 | {
6 | "src": "favicon.ico",
7 | "sizes": "64x64 32x32 24x24 16x16",
8 | "type": "image/x-icon"
9 | }
10 | ],
11 | "start_url": "./index.html",
12 | "display": "standalone",
13 | "theme_color": "#000000",
14 | "background_color": "#ffffff"
15 | }
16 |
--------------------------------------------------------------------------------
/packages/react-refactor/test/__fixtures__/ClassComp.jsx.txt:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import PropTypes from 'prop-types';
3 |
4 | class ClassComp extends React.Component {
5 | constructor(props) {
6 | super(props);
7 | }
8 |
9 | render() {
10 | return (
11 |
12 |
13 |
14 | );
15 | }
16 | }
17 |
18 | ClassComp.propTypes = {};
19 | ClassComp.defaultProps = {};
20 |
21 |
22 | export default ClassComp;
23 |
--------------------------------------------------------------------------------
/packages/react-refactor/src/treeUtils.js:
--------------------------------------------------------------------------------
1 | const traverse = require('traverse')
2 |
3 | export function getOrThrow(object, path) {
4 | let wrapped = traverse(object)
5 | if(wrapped.has(path)) return wrapped.get(path)
6 | throw new Error(`${path.join(',')} not found`)
7 | }
8 |
9 | export function get(object, path, defaultValue = undefined) {
10 | let wrapped = traverse(object)
11 | if(wrapped.has(path)) return wrapped.get(path)
12 | return defaultValue
13 | }
14 |
--------------------------------------------------------------------------------
/packages/react-refactor/src/parser.js:
--------------------------------------------------------------------------------
1 | import {parse as astParser} from 'babylon'
2 | import {js_beautify as codeBeautify} from "js-beautify"
3 |
4 | export function parse(code) {
5 | const parserOpt = {
6 | sourceType: 'module',
7 | plugins: [
8 | "jsx",
9 | "objectRestSpread",
10 | ]
11 | }
12 | return astParser(code, parserOpt).program
13 | }
14 |
15 | export function beautify(code) {
16 | const beautifyOpt = {
17 | indent_size: 2, e4x: true
18 | }
19 | return codeBeautify(code, beautifyOpt)
20 | }
21 |
--------------------------------------------------------------------------------
/packages/react-refactor-cli/src/argv.js:
--------------------------------------------------------------------------------
1 | const yargs = require('yargs')
2 | .usage('Usage: $0 [args] ')
3 | .help('help')
4 | .alias('help', 'h')
5 | .version()
6 |
7 | //INTO ROADMAP
8 | // .option('clipboard', {
9 | // alias: 'c',
10 | // describe: 'Copy output on clipboard',
11 | // type: 'boolean'
12 | // })
13 |
14 | .option('output', {
15 | alias: 'o',
16 | describe: 'Save output on file',
17 | type: 'string'
18 | })
19 |
20 | const argv = yargs.argv
21 |
22 | if (!argv._[0]) {
23 | yargs.showHelp();
24 | process.exit(-1)
25 | }
26 |
27 | export default argv
28 |
--------------------------------------------------------------------------------
/packages/react-refactor-cli/src/command.js:
--------------------------------------------------------------------------------
1 | import fs from 'fs'
2 | const write = message => console.log(message)
3 |
4 | import {execRefactor} from 'react-refactor'
5 |
6 | export default createCommand(write, fs);
7 |
8 | export function createCommand(write, fs) {
9 | return function command(file, options) {
10 | const {
11 | output = null,
12 | } = options;
13 |
14 | let source = fs.readFileSync(file, 'utf8')
15 | let result = execRefactor(source)
16 |
17 | if (output) {
18 | fs.writeFileSync(output, result.output, 'utf8')
19 | write('Output saved on file')
20 | return
21 | }
22 |
23 | write(result.output)
24 | }
25 | }
26 |
27 |
--------------------------------------------------------------------------------
/packages/react-refactor-gui/src/Header.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 |
3 | const S_HEADER = {
4 | backgroundColor: '#2a2d33',
5 | }
6 |
7 | const S_TITLE = {
8 | padding: "10px 20px",
9 | color: '#fff',
10 | fontSize: '1.3em',
11 | margin: 0,
12 | fontWeight: "bold",
13 | }
14 |
15 | const S_SUBTITLE = {
16 | fontSize: "0.8em",
17 | fontWeight: 100,
18 | }
19 |
20 | function Header(props) {
21 | return (
22 |
23 |
24 | React Refactor - Convert your React Class Component to Functional Component and vice-versa
25 |
26 |
27 | );
28 | }
29 |
30 | export default Header;
31 |
--------------------------------------------------------------------------------
/packages/react-refactor-gui/src/GithubRibbon.css:
--------------------------------------------------------------------------------
1 | .github-corner:hover .octo-arm {
2 | animation: octocat-wave 560ms ease-in-out
3 | }
4 |
5 | @keyframes octocat-wave {
6 | 0%, 100% {
7 | transform: rotate(0)
8 | }
9 | 20%, 60% {
10 | transform: rotate(-25deg)
11 | }
12 | 40%, 80% {
13 | transform: rotate(10deg)
14 | }
15 | }
16 |
17 | @media (max-width: 500px) {
18 | .github-corner:hover .octo-arm {
19 | animation: none
20 | }
21 |
22 | .github-corner .octo-arm {
23 | animation: octocat-wave 560ms ease-in-out
24 | }
25 | }
26 |
27 | .github-corner svg {
28 | fill: #64CEAA;
29 | color: #fff;
30 | position: absolute;
31 | top: 0;
32 | border: 0;
33 | right: 0;
34 | }
35 |
--------------------------------------------------------------------------------
/packages/react-refactor-gui/src/App.js:
--------------------------------------------------------------------------------
1 | import React, {Component} from 'react';
2 | import Content from "./Content";
3 | import Header from "./Header";
4 | import Introduction from "./Introduction";
5 | import GithubRibbon from "./GithubRibbon";
6 | import Footer from "./Footer";
7 |
8 | const S_App = {
9 | height: '100%',
10 | display: 'flex',
11 | flexDirection: 'column',
12 | alignItems: 'stretch',
13 | alignContent: 'flex-end',
14 | }
15 |
16 | class App extends Component {
17 | render() {
18 | return [
19 | ,
20 |
21 |
22 |
23 |
24 |
25 |
26 | ]
27 | }
28 | }
29 |
30 | export default App;
31 |
--------------------------------------------------------------------------------
/packages/react-refactor-gui/src/Footer.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 |
3 | const S_FOOTER = {
4 | display: 'flex',
5 | justifyContent: "space-around",
6 |
7 | background: '#efefef',
8 | borderTop: "1px solid #ddd",
9 | fontSize: '0.9em',
10 | padding: 2,
11 | color: "#555",
12 | height: 25,
13 | textAlign: 'center',
14 | }
15 |
16 | function Footer(props) {
17 | return (
18 |
19 |
23 |
24 |
Install CLI tool npm i -g react-refactor-cli
25 |
26 | );
27 | }
28 |
29 | Footer.propTypes = {};
30 | Footer.defaultProps = {};
31 |
32 |
33 | export default Footer;
34 |
--------------------------------------------------------------------------------
/packages/react-refactor-gui/src/CodeEditor.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import PropTypes from 'prop-types';
3 |
4 | import AceEditor from 'react-ace';
5 |
6 | import 'brace/mode/jsx';
7 | import 'brace/theme/tomorrow';
8 |
9 | class CodeEditor extends React.PureComponent {
10 | render() {
11 | return (
12 | this.props.onChange(v)}
16 | value={this.props.value}
17 | editorProps={{$blockScrolling: true}}
18 | style={{width: '100%', height: '100%'}}
19 | readOnly={!this.props.onChange}
20 | />
21 | );
22 | }
23 | }
24 |
25 | CodeEditor.propTypes = {
26 | value: PropTypes.string.isRequired,
27 | onChange: PropTypes.func,
28 | };
29 | CodeEditor.defaultProps = {};
30 |
31 |
32 | export default CodeEditor;
33 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@chrvadala/react-refactor",
3 | "private": true,
4 | "devDependencies": {
5 | "cpy-cli": "^2.0.0",
6 | "gh-pages": "^1.2.0",
7 | "lerna": "^3.0.0-rc.0",
8 | "npm-run-all": "^4.1.3"
9 | },
10 | "scripts": {
11 | "bootstrap": "lerna bootstrap",
12 | "publish": "lerna publish",
13 | "build": "lerna run build",
14 | "test": "lerna --ignore react-refactor-gui run test",
15 | "deploy": "lerna --scope react-refactor-gui run deploy",
16 | "clean": "lerna run clean && lerna clean --yes"
17 | },
18 | "license": "MIT",
19 | "repository": {
20 | "type": "git",
21 | "url": "git+https://github.com/chrvadala/react-refactor.git"
22 | },
23 | "bugs": {
24 | "url": "https://github.com/chrvadala/react-refactor/issues"
25 | },
26 | "homepage": "https://github.com/chrvadala/react-refactor#readme",
27 | "jest": {
28 | "testEnvironment": "node"
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/packages/react-refactor/test/__fixtures__/refactoredExample.jsx.txt:
--------------------------------------------------------------------------------
1 | class funcComp extends React.Component {
2 | render() {
3 | let props = this.props;
4 | return ciao
5 | }
6 | }
7 |
8 | export default class funcCompExport extends React.Component {
9 | render() {
10 | let props = this.props;
11 | return ciao
12 | }
13 | }
14 |
15 | function ClassComp(props) {
16 | let self = {props};
17 |
18 | let {def} = self.props
19 | let {props: {ghi}} = self
20 | return (
21 | {self.props.abc}
22 | );
23 | }
24 |
25 | export function ClassCompExport(props) {
26 | let self = {props};
27 |
28 | let {def} = self.props
29 | let {props: {ghi}} = self
30 | return (
31 | {self.props.abc}
32 | );
33 | }
34 |
35 | function simpleFunc(){
36 | return 'ciao'
37 | }
38 |
39 | class simpleClass{
40 | do(){
41 | return 'ciao'
42 | }
43 | }
44 |
45 | export {funcComp}
46 | export {ClassComp}
47 |
--------------------------------------------------------------------------------
/packages/react-refactor-gui/src/Introduction.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 |
3 | const S_INTRODUCTION = {
4 | margin: "10px 20px",
5 | }
6 |
7 | const S_DESC = {
8 | margin: 0,
9 | fontSize: "0.9em",
10 | fontWeight: 100,
11 | lineHeight: "1.5em",
12 | }
13 |
14 | function Introduction(props) {
15 | return (
16 |
17 |
18 | How many times have you converted a React Class component to a React Functional component and vice-versa? It’s a boring task, and we know... "developers don’t like boring tasks ".
19 | Thanks to React Refactor you can convert any React component from and to Class component.
20 | Paste your code into left panel
21 |
22 |
23 | );
24 | }
25 |
26 | Introduction.propTypes = {};
27 | Introduction.defaultProps = {};
28 |
29 |
30 | export default Introduction;
31 |
--------------------------------------------------------------------------------
/packages/react-refactor/test/parser.spec.js:
--------------------------------------------------------------------------------
1 | const path = require("path");
2 | const fs = require('fs')
3 | const {parse} = require('../src/parser')
4 | const fixture = filename => path.join(__dirname, '__fixtures__', filename)
5 |
6 |
7 | let classComp;
8 | beforeAll(done => {
9 | fs.readFile(fixture('ClassComp.jsx.txt'), 'utf8', (err, data) => {
10 | if (err) throw new Error()
11 | classComp = data
12 | done()
13 | })
14 | })
15 |
16 | let funcComp;
17 | beforeAll(done => {
18 | fs.readFile(fixture('FuncComp.jsx.txt'), 'utf8', (err, data) => {
19 | if (err) throw new Error()
20 | funcComp = data
21 | done()
22 | })
23 | })
24 |
25 | describe('parser', () => {
26 | it('should parse Class file', () => {
27 | let parsedClass = parse(classComp)
28 | expect(parsedClass).toMatchSnapshot()
29 | })
30 | it('should parse Func file', () => {
31 | let parsedFunc = parse(funcComp)
32 | expect(parsedFunc).toMatchSnapshot()
33 | })
34 | })
35 |
--------------------------------------------------------------------------------
/packages/react-refactor-gui/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "react-refactor-gui",
3 | "version": "1.0.0",
4 | "description": "Convert your React Class Component to Functional Component and vice-versa",
5 | "license": "MIT",
6 | "scripts": {
7 | "start": "react-scripts start",
8 | "build": "react-scripts build",
9 | "clean": "del build README.md",
10 | "cp-readme": "cpy ../../README.md .",
11 | "prepare": "npm-run-all build cp-readme",
12 | "deploy": "gh-pages -m 'Updates gh-pages' -d ./build"
13 | },
14 | "repository": {
15 | "type": "git",
16 | "url": "git+https://github.com/chrvadala/react-refactor.git"
17 | },
18 | "bugs": {
19 | "url": "https://github.com/chrvadala/react-refactor/issues"
20 | },
21 | "homepage": "https://chrvadala.github.io/react-refactor",
22 | "devDependencies": {
23 | "del-cli": "^1.1.0",
24 | "react": "^16.2.0",
25 | "react-ace": "^5.9.0",
26 | "react-dom": "^16.2.0",
27 | "react-refactor": "^1.0.0",
28 | "react-scripts": "^1.1.0"
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/packages/react-refactor/test/__fixtures__/example.jsx.txt:
--------------------------------------------------------------------------------
1 | function funcComp(props) {
2 | return ciao
3 | }
4 |
5 | export default function funcCompExport(props) {
6 | return ciao
7 | }
8 |
9 | class ClassComp extends React.Component {
10 | constructor(props) {
11 | super(props);
12 | }
13 |
14 | doSomething() {
15 | //doSomething()
16 | }
17 |
18 | render() {
19 | let {def} = this.props
20 | let {props: {ghi}} = this
21 | return (
22 | {this.props.abc}
23 | );
24 | }
25 | }
26 |
27 | export class ClassCompExport extends React.Component {
28 | constructor(props) {
29 | super(props);
30 | }
31 |
32 | doSomething() {
33 | //doSomething()
34 | }
35 |
36 | render() {
37 | let {def} = this.props
38 | let {props: {ghi}} = this
39 | return (
40 | {this.props.abc}
41 | );
42 | }
43 | }
44 |
45 | function simpleFunc(){
46 | return 'ciao'
47 | }
48 |
49 | class simpleClass{
50 | do(){
51 | return 'ciao'
52 | }
53 | }
54 |
55 | export {funcComp}
56 | export {ClassComp}
57 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2017 Christian
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 |
--------------------------------------------------------------------------------
/packages/react-refactor/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "react-refactor",
3 | "version": "1.0.0",
4 | "main": "build/index.js",
5 | "module": "build/index.js",
6 | "license": "MIT",
7 | "description": "Convert your React Class Component to Functional Component and vice-versa",
8 | "devDependencies": {
9 | "babel-cli": "^6.26.0",
10 | "babel-plugin-transform-object-rest-spread": "^6.26.0",
11 | "babel-preset-es2015": "^6.24.1",
12 | "del-cli": "^1.1.0",
13 | "jest": "^22.1.4"
14 | },
15 | "scripts": {
16 | "test": "jest",
17 | "build": "babel src --out-dir build",
18 | "clean": "del build README.md",
19 | "cp-readme": "cpy ../../README.md .",
20 | "prepare": "npm-run-all build cp-readme"
21 | },
22 | "dependencies": {
23 | "babylon": "^6.18.0",
24 | "js-beautify": "^1.7.5",
25 | "traverse": "^0.6.6"
26 | },
27 | "repository": {
28 | "type": "git",
29 | "url": "git+https://github.com/chrvadala/react-refactor.git"
30 | },
31 | "bugs": {
32 | "url": "https://github.com/chrvadala/react-refactor/issues"
33 | },
34 | "homepage": "https://chrvadala.github.io/react-refactor",
35 | "jest": {
36 | "testEnvironment": "node"
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/packages/react-refactor/test/index.spec.js:
--------------------------------------------------------------------------------
1 | const path = require("path");
2 | const fs = require("fs")
3 | const {removeSpaces} = require("./testUtils");
4 | const {patchString} = require("../src/stringUtils");
5 | const {execRefactor} = require("../src/index");
6 | const {parseFile} = require('../src/parser')
7 | const fixture = filename => path.join(__dirname, '__fixtures__', filename)
8 |
9 | let example, refactoredExample;
10 |
11 | beforeAll(done => {
12 | fs.readFile(fixture('example.jsx.txt'), 'utf8', (err, data) => {
13 | if (err) throw new Error()
14 | example = data
15 | done()
16 | })
17 | })
18 |
19 | beforeAll(done => {
20 | fs.readFile(fixture('refactoredExample.jsx.txt'), 'utf8', (err, data) => {
21 | if (err) throw new Error()
22 | refactoredExample = data
23 | done()
24 | })
25 | })
26 |
27 |
28 | describe('refactor', () => {
29 | it.only('should convert', () => {
30 | let {patch, skipped, output} = execRefactor(example)
31 |
32 | expect(Array.isArray(patch)).toBe(true)
33 | expect(Array.isArray(skipped)).toBe(true)
34 | expect(patch).toMatchSnapshot()
35 | expect(skipped).toMatchSnapshot()
36 |
37 | expect(removeSpaces(output))
38 | .toBe(removeSpaces(refactoredExample))
39 | })
40 | })
41 |
--------------------------------------------------------------------------------
/packages/react-refactor-cli/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "react-refactor-cli",
3 | "description": "Convert your React Class Component to Functional Component and vice-versa",
4 | "bin": {
5 | "react-refactor": "./bin/react-refactor.js",
6 | "react-refactor-cli": "./bin/react-refactor.js"
7 | },
8 | "devDependencies": {
9 | "babel-cli": "^6.26.0",
10 | "babel-plugin-transform-object-rest-spread": "^6.26.0",
11 | "babel-preset-es2015": "^6.24.1",
12 | "del-cli": "^1.1.0",
13 | "jest": "^22.1.4"
14 | },
15 | "scripts": {
16 | "test": "jest",
17 | "build": "babel src --out-dir build",
18 | "clean": "del build README.md",
19 | "cp-readme": "cpy ../../README.md .",
20 | "prepare": "npm-run-all build cp-readme"
21 | },
22 | "engines": {
23 | "node": ">=6.0.0"
24 | },
25 | "version": "1.0.0",
26 | "main": "index.js",
27 | "license": "MIT",
28 | "repository": {
29 | "type": "git",
30 | "url": "git+https://github.com/chrvadala/react-refactor.git"
31 | },
32 | "bugs": {
33 | "url": "https://github.com/chrvadala/react-refactor/issues"
34 | },
35 | "homepage": "https://chrvadala.github.io/react-refactor",
36 | "dependencies": {
37 | "react-refactor": "^1.0.0",
38 | "update-notifier": "^2.3.0",
39 | "yargs": "^11.0.0"
40 | },
41 | "jest": {
42 | "testEnvironment": "node"
43 | }
44 | }
45 |
--------------------------------------------------------------------------------
/packages/react-refactor/src/stringUtils.js:
--------------------------------------------------------------------------------
1 | export const INSERT = 'insert';
2 | export const REMOVE = 'remove';
3 |
4 | export const insert = (start, payload) => ({operation: INSERT, start, payload})
5 | export const remove = (start, end) => ({operation: REMOVE, start, end})
6 |
7 | const StartGTEndError = new Error('Start offset should be greater than end offset')
8 | const StartGTLengthError = new Error('start offset should be lower than the size of the string')
9 | const PatchesUnsortedError = new Error('Patches should be provided sorted by start offset')
10 | const UnsupportedOperationError = new Error('Unsupported operation')
11 |
12 | export function patchString(string, patch) {
13 | let cur = 0;
14 | let out = '';
15 |
16 | for (let i = 0; i < patch.length; i++) {
17 | let {operation, start, end, payload} = patch[i];
18 |
19 | //check constraints
20 | if (end && (start > end)) throw new StartGTEndError()
21 | if (start > string.length) throw new StartGTLengthError()
22 | if (cur > start) throw new PatchesUnsortedError()
23 |
24 | //move on cursor
25 | out += string.substring(cur, start);
26 | cur = start;
27 |
28 | //perform operation
29 | switch (operation) {
30 | case INSERT:
31 | out += payload;
32 | break;
33 |
34 | case REMOVE:
35 | cur = end;
36 | break;
37 |
38 | default:
39 | throw new UnsupportedOperationError()
40 | }
41 | }
42 |
43 | //copy remained part
44 | out += string.substring(cur);
45 |
46 | return out
47 | }
48 |
49 |
--------------------------------------------------------------------------------
/packages/react-refactor-cli/test/command.spec.js:
--------------------------------------------------------------------------------
1 | import path from 'path'
2 | import fs from 'fs'
3 | import {createCommand} from '../src/command'
4 |
5 | const testfile = path.join(__dirname, './__fixture__/Func.jsx')
6 |
7 | describe('command', () => {
8 |
9 | it('should generate output on console', () => {
10 | const writeMock = jest.fn()
11 | const fsMock = {
12 | readFileSync: jest.fn(fs.readFileSync),
13 | writeFileSync: jest.fn(),
14 | }
15 |
16 | const command = createCommand(writeMock, fsMock)
17 | const options = {}
18 |
19 | command(testfile, options)
20 |
21 | expect(fsMock.readFileSync).toBeCalledWith(testfile, 'utf8');
22 | expect(fsMock.writeFileSync).not.toBeCalled();
23 | expect(writeMock).toBeCalled();
24 | expect(writeMock.mock.calls[0][0]).toMatch(/class *Func/g)
25 | })
26 |
27 | it('should generate output on file', () => {
28 | const writeMock = jest.fn()
29 | const fsMock = {
30 | readFileSync: jest.fn(fs.readFileSync),
31 | writeFileSync: jest.fn(),
32 | }
33 |
34 | const command = createCommand(writeMock, fsMock)
35 | const options = {
36 | output: path.join(__dirname, './__fixture__/Output.jsx')
37 | }
38 |
39 | command(testfile, options)
40 |
41 | expect(fsMock.readFileSync).toBeCalledWith(testfile, 'utf8');
42 | expect(fsMock.writeFileSync).toBeCalled();
43 | expect(fsMock.writeFileSync.mock.calls[0][1]).toMatch(/class *Func/g)
44 | expect(writeMock).toBeCalledWith('Output saved on file');
45 | })
46 | })
47 |
48 |
49 |
50 |
--------------------------------------------------------------------------------
/packages/react-refactor-gui/src/GithubRibbon.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import PropTypes from 'prop-types';
3 | import "./GithubRibbon.css"
4 |
5 | function GithubRibbon({url}) {
6 | return (
7 |
8 |
9 |
10 |
13 |
16 |
17 |
18 |
19 | );
20 | }
21 |
22 | GithubRibbon.propTypes = {
23 | url: PropTypes.string.isRequired,
24 | };
25 | GithubRibbon.defaultProps = {};
26 |
27 |
28 | export default GithubRibbon;
29 |
--------------------------------------------------------------------------------
/packages/react-refactor-gui/src/Content.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import CodeEditor from "./CodeEditor";
3 | import * as ReactRefactor from 'react-refactor'
4 | import defaultSource from "./defaultSource";
5 |
6 | const S_CONTENT = {
7 | display: 'flex',
8 | alignContent: 'stretch',
9 | justifyContent: 'center',
10 | padding: "0 10px",
11 | alignItems: "stretch",
12 | height: "100%",
13 | }
14 |
15 | const S_COL = {
16 | width: '50%',
17 | border: "1px solid #2085c1",
18 | margin: 10,
19 | }
20 |
21 | class Content extends React.Component {
22 | constructor(props) {
23 | super(props)
24 | this.state = {
25 | code: defaultSource,
26 | refactoredCode: this.update(defaultSource, true)
27 | }
28 | this.update = this.update.bind(this)
29 | }
30 |
31 | update(source, skipStateUpdate = false) {
32 | let output;
33 | try {
34 | ({output} = ReactRefactor.execRefactor(source))
35 | } catch (err) {
36 | this.setState({code: source, refactoredCode: '// Source file can\'t be refactored'})
37 | console.warn(err)
38 | return;
39 | }
40 | if (!skipStateUpdate) this.setState({code: source, refactoredCode: output})
41 | return output;
42 | }
43 |
44 | render() {
45 | return (
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 | );
55 | }
56 | }
57 |
58 | Content.propTypes = {};
59 | Content.defaultProps = {};
60 |
61 |
62 | export default Content;
63 |
--------------------------------------------------------------------------------
/packages/react-refactor/src/functionalToClass.js:
--------------------------------------------------------------------------------
1 | import traverse from "traverse"
2 | import {NotAReactComponent} from "./errors"
3 | import {getOrThrow} from './treeUtils'
4 | import {insert, remove} from "./stringUtils"
5 |
6 | export function functionalToClass(source, functionalDeclaration) {
7 |
8 | //check if is a React component (has at least 1 comp)
9 | let isReactComponent = false;
10 | traverse(functionalDeclaration)
11 | .forEach(function () {
12 | let {node, path} = this
13 | let tail = path[path.length - 1]
14 | if (tail !== 'type') return
15 | if (node !== 'JSXElement') return
16 | isReactComponent = true;
17 | })
18 | if (!isReactComponent) throw NotAReactComponent
19 |
20 | //detect component info
21 | let functionalName = getOrThrow(functionalDeclaration, ['id', 'name'], 'ReactComponent')
22 | let functionalStart = getOrThrow(functionalDeclaration, ['start'])
23 | let functionalEnd = getOrThrow(functionalDeclaration, ['end'])
24 |
25 | let hasParams = getOrThrow(functionalDeclaration, ['params']).length > 0;
26 | let paramsCode;
27 | if(hasParams) {
28 | let paramsStart = getOrThrow(functionalDeclaration, ['params', 0, 'start'])
29 | let paramsEnd = getOrThrow(functionalDeclaration, ['params', 0, 'end'])
30 | paramsCode = source.substring(paramsStart, paramsEnd)
31 | }
32 |
33 | let renderStart = getOrThrow(functionalDeclaration, ['body', 'start'])
34 | let renderEnd = getOrThrow(functionalDeclaration, ['body', 'end'])
35 |
36 | let patch = []
37 | patch.push(insert(functionalStart, `class ${functionalName} extends React.Component {`))
38 | patch.push(insert(functionalStart, `render()`))
39 | patch.push(remove(functionalStart, renderStart))
40 | if(hasParams) patch.push(insert(renderStart + 1, `\nlet ${paramsCode} = this.props;`))
41 | patch.push(insert(renderEnd, `}`))
42 |
43 | return patch
44 | }
45 |
46 |
--------------------------------------------------------------------------------
/packages/react-refactor/test/treeUtils.spec.js:
--------------------------------------------------------------------------------
1 | const {getOrThrow, get} = require('../src/treeUtils')
2 |
3 | const obj = {
4 | a: 10,
5 | z: 0,
6 | b: {
7 | ba: 20,
8 | bb: {
9 | bba: 30,
10 | bbb: {
11 | bbba: 40
12 | }
13 | }
14 | }
15 | }
16 |
17 | describe('getOrThrow', () => {
18 | it('should get a data', () => {
19 | expect(getOrThrow(obj, ['z'])).toBe(0)
20 | expect(getOrThrow(obj, ['a'])).toBe(10)
21 | expect(getOrThrow(obj, ['b', 'ba'])).toBe(20)
22 | expect(getOrThrow(obj, ['b', 'bb', 'bba'])).toBe(30)
23 | expect(getOrThrow(obj, ['b', 'bb', 'bbb', 'bbba'])).toBe(40)
24 | })
25 |
26 | it('should throw an exception', () => {
27 | expect(() => getOrThrow(obj, ['c'])).toThrow()
28 | expect(() => getOrThrow(obj, ['b', 'c'])).toThrow()
29 | expect(() => getOrThrow(obj, ['b', 'bb', 'c'])).toThrow()
30 | expect(() => getOrThrow(obj, ['b', 'bb', 'bba', 'c'])).toThrow()
31 | })
32 | })
33 |
34 | describe('get', () => {
35 | it('should get a data', () => {
36 | expect(get(obj, ['z'])).toBe(0)
37 | expect(get(obj, ['a'])).toBe(10)
38 | expect(get(obj, ['b', 'ba'])).toBe(20)
39 | expect(get(obj, ['b', 'bb', 'bba'])).toBe(30)
40 | expect(get(obj, ['b', 'bb', 'bbb', 'bbba'])).toBe(40)
41 | })
42 |
43 | it('should return undefined', () => {
44 | expect(get(obj, ['c'])).toBeFalsy()
45 | expect(get(obj, ['b', 'c'])).toBeFalsy()
46 | expect(get(obj, ['b', 'bb', 'c'])).toBeFalsy()
47 | expect(get(obj, ['b', 'bb', 'bba', 'c'])).toBeFalsy()
48 | })
49 |
50 | it('should return defaultValue', () => {
51 | const defVal = '__DEFAULT__'
52 | expect(get(obj, ['c'], defVal)).toBe(defVal)
53 | expect(get(obj, ['b', 'c'], defVal)).toBe(defVal)
54 | expect(get(obj, ['b', 'bb', 'c'], defVal)).toBe(defVal)
55 | expect(get(obj, ['b', 'bb', 'bba', 'c'], defVal)).toBe(defVal)
56 | })
57 | })
58 |
--------------------------------------------------------------------------------
/packages/react-refactor/src/index.js:
--------------------------------------------------------------------------------
1 | import {patchString} from "./stringUtils"
2 | import {parse, beautify} from "./parser"
3 | import {classToFunctional} from "./classToFunctional"
4 | import {functionalToClass} from "./functionalToClass"
5 | import {getOrThrow, get} from "./treeUtils"
6 | import {NotAProgram} from './errors'
7 |
8 | export function execRefactor(source) {
9 | let programDeclaration = parse(source)
10 | let type = getOrThrow(programDeclaration, ['type'])
11 | let body = getOrThrow(programDeclaration, ['body'])
12 |
13 | if (type !== 'Program') throw NotAProgram
14 |
15 | let patch = []
16 | let skipped = []
17 |
18 | body.forEach(block => {
19 | let type = get(block, ['type'])
20 | if (!type) return
21 |
22 | switch (type) {
23 | case 'ExportDefaultDeclaration':
24 | case 'ExportNamedDeclaration':
25 | block = get(block, ['declaration'], block)
26 | type = get(block, ['type'])
27 | }
28 |
29 | let start = get(block, ['start']), end = get(block, ['end']);
30 |
31 | switch (type) {
32 | case 'FunctionDeclaration':
33 | try {
34 | let curPatch = functionalToClass(source, block)
35 | patch = patch.concat(curPatch)
36 | } catch (err) {
37 | skipped.push({type: 'FunctionDeclaration', start, end, message: err.message, stack: err})
38 | }
39 | break;
40 |
41 |
42 | case 'ClassDeclaration':
43 | try {
44 | let curPatch = classToFunctional(source, block)
45 | patch = patch.concat(curPatch)
46 | } catch (err) {
47 | skipped.push({type: 'FunctionDeclaration', start, end, message: err.message, stack: err})
48 | }
49 | break;
50 | }
51 | });
52 |
53 | let output = patchString(source, patch)
54 |
55 | try {
56 | output = beautify(output)
57 | } catch (err) {
58 | skipped.push({type: 'Beautify'})
59 | }
60 |
61 | return {patch, skipped, output}
62 | }
63 |
--------------------------------------------------------------------------------
/packages/react-refactor/test/__snapshots__/functionalToClass.spec.js.snap:
--------------------------------------------------------------------------------
1 | // Jest Snapshot v1, https://goo.gl/fbAQLP
2 |
3 | exports[`functionalToClass should convert functional to class comp 1`] = `
4 | Array [
5 | Object {
6 | "operation": "insert",
7 | "payload": "class FunctionalComp extends React.Component {",
8 | "start": 1,
9 | },
10 | Object {
11 | "operation": "insert",
12 | "payload": "render()",
13 | "start": 1,
14 | },
15 | Object {
16 | "end": 31,
17 | "operation": "remove",
18 | "start": 1,
19 | },
20 | Object {
21 | "operation": "insert",
22 | "payload": "
23 | let props = this.props;",
24 | "start": 32,
25 | },
26 | Object {
27 | "operation": "insert",
28 | "payload": "}",
29 | "start": 77,
30 | },
31 | ]
32 | `;
33 |
34 | exports[`functionalToClass should convert functional to class comp with destructuring 1`] = `
35 | Array [
36 | Object {
37 | "operation": "insert",
38 | "payload": "class FunctionalComp extends React.Component {",
39 | "start": 1,
40 | },
41 | Object {
42 | "operation": "insert",
43 | "payload": "render()",
44 | "start": 1,
45 | },
46 | Object {
47 | "end": 36,
48 | "operation": "remove",
49 | "start": 1,
50 | },
51 | Object {
52 | "operation": "insert",
53 | "payload": "
54 | let {abc, cde} = this.props;",
55 | "start": 37,
56 | },
57 | Object {
58 | "operation": "insert",
59 | "payload": "}",
60 | "start": 76,
61 | },
62 | ]
63 | `;
64 |
65 | exports[`functionalToClass should convert functional to class comp without props 1`] = `
66 | Array [
67 | Object {
68 | "operation": "insert",
69 | "payload": "class FunctionalComp extends React.Component {",
70 | "start": 1,
71 | },
72 | Object {
73 | "operation": "insert",
74 | "payload": "render()",
75 | "start": 1,
76 | },
77 | Object {
78 | "end": 26,
79 | "operation": "remove",
80 | "start": 1,
81 | },
82 | Object {
83 | "operation": "insert",
84 | "payload": "}",
85 | "start": 70,
86 | },
87 | ]
88 | `;
89 |
--------------------------------------------------------------------------------
/packages/react-refactor-gui/public/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
11 |
12 |
13 |
14 |
23 | React Refactor
24 |
25 |
26 |
27 |
34 |
35 |
36 |
37 | You need to enable JavaScript to run this app.
38 |
39 |
40 |
50 |
51 |
52 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # react-refactor
2 | [](https://travis-ci.org/chrvadala/react-refactor)
3 | [](https://beerpay.io/chrvadala/react-refactor)
4 |
5 | How many times have you converted a **React Class component** to a **React Functional component** and vice-versa? It’s a boring task, and we know... "*developers don’t like boring tasks*".
6 | Thanks to **React Refactor** you can convert any React component from and to Class component.
7 |
8 | It's made with Babel Babylon and thanks to string replacing it’s able to instantly convert your component to the opposite kind of component that you provided.
9 |
10 | **React Refactor** is available in three different packages: Library, CLI, Web Interface
11 |
12 | ## Library
13 | [](https://www.npmjs.com/package/react-refactor)
14 | [](https://www.npmjs.com/package/react-refactor)
15 |
16 | The package *react-refactor* offers methods to programmatically convert a component. You can use it to make new useful utilities that integrate this ability.
17 | ````js
18 | const {execRefactor} = require('react-refactor')
19 | let {output} = execRefactor(source)
20 | ````
21 |
22 |
23 | ## CLI
24 | [](https://www.npmjs.com/package/react-refactor-cli)
25 | [](https://www.npmjs.com/package/react-refactor-cli)
26 |
27 | You can globally install the package **react-refactor-cli** and use it to convert your component on the fly.
28 | ````
29 | $ yarn global add react-refactor-cli
30 | $ react-refactor [--output ]
31 | ````
32 |
33 | ## Web interface
34 | You can avoid installing anything and convert your component through the web interface available at [https://chrvadala.github.io/react-refactor/](https://chrvadala.github.io/react-refactor/)
35 |
36 | ## Changelog
37 | - **v0.0** - Preview version
38 | - **v1.0** - First stable version
39 |
40 | ## Run tests
41 | ````
42 | yarn install
43 | yarn run bootstrap
44 | yarn build
45 | yarn test
46 | yarn run clean
47 | ````
48 |
49 | ## Contributors
50 | - [chrvadala](https://github.com/chrvadala) (author)
51 |
--------------------------------------------------------------------------------
/packages/react-refactor/src/classToFunctional.js:
--------------------------------------------------------------------------------
1 | import {get, getOrThrow} from'./treeUtils'
2 | import traverse from 'traverse'
3 | import {NotAReactComponent} from "./errors"
4 | import {insert, remove} from "./stringUtils"
5 |
6 | const THIS_REPLACER = 'self'
7 | const SUPER_COMPONENTS = ['Component', 'PureComponent']
8 |
9 | export function classToFunctional(source, classDeclaration) {
10 |
11 | //check if is a React component
12 | let isReactComponent;
13 | switch (get(classDeclaration, ['superClass', 'type'])) {
14 | case 'MemberExpression':
15 | let superObject = get(classDeclaration, ['superClass', 'object', 'name'])
16 | let superProperty = get(classDeclaration, ['superClass', 'property', 'name'])
17 |
18 | isReactComponent = superObject
19 | && superProperty
20 | && superObject === "React"
21 | && SUPER_COMPONENTS.includes(superProperty)
22 | break;
23 |
24 | case 'Identifier':
25 | let superIdentifier = get(classDeclaration, ['superClass', 'name'])
26 | isReactComponent = superIdentifier
27 | && SUPER_COMPONENTS.includes(superIdentifier)
28 | break
29 |
30 | default:
31 | isReactComponent = false
32 | }
33 | if (!isReactComponent) throw NotAReactComponent;
34 |
35 |
36 | //detect component info
37 | let className = getOrThrow(classDeclaration, ['id', 'name'], 'ReactComponent')
38 | let classStart = getOrThrow(classDeclaration, ['start'])
39 | let classEnd = getOrThrow(classDeclaration, ['end'])
40 | let classBody = getOrThrow(classDeclaration, ['body'])
41 |
42 |
43 | //detect render method
44 | let renderMethod;
45 | getOrThrow(classBody, ['body']).forEach(node => {
46 | if (get(node, ['type']) !== 'ClassMethod') return
47 | if (get(node, ['key', 'name']) !== 'render') return
48 | renderMethod = node
49 | })
50 | if (!renderMethod) throw NotAReactComponent;
51 | let renderStart = getOrThrow(renderMethod, ['body', 'start'])
52 | let renderEnd = getOrThrow(renderMethod, ['body', 'end'])
53 |
54 |
55 | //generates patch to convert it from class to func
56 | let patch = []
57 | patch.push(insert(classStart, `function ${className}(props)`))
58 | patch.push(remove(classStart, renderStart))
59 | patch.push(insert(renderStart + 1, `\n\tlet ${THIS_REPLACER} = {props};\n`))
60 | traverse(classDeclaration).map(node => {
61 | if (get(node, ['type']) !== 'ThisExpression') return
62 | patch.push(insert(node.start, THIS_REPLACER))
63 | patch.push(remove(node.start, node.end))
64 | })
65 | patch.push(remove(renderEnd, classEnd))
66 |
67 | //patch
68 | return patch
69 | }
70 |
--------------------------------------------------------------------------------
/packages/react-refactor/test/classToFunctional.spec.js:
--------------------------------------------------------------------------------
1 | const {NotAReactComponent} = require("../src/errors");
2 | const {patchString} = require("../src/stringUtils");
3 | const {removeSpaces} = require("./testUtils");
4 | const {classToFunctional} = require('../src/classToFunctional')
5 | const {parse} = require('../src/parser')
6 |
7 | const classTemplate = `
8 | class ClassComp extends React.Component {
9 | constructor(props) {
10 | super(props);
11 | }
12 | doSomething(){
13 | //doSomething()
14 | }
15 | render() {
16 | let {def} = this.props
17 | let {props: {ghi}} = this
18 | return (
19 | {this.props.abc}
20 | );
21 | }
22 | }
23 | `.trim()
24 |
25 | const functionalTemplate = `
26 | function ClassComp(props){
27 | let self = {props};
28 |
29 | let {def} = self.props
30 | let {props: {ghi}} = self
31 | return (
32 | {self.props.abc}
33 | );
34 | }
35 | `
36 |
37 | const genericClassTemplate = `
38 | class ClassComp extends React.Component {
39 | constructor(props) {
40 | super(props);
41 | }
42 | doSomething(){
43 | //doSomething()
44 | }
45 | }
46 | `.trim()
47 |
48 | describe('classToFunctional', () => {
49 | it('should convert class to functional comp', () => {
50 | let classComp = parse(classTemplate)
51 | let patch = classToFunctional(classTemplate, classComp.body[0])
52 | expect(Array.isArray(patch)).toBe(true)
53 | expect(patch).toMatchSnapshot()
54 | let output = patchString(classTemplate, patch)
55 | expect(removeSpaces(output)).toBe(removeSpaces(functionalTemplate))
56 | })
57 | it('should throw exception', () => {
58 | let classComp = parse(genericClassTemplate)
59 | expect(() => {
60 | classToFunctional(classTemplate, classComp.body[0])
61 | }).toThrow(NotAReactComponent)
62 | })
63 |
64 | describe('should supports different superclass', () => {
65 | [
66 | 'React.PureComponent',
67 | 'PureComponent',
68 | 'Component',
69 | ].forEach(superClass => {
70 | it(`should support ${superClass}`, () => {
71 | let classCompVariant = classTemplate.replace('React.Component', superClass)
72 | let functionalCompVariant = functionalTemplate.replace('React.Component', superClass)
73 | let classComp = parse(classCompVariant)
74 | let patch = classToFunctional(classTemplate, classComp.body[0])
75 | expect(Array.isArray(patch)).toBe(true)
76 | expect(patch).toMatchSnapshot()
77 | let output = patchString(classCompVariant, patch)
78 | expect(removeSpaces(output)).toBe(removeSpaces(functionalCompVariant))
79 | })
80 | })
81 | })
82 | })
83 |
--------------------------------------------------------------------------------
/packages/react-refactor/test/stringUtils.spec.js:
--------------------------------------------------------------------------------
1 | const {patchString, INSERT, REMOVE, insert, remove} = require('../src/stringUtils');
2 | const alphabet = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ';
3 |
4 | describe('patchString', () => {
5 | it('should insert a new part', () => {
6 | const patch = [
7 | {operation: INSERT, start: 5, payload: 'xxabcdxx'}
8 | ];
9 | expect(patchString(alphabet, patch)).toBe('ABCDExxabcdxxFGHIJKLMNOPQRSTUVWXYZ')
10 | });
11 |
12 | it('should remove a part', () => {
13 | const patch = [
14 | {operation: REMOVE, start: 5, end: 10}
15 | ];
16 | expect(patchString(alphabet, patch)).toBe('ABCDEKLMNOPQRSTUVWXYZ')
17 | });
18 |
19 | it('should apply differents patches (ir)', () => {
20 | const patch = [
21 | insert(0, 'xxabcdxx'),
22 | remove(2, 5), //CDE
23 | ];
24 | expect(patchString(alphabet, patch)).toBe('xxabcdxxABFGHIJKLMNOPQRSTUVWXYZ')
25 | })
26 |
27 | it('should apply differents patches (irir)', () => {
28 | const patch = [
29 | insert(0, 'xxabcdxx'),
30 | remove(2, 5), //CDE
31 | insert(5, 'yyefghyy'),
32 | remove(5, 9), //EFGH
33 | ];
34 |
35 | expect(patchString(alphabet, patch)).toBe('xxabcdxxAByyefghyyJKLMNOPQRSTUVWXYZ')
36 | })
37 |
38 | it('should apply differents patches (rrirri)', () => {
39 | let patch = []
40 |
41 | //rem AB
42 | patch.push(remove(0, 2))
43 | expect(patchString(alphabet, patch)).toBe('CDEFGHIJKLMNOPQRSTUVWXYZ')
44 |
45 | //rem FG
46 | patch.push(remove(5, 7))
47 | expect(patchString(alphabet, patch)).toBe('CDEHIJKLMNOPQRSTUVWXYZ')
48 |
49 | //ins yy123yy
50 | patch.push(insert(11, 'yy123yy'))
51 | expect(patchString(alphabet, patch)).toBe('CDEHIJKyy123yyLMNOPQRSTUVWXYZ')
52 |
53 | //rem L
54 | patch.push(remove(11, 12))
55 | expect(patchString(alphabet, patch)).toBe('CDEHIJKyy123yyMNOPQRSTUVWXYZ')
56 |
57 | //rem Z
58 | patch.push(remove(25, 26))
59 | expect(patchString(alphabet, patch)).toBe('CDEHIJKyy123yyMNOPQRSTUVWXY')
60 |
61 | //ins xx456xx
62 | patch.push(insert(26, 'xx456xx'))
63 | expect(patchString(alphabet, patch)).toBe('CDEHIJKyy123yyMNOPQRSTUVWXYxx456xx')
64 | })
65 |
66 | it('should throw expection', () => {
67 | expect(() => {
68 | patchString(alphabet, [{start: 20, end: 5}])
69 | }).toThrowError('StartGTEndError')
70 |
71 | expect(() => {
72 | patchString(alphabet, [{start: 50}])
73 | }).toThrowError('StartGTLengthError')
74 |
75 | expect(() => {
76 | patchString(alphabet, [insert(20, 'hei'), insert(19, 'ciao')])
77 | }).toThrowError('PatchesUnsortedError')
78 |
79 | expect(() => {
80 | patchString(alphabet, [{operation: 'ciao', start: 20}])
81 | }).toThrowError('UnsupportedOperationError')
82 | })
83 | });
84 |
--------------------------------------------------------------------------------
/packages/react-refactor/test/functionalToClass.spec.js:
--------------------------------------------------------------------------------
1 | const {NotAReactComponent} = require("../src/errors");
2 | const {patchString} = require("../src/stringUtils");
3 | const {removeSpaces} = require("./testUtils");
4 | const {parse} = require('../src/parser')
5 | const {functionalToClass} = require('../src/functionalToClass')
6 |
7 | const functionalTemplate1 = `
8 | function FunctionalComp(props){
9 | return (
10 | {props.abc}
11 | );
12 | }
13 | `
14 | const classTemplate1 = `
15 | class FunctionalComp extends React.Component {
16 | render() {
17 | let props = this.props;
18 | return (
19 | {props.abc}
20 | );
21 | }
22 | }
23 | `.trim()
24 |
25 | const functionalTemplate2 = `
26 | function FunctionalComp({abc, cde}){
27 | return (
28 | {abc}
29 | );
30 | }
31 | `
32 | const classTemplate2 = `
33 | class FunctionalComp extends React.Component {
34 | render() {
35 | let {abc, cde} = this.props;
36 | return (
37 | {abc}
38 | );
39 | }
40 | }
41 | `.trim()
42 |
43 | const functionalTemplate3 = `
44 | function FunctionalComp(){
45 | return (
46 | blablabla
47 | );
48 | }
49 | `
50 | const classTemplate3 = `
51 | class FunctionalComp extends React.Component {
52 | render() {
53 | return (
54 | blablabla
55 | );
56 | }
57 | }
58 | `.trim()
59 |
60 | const genericFunction = `
61 | function testFunc(props){
62 | return 'Saluti from Rome!'
63 | }
64 | `
65 |
66 |
67 | describe('functionalToClass', () => {
68 | it('should convert functional to class comp', () => {
69 | let classComp = parse(functionalTemplate1)
70 | let patch = functionalToClass(functionalTemplate1, classComp.body[0])
71 | expect(Array.isArray(patch)).toBe(true)
72 | expect(patch).toMatchSnapshot()
73 | let output = patchString(functionalTemplate1, patch)
74 | expect(removeSpaces(output)).toBe(removeSpaces(classTemplate1))
75 | })
76 |
77 | it('should convert functional to class comp with destructuring', () => {
78 | let classComp = parse(functionalTemplate2)
79 | let patch = functionalToClass(functionalTemplate2, classComp.body[0])
80 | expect(Array.isArray(patch)).toBe(true)
81 | expect(patch).toMatchSnapshot()
82 | let output = patchString(functionalTemplate2, patch)
83 | expect(removeSpaces(output)).toBe(removeSpaces(classTemplate2))
84 | })
85 |
86 | it('should convert functional to class comp without props', () => {
87 | let classComp = parse(functionalTemplate3)
88 | let patch = functionalToClass(functionalTemplate3, classComp.body[0])
89 | expect(Array.isArray(patch)).toBe(true)
90 | expect(patch).toMatchSnapshot()
91 | let output = patchString(functionalTemplate3, patch)
92 | expect(removeSpaces(output)).toBe(removeSpaces(classTemplate3))
93 | })
94 |
95 | it('should throw', () => {
96 | let parsedFunc = parse(genericFunction)
97 | expect(() => {
98 | functionalToClass(genericFunction, parsedFunc.body[0])
99 | }).toThrow(NotAReactComponent)
100 | })
101 | })
102 |
--------------------------------------------------------------------------------
/packages/react-refactor/test/__snapshots__/index.spec.js.snap:
--------------------------------------------------------------------------------
1 | // Jest Snapshot v1, https://goo.gl/fbAQLP
2 |
3 | exports[`refactor should convert 1`] = `
4 | Array [
5 | Object {
6 | "operation": "insert",
7 | "payload": "class funcComp extends React.Component {",
8 | "start": 0,
9 | },
10 | Object {
11 | "operation": "insert",
12 | "payload": "render()",
13 | "start": 0,
14 | },
15 | Object {
16 | "end": 25,
17 | "operation": "remove",
18 | "start": 0,
19 | },
20 | Object {
21 | "operation": "insert",
22 | "payload": "
23 | let props = this.props;",
24 | "start": 26,
25 | },
26 | Object {
27 | "operation": "insert",
28 | "payload": "}",
29 | "start": 53,
30 | },
31 | Object {
32 | "operation": "insert",
33 | "payload": "class funcCompExport extends React.Component {",
34 | "start": 70,
35 | },
36 | Object {
37 | "operation": "insert",
38 | "payload": "render()",
39 | "start": 70,
40 | },
41 | Object {
42 | "end": 101,
43 | "operation": "remove",
44 | "start": 70,
45 | },
46 | Object {
47 | "operation": "insert",
48 | "payload": "
49 | let props = this.props;",
50 | "start": 102,
51 | },
52 | Object {
53 | "operation": "insert",
54 | "payload": "}",
55 | "start": 129,
56 | },
57 | Object {
58 | "operation": "insert",
59 | "payload": "function ClassComp(props)",
60 | "start": 131,
61 | },
62 | Object {
63 | "end": 273,
64 | "operation": "remove",
65 | "start": 131,
66 | },
67 | Object {
68 | "operation": "insert",
69 | "payload": "
70 | let self = {props};
71 | ",
72 | "start": 274,
73 | },
74 | Object {
75 | "operation": "insert",
76 | "payload": "self",
77 | "start": 291,
78 | },
79 | Object {
80 | "end": 295,
81 | "operation": "remove",
82 | "start": 291,
83 | },
84 | Object {
85 | "operation": "insert",
86 | "payload": "self",
87 | "start": 327,
88 | },
89 | Object {
90 | "end": 331,
91 | "operation": "remove",
92 | "start": 327,
93 | },
94 | Object {
95 | "operation": "insert",
96 | "payload": "self",
97 | "start": 357,
98 | },
99 | Object {
100 | "end": 361,
101 | "operation": "remove",
102 | "start": 357,
103 | },
104 | Object {
105 | "end": 391,
106 | "operation": "remove",
107 | "start": 389,
108 | },
109 | Object {
110 | "operation": "insert",
111 | "payload": "function ClassCompExport(props)",
112 | "start": 400,
113 | },
114 | Object {
115 | "end": 548,
116 | "operation": "remove",
117 | "start": 400,
118 | },
119 | Object {
120 | "operation": "insert",
121 | "payload": "
122 | let self = {props};
123 | ",
124 | "start": 549,
125 | },
126 | Object {
127 | "operation": "insert",
128 | "payload": "self",
129 | "start": 566,
130 | },
131 | Object {
132 | "end": 570,
133 | "operation": "remove",
134 | "start": 566,
135 | },
136 | Object {
137 | "operation": "insert",
138 | "payload": "self",
139 | "start": 602,
140 | },
141 | Object {
142 | "end": 606,
143 | "operation": "remove",
144 | "start": 602,
145 | },
146 | Object {
147 | "operation": "insert",
148 | "payload": "self",
149 | "start": 632,
150 | },
151 | Object {
152 | "end": 636,
153 | "operation": "remove",
154 | "start": 632,
155 | },
156 | Object {
157 | "end": 666,
158 | "operation": "remove",
159 | "start": 664,
160 | },
161 | ]
162 | `;
163 |
164 | exports[`refactor should convert 2`] = `
165 | Array [
166 | Object {
167 | "end": 708,
168 | "message": "This isn't a React component",
169 | "stack": [Error: This isn't a React component],
170 | "start": 668,
171 | "type": "FunctionDeclaration",
172 | },
173 | Object {
174 | "end": 760,
175 | "message": "This isn't a React component",
176 | "stack": [Error: This isn't a React component],
177 | "start": 710,
178 | "type": "FunctionDeclaration",
179 | },
180 | ]
181 | `;
182 |
--------------------------------------------------------------------------------
/packages/react-refactor/test/__snapshots__/classToFunctional.spec.js.snap:
--------------------------------------------------------------------------------
1 | // Jest Snapshot v1, https://goo.gl/fbAQLP
2 |
3 | exports[`classToFunctional should convert class to functional comp 1`] = `
4 | Array [
5 | Object {
6 | "operation": "insert",
7 | "payload": "function ClassComp(props)",
8 | "start": 0,
9 | },
10 | Object {
11 | "end": 139,
12 | "operation": "remove",
13 | "start": 0,
14 | },
15 | Object {
16 | "operation": "insert",
17 | "payload": "
18 | let self = {props};
19 | ",
20 | "start": 140,
21 | },
22 | Object {
23 | "operation": "insert",
24 | "payload": "self",
25 | "start": 157,
26 | },
27 | Object {
28 | "end": 161,
29 | "operation": "remove",
30 | "start": 157,
31 | },
32 | Object {
33 | "operation": "insert",
34 | "payload": "self",
35 | "start": 193,
36 | },
37 | Object {
38 | "end": 197,
39 | "operation": "remove",
40 | "start": 193,
41 | },
42 | Object {
43 | "operation": "insert",
44 | "payload": "self",
45 | "start": 223,
46 | },
47 | Object {
48 | "end": 227,
49 | "operation": "remove",
50 | "start": 223,
51 | },
52 | Object {
53 | "end": 257,
54 | "operation": "remove",
55 | "start": 255,
56 | },
57 | ]
58 | `;
59 |
60 | exports[`classToFunctional should supports different superclass should support Component 1`] = `
61 | Array [
62 | Object {
63 | "operation": "insert",
64 | "payload": "function ClassComp(props)",
65 | "start": 0,
66 | },
67 | Object {
68 | "end": 133,
69 | "operation": "remove",
70 | "start": 0,
71 | },
72 | Object {
73 | "operation": "insert",
74 | "payload": "
75 | let self = {props};
76 | ",
77 | "start": 134,
78 | },
79 | Object {
80 | "operation": "insert",
81 | "payload": "self",
82 | "start": 151,
83 | },
84 | Object {
85 | "end": 155,
86 | "operation": "remove",
87 | "start": 151,
88 | },
89 | Object {
90 | "operation": "insert",
91 | "payload": "self",
92 | "start": 187,
93 | },
94 | Object {
95 | "end": 191,
96 | "operation": "remove",
97 | "start": 187,
98 | },
99 | Object {
100 | "operation": "insert",
101 | "payload": "self",
102 | "start": 217,
103 | },
104 | Object {
105 | "end": 221,
106 | "operation": "remove",
107 | "start": 217,
108 | },
109 | Object {
110 | "end": 251,
111 | "operation": "remove",
112 | "start": 249,
113 | },
114 | ]
115 | `;
116 |
117 | exports[`classToFunctional should supports different superclass should support PureComponent 1`] = `
118 | Array [
119 | Object {
120 | "operation": "insert",
121 | "payload": "function ClassComp(props)",
122 | "start": 0,
123 | },
124 | Object {
125 | "end": 137,
126 | "operation": "remove",
127 | "start": 0,
128 | },
129 | Object {
130 | "operation": "insert",
131 | "payload": "
132 | let self = {props};
133 | ",
134 | "start": 138,
135 | },
136 | Object {
137 | "operation": "insert",
138 | "payload": "self",
139 | "start": 155,
140 | },
141 | Object {
142 | "end": 159,
143 | "operation": "remove",
144 | "start": 155,
145 | },
146 | Object {
147 | "operation": "insert",
148 | "payload": "self",
149 | "start": 191,
150 | },
151 | Object {
152 | "end": 195,
153 | "operation": "remove",
154 | "start": 191,
155 | },
156 | Object {
157 | "operation": "insert",
158 | "payload": "self",
159 | "start": 221,
160 | },
161 | Object {
162 | "end": 225,
163 | "operation": "remove",
164 | "start": 221,
165 | },
166 | Object {
167 | "end": 255,
168 | "operation": "remove",
169 | "start": 253,
170 | },
171 | ]
172 | `;
173 |
174 | exports[`classToFunctional should supports different superclass should support React.PureComponent 1`] = `
175 | Array [
176 | Object {
177 | "operation": "insert",
178 | "payload": "function ClassComp(props)",
179 | "start": 0,
180 | },
181 | Object {
182 | "end": 143,
183 | "operation": "remove",
184 | "start": 0,
185 | },
186 | Object {
187 | "operation": "insert",
188 | "payload": "
189 | let self = {props};
190 | ",
191 | "start": 144,
192 | },
193 | Object {
194 | "operation": "insert",
195 | "payload": "self",
196 | "start": 161,
197 | },
198 | Object {
199 | "end": 165,
200 | "operation": "remove",
201 | "start": 161,
202 | },
203 | Object {
204 | "operation": "insert",
205 | "payload": "self",
206 | "start": 197,
207 | },
208 | Object {
209 | "end": 201,
210 | "operation": "remove",
211 | "start": 197,
212 | },
213 | Object {
214 | "operation": "insert",
215 | "payload": "self",
216 | "start": 227,
217 | },
218 | Object {
219 | "end": 231,
220 | "operation": "remove",
221 | "start": 227,
222 | },
223 | Object {
224 | "end": 261,
225 | "operation": "remove",
226 | "start": 259,
227 | },
228 | ]
229 | `;
230 |
--------------------------------------------------------------------------------
/packages/react-refactor/test/__snapshots__/parser.spec.js.snap:
--------------------------------------------------------------------------------
1 | // Jest Snapshot v1, https://goo.gl/fbAQLP
2 |
3 | exports[`parser should parse Class file 1`] = `
4 | Node {
5 | "body": Array [
6 | Node {
7 | "end": 26,
8 | "loc": SourceLocation {
9 | "end": Position {
10 | "column": 26,
11 | "line": 1,
12 | },
13 | "start": Position {
14 | "column": 0,
15 | "line": 1,
16 | },
17 | },
18 | "source": Node {
19 | "end": 25,
20 | "extra": Object {
21 | "raw": "'react'",
22 | "rawValue": "react",
23 | },
24 | "loc": SourceLocation {
25 | "end": Position {
26 | "column": 25,
27 | "line": 1,
28 | },
29 | "start": Position {
30 | "column": 18,
31 | "line": 1,
32 | },
33 | },
34 | "start": 18,
35 | "type": "StringLiteral",
36 | "value": "react",
37 | },
38 | "specifiers": Array [
39 | Node {
40 | "end": 12,
41 | "loc": SourceLocation {
42 | "end": Position {
43 | "column": 12,
44 | "line": 1,
45 | },
46 | "start": Position {
47 | "column": 7,
48 | "line": 1,
49 | },
50 | },
51 | "local": Node {
52 | "end": 12,
53 | "loc": SourceLocation {
54 | "end": Position {
55 | "column": 12,
56 | "line": 1,
57 | },
58 | "identifierName": "React",
59 | "start": Position {
60 | "column": 7,
61 | "line": 1,
62 | },
63 | },
64 | "name": "React",
65 | "start": 7,
66 | "type": "Identifier",
67 | },
68 | "start": 7,
69 | "type": "ImportDefaultSpecifier",
70 | },
71 | ],
72 | "start": 0,
73 | "type": "ImportDeclaration",
74 | },
75 | Node {
76 | "end": 62,
77 | "loc": SourceLocation {
78 | "end": Position {
79 | "column": 35,
80 | "line": 2,
81 | },
82 | "start": Position {
83 | "column": 0,
84 | "line": 2,
85 | },
86 | },
87 | "source": Node {
88 | "end": 61,
89 | "extra": Object {
90 | "raw": "'prop-types'",
91 | "rawValue": "prop-types",
92 | },
93 | "loc": SourceLocation {
94 | "end": Position {
95 | "column": 34,
96 | "line": 2,
97 | },
98 | "start": Position {
99 | "column": 22,
100 | "line": 2,
101 | },
102 | },
103 | "start": 49,
104 | "type": "StringLiteral",
105 | "value": "prop-types",
106 | },
107 | "specifiers": Array [
108 | Node {
109 | "end": 43,
110 | "loc": SourceLocation {
111 | "end": Position {
112 | "column": 16,
113 | "line": 2,
114 | },
115 | "start": Position {
116 | "column": 7,
117 | "line": 2,
118 | },
119 | },
120 | "local": Node {
121 | "end": 43,
122 | "loc": SourceLocation {
123 | "end": Position {
124 | "column": 16,
125 | "line": 2,
126 | },
127 | "identifierName": "PropTypes",
128 | "start": Position {
129 | "column": 7,
130 | "line": 2,
131 | },
132 | },
133 | "name": "PropTypes",
134 | "start": 34,
135 | "type": "Identifier",
136 | },
137 | "start": 34,
138 | "type": "ImportDefaultSpecifier",
139 | },
140 | ],
141 | "start": 27,
142 | "type": "ImportDeclaration",
143 | },
144 | Node {
145 | "body": Node {
146 | "body": Array [
147 | Node {
148 | "async": false,
149 | "body": Node {
150 | "body": Array [
151 | Node {
152 | "end": 146,
153 | "expression": Node {
154 | "arguments": Array [
155 | Node {
156 | "end": 144,
157 | "loc": SourceLocation {
158 | "end": Position {
159 | "column": 15,
160 | "line": 6,
161 | },
162 | "identifierName": "props",
163 | "start": Position {
164 | "column": 10,
165 | "line": 6,
166 | },
167 | },
168 | "name": "props",
169 | "start": 139,
170 | "type": "Identifier",
171 | },
172 | ],
173 | "callee": Node {
174 | "end": 138,
175 | "loc": SourceLocation {
176 | "end": Position {
177 | "column": 9,
178 | "line": 6,
179 | },
180 | "start": Position {
181 | "column": 4,
182 | "line": 6,
183 | },
184 | },
185 | "start": 133,
186 | "type": "Super",
187 | },
188 | "end": 145,
189 | "loc": SourceLocation {
190 | "end": Position {
191 | "column": 16,
192 | "line": 6,
193 | },
194 | "start": Position {
195 | "column": 4,
196 | "line": 6,
197 | },
198 | },
199 | "start": 133,
200 | "type": "CallExpression",
201 | },
202 | "loc": SourceLocation {
203 | "end": Position {
204 | "column": 17,
205 | "line": 6,
206 | },
207 | "start": Position {
208 | "column": 4,
209 | "line": 6,
210 | },
211 | },
212 | "start": 133,
213 | "type": "ExpressionStatement",
214 | },
215 | ],
216 | "directives": Array [],
217 | "end": 150,
218 | "loc": SourceLocation {
219 | "end": Position {
220 | "column": 3,
221 | "line": 7,
222 | },
223 | "start": Position {
224 | "column": 21,
225 | "line": 5,
226 | },
227 | },
228 | "start": 127,
229 | "type": "BlockStatement",
230 | },
231 | "computed": false,
232 | "end": 150,
233 | "expression": false,
234 | "generator": false,
235 | "id": null,
236 | "key": Node {
237 | "end": 119,
238 | "loc": SourceLocation {
239 | "end": Position {
240 | "column": 13,
241 | "line": 5,
242 | },
243 | "identifierName": "constructor",
244 | "start": Position {
245 | "column": 2,
246 | "line": 5,
247 | },
248 | },
249 | "name": "constructor",
250 | "start": 108,
251 | "type": "Identifier",
252 | },
253 | "kind": "constructor",
254 | "loc": SourceLocation {
255 | "end": Position {
256 | "column": 3,
257 | "line": 7,
258 | },
259 | "start": Position {
260 | "column": 2,
261 | "line": 5,
262 | },
263 | },
264 | "params": Array [
265 | Node {
266 | "end": 125,
267 | "loc": SourceLocation {
268 | "end": Position {
269 | "column": 19,
270 | "line": 5,
271 | },
272 | "identifierName": "props",
273 | "start": Position {
274 | "column": 14,
275 | "line": 5,
276 | },
277 | },
278 | "name": "props",
279 | "start": 120,
280 | "type": "Identifier",
281 | },
282 | ],
283 | "start": 108,
284 | "static": false,
285 | "type": "ClassMethod",
286 | },
287 | Node {
288 | "async": false,
289 | "body": Node {
290 | "body": Array [
291 | Node {
292 | "argument": Node {
293 | "children": Array [
294 | Node {
295 | "end": 197,
296 | "extra": null,
297 | "loc": SourceLocation {
298 | "end": Position {
299 | "column": 6,
300 | "line": 13,
301 | },
302 | "start": Position {
303 | "column": 11,
304 | "line": 11,
305 | },
306 | },
307 | "start": 189,
308 | "type": "JSXText",
309 | "value": "
310 |
311 | ",
312 | },
313 | ],
314 | "closingElement": Node {
315 | "end": 203,
316 | "loc": SourceLocation {
317 | "end": Position {
318 | "column": 12,
319 | "line": 13,
320 | },
321 | "start": Position {
322 | "column": 6,
323 | "line": 13,
324 | },
325 | },
326 | "name": Node {
327 | "end": 202,
328 | "loc": SourceLocation {
329 | "end": Position {
330 | "column": 11,
331 | "line": 13,
332 | },
333 | "start": Position {
334 | "column": 8,
335 | "line": 13,
336 | },
337 | },
338 | "name": "div",
339 | "start": 199,
340 | "type": "JSXIdentifier",
341 | },
342 | "start": 197,
343 | "type": "JSXClosingElement",
344 | },
345 | "end": 203,
346 | "extra": Object {
347 | "parenStart": 176,
348 | "parenthesized": true,
349 | },
350 | "loc": SourceLocation {
351 | "end": Position {
352 | "column": 12,
353 | "line": 13,
354 | },
355 | "start": Position {
356 | "column": 6,
357 | "line": 11,
358 | },
359 | },
360 | "openingElement": Node {
361 | "attributes": Array [],
362 | "end": 189,
363 | "loc": SourceLocation {
364 | "end": Position {
365 | "column": 11,
366 | "line": 11,
367 | },
368 | "start": Position {
369 | "column": 6,
370 | "line": 11,
371 | },
372 | },
373 | "name": Node {
374 | "end": 188,
375 | "loc": SourceLocation {
376 | "end": Position {
377 | "column": 10,
378 | "line": 11,
379 | },
380 | "start": Position {
381 | "column": 7,
382 | "line": 11,
383 | },
384 | },
385 | "name": "div",
386 | "start": 185,
387 | "type": "JSXIdentifier",
388 | },
389 | "selfClosing": false,
390 | "start": 184,
391 | "type": "JSXOpeningElement",
392 | },
393 | "start": 184,
394 | "type": "JSXElement",
395 | },
396 | "end": 210,
397 | "loc": SourceLocation {
398 | "end": Position {
399 | "column": 6,
400 | "line": 14,
401 | },
402 | "start": Position {
403 | "column": 4,
404 | "line": 10,
405 | },
406 | },
407 | "start": 169,
408 | "type": "ReturnStatement",
409 | },
410 | ],
411 | "directives": Array [],
412 | "end": 214,
413 | "loc": SourceLocation {
414 | "end": Position {
415 | "column": 3,
416 | "line": 15,
417 | },
418 | "start": Position {
419 | "column": 11,
420 | "line": 9,
421 | },
422 | },
423 | "start": 163,
424 | "type": "BlockStatement",
425 | },
426 | "computed": false,
427 | "end": 214,
428 | "expression": false,
429 | "generator": false,
430 | "id": null,
431 | "key": Node {
432 | "end": 160,
433 | "loc": SourceLocation {
434 | "end": Position {
435 | "column": 8,
436 | "line": 9,
437 | },
438 | "identifierName": "render",
439 | "start": Position {
440 | "column": 2,
441 | "line": 9,
442 | },
443 | },
444 | "name": "render",
445 | "start": 154,
446 | "type": "Identifier",
447 | },
448 | "kind": "method",
449 | "loc": SourceLocation {
450 | "end": Position {
451 | "column": 3,
452 | "line": 15,
453 | },
454 | "start": Position {
455 | "column": 2,
456 | "line": 9,
457 | },
458 | },
459 | "params": Array [],
460 | "start": 154,
461 | "static": false,
462 | "type": "ClassMethod",
463 | },
464 | ],
465 | "end": 216,
466 | "loc": SourceLocation {
467 | "end": Position {
468 | "column": 1,
469 | "line": 16,
470 | },
471 | "start": Position {
472 | "column": 40,
473 | "line": 4,
474 | },
475 | },
476 | "start": 104,
477 | "type": "ClassBody",
478 | },
479 | "end": 216,
480 | "id": Node {
481 | "end": 79,
482 | "loc": SourceLocation {
483 | "end": Position {
484 | "column": 15,
485 | "line": 4,
486 | },
487 | "identifierName": "ClassComp",
488 | "start": Position {
489 | "column": 6,
490 | "line": 4,
491 | },
492 | },
493 | "name": "ClassComp",
494 | "start": 70,
495 | "type": "Identifier",
496 | },
497 | "loc": SourceLocation {
498 | "end": Position {
499 | "column": 1,
500 | "line": 16,
501 | },
502 | "start": Position {
503 | "column": 0,
504 | "line": 4,
505 | },
506 | },
507 | "start": 64,
508 | "superClass": Node {
509 | "computed": false,
510 | "end": 103,
511 | "loc": SourceLocation {
512 | "end": Position {
513 | "column": 39,
514 | "line": 4,
515 | },
516 | "start": Position {
517 | "column": 24,
518 | "line": 4,
519 | },
520 | },
521 | "object": Node {
522 | "end": 93,
523 | "loc": SourceLocation {
524 | "end": Position {
525 | "column": 29,
526 | "line": 4,
527 | },
528 | "identifierName": "React",
529 | "start": Position {
530 | "column": 24,
531 | "line": 4,
532 | },
533 | },
534 | "name": "React",
535 | "start": 88,
536 | "type": "Identifier",
537 | },
538 | "property": Node {
539 | "end": 103,
540 | "loc": SourceLocation {
541 | "end": Position {
542 | "column": 39,
543 | "line": 4,
544 | },
545 | "identifierName": "Component",
546 | "start": Position {
547 | "column": 30,
548 | "line": 4,
549 | },
550 | },
551 | "name": "Component",
552 | "start": 94,
553 | "type": "Identifier",
554 | },
555 | "start": 88,
556 | "type": "MemberExpression",
557 | },
558 | "type": "ClassDeclaration",
559 | },
560 | Node {
561 | "end": 243,
562 | "expression": Node {
563 | "end": 242,
564 | "left": Node {
565 | "computed": false,
566 | "end": 237,
567 | "loc": SourceLocation {
568 | "end": Position {
569 | "column": 19,
570 | "line": 18,
571 | },
572 | "start": Position {
573 | "column": 0,
574 | "line": 18,
575 | },
576 | },
577 | "object": Node {
578 | "end": 227,
579 | "loc": SourceLocation {
580 | "end": Position {
581 | "column": 9,
582 | "line": 18,
583 | },
584 | "identifierName": "ClassComp",
585 | "start": Position {
586 | "column": 0,
587 | "line": 18,
588 | },
589 | },
590 | "name": "ClassComp",
591 | "start": 218,
592 | "type": "Identifier",
593 | },
594 | "property": Node {
595 | "end": 237,
596 | "loc": SourceLocation {
597 | "end": Position {
598 | "column": 19,
599 | "line": 18,
600 | },
601 | "identifierName": "propTypes",
602 | "start": Position {
603 | "column": 10,
604 | "line": 18,
605 | },
606 | },
607 | "name": "propTypes",
608 | "start": 228,
609 | "type": "Identifier",
610 | },
611 | "start": 218,
612 | "type": "MemberExpression",
613 | },
614 | "loc": SourceLocation {
615 | "end": Position {
616 | "column": 24,
617 | "line": 18,
618 | },
619 | "start": Position {
620 | "column": 0,
621 | "line": 18,
622 | },
623 | },
624 | "operator": "=",
625 | "right": Node {
626 | "end": 242,
627 | "loc": SourceLocation {
628 | "end": Position {
629 | "column": 24,
630 | "line": 18,
631 | },
632 | "start": Position {
633 | "column": 22,
634 | "line": 18,
635 | },
636 | },
637 | "properties": Array [],
638 | "start": 240,
639 | "type": "ObjectExpression",
640 | },
641 | "start": 218,
642 | "type": "AssignmentExpression",
643 | },
644 | "loc": SourceLocation {
645 | "end": Position {
646 | "column": 25,
647 | "line": 18,
648 | },
649 | "start": Position {
650 | "column": 0,
651 | "line": 18,
652 | },
653 | },
654 | "start": 218,
655 | "type": "ExpressionStatement",
656 | },
657 | Node {
658 | "end": 272,
659 | "expression": Node {
660 | "end": 271,
661 | "left": Node {
662 | "computed": false,
663 | "end": 266,
664 | "loc": SourceLocation {
665 | "end": Position {
666 | "column": 22,
667 | "line": 19,
668 | },
669 | "start": Position {
670 | "column": 0,
671 | "line": 19,
672 | },
673 | },
674 | "object": Node {
675 | "end": 253,
676 | "loc": SourceLocation {
677 | "end": Position {
678 | "column": 9,
679 | "line": 19,
680 | },
681 | "identifierName": "ClassComp",
682 | "start": Position {
683 | "column": 0,
684 | "line": 19,
685 | },
686 | },
687 | "name": "ClassComp",
688 | "start": 244,
689 | "type": "Identifier",
690 | },
691 | "property": Node {
692 | "end": 266,
693 | "loc": SourceLocation {
694 | "end": Position {
695 | "column": 22,
696 | "line": 19,
697 | },
698 | "identifierName": "defaultProps",
699 | "start": Position {
700 | "column": 10,
701 | "line": 19,
702 | },
703 | },
704 | "name": "defaultProps",
705 | "start": 254,
706 | "type": "Identifier",
707 | },
708 | "start": 244,
709 | "type": "MemberExpression",
710 | },
711 | "loc": SourceLocation {
712 | "end": Position {
713 | "column": 27,
714 | "line": 19,
715 | },
716 | "start": Position {
717 | "column": 0,
718 | "line": 19,
719 | },
720 | },
721 | "operator": "=",
722 | "right": Node {
723 | "end": 271,
724 | "loc": SourceLocation {
725 | "end": Position {
726 | "column": 27,
727 | "line": 19,
728 | },
729 | "start": Position {
730 | "column": 25,
731 | "line": 19,
732 | },
733 | },
734 | "properties": Array [],
735 | "start": 269,
736 | "type": "ObjectExpression",
737 | },
738 | "start": 244,
739 | "type": "AssignmentExpression",
740 | },
741 | "loc": SourceLocation {
742 | "end": Position {
743 | "column": 28,
744 | "line": 19,
745 | },
746 | "start": Position {
747 | "column": 0,
748 | "line": 19,
749 | },
750 | },
751 | "start": 244,
752 | "type": "ExpressionStatement",
753 | },
754 | Node {
755 | "declaration": Node {
756 | "end": 299,
757 | "loc": SourceLocation {
758 | "end": Position {
759 | "column": 24,
760 | "line": 22,
761 | },
762 | "identifierName": "ClassComp",
763 | "start": Position {
764 | "column": 15,
765 | "line": 22,
766 | },
767 | },
768 | "name": "ClassComp",
769 | "start": 290,
770 | "type": "Identifier",
771 | },
772 | "end": 300,
773 | "loc": SourceLocation {
774 | "end": Position {
775 | "column": 25,
776 | "line": 22,
777 | },
778 | "start": Position {
779 | "column": 0,
780 | "line": 22,
781 | },
782 | },
783 | "start": 275,
784 | "type": "ExportDefaultDeclaration",
785 | },
786 | ],
787 | "directives": Array [],
788 | "end": 301,
789 | "loc": SourceLocation {
790 | "end": Position {
791 | "column": 0,
792 | "line": 23,
793 | },
794 | "start": Position {
795 | "column": 0,
796 | "line": 1,
797 | },
798 | },
799 | "sourceType": "module",
800 | "start": 0,
801 | "type": "Program",
802 | }
803 | `;
804 |
805 | exports[`parser should parse Func file 1`] = `
806 | Node {
807 | "body": Array [
808 | Node {
809 | "end": 26,
810 | "loc": SourceLocation {
811 | "end": Position {
812 | "column": 26,
813 | "line": 1,
814 | },
815 | "start": Position {
816 | "column": 0,
817 | "line": 1,
818 | },
819 | },
820 | "source": Node {
821 | "end": 25,
822 | "extra": Object {
823 | "raw": "'react'",
824 | "rawValue": "react",
825 | },
826 | "loc": SourceLocation {
827 | "end": Position {
828 | "column": 25,
829 | "line": 1,
830 | },
831 | "start": Position {
832 | "column": 18,
833 | "line": 1,
834 | },
835 | },
836 | "start": 18,
837 | "type": "StringLiteral",
838 | "value": "react",
839 | },
840 | "specifiers": Array [
841 | Node {
842 | "end": 12,
843 | "loc": SourceLocation {
844 | "end": Position {
845 | "column": 12,
846 | "line": 1,
847 | },
848 | "start": Position {
849 | "column": 7,
850 | "line": 1,
851 | },
852 | },
853 | "local": Node {
854 | "end": 12,
855 | "loc": SourceLocation {
856 | "end": Position {
857 | "column": 12,
858 | "line": 1,
859 | },
860 | "identifierName": "React",
861 | "start": Position {
862 | "column": 7,
863 | "line": 1,
864 | },
865 | },
866 | "name": "React",
867 | "start": 7,
868 | "type": "Identifier",
869 | },
870 | "start": 7,
871 | "type": "ImportDefaultSpecifier",
872 | },
873 | ],
874 | "start": 0,
875 | "type": "ImportDeclaration",
876 | },
877 | Node {
878 | "end": 62,
879 | "loc": SourceLocation {
880 | "end": Position {
881 | "column": 35,
882 | "line": 2,
883 | },
884 | "start": Position {
885 | "column": 0,
886 | "line": 2,
887 | },
888 | },
889 | "source": Node {
890 | "end": 61,
891 | "extra": Object {
892 | "raw": "'prop-types'",
893 | "rawValue": "prop-types",
894 | },
895 | "loc": SourceLocation {
896 | "end": Position {
897 | "column": 34,
898 | "line": 2,
899 | },
900 | "start": Position {
901 | "column": 22,
902 | "line": 2,
903 | },
904 | },
905 | "start": 49,
906 | "type": "StringLiteral",
907 | "value": "prop-types",
908 | },
909 | "specifiers": Array [
910 | Node {
911 | "end": 43,
912 | "loc": SourceLocation {
913 | "end": Position {
914 | "column": 16,
915 | "line": 2,
916 | },
917 | "start": Position {
918 | "column": 7,
919 | "line": 2,
920 | },
921 | },
922 | "local": Node {
923 | "end": 43,
924 | "loc": SourceLocation {
925 | "end": Position {
926 | "column": 16,
927 | "line": 2,
928 | },
929 | "identifierName": "PropTypes",
930 | "start": Position {
931 | "column": 7,
932 | "line": 2,
933 | },
934 | },
935 | "name": "PropTypes",
936 | "start": 34,
937 | "type": "Identifier",
938 | },
939 | "start": 34,
940 | "type": "ImportDefaultSpecifier",
941 | },
942 | ],
943 | "start": 27,
944 | "type": "ImportDeclaration",
945 | },
946 | Node {
947 | "async": false,
948 | "body": Node {
949 | "body": Array [
950 | Node {
951 | "argument": Node {
952 | "children": Array [
953 | Node {
954 | "end": 117,
955 | "extra": null,
956 | "loc": SourceLocation {
957 | "end": Position {
958 | "column": 4,
959 | "line": 8,
960 | },
961 | "start": Position {
962 | "column": 9,
963 | "line": 6,
964 | },
965 | },
966 | "start": 111,
967 | "type": "JSXText",
968 | "value": "
969 |
970 | ",
971 | },
972 | ],
973 | "closingElement": Node {
974 | "end": 123,
975 | "loc": SourceLocation {
976 | "end": Position {
977 | "column": 10,
978 | "line": 8,
979 | },
980 | "start": Position {
981 | "column": 4,
982 | "line": 8,
983 | },
984 | },
985 | "name": Node {
986 | "end": 122,
987 | "loc": SourceLocation {
988 | "end": Position {
989 | "column": 9,
990 | "line": 8,
991 | },
992 | "start": Position {
993 | "column": 6,
994 | "line": 8,
995 | },
996 | },
997 | "name": "div",
998 | "start": 119,
999 | "type": "JSXIdentifier",
1000 | },
1001 | "start": 117,
1002 | "type": "JSXClosingElement",
1003 | },
1004 | "end": 123,
1005 | "extra": Object {
1006 | "parenStart": 100,
1007 | "parenthesized": true,
1008 | },
1009 | "loc": SourceLocation {
1010 | "end": Position {
1011 | "column": 10,
1012 | "line": 8,
1013 | },
1014 | "start": Position {
1015 | "column": 4,
1016 | "line": 6,
1017 | },
1018 | },
1019 | "openingElement": Node {
1020 | "attributes": Array [],
1021 | "end": 111,
1022 | "loc": SourceLocation {
1023 | "end": Position {
1024 | "column": 9,
1025 | "line": 6,
1026 | },
1027 | "start": Position {
1028 | "column": 4,
1029 | "line": 6,
1030 | },
1031 | },
1032 | "name": Node {
1033 | "end": 110,
1034 | "loc": SourceLocation {
1035 | "end": Position {
1036 | "column": 8,
1037 | "line": 6,
1038 | },
1039 | "start": Position {
1040 | "column": 5,
1041 | "line": 6,
1042 | },
1043 | },
1044 | "name": "div",
1045 | "start": 107,
1046 | "type": "JSXIdentifier",
1047 | },
1048 | "selfClosing": false,
1049 | "start": 106,
1050 | "type": "JSXOpeningElement",
1051 | },
1052 | "start": 106,
1053 | "type": "JSXElement",
1054 | },
1055 | "end": 128,
1056 | "loc": SourceLocation {
1057 | "end": Position {
1058 | "column": 4,
1059 | "line": 9,
1060 | },
1061 | "start": Position {
1062 | "column": 2,
1063 | "line": 5,
1064 | },
1065 | },
1066 | "start": 93,
1067 | "type": "ReturnStatement",
1068 | },
1069 | ],
1070 | "directives": Array [],
1071 | "end": 130,
1072 | "loc": SourceLocation {
1073 | "end": Position {
1074 | "column": 1,
1075 | "line": 10,
1076 | },
1077 | "start": Position {
1078 | "column": 25,
1079 | "line": 4,
1080 | },
1081 | },
1082 | "start": 89,
1083 | "type": "BlockStatement",
1084 | },
1085 | "end": 130,
1086 | "expression": false,
1087 | "generator": false,
1088 | "id": Node {
1089 | "end": 81,
1090 | "loc": SourceLocation {
1091 | "end": Position {
1092 | "column": 17,
1093 | "line": 4,
1094 | },
1095 | "identifierName": "FuncComp",
1096 | "start": Position {
1097 | "column": 9,
1098 | "line": 4,
1099 | },
1100 | },
1101 | "name": "FuncComp",
1102 | "start": 73,
1103 | "type": "Identifier",
1104 | },
1105 | "loc": SourceLocation {
1106 | "end": Position {
1107 | "column": 1,
1108 | "line": 10,
1109 | },
1110 | "start": Position {
1111 | "column": 0,
1112 | "line": 4,
1113 | },
1114 | },
1115 | "params": Array [
1116 | Node {
1117 | "end": 87,
1118 | "loc": SourceLocation {
1119 | "end": Position {
1120 | "column": 23,
1121 | "line": 4,
1122 | },
1123 | "identifierName": "props",
1124 | "start": Position {
1125 | "column": 18,
1126 | "line": 4,
1127 | },
1128 | },
1129 | "name": "props",
1130 | "start": 82,
1131 | "type": "Identifier",
1132 | },
1133 | ],
1134 | "start": 64,
1135 | "type": "FunctionDeclaration",
1136 | },
1137 | Node {
1138 | "end": 156,
1139 | "expression": Node {
1140 | "end": 155,
1141 | "left": Node {
1142 | "computed": false,
1143 | "end": 150,
1144 | "loc": SourceLocation {
1145 | "end": Position {
1146 | "column": 18,
1147 | "line": 12,
1148 | },
1149 | "start": Position {
1150 | "column": 0,
1151 | "line": 12,
1152 | },
1153 | },
1154 | "object": Node {
1155 | "end": 140,
1156 | "loc": SourceLocation {
1157 | "end": Position {
1158 | "column": 8,
1159 | "line": 12,
1160 | },
1161 | "identifierName": "FuncComp",
1162 | "start": Position {
1163 | "column": 0,
1164 | "line": 12,
1165 | },
1166 | },
1167 | "name": "FuncComp",
1168 | "start": 132,
1169 | "type": "Identifier",
1170 | },
1171 | "property": Node {
1172 | "end": 150,
1173 | "loc": SourceLocation {
1174 | "end": Position {
1175 | "column": 18,
1176 | "line": 12,
1177 | },
1178 | "identifierName": "propTypes",
1179 | "start": Position {
1180 | "column": 9,
1181 | "line": 12,
1182 | },
1183 | },
1184 | "name": "propTypes",
1185 | "start": 141,
1186 | "type": "Identifier",
1187 | },
1188 | "start": 132,
1189 | "type": "MemberExpression",
1190 | },
1191 | "loc": SourceLocation {
1192 | "end": Position {
1193 | "column": 23,
1194 | "line": 12,
1195 | },
1196 | "start": Position {
1197 | "column": 0,
1198 | "line": 12,
1199 | },
1200 | },
1201 | "operator": "=",
1202 | "right": Node {
1203 | "end": 155,
1204 | "loc": SourceLocation {
1205 | "end": Position {
1206 | "column": 23,
1207 | "line": 12,
1208 | },
1209 | "start": Position {
1210 | "column": 21,
1211 | "line": 12,
1212 | },
1213 | },
1214 | "properties": Array [],
1215 | "start": 153,
1216 | "type": "ObjectExpression",
1217 | },
1218 | "start": 132,
1219 | "type": "AssignmentExpression",
1220 | },
1221 | "loc": SourceLocation {
1222 | "end": Position {
1223 | "column": 24,
1224 | "line": 12,
1225 | },
1226 | "start": Position {
1227 | "column": 0,
1228 | "line": 12,
1229 | },
1230 | },
1231 | "start": 132,
1232 | "type": "ExpressionStatement",
1233 | },
1234 | Node {
1235 | "end": 184,
1236 | "expression": Node {
1237 | "end": 183,
1238 | "left": Node {
1239 | "computed": false,
1240 | "end": 178,
1241 | "loc": SourceLocation {
1242 | "end": Position {
1243 | "column": 21,
1244 | "line": 13,
1245 | },
1246 | "start": Position {
1247 | "column": 0,
1248 | "line": 13,
1249 | },
1250 | },
1251 | "object": Node {
1252 | "end": 165,
1253 | "loc": SourceLocation {
1254 | "end": Position {
1255 | "column": 8,
1256 | "line": 13,
1257 | },
1258 | "identifierName": "FuncComp",
1259 | "start": Position {
1260 | "column": 0,
1261 | "line": 13,
1262 | },
1263 | },
1264 | "name": "FuncComp",
1265 | "start": 157,
1266 | "type": "Identifier",
1267 | },
1268 | "property": Node {
1269 | "end": 178,
1270 | "loc": SourceLocation {
1271 | "end": Position {
1272 | "column": 21,
1273 | "line": 13,
1274 | },
1275 | "identifierName": "defaultProps",
1276 | "start": Position {
1277 | "column": 9,
1278 | "line": 13,
1279 | },
1280 | },
1281 | "name": "defaultProps",
1282 | "start": 166,
1283 | "type": "Identifier",
1284 | },
1285 | "start": 157,
1286 | "type": "MemberExpression",
1287 | },
1288 | "loc": SourceLocation {
1289 | "end": Position {
1290 | "column": 26,
1291 | "line": 13,
1292 | },
1293 | "start": Position {
1294 | "column": 0,
1295 | "line": 13,
1296 | },
1297 | },
1298 | "operator": "=",
1299 | "right": Node {
1300 | "end": 183,
1301 | "loc": SourceLocation {
1302 | "end": Position {
1303 | "column": 26,
1304 | "line": 13,
1305 | },
1306 | "start": Position {
1307 | "column": 24,
1308 | "line": 13,
1309 | },
1310 | },
1311 | "properties": Array [],
1312 | "start": 181,
1313 | "type": "ObjectExpression",
1314 | },
1315 | "start": 157,
1316 | "type": "AssignmentExpression",
1317 | },
1318 | "loc": SourceLocation {
1319 | "end": Position {
1320 | "column": 27,
1321 | "line": 13,
1322 | },
1323 | "start": Position {
1324 | "column": 0,
1325 | "line": 13,
1326 | },
1327 | },
1328 | "start": 157,
1329 | "type": "ExpressionStatement",
1330 | },
1331 | Node {
1332 | "declaration": Node {
1333 | "end": 210,
1334 | "loc": SourceLocation {
1335 | "end": Position {
1336 | "column": 23,
1337 | "line": 16,
1338 | },
1339 | "identifierName": "FuncComp",
1340 | "start": Position {
1341 | "column": 15,
1342 | "line": 16,
1343 | },
1344 | },
1345 | "name": "FuncComp",
1346 | "start": 202,
1347 | "type": "Identifier",
1348 | },
1349 | "end": 211,
1350 | "loc": SourceLocation {
1351 | "end": Position {
1352 | "column": 24,
1353 | "line": 16,
1354 | },
1355 | "start": Position {
1356 | "column": 0,
1357 | "line": 16,
1358 | },
1359 | },
1360 | "start": 187,
1361 | "type": "ExportDefaultDeclaration",
1362 | },
1363 | ],
1364 | "directives": Array [],
1365 | "end": 212,
1366 | "loc": SourceLocation {
1367 | "end": Position {
1368 | "column": 0,
1369 | "line": 17,
1370 | },
1371 | "start": Position {
1372 | "column": 0,
1373 | "line": 1,
1374 | },
1375 | },
1376 | "sourceType": "module",
1377 | "start": 0,
1378 | "type": "Program",
1379 | }
1380 | `;
1381 |
--------------------------------------------------------------------------------