Skip to content

Commit ba09477

Browse files
Handle recursive code (#148)
1 parent 517bc51 commit ba09477

File tree

6 files changed

+72
-7
lines changed

6 files changed

+72
-7
lines changed

src/index.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -227,7 +227,7 @@ export default function(api) {
227227
})
228228
} else {
229229
throw new Error(
230-
'react-remove-prop-types: removeImport and mode=remove can not be used at the same time.'
230+
'transform-react-remove-prop-type: removeImport = true and mode != "remove" can not be used at the same time.'
231231
)
232232
}
233233
}

src/isStatelessComponent.js

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
// @flow weak
22

3+
const traversed = Symbol('traversed')
4+
35
function isJSXElementOrReactCreateElement(path) {
46
let visited = false
57

@@ -9,7 +11,8 @@ function isJSXElementOrReactCreateElement(path) {
911

1012
if (
1113
callee.matchesPattern('React.createElement') ||
12-
callee.matchesPattern('React.cloneElement')
14+
callee.matchesPattern('React.cloneElement') ||
15+
callee.node.name === 'cloneElement'
1316
) {
1417
visited = true
1518
}
@@ -22,12 +25,16 @@ function isJSXElementOrReactCreateElement(path) {
2225
return visited
2326
}
2427

25-
function isReturningJSXElement(path) {
28+
function isReturningJSXElement(path, iteration = 0) {
2629
// Early exit for ArrowFunctionExpressions, there is no ReturnStatement node.
2730
if (path.node.init && path.node.init.body && isJSXElementOrReactCreateElement(path)) {
2831
return true
2932
}
3033

34+
if (iteration > 20) {
35+
throw new Error('transform-react-remove-prop-type: infinite loop detected.')
36+
}
37+
3138
let visited = false
3239

3340
path.traverse({
@@ -57,7 +64,14 @@ function isReturningJSXElement(path) {
5764
return
5865
}
5966

60-
if (isReturningJSXElement(binding.path)) {
67+
// Prevents infinite traverse loop.
68+
if (binding.path[traversed]) {
69+
return
70+
}
71+
72+
binding.path[traversed] = true
73+
74+
if (isReturningJSXElement(binding.path, iteration + 1)) {
6175
visited = true
6276
}
6377
}
Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,8 +22,8 @@ describe('fixtures', () => {
2222
const fixtureDir = path.join(fixturesDir, caseName)
2323

2424
// Only run a specific test
25-
// if (caseName !== 'issue-106') {
26-
// return;
25+
// if (caseName !== 'recursive') {
26+
// return
2727
// }
2828

2929
modes.forEach(mode => {

test/fixtures/recursive/actual.js

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
import { cloneElement } from 'react'
2+
import PropTypes from 'prop-types'
3+
4+
export default function Composer(props) {
5+
return renderRecursive(props.children, props.components)
6+
}
7+
8+
Composer.propTypes = {
9+
children: PropTypes.func.isRequired,
10+
components: PropTypes.arrayOf(PropTypes.oneOfType([PropTypes.element, PropTypes.func]))
11+
.isRequired,
12+
}
13+
14+
function renderRecursive(render, remaining, results) {
15+
results = results || []
16+
if (!remaining[0]) {
17+
return render(results)
18+
}
19+
20+
function nextRender(value) {
21+
return renderRecursive(render, remaining.slice(1), results.concat([value]))
22+
}
23+
24+
return typeof remaining[0] === 'function'
25+
? remaining[0]({ results, render: nextRender })
26+
: cloneElement(remaining[0], { children: nextRender })
27+
}
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
import { cloneElement } from 'react';
2+
import PropTypes from 'prop-types';
3+
export default function Composer(props) {
4+
return renderRecursive(props.children, props.components);
5+
}
6+
7+
function renderRecursive(render, remaining, results) {
8+
results = results || [];
9+
10+
if (!remaining[0]) {
11+
return render(results);
12+
}
13+
14+
function nextRender(value) {
15+
return renderRecursive(render, remaining.slice(1), results.concat([value]));
16+
}
17+
18+
return typeof remaining[0] === 'function' ? remaining[0]({
19+
results,
20+
render: nextRender
21+
}) : cloneElement(remaining[0], {
22+
children: nextRender
23+
});
24+
}

test/mocha.opts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
11
--require @babel/register
22
--reporter dot
33
--recursive
4-
test/{,**/}*.spec.js
4+
test/{,**/}*.test.js

0 commit comments

Comments
 (0)