├── .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 | [](https://travis-ci.org/davesnx/babel-plugin-transform-react-qa-classes) [](http://standardjs.com/) [](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 | Before |
9 | After |
10 |
11 |
12 |
13 |
14 | function FancyComponent () {
15 | return (
16 | <div>
17 | <div>Hello world</div>
18 | </div>
19 | )
20 | }
21 |
22 | |
23 |
24 |
25 | function FancyComponent () {
26 | return (
27 | <div data-qa='fancy-component'>
28 | <div>Hello world</div>
29 | </div>
30 | )
31 | }
32 |
33 | |
34 |
35 |
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) => ;
4 |
5 | const FancyButton = React.forwardRef(Button);
6 |
7 | export default FancyButton;
8 |
--------------------------------------------------------------------------------
/test/fixtures/react/forward-ref/expected.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 |
3 | const Button = (props, ref) => ;
4 |
5 | const FancyButton = React.forwardRef(Button);
6 |
7 | export default FancyButton;
8 |
--------------------------------------------------------------------------------
/test/fixtures/react/forward-ref/options.json:
--------------------------------------------------------------------------------
1 | {
2 | "plugins": [
3 | "syntax-jsx",
4 | ["../../../../src"]
5 | ]
6 | }
7 |
--------------------------------------------------------------------------------
/test/fixtures/react/nonJSX/actual.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react';
2 |
3 | class TestClass extends Component {
4 | test() {
5 | return true;
6 | }
7 | }
8 |
9 | export default TestClass;
10 |
--------------------------------------------------------------------------------
/test/fixtures/react/nonJSX/expected.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react';
2 |
3 | class TestClass extends Component {
4 | test() {
5 | return true;
6 | }
7 | }
8 |
9 | export default TestClass;
10 |
--------------------------------------------------------------------------------
/test/fixtures/react/nonJSX/options.json:
--------------------------------------------------------------------------------
1 | {
2 | "plugins": [
3 | "syntax-jsx",
4 | [
5 | "../../../../src"
6 | ]
7 | ]
8 | }
--------------------------------------------------------------------------------
/test/fixtures/react/option-attribute/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/option-attribute/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/option-attribute/options.json:
--------------------------------------------------------------------------------
1 | {
2 | "plugins": [
3 | "syntax-jsx",
4 | ["../../../../src", {
5 | "attribute": "qa"
6 | }]
7 | ]
8 | }
9 |
--------------------------------------------------------------------------------
/test/fixtures/react/option-format/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/option-format/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/option-format/options.json:
--------------------------------------------------------------------------------
1 | {
2 | "plugins": [
3 | "syntax-jsx",
4 | [
5 | "../../../../src",
6 | {
7 | "format": "kebab"
8 | }
9 | ]
10 | ]
11 | }
12 |
--------------------------------------------------------------------------------
/test/fixtures/react/pure/actual.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 |
3 | class PureComponentName extends React.PureComponent {
4 | render() {
5 | return
6 |
Hello world
7 | ;
8 | }
9 | }
10 |
11 | export default PureComponentName;
12 |
--------------------------------------------------------------------------------
/test/fixtures/react/pure/expected.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 |
3 | class PureComponentName extends React.PureComponent {
4 | render() {
5 | return
6 |
Hello world
7 | ;
8 | }
9 | }
10 |
11 | export default PureComponentName;
12 |
--------------------------------------------------------------------------------
/test/fixtures/react/pure/options.json:
--------------------------------------------------------------------------------
1 | {
2 | "plugins": [
3 | "syntax-jsx",
4 | ["../../../../src"]
5 | ]
6 | }
7 |
--------------------------------------------------------------------------------
/test/fixtures/react/pureComponent-fragment/actual.js:
--------------------------------------------------------------------------------
1 | import React, { Fragment } from 'react';
2 |
3 | class PureComponentName extends React.PureComponent {
4 | render() {
5 | return
6 | Hello world
7 | ;
8 | }
9 | }
10 |
11 | export default PureComponentName;
12 |
--------------------------------------------------------------------------------
/test/fixtures/react/pureComponent-fragment/expected.js:
--------------------------------------------------------------------------------
1 | import React, { Fragment } from 'react';
2 |
3 | class PureComponentName extends React.PureComponent {
4 | render() {
5 | return
6 | Hello world
7 | ;
8 | }
9 | }
10 |
11 | export default PureComponentName;
12 |
--------------------------------------------------------------------------------
/test/fixtures/react/pureComponent-fragment/options.json:
--------------------------------------------------------------------------------
1 | {
2 | "plugins": [
3 | "syntax-jsx",
4 | [
5 | "../../../../src"
6 | ]
7 | ]
8 | }
--------------------------------------------------------------------------------
/test/fixtures/react/pureComponent-react-fragment/actual.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 |
3 | class PureComponentName extends React.PureComponent {
4 | render() {
5 | return
6 | Hello world
7 | ;
8 | }
9 | }
10 |
11 | export default PureComponentName;
12 |
--------------------------------------------------------------------------------
/test/fixtures/react/pureComponent-react-fragment/expected.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 |
3 | class PureComponentName extends React.PureComponent {
4 | render() {
5 | return
6 | Hello world
7 | ;
8 | }
9 | }
10 |
11 | export default PureComponentName;
12 |
--------------------------------------------------------------------------------
/test/fixtures/react/pureComponent-react-fragment/options.json:
--------------------------------------------------------------------------------
1 | {
2 | "plugins": [
3 | "syntax-jsx",
4 | [
5 | "../../../../src"
6 | ]
7 | ]
8 | }
--------------------------------------------------------------------------------
/test/fixtures/react/rawfunction-fragment/actual.js:
--------------------------------------------------------------------------------
1 | import React, { Component, Fragment } from 'react';
2 |
3 | function SubComponent() {
4 | return Sub;
5 | }
6 |
7 | const componentName = () => {
8 | return
9 |
10 | ;
11 | };
12 |
13 | export default componentName;
14 |
--------------------------------------------------------------------------------
/test/fixtures/react/rawfunction-fragment/expected.js:
--------------------------------------------------------------------------------
1 | import React, { Component, Fragment } from 'react';
2 |
3 | function SubComponent() {
4 | return Sub;
5 | }
6 |
7 | const componentName = () => {
8 | return
9 |
10 | ;
11 | };
12 |
13 | export default componentName;
14 |
--------------------------------------------------------------------------------
/test/fixtures/react/rawfunction-fragment/options.json:
--------------------------------------------------------------------------------
1 | {
2 | "plugins": [
3 | "syntax-jsx",
4 | ["../../../../src"]
5 | ]
6 | }
7 |
--------------------------------------------------------------------------------
/test/fixtures/react/rawfunction-react-fragment/actual.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react';
2 |
3 | function SubComponent() {
4 | return Sub;
5 | }
6 |
7 | const componentName = () => {
8 | return
9 |
10 | ;
11 | };
12 |
13 | export default componentName;
14 |
--------------------------------------------------------------------------------
/test/fixtures/react/rawfunction-react-fragment/expected.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react';
2 |
3 | function SubComponent() {
4 | return Sub;
5 | }
6 |
7 | const componentName = () => {
8 | return
9 |
10 | ;
11 | };
12 |
13 | export default componentName;
14 |
--------------------------------------------------------------------------------
/test/fixtures/react/rawfunction-react-fragment/options.json:
--------------------------------------------------------------------------------
1 | {
2 | "plugins": [
3 | "syntax-jsx",
4 | ["../../../../src"]
5 | ]
6 | }
7 |
--------------------------------------------------------------------------------
/test/fixtures/react/rawfunction/actual.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react';
2 |
3 | function SubComponent() {
4 | return Sub
;
5 | }
6 |
7 | const componentName = () => {
8 | return
9 |
10 |
;
11 | };
12 |
13 | export default componentName;
14 |
--------------------------------------------------------------------------------
/test/fixtures/react/rawfunction/expected.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react';
2 |
3 | function SubComponent() {
4 | return Sub
;
5 | }
6 |
7 | const componentName = () => {
8 | return
9 |
10 |
;
11 | };
12 |
13 | export default componentName;
14 |
--------------------------------------------------------------------------------
/test/fixtures/react/rawfunction/options.json:
--------------------------------------------------------------------------------
1 | {
2 | "plugins": [
3 | "syntax-jsx",
4 | ["../../../../src"]
5 | ]
6 | }
7 |
--------------------------------------------------------------------------------
/test/fixtures/react/styled-components/actual.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react'
2 | import styled from 'styled-components'
3 |
4 | /* should be transformed */
5 |
6 | const basic = styled.div`
7 | color: black;
8 | `
9 |
10 | const withChain = styled.div.withConfig()`
11 | color: black;
12 | `
13 |
14 | const withInterpolation = styled.div`
15 | color: ${Colors.black};
16 | `
17 |
18 | const el = styled(El)`
19 | color: black;
20 | `
21 |
22 | const arrowExpr = () => styled.div`
23 | color: black;
24 | `
25 |
26 | // covered by VariableDeclaraion cases
27 | export const exported = styled.div`
28 | color: black;
29 | `
30 |
31 | export default styled.div`
32 | color: black;
33 | `
34 |
35 | /* should not be transformed */
36 |
37 | let assigned
38 | assigned = styled.div`
39 | color: black;
40 | `
41 |
42 | const arrowReturn = () => {
43 | return styled.div`
44 | color: black;
45 | `
46 | }
47 |
48 | function fnReturn(){
49 | return styled.div`
50 | color: black;
51 | `
52 | }
53 |
54 | const fnExprReturn = function(){
55 | return styled.div`
56 | color: black;
57 | `
58 | }
59 |
60 | const namedFnExprReturn = function namedFnExpr(){
61 | return styled.div`
62 | color: black;
63 | `
64 | }
65 |
66 | const ternary = cond ? styled.div`color: black;` : null
67 |
68 | const conditional = cond && styled.div`color: black;`
69 |
70 | const map = {
71 | comp: styled.div`color: black;`
72 | }
73 |
74 | const comps = [styled.div`color: black;`]
--------------------------------------------------------------------------------
/test/fixtures/react/styled-components/expected.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react';
2 | import styled from 'styled-components';
3 |
4 | /* should be transformed */
5 |
6 | const basic = styled.div.attrs({
7 | 'data-qa': 'basic'
8 | })`
9 | color: black;
10 | `;
11 |
12 | const withChain = styled.div.attrs({
13 | 'data-qa': 'with-chain'
14 | }).withConfig()`
15 | color: black;
16 | `;
17 |
18 | const withInterpolation = styled.div.attrs({
19 | 'data-qa': 'with-interpolation'
20 | })`
21 | color: ${Colors.black};
22 | `;
23 |
24 | const el = styled(El).attrs({
25 | 'data-qa': 'el'
26 | })`
27 | color: black;
28 | `;
29 |
30 | const arrowExpr = () => styled.div.attrs({
31 | 'data-qa': 'arrow-expr'
32 | })`
33 | color: black;
34 | `;
35 |
36 | // covered by VariableDeclaraion cases
37 | export const exported = styled.div.attrs({
38 | 'data-qa': 'exported'
39 | })`
40 | color: black;
41 | `;
42 |
43 | export default styled.div.attrs({
44 | 'data-qa': 'actual'
45 | })`
46 | color: black;
47 | `;
48 |
49 | /* should not be transformed */
50 |
51 | let assigned;
52 | assigned = styled.div`
53 | color: black;
54 | `;
55 |
56 | const arrowReturn = () => {
57 | return styled.div`
58 | color: black;
59 | `;
60 | };
61 |
62 | function fnReturn() {
63 | return styled.div`
64 | color: black;
65 | `;
66 | }
67 |
68 | const fnExprReturn = function () {
69 | return styled.div`
70 | color: black;
71 | `;
72 | };
73 |
74 | const namedFnExprReturn = function namedFnExpr() {
75 | return styled.div`
76 | color: black;
77 | `;
78 | };
79 |
80 | const ternary = cond ? styled.div`color: black;` : null;
81 |
82 | const conditional = cond && styled.div`color: black;`;
83 |
84 | const map = {
85 | comp: styled.div`color: black;`
86 | };
87 |
88 | const comps = [styled.div`color: black;`];
--------------------------------------------------------------------------------
/test/fixtures/react/styled-components/options.json:
--------------------------------------------------------------------------------
1 | {
2 | "plugins": [
3 | "syntax-jsx",
4 | ["../../../../src"]
5 | ]
6 | }
7 |
--------------------------------------------------------------------------------
/test/index.js:
--------------------------------------------------------------------------------
1 | require('babel-helper-plugin-test-runner')(__dirname)
2 |
--------------------------------------------------------------------------------