├── .editorconfig ├── .eslintignore ├── .eslintrc.js ├── .github └── workflows │ └── publish.yml ├── .gitignore ├── LICENSE ├── README.md ├── commitlint.config.js ├── package.json ├── src ├── index.js ├── options.js └── styled-components.js ├── test ├── README.md ├── fixtures │ └── react │ │ ├── arrow-anonymous-fragment │ │ ├── actual.js │ │ ├── expected.js │ │ └── options.json │ │ ├── arrow-anonymous-react-fragment │ │ ├── actual.js │ │ ├── expected.js │ │ └── options.json │ │ ├── arrow-fragment │ │ ├── actual.js │ │ ├── expected.js │ │ └── options.json │ │ ├── arrow-noreturn-fragment │ │ ├── actual.js │ │ ├── expected.js │ │ └── options.json │ │ ├── arrow-noreturn-react-fragment │ │ ├── actual.js │ │ ├── expected.js │ │ └── options.json │ │ ├── arrow-noreturn │ │ ├── actual.js │ │ ├── expected.js │ │ └── options.json │ │ ├── arrow-react-fragment │ │ ├── actual.js │ │ ├── expected.js │ │ └── options.json │ │ ├── arrow │ │ ├── actual.js │ │ ├── expected.js │ │ └── options.json │ │ ├── component-fragment │ │ ├── actual.js │ │ ├── expected.js │ │ └── options.json │ │ ├── component-react-fragment │ │ ├── actual.js │ │ ├── expected.js │ │ └── options.json │ │ ├── component │ │ ├── actual.js │ │ ├── expected.js │ │ └── options.json │ │ ├── custom-attribute │ │ ├── actual.js │ │ ├── expected.js │ │ └── options.json │ │ ├── forward-ref │ │ ├── actual.js │ │ ├── expected.js │ │ └── options.json │ │ ├── nonJSX │ │ ├── actual.js │ │ ├── expected.js │ │ └── options.json │ │ ├── option-attribute │ │ ├── actual.js │ │ ├── expected.js │ │ └── options.json │ │ ├── option-format │ │ ├── actual.js │ │ ├── expected.js │ │ └── options.json │ │ ├── pure │ │ ├── actual.js │ │ ├── expected.js │ │ └── options.json │ │ ├── pureComponent-fragment │ │ ├── actual.js │ │ ├── expected.js │ │ └── options.json │ │ ├── pureComponent-react-fragment │ │ ├── actual.js │ │ ├── expected.js │ │ └── options.json │ │ ├── rawfunction-fragment │ │ ├── actual.js │ │ ├── expected.js │ │ └── options.json │ │ ├── rawfunction-react-fragment │ │ ├── actual.js │ │ ├── expected.js │ │ └── options.json │ │ ├── rawfunction │ │ ├── actual.js │ │ ├── expected.js │ │ └── options.json │ │ └── styled-components │ │ ├── actual.js │ │ ├── expected.js │ │ └── options.json └── index.js └── yarn.lock /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | indent_style = space 5 | indent_size = 2 6 | end_of_line = lf 7 | charset = utf-8 8 | trim_trailing_whitespace = true 9 | insert_final_newline = true 10 | 11 | [*.md] 12 | trim_trailing_whitespace = false 13 | -------------------------------------------------------------------------------- /.eslintignore: -------------------------------------------------------------------------------- 1 | **/node_modules/** 2 | **/lib/** 3 | test/fixtures/** 4 | -------------------------------------------------------------------------------- /.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | extends: 'davesnx-rules' 3 | } 4 | -------------------------------------------------------------------------------- /.github/workflows/publish.yml: -------------------------------------------------------------------------------- 1 | name: publish 2 | 3 | on: 4 | pull_request: 5 | branches: 6 | - master 7 | push: 8 | branches: 9 | - master 10 | 11 | jobs: 12 | install: 13 | runs-on: ubuntu-18.04 14 | steps: 15 | - uses: actions/checkout@v1 16 | - uses: actions/setup-node@v1 17 | with: 18 | node-version: 12 19 | - run: | 20 | yarn install 21 | yarn test 22 | - name: Release 23 | run: | 24 | yarn build 25 | echo "//registry.npmjs.org/:_authToken=${NPM_TOKEN}" 26 | npx semantic-release 27 | env: 28 | NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} 29 | NPM_TOKEN: ${{ secrets.NPM_TOKEN }} 30 | GH_TOKEN: ${{ secrets.GH_TOKEN }} 31 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # OSX and ide's folder attributes 2 | .DS_Store 3 | Thumbs.db 4 | .cache 5 | .project 6 | .settings 7 | .tmproj 8 | *.esproj 9 | nbproject 10 | *.sublime-project 11 | *.sublime-workspace 12 | *.sublime-* 13 | .idea 14 | 15 | # always-ignore extensions 16 | *.diff 17 | *.err 18 | *.orig 19 | *.rej 20 | *.swo 21 | *.swp 22 | *.vi 23 | *~ 24 | 25 | node_modules 26 | *.log 27 | lib 28 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2017 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 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## Babel plugin transform React qa classes 2 | [![Build Status](https://github.com/davesnx/babel-plugin-transform-react-qa-classes/workflows/publish/badge.svg)](https://travis-ci.org/davesnx/babel-plugin-transform-react-qa-classes) [![js-standard-style](https://img.shields.io/badge/code%20style-standard-brightgreen.svg)](http://standardjs.com/) [![npm](https://img.shields.io/npm/dm/localeval.svg)](https://www.npmjs.com/package/babel-plugin-transform-react-qa-classes) 3 | 4 | This babel plugin adds the component name as a `data-qa` in each React Component. 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 23 | 34 | 35 |
BeforeAfter
13 |
 14 | function FancyComponent () {
 15 |   return (
 16 |     <div>
 17 |       <div>Hello world</div>
 18 |     </div>
 19 |   )
 20 | }
 21 |       
22 |
24 |
 25 | function FancyComponent () {
 26 |   return (
 27 |     <div data-qa='fancy-component'>
 28 |       <div>Hello world</div>
 29 |     </div>
 30 |   )
 31 | }
 32 |       
33 |
36 | 37 | > This plugin asumes that you are using [React](https://reactjs.org) and [Babel](https://babeljs.io) as a building tool to generate your bundle. 38 | 39 | ### Features 40 | Works with: 41 | - class components 42 | - styled-components 43 | - arrow components 44 | - without JSX 45 | - option to configure the attribute name (default `data-qa`) 46 | - formats different the value of the attribute (default `camelCase`) 47 | 48 | ### Why? 49 | 50 | The idea is to facilitate Automate Testing on Frontend Applications. Automate Frontend highly requires to get the DOMElements and interact with them, adding `data-qa` attributes automatically to all the components will make it more easy, rather than do it by code, with this way you won't have this `data-qa` in production code. 51 | 52 | On the testing site would need to get the element like that: 53 | 54 | ```js 55 | document.querySelectorAll('[data-qa="component"]') 56 | ``` 57 | 58 | That depends on the Test suit stack, for example with Ruby and [`PageObject`](https://github.com/cheezy/page-object) looks like that: 59 | 60 | ```ruby 61 | div(:component, data_qa: 'component') 62 | ``` 63 | 64 | ### Install 65 | ```bash 66 | npm install --save-dev babel-plugin-transform-react-qa-classes 67 | # or yarn add -D 68 | ``` 69 | 70 | ### Use 71 | Inside `.babelrc`: 72 | ```json 73 | { 74 | "presets": ["es2015", "react"], 75 | "env": { 76 | "dev": { 77 | "plugins": ["transform-react-qa-classes"] 78 | } 79 | } 80 | } 81 | ``` 82 | 83 | > Note: Adding this plugin only on `DEV` mode (or `PREPROD`) allows not having `data-qa` attributes on production. 84 | 85 | You can specify the format of the name that you want and the name of the attribute, inside your `babelrc`: 86 | 87 | ```json 88 | { 89 | "presets": ["es2015", "react"], 90 | "env": { 91 | "dev": { 92 | "plugins": ["transform-react-qa-classes", { 93 | "attribute": "qa-property", 94 | "format": "camel" 95 | }] 96 | } 97 | } 98 | } 99 | ``` 100 | 101 | > Note: format can be: "camel" (camelCase), "snake" (snake_case), "kebab" (kebab-case) or "pascal" (PascalCase). 102 | 103 | #### with CLI 104 | 105 | ```bash 106 | babel --plugins transform-react-qa-classes component.js 107 | ``` 108 | 109 | #### or programatically with [babel-core](https://www.npmjs.com/package/babel-core) 110 | 111 | ```js 112 | require('babel-core').transform(`code`, { 113 | plugins: ['transform-react-qa-classes'] 114 | }) 115 | ``` 116 | 117 | ## Contributing 118 | PRs for additional features are welcome! 119 | 120 | There's still a few feature that are missing, for example each change of the state of the component is added as a `data-qa-state` into the DOM. Support for more libraries. 121 | 122 | I recommend checking this [handbook](https://github.com/jamiebuilds/babel-handbook) about how to write babel plugins in order to learn. 123 | 124 | - Clone the repo: `git clone https://github.com/davesnx/babel-plugin-transform-react-qa-classes` 125 | - Fork it & set origin as this repo: `git remote set-url origin https://github.com/YOUR_USERNAME/babel-plugin-transform-react-qa-classes.git` 126 | - Create a branch: `git checkout -b BRANCH_NAME` 127 | - Do the code 128 | - Create a PR to this repo. 129 | 130 | In order to do the commits I prefer to use [Commitizen](https://github.com/commitizen/cz-cli) and there's a githook setted up when you push it runs the tests. 131 | 132 | ## Feedback 133 | 134 | Is your company using it? I would love to know more! 135 | Could you answer this small [Typeform](https://davesnx.typeform.com/to/JrKgBc) :P 136 | 137 | ## License 138 | MIT 139 | -------------------------------------------------------------------------------- /commitlint.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | parserPreset: 'conventional-changelog-conventionalcommits', 3 | rules: { 4 | 'body-leading-blank': [1, 'always'], 5 | 'footer-leading-blank': [1, 'always'], 6 | 'scope-case': [2, 'always', 'lower-case'], 7 | 'subject-full-stop': [2, 'never', '.'], 8 | 'type-case': [2, 'always', 'lower-case'], 9 | 'type-empty': [2, 'never'], 10 | 'type-enum': [ 11 | 2, 12 | 'always', 13 | [ 14 | 'build', 15 | 'chore', 16 | 'ci', 17 | 'docs', 18 | 'feat', 19 | 'fix', 20 | 'improvement', 21 | 'perf', 22 | 'refactor', 23 | 'revert', 24 | 'style', 25 | 'test' 26 | ] 27 | ] 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "babel-plugin-transform-react-qa-classes", 3 | "version": "1.6.0", 4 | "license": "MIT", 5 | "description": "Add component's name in `data-qa` attributes to React Components Edit", 6 | "repository": { 7 | "type": "git", 8 | "url": "git+https://github.com/davesnx/babel-plugin-transform-react-qa-classes.git" 9 | }, 10 | "author": "davesnx ", 11 | "keywords": [ 12 | "babel", 13 | "plugin", 14 | "react", 15 | "component", 16 | "qa-classes" 17 | ], 18 | "main": "lib/index.js", 19 | "files": [ 20 | "lib", 21 | "README.md" 22 | ], 23 | "scripts": { 24 | "clean": "rm -rf lib", 25 | "build": "babel src -d lib", 26 | "test": "mocha --compilers js:babel-register", 27 | "test:watch": "yarn test -- --watch", 28 | "lint": "eslint .", 29 | "prepublishOnly": "yarn clean && yarn build", 30 | "semantic-release": "semantic-release", 31 | "travis-deploy-once": "travis-deploy-once" 32 | }, 33 | "dependencies": { 34 | "babel-types": "^6.26.0", 35 | "lodash.camelcase": "^4.3.0", 36 | "lodash.isstring": "^4.0.1", 37 | "lodash.kebabcase": "^4.1.1", 38 | "lodash.snakecase": "^4.1.1", 39 | "pascalcase": "^1.0.0" 40 | }, 41 | "devDependencies": { 42 | "@commitlint/cli": "^8.3.4", 43 | "@commitlint/config-conventional": "^8.3.4", 44 | "@semantic-release/github": "^5.5.5", 45 | "@semantic-release/npm": "^5.3.4", 46 | "babel-cli": "6.24.1", 47 | "babel-helper-plugin-test-runner": "^6.24.1", 48 | "babel-preset-es2015": "^6.24.1", 49 | "babel-preset-react": "^6.24.1", 50 | "babel-preset-stage-0": "^6.24.1", 51 | "eslint-config-davesnx-rules": "^1.0.0", 52 | "husky": "^1.1.2", 53 | "mocha": "^3.1.2", 54 | "semantic-release": "^15.10.3", 55 | "travis-deploy-once": "^5.0.9" 56 | }, 57 | "babel": { 58 | "presets": [ 59 | "es2015", 60 | "stage-0" 61 | ] 62 | }, 63 | "husky": { 64 | "hooks": { 65 | "commit-msg": "commitlint -E HUSKY_GIT_PARAMS", 66 | "pre-commit": "npm run lint", 67 | "pre-push": "npm run test" 68 | } 69 | }, 70 | "release": { 71 | "plugins": [ 72 | "@semantic-release/commit-analyzer", 73 | "@semantic-release/release-notes-generator", 74 | "@semantic-release/github", 75 | "@semantic-release/npm" 76 | ] 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /src/index.js: -------------------------------------------------------------------------------- 1 | import checkValidOptions from './options' 2 | import TaggedTemplateExpression from './styled-components' 3 | 4 | function isReactFragment(openingElement) { 5 | return ( 6 | openingElement.node.name.name === 'Fragment' || 7 | (openingElement.node.name.type === 'JSXMemberExpression' && 8 | openingElement.node.name.object.name === 'React' && 9 | openingElement.node.name.property.name === 'Fragment') 10 | ) 11 | } 12 | 13 | function applyAttribute({ openingElement, t, name, options }) { 14 | if (!openingElement || isReactFragment(openingElement)) return 15 | 16 | const isAttributeAlreadySet = openingElement.node.attributes.find(node => { 17 | if (!node.name) return 18 | return node.name.name === options.attribute 19 | }) 20 | 21 | if (isAttributeAlreadySet) return 22 | 23 | openingElement.node.attributes.push( 24 | t.jSXAttribute( 25 | t.jSXIdentifier(options.attribute), 26 | t.stringLiteral(options.format(name)) 27 | ) 28 | ) 29 | } 30 | 31 | function functionBodyPushAttributes(t, path, options, componentName) { 32 | let openingElement = null 33 | const functionBody = path.get('body').get('body') 34 | if (functionBody.parent && functionBody.parent.type === 'JSXElement') { 35 | const jsxElement = functionBody.find(c => { 36 | return c.type === 'JSXElement' 37 | }) 38 | if (!jsxElement) return 39 | openingElement = jsxElement.get('openingElement') 40 | } else { 41 | const returnStatement = functionBody.find(c => { 42 | return c.type === 'ReturnStatement' 43 | }) 44 | if (!returnStatement) return 45 | 46 | const arg = returnStatement.get('argument') 47 | if (!arg.isJSXElement()) return 48 | 49 | openingElement = arg.get('openingElement') 50 | } 51 | 52 | applyAttribute({ openingElement, t, name: componentName, options }) 53 | } 54 | 55 | export default function({ types: t }) { 56 | return { 57 | visitor: { 58 | TaggedTemplateExpression, 59 | FunctionDeclaration(path, state) { 60 | if (!path.node.id || !path.node.id.name) return 61 | 62 | const options = checkValidOptions(state) 63 | const componentName = path.node.id.name 64 | 65 | functionBodyPushAttributes(t, path, options, componentName) 66 | }, 67 | ArrowFunctionExpression(path, state) { 68 | const options = checkValidOptions(state) 69 | if (!path.parent.id || !path.parent.id.name) return 70 | const componentName = path.parent.id.name 71 | 72 | functionBodyPushAttributes(t, path, options, componentName) 73 | }, 74 | ClassDeclaration(path, state) { 75 | const name = path.get('id') 76 | const properties = path.get('body').get('body') 77 | 78 | const render = properties.find(prop => { 79 | return ( 80 | prop.isClassMethod() && 81 | prop.get('key').isIdentifier({ name: 'render' }) 82 | ) 83 | }) 84 | 85 | if (!render || !render.traverse) { 86 | return 87 | } 88 | 89 | const options = checkValidOptions(state) 90 | 91 | render.traverse({ 92 | ReturnStatement(returnStatement) { 93 | const arg = returnStatement.get('argument') 94 | if (!arg.isJSXElement()) return 95 | 96 | const openingElement = arg.get('openingElement') 97 | 98 | applyAttribute({ 99 | openingElement, 100 | t, 101 | name: name.node && name.node.name, 102 | options 103 | }) 104 | } 105 | }) 106 | } 107 | } 108 | } 109 | } 110 | -------------------------------------------------------------------------------- /src/options.js: -------------------------------------------------------------------------------- 1 | import kebabCase from 'lodash.kebabcase' 2 | import camelCase from 'lodash.camelcase' 3 | import snakeCase from 'lodash.snakecase' 4 | import pascalCase from 'pascalcase' 5 | import isString from 'lodash.isstring' 6 | 7 | const langTransforms = { 8 | kebab: kebabCase, 9 | camel: camelCase, 10 | snake: snakeCase, 11 | pascal: pascalCase 12 | } 13 | 14 | const isValidOption = opt => opt && isString(opt) 15 | const validTranform = opt => Object.keys(langTransforms).indexOf(opt) > -1 16 | 17 | const checkValidOptions = state => { 18 | let attribute = 'data-qa' 19 | let format = 'kebab' 20 | 21 | if (isValidOption(state.opts.attribute)) { 22 | attribute = state.opts.attribute 23 | } 24 | 25 | if (isValidOption(state.opts.format) && validTranform(state.opts.format)) { 26 | format = state.opts.format 27 | } 28 | 29 | return { 30 | format: langTransforms[format], 31 | attribute: attribute 32 | } 33 | } 34 | 35 | export default checkValidOptions 36 | -------------------------------------------------------------------------------- /src/styled-components.js: -------------------------------------------------------------------------------- 1 | import * as _path from 'path' 2 | import * as t from 'babel-types' 3 | 4 | import checkValidOptions from './options' 5 | 6 | export default function TaggedTemplateExpression(path, state) { 7 | const options = checkValidOptions(state) 8 | const scope = path.scope 9 | 10 | try { 11 | // simple case 12 | if (isStyledPrefix(path.node.tag)) { 13 | const id = getIdFrom(path.parentPath) 14 | if (!id) return 15 | path.node.tag = insertBefore(path.node.tag, id) 16 | return 17 | } 18 | 19 | // chained case. traverse until prefix found. 20 | // NB: styled-component chain api is always CallExpression+MemberExpression pairs. 21 | let node = path.node.tag 22 | while (true) { 23 | if (!node || !node.callee) break 24 | if (isStyledPrefix(node.callee.object)) { 25 | const id = getIdFrom(path.parentPath) 26 | if (!id) return 27 | node.callee.object = insertBefore(node.callee.object, id) 28 | break 29 | } 30 | node = node.callee.object 31 | } 32 | } catch (e) {} 33 | 34 | // hoisted helpers in closure 35 | function insertBefore(node, id) { 36 | return t.callExpression(t.memberExpression(node, t.identifier('attrs')), [ 37 | t.objectExpression([ 38 | t.objectProperty( 39 | t.StringLiteral(options.attribute), 40 | t.StringLiteral(options.format(id)) 41 | ) 42 | ]) 43 | ]) 44 | } 45 | function isStyledPrefix(node) { 46 | // handle two forms: styled.div and styled(Comp) 47 | return ( 48 | (t.isMemberExpression(node) && isStyledComponentsBinding(node.object)) || 49 | (t.isCallExpression(node) && isStyledComponentsBinding(node.callee)) 50 | ) 51 | function isStyledComponentsBinding(node) { 52 | if (!t.isIdentifier(node)) return false 53 | const binding = scope.getBinding(node.name) 54 | if (!binding || binding.kind !== 'module') return false 55 | return binding.path.parent.source.value === 'styled-components' 56 | } 57 | } 58 | function getIdFrom(parentPath) { 59 | if (t.isVariableDeclarator(parentPath.node)) { 60 | return parentPath.node.id.name 61 | } 62 | if (t.isArrowFunctionExpression(parentPath.node)) { 63 | if (t.isVariableDeclarator(parentPath.parentPath.node)) { 64 | return parentPath.parentPath.node.id.name 65 | } 66 | } 67 | if (t.isExportDefaultDeclaration(parentPath.node)) { 68 | const path = state.file.opts.filename 69 | const filename = _path.parse(path).name 70 | if (filename === 'index') { 71 | const parent = _path.basename(_path.dirname(filename)) 72 | return parent 73 | } 74 | return filename 75 | } 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /test/README.md: -------------------------------------------------------------------------------- 1 | It uses [`babel-helper-plugin-test-runner`](https://github.com/babel/babel/tree/master/packages/babel-helper-plugin-test-runner) that actually isn't recommened to use outside Babel monorepo, but if you follow the folder structure for the fixtures works perfectly. 2 | 3 | The idea behind that is create the tests based on the folders and run babel-core on `actual.js` and comparing it to `expected.js` given a .babelrc with the options. 4 | 5 | **babel-helper-plugin-test-runner** looks something similar like that: 6 | 7 | ```js 8 | const path = require("path") 9 | const fs = require("fs") 10 | const assert = require("assert") 11 | const babel = require("babel-core") 12 | const plugin = require("../src/index") 13 | 14 | const trim = str => str.replace(/^\s+|\s+$/, '') 15 | const unslug = str => str.split('-').join(' ') 16 | 17 | describe('Fixtures', () => { 18 | const fixturesDir = path.join(__dirname, "fixtures") 19 | 20 | fs.readdirSync(fixturesDir).map((caseName) => { 21 | it(unslug(caseName), () => { 22 | const fixtureDir = path.join(fixturesDir, caseName) 23 | 24 | const actual = babel.transformFileSync( 25 | path.join(fixtureDir, 'actual.js') 26 | ).code 27 | 28 | const expected = fs.readFileSync( 29 | path.join(fixtureDir, 'expected.js') 30 | ).toString() 31 | 32 | assert.equal(trim(actual), trim(expected)) 33 | }) 34 | }) 35 | }) 36 | ``` 37 | -------------------------------------------------------------------------------- /test/fixtures/react/arrow-anonymous-fragment/actual.js: -------------------------------------------------------------------------------- 1 | import React, { Component, Fragment } from 'react'; 2 | 3 | const componentName = () => { 4 | return (() => 5 |

Hello world

6 |
)(); 7 | }; 8 | 9 | export default componentName; 10 | -------------------------------------------------------------------------------- /test/fixtures/react/arrow-anonymous-fragment/expected.js: -------------------------------------------------------------------------------- 1 | import React, { Component, Fragment } from 'react'; 2 | 3 | const componentName = () => { 4 | return (() => 5 |

Hello world

6 |
)(); 7 | }; 8 | 9 | export default componentName; 10 | -------------------------------------------------------------------------------- /test/fixtures/react/arrow-anonymous-fragment/options.json: -------------------------------------------------------------------------------- 1 | { 2 | "plugins": [ 3 | "syntax-jsx", 4 | ["../../../../src"] 5 | ] 6 | } 7 | -------------------------------------------------------------------------------- /test/fixtures/react/arrow-anonymous-react-fragment/actual.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | 3 | const componentName = () => { 4 | return (() => 5 |

Hello world

6 |
)(); 7 | }; 8 | 9 | export default componentName; 10 | -------------------------------------------------------------------------------- /test/fixtures/react/arrow-anonymous-react-fragment/expected.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | 3 | const componentName = () => { 4 | return (() => 5 |

Hello world

6 |
)(); 7 | }; 8 | 9 | export default componentName; 10 | -------------------------------------------------------------------------------- /test/fixtures/react/arrow-anonymous-react-fragment/options.json: -------------------------------------------------------------------------------- 1 | { 2 | "plugins": [ 3 | "syntax-jsx", 4 | ["../../../../src"] 5 | ] 6 | } 7 | -------------------------------------------------------------------------------- /test/fixtures/react/arrow-fragment/actual.js: -------------------------------------------------------------------------------- 1 | import React, { Component, Fragment } from 'react'; 2 | 3 | const componentName = () => { 4 | return 5 |

Hello world

6 |
; 7 | }; 8 | 9 | export default componentName; 10 | -------------------------------------------------------------------------------- /test/fixtures/react/arrow-fragment/expected.js: -------------------------------------------------------------------------------- 1 | import React, { Component, Fragment } from 'react'; 2 | 3 | const componentName = () => { 4 | return 5 |

Hello world

6 |
; 7 | }; 8 | 9 | export default componentName; 10 | -------------------------------------------------------------------------------- /test/fixtures/react/arrow-fragment/options.json: -------------------------------------------------------------------------------- 1 | { 2 | "plugins": [ 3 | "syntax-jsx", 4 | ["../../../../src"] 5 | ] 6 | } 7 | -------------------------------------------------------------------------------- /test/fixtures/react/arrow-noreturn-fragment/actual.js: -------------------------------------------------------------------------------- 1 | import React, { Component, Fragment } from 'react'; 2 | 3 | const componentName = () => ( 4 | 5 |

Hello world

6 |
7 | ); 8 | 9 | export default componentName; 10 | -------------------------------------------------------------------------------- /test/fixtures/react/arrow-noreturn-fragment/expected.js: -------------------------------------------------------------------------------- 1 | import React, { Component, Fragment } from 'react'; 2 | 3 | const componentName = () => 4 |

Hello world

5 |
; 6 | 7 | export default componentName; 8 | -------------------------------------------------------------------------------- /test/fixtures/react/arrow-noreturn-fragment/options.json: -------------------------------------------------------------------------------- 1 | { 2 | "plugins": [ 3 | "syntax-jsx", 4 | ["../../../../src"] 5 | ] 6 | } 7 | -------------------------------------------------------------------------------- /test/fixtures/react/arrow-noreturn-react-fragment/actual.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | 3 | const componentName = () => ( 4 | 5 |

Hello world

6 |
7 | ); 8 | 9 | export default componentName; 10 | -------------------------------------------------------------------------------- /test/fixtures/react/arrow-noreturn-react-fragment/expected.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | 3 | const componentName = () => 4 |

Hello world

5 |
; 6 | 7 | export default componentName; 8 | -------------------------------------------------------------------------------- /test/fixtures/react/arrow-noreturn-react-fragment/options.json: -------------------------------------------------------------------------------- 1 | { 2 | "plugins": [ 3 | "syntax-jsx", 4 | ["../../../../src"] 5 | ] 6 | } 7 | -------------------------------------------------------------------------------- /test/fixtures/react/arrow-noreturn/actual.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | 3 | const componentName = () => ( 4 |
5 |

Hello world

6 |
7 | ); 8 | 9 | export default componentName; 10 | -------------------------------------------------------------------------------- /test/fixtures/react/arrow-noreturn/expected.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | 3 | const componentName = () =>
4 |

Hello world

5 |
; 6 | 7 | export default componentName; 8 | -------------------------------------------------------------------------------- /test/fixtures/react/arrow-noreturn/options.json: -------------------------------------------------------------------------------- 1 | { 2 | "plugins": [ 3 | "syntax-jsx", 4 | ["../../../../src"] 5 | ] 6 | } 7 | -------------------------------------------------------------------------------- /test/fixtures/react/arrow-react-fragment/actual.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | 3 | const componentName = () => { 4 | return 5 |

Hello world

6 |
; 7 | }; 8 | 9 | export default componentName; 10 | -------------------------------------------------------------------------------- /test/fixtures/react/arrow-react-fragment/expected.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | 3 | const componentName = () => { 4 | return 5 |

Hello world

6 |
; 7 | }; 8 | 9 | export default componentName; 10 | -------------------------------------------------------------------------------- /test/fixtures/react/arrow-react-fragment/options.json: -------------------------------------------------------------------------------- 1 | { 2 | "plugins": [ 3 | "syntax-jsx", 4 | ["../../../../src"] 5 | ] 6 | } 7 | -------------------------------------------------------------------------------- /test/fixtures/react/arrow/actual.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | 3 | const componentName = () => { 4 | return
5 |

Hello world

6 |
; 7 | }; 8 | 9 | export default componentName; 10 | -------------------------------------------------------------------------------- /test/fixtures/react/arrow/expected.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | 3 | const componentName = () => { 4 | return
5 |

Hello world

6 |
; 7 | }; 8 | 9 | export default componentName; 10 | -------------------------------------------------------------------------------- /test/fixtures/react/arrow/options.json: -------------------------------------------------------------------------------- 1 | { 2 | "plugins": [ 3 | "syntax-jsx", 4 | ["../../../../src"] 5 | ] 6 | } 7 | -------------------------------------------------------------------------------- /test/fixtures/react/component-fragment/actual.js: -------------------------------------------------------------------------------- 1 | import React, { Component, Fragment } from 'react'; 2 | 3 | class componentName extends Component { 4 | render() { 5 | return A; 6 | } 7 | } 8 | 9 | export default componentName; 10 | -------------------------------------------------------------------------------- /test/fixtures/react/component-fragment/expected.js: -------------------------------------------------------------------------------- 1 | import React, { Component, Fragment } from 'react'; 2 | 3 | class componentName extends Component { 4 | render() { 5 | return A; 6 | } 7 | } 8 | 9 | export default componentName; 10 | -------------------------------------------------------------------------------- /test/fixtures/react/component-fragment/options.json: -------------------------------------------------------------------------------- 1 | { 2 | "plugins": [ 3 | "syntax-jsx", 4 | ["../../../../src"] 5 | ] 6 | } 7 | -------------------------------------------------------------------------------- /test/fixtures/react/component-react-fragment/actual.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | 3 | class componentName extends Component { 4 | render() { 5 | return A; 6 | } 7 | } 8 | 9 | export default componentName; 10 | -------------------------------------------------------------------------------- /test/fixtures/react/component-react-fragment/expected.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | 3 | class componentName extends Component { 4 | render() { 5 | return A; 6 | } 7 | } 8 | 9 | export default componentName; 10 | -------------------------------------------------------------------------------- /test/fixtures/react/component-react-fragment/options.json: -------------------------------------------------------------------------------- 1 | { 2 | "plugins": [ 3 | "syntax-jsx", 4 | ["../../../../src"] 5 | ] 6 | } 7 | -------------------------------------------------------------------------------- /test/fixtures/react/component/actual.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | 3 | class componentName extends Component { 4 | render() { 5 | return
6 |

Hello world

7 |
; 8 | } 9 | } 10 | 11 | export default componentName; 12 | -------------------------------------------------------------------------------- /test/fixtures/react/component/expected.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | 3 | class componentName extends Component { 4 | render() { 5 | return
6 |

Hello world

7 |
; 8 | } 9 | } 10 | 11 | export default componentName; 12 | -------------------------------------------------------------------------------- /test/fixtures/react/component/options.json: -------------------------------------------------------------------------------- 1 | { 2 | "plugins": [ 3 | "syntax-jsx", 4 | ["../../../../src"] 5 | ] 6 | } 7 | -------------------------------------------------------------------------------- /test/fixtures/react/custom-attribute/actual.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | class ComponentName extends React.Component { 4 | render() { 5 | return
6 |

Hello world

7 |
; 8 | } 9 | } 10 | 11 | export default ComponentName; 12 | -------------------------------------------------------------------------------- /test/fixtures/react/custom-attribute/expected.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | class ComponentName extends React.Component { 4 | render() { 5 | return
6 |

Hello world

7 |
; 8 | } 9 | } 10 | 11 | export default ComponentName; 12 | -------------------------------------------------------------------------------- /test/fixtures/react/custom-attribute/options.json: -------------------------------------------------------------------------------- 1 | { 2 | "plugins": [ 3 | "syntax-jsx", 4 | ["../../../../src"] 5 | ] 6 | } 7 | -------------------------------------------------------------------------------- /test/fixtures/react/forward-ref/actual.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | 3 | const Button = (props, ref) =>