forked from vercel/next.js
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add codemod for files that do not support the new React JSX transform (…
…vercel#21281) Previously our automatic React injection approach injected `import React from 'react'` automatically whenever JSX was detected. The new official JSX transform solves this by enforcing importing `React` when it is used. This codemod automatically converted files that are using a "global React variable" to use `import React from 'react'`
- Loading branch information
1 parent
699a7ae
commit c5b5c43
Showing
9 changed files
with
175 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
5 changes: 5 additions & 0 deletions
5
...ext-codemod/transforms/__testfixtures__/add-missing-react-import/class-component.input.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
export default class Home extends React.Component { | ||
render() { | ||
return <div>Hello World</div> | ||
} | ||
} |
6 changes: 6 additions & 0 deletions
6
...xt-codemod/transforms/__testfixtures__/add-missing-react-import/class-component.output.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
import React from 'react' | ||
export default class Home extends React.Component { | ||
render() { | ||
return <div>Hello World</div> | ||
} | ||
} |
16 changes: 16 additions & 0 deletions
16
...orms/__testfixtures__/add-missing-react-import/missing-react-import-in-component.input.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
import { Children, isValidElement } from 'react'; | ||
|
||
function Heading(props) { | ||
const { component, className, children, ...rest } = props; | ||
return React.cloneElement( | ||
component, | ||
{ | ||
className: [className, component.props.className || ''].join(' '), | ||
...rest | ||
}, | ||
children | ||
); | ||
} | ||
|
||
|
||
export default Heading; |
16 changes: 16 additions & 0 deletions
16
...rms/__testfixtures__/add-missing-react-import/missing-react-import-in-component.output.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
import React, { Children, isValidElement } from 'react'; | ||
|
||
function Heading(props) { | ||
const { component, className, children, ...rest } = props; | ||
return React.cloneElement( | ||
component, | ||
{ | ||
className: [className, component.props.className || ''].join(' '), | ||
...rest | ||
}, | ||
children | ||
); | ||
} | ||
|
||
|
||
export default Heading; |
16 changes: 16 additions & 0 deletions
16
packages/next-codemod/transforms/__tests__/add-missing-react-import.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
/* global jest */ | ||
jest.autoMockOff() | ||
const defineTest = require('jscodeshift/dist/testUtils').defineTest | ||
|
||
const fixtures = [ | ||
'missing-react-import-in-component' | ||
] | ||
|
||
for (const fixture of fixtures) { | ||
defineTest( | ||
__dirname, | ||
'add-missing-react-import', | ||
null, | ||
`add-missing-react-import/${fixture}` | ||
) | ||
} |
77 changes: 77 additions & 0 deletions
77
packages/next-codemod/transforms/add-missing-react-import.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,77 @@ | ||
function addReactImport(j, root) { | ||
// We create an import specifier, this is the value of an import, eg: | ||
// import React from 'react' | ||
// The specifier would be `React` | ||
const ReactDefaultSpecifier = j.importDefaultSpecifier(j.identifier('React')) | ||
|
||
// Check if this file is already importing `react` | ||
// so that we can attach `React` to the existing import instead of creating a new `import` node | ||
const originalReactImport = root.find(j.ImportDeclaration, { | ||
source: { | ||
value: 'react', | ||
}, | ||
}) | ||
if (originalReactImport.length > 0) { | ||
// Check if `React` is already imported. In that case we don't have to do anything | ||
if (originalReactImport.find(j.ImportDefaultSpecifier).length > 0) { | ||
return | ||
} | ||
|
||
// Attach `React` to the existing `react` import node | ||
originalReactImport.forEach((node) => { | ||
node.value.specifiers.unshift(ReactDefaultSpecifier) | ||
}) | ||
return | ||
} | ||
|
||
// Create import node | ||
// import React from 'react' | ||
const ReactImport = j.importDeclaration( | ||
[ReactDefaultSpecifier], | ||
j.stringLiteral('react') | ||
) | ||
|
||
// Find the Program, this is the top level AST node | ||
const Program = root.find(j.Program) | ||
// Attach the import at the top of the body | ||
Program.forEach((node) => { | ||
node.value.body.unshift(ReactImport) | ||
}) | ||
} | ||
|
||
export default function transformer(file, api, options) { | ||
const j = api.jscodeshift | ||
const root = j(file.source) | ||
|
||
const hasReactImport = (r) => { | ||
return ( | ||
r.find(j.ImportDefaultSpecifier, { | ||
local: { | ||
type: 'Identifier', | ||
name: 'React', | ||
}, | ||
}).length > 0 | ||
) | ||
} | ||
|
||
const hasReactVariableUsage = (r) => { | ||
return ( | ||
r.find(j.MemberExpression, { | ||
object: { | ||
type: 'Identifier', | ||
name: 'React', | ||
}, | ||
}).length > 0 | ||
) | ||
} | ||
|
||
if (hasReactImport(root)) { | ||
return | ||
} | ||
|
||
if (hasReactVariableUsage(root)) { | ||
addReactImport(j, root) | ||
} | ||
|
||
return root.toSource(options) | ||
} |