-
Notifications
You must be signed in to change notification settings - Fork 27k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add experimental cra-to-next transform in codemod cli (#24969)
Co-authored-by: Tim Neutkens <tim@timneutkens.nl> Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com>
- Loading branch information
1 parent
7530865
commit dde9ad4
Showing
17 changed files
with
1,727 additions
and
69 deletions.
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,34 @@ | ||
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files. | ||
|
||
# dependencies | ||
/node_modules | ||
/.pnp | ||
.pnp.js | ||
|
||
# testing | ||
/coverage | ||
|
||
# next.js | ||
/.next/ | ||
/out/ | ||
|
||
# production | ||
/build | ||
|
||
# misc | ||
.DS_Store | ||
*.pem | ||
|
||
# debug | ||
npm-debug.log* | ||
yarn-debug.log* | ||
yarn-error.log* | ||
|
||
# local env files | ||
.env.local | ||
.env.development.local | ||
.env.test.local | ||
.env.production.local | ||
|
||
# vercel | ||
.vercel |
65 changes: 65 additions & 0 deletions
65
packages/next-codemod/lib/cra-to-next/global-css-transform.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,65 @@ | ||
import nodePath from 'path' | ||
import { API, FileInfo, Options } from 'jscodeshift' | ||
|
||
export const globalCssContext = { | ||
cssImports: new Set<string>(), | ||
reactSvgImports: new Set<string>(), | ||
} | ||
const globalStylesRegex = /(?<!\.module)\.(css|scss|sass)$/i | ||
|
||
export default function transformer( | ||
file: FileInfo, | ||
api: API, | ||
options: Options | ||
) { | ||
const j = api.jscodeshift | ||
const root = j(file.source) | ||
let hasModifications = false | ||
|
||
root | ||
.find(j.ImportDeclaration) | ||
.filter((path) => { | ||
const { | ||
node: { | ||
source: { value }, | ||
}, | ||
} = path | ||
|
||
if (typeof value === 'string') { | ||
if (globalStylesRegex.test(value)) { | ||
let resolvedPath = value | ||
|
||
if (value.startsWith('.')) { | ||
resolvedPath = nodePath.resolve(nodePath.dirname(file.path), value) | ||
} | ||
globalCssContext.cssImports.add(resolvedPath) | ||
|
||
const { start, end } = path.node as any | ||
|
||
if (!path.parentPath.node.comments) { | ||
path.parentPath.node.comments = [] | ||
} | ||
|
||
path.parentPath.node.comments = [ | ||
j.commentLine(' ' + file.source.substring(start, end)), | ||
] | ||
hasModifications = true | ||
return true | ||
} else if (value.endsWith('.svg')) { | ||
const isComponentImport = path.node.specifiers.some((specifier) => { | ||
return (specifier as any).imported?.name === 'ReactComponent' | ||
}) | ||
|
||
if (isComponentImport) { | ||
globalCssContext.reactSvgImports.add(file.path) | ||
} | ||
} | ||
} | ||
return false | ||
}) | ||
.remove() | ||
|
||
return hasModifications && globalCssContext.reactSvgImports.size === 0 | ||
? root.toSource(options) | ||
: null | ||
} |
101 changes: 101 additions & 0 deletions
101
packages/next-codemod/lib/cra-to-next/index-to-component.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,101 @@ | ||
import { API, FileInfo, JSXElement, Options } from 'jscodeshift' | ||
|
||
export const indexContext = { | ||
multipleRenderRoots: false, | ||
nestedRender: false, | ||
} | ||
|
||
export default function transformer( | ||
file: FileInfo, | ||
api: API, | ||
options: Options | ||
) { | ||
const j = api.jscodeshift | ||
const root = j(file.source) | ||
let hasModifications = false | ||
let foundReactRender = 0 | ||
let hasRenderImport = false | ||
let defaultReactDomImport: string | undefined | ||
|
||
root.find(j.ImportDeclaration).forEach((path) => { | ||
if (path.node.source.value === 'react-dom') { | ||
return path.node.specifiers.forEach((specifier) => { | ||
if (specifier.local.name === 'render') { | ||
hasRenderImport = true | ||
} | ||
if (specifier.type === 'ImportDefaultSpecifier') { | ||
defaultReactDomImport = specifier.local.name | ||
} | ||
}) | ||
} | ||
return false | ||
}) | ||
|
||
root | ||
.find(j.CallExpression) | ||
.filter((path) => { | ||
const { node } = path | ||
let found = false | ||
|
||
if ( | ||
defaultReactDomImport && | ||
node.callee.type === 'MemberExpression' && | ||
(node.callee.object as any).name === defaultReactDomImport && | ||
(node.callee.property as any).name === 'render' | ||
) { | ||
found = true | ||
} | ||
|
||
if (hasRenderImport && (node.callee as any).name === 'render') { | ||
found = true | ||
} | ||
|
||
if (found) { | ||
foundReactRender++ | ||
hasModifications = true | ||
|
||
if (!Array.isArray(path.parentPath?.parentPath?.value)) { | ||
indexContext.nestedRender = true | ||
return false | ||
} | ||
|
||
const newNode = j.exportDefaultDeclaration( | ||
j.functionDeclaration( | ||
j.identifier('NextIndexWrapper'), | ||
[], | ||
j.blockStatement([ | ||
j.returnStatement( | ||
// TODO: remove React.StrictMode wrapper and use | ||
// next.config.js option instead? | ||
path.node.arguments.find( | ||
(a) => a.type === 'JSXElement' | ||
) as JSXElement | ||
), | ||
]) | ||
) | ||
) | ||
|
||
path.parentPath.insertBefore(newNode) | ||
return true | ||
} | ||
return false | ||
}) | ||
.remove() | ||
|
||
indexContext.multipleRenderRoots = foundReactRender > 1 | ||
hasModifications = | ||
hasModifications && | ||
!indexContext.nestedRender && | ||
!indexContext.multipleRenderRoots | ||
|
||
// TODO: move function passed to reportWebVitals if present to | ||
// _app reportWebVitals and massage values to expected shape | ||
|
||
// root.find(j.CallExpression, { | ||
// callee: { | ||
// name: 'reportWebVitals' | ||
// } | ||
// }).remove() | ||
|
||
return hasModifications ? root.toSource(options) : null | ||
} |
Oops, something went wrong.