Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(codemod): Add codemod for js to jsx conversion #8551

Merged
merged 10 commits into from
Jun 12, 2023
1 change: 1 addition & 0 deletions packages/codemods/jest.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ module.exports = {
testMatch: ['**/__tests__/**/*.[jt]s?(x)', '**/*.test.[jt]s?(x)'],
testPathIgnorePatterns: [
'__fixtures__',
'__testfixtures__',
'__tests__/utils/*',
'.d.ts',
'dist',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ export const description =
'(v2.3.x->v2.3.x) Allow $api imports in *.routesHooks.ts files'

export const handler = () => {
// @ts-expect-error ignore, old codemod
task('Tsconfig For Route Hooks', async ({ setOutput }: task.TaskInnerApi) => {
addApiAliasToTsConfig()
setOutput('All done! Run `yarn rw lint --fix` to prettify your code')
Expand Down
47 changes: 47 additions & 0 deletions packages/codemods/src/codemods/v6.x.x/convertJsToJsx/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
# Convert Js To Jsx

**Description**

Vite works best when you avoid using `.js` files which actually contain JSX inside them. They should ideally be given the `.jsx` extension. Features such as hot reloading is unavailable in cases where you use `.js` where `.jsx` is more appropriate.

This codemod examines all files ending in `.js` within your `web/src` and renames any files which contains JSX to end with `.jsx` instead of `.js`.

**NOTE**: The contents of your files are untouched. This only affects the extension.

**Examples**

For example the following `App.js`:
```js
import { FatalErrorBoundary, RedwoodProvider } from '@redwoodjs/web'
import { RedwoodApolloProvider } from '@redwoodjs/web/apollo'

import FatalErrorPage from 'src/pages/FatalErrorPage'
import Routes from 'src/Routes'

import './index.css'

const App = () => (
<FatalErrorBoundary page={FatalErrorPage}>
<RedwoodProvider titleTemplate="%PageTitle | %AppTitle">
<RedwoodApolloProvider>
<Routes />
</RedwoodApolloProvider>
</RedwoodProvider>
</FatalErrorBoundary>
)

export default App
```
would become `App.jsx` as it clearly contains JSX.

However a file such as `TestCell.mock.js`:
```js
// Define your own mock data here:
export const standard = (/* vars, { ctx, req } */) => ({
test: {
id: 42,
},
})
```
would remain `TestCell.mock.js` as it does not contain JSX.

Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import { FatalErrorBoundary, RedwoodProvider } from '@redwoodjs/web'
import { RedwoodApolloProvider } from '@redwoodjs/web/apollo'

import FatalErrorPage from 'src/pages/FatalErrorPage'
import Routes from 'src/Routes'

import './index.css'

const App = () => (
<FatalErrorBoundary page={FatalErrorPage}>
<RedwoodProvider titleTemplate="%PageTitle | %AppTitle">
<RedwoodApolloProvider>
<Routes />
</RedwoodApolloProvider>
</RedwoodProvider>
</FatalErrorBoundary>
)

export default App
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
// In this file, all Page components from 'src/pages` are auto-imported. Nested
// directories are supported, and should be uppercase. Each subdirectory will be
// prepended onto the component name.
//
// Examples:
//
// 'src/pages/HomePage/HomePage.js' -> HomePage
// 'src/pages/Admin/BooksPage/BooksPage.js' -> AdminBooksPage

import { Router, Route } from '@redwoodjs/router'

const Routes = () => {
return (
<Router>
<Route path="/test" page={TestPage} name="test" />
<Route notfound page={NotFoundPage} />
</Router>
)
}

export default Routes
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
export const QUERY = gql`
query FindTestQuery($id: Int!) {
test: test(id: $id) {
id
}
}
`

export const Loading = () => <div>Loading...</div>

export const Empty = () => <div>Empty</div>

export const Failure = ({ error }) => (
<div style={{ color: 'red' }}>Error: {error?.message}</div>
)

export const Success = ({ test }) => {
return <div>{JSON.stringify(test)}</div>
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
// Define your own mock data here:
export const standard = (/* vars, { ctx, req } */) => ({
test: {
id: 42,
},
})
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import { Loading, Empty, Failure, Success } from './TestCell'
import { standard } from './TestCell.mock'

export const loading = () => {
return Loading ? <Loading /> : <></>
}

export const empty = () => {
return Empty ? <Empty /> : <></>
}

export const failure = (args) => {
return Failure ? <Failure error={new Error('Oh no')} {...args} /> : <></>
}

export const success = (args) => {
return Success ? <Success {...standard()} {...args} /> : <></>
}

export default { title: 'Cells/TestCell' }
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import { render } from '@redwoodjs/testing/web'
import { Loading, Empty, Failure, Success } from './TestCell'
import { standard } from './TestCell.mock'

// Generated boilerplate tests do not account for all circumstances
// and can fail without adjustments, e.g. Float and DateTime types.
// Please refer to the RedwoodJS Testing Docs:
// https://redwoodjs.com/docs/testing#testing-cells
// https://redwoodjs.com/docs/testing#jest-expect-type-considerations

describe('TestCell', () => {
it('renders Loading successfully', () => {
expect(() => {
render(<Loading />)
}).not.toThrow()
})

it('renders Empty successfully', async () => {
expect(() => {
render(<Empty />)
}).not.toThrow()
})

it('renders Failure successfully', async () => {
expect(() => {
render(<Failure error={new Error('Oh no')} />)
}).not.toThrow()
})

// When you're ready to test the actual output of your component render
// you could test that, for example, certain text is present:
//
// 1. import { screen } from '@redwoodjs/testing/web'
// 2. Add test: expect(screen.getByText('Hello, world')).toBeInTheDocument()

it('renders Success successfully', async () => {
expect(() => {
render(<Success test={standard().test} />)
}).not.toThrow()
})
})
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
const TestComponent = () => {
return (
<div>
<h2>{'TestComponent'}</h2>
<p>{'Find me in ./web/src/components/TestComponent/TestComponent.js'}</p>
</div>
)
}

export default TestComponent
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
// When you've added props to your component,
// pass Storybook's `args` through this story to control it from the addons panel:
//
// ```jsx
// export const generated = (args) => {
// return <TestComponent {...args} />
// }
// ```
//
// See https://storybook.js.org/docs/react/writing-stories/args.

import TestComponent from './TestComponent'

export const generated = () => {
return <TestComponent />
}

export default {
title: 'Components/TestComponent',
component: TestComponent,
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import { render } from '@redwoodjs/testing/web'

import TestComponent from './TestComponent'

// Improve this test with help from the Redwood Testing Doc:
// https://redwoodjs.com/docs/testing#testing-components

describe('TestComponent', () => {
it('renders successfully', () => {
expect(() => {
render(<TestComponent />)
}).not.toThrow()
})
})
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
<!DOCTYPE html>
<html lang="en">

<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<link rel="icon" type="image/png" href="/favicon.png" />
</head>

<body>
<!-- Please keep this div empty -->
<div id="redwood-app"></div>
</body>

</html>
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
const TestLayout = ({ children }) => {
return <>{children}</>
}

export default TestLayout
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import TestLayout from './TestLayout'

export const generated = (args) => {
return <TestLayout {...args} />
}

export default {
title: 'Layouts/TestLayout',
component: TestLayout,
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import { render } from '@redwoodjs/testing/web'

import TestLayout from './TestLayout'

// Improve this test with help from the Redwood Testing Doc:
// https://redwoodjs.com/docs/testing#testing-pages-layouts

describe('TestLayout', () => {
it('renders successfully', () => {
expect(() => {
render(<TestLayout />)
}).not.toThrow()
})
})
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
// This page will be rendered when an error makes it all the way to the top of the
// application without being handled by a Javascript catch statement or React error
// boundary.
//
// You can modify this page as you wish, but it is important to keep things simple to
// avoid the possibility that it will cause its own error. If it does, Redwood will
// still render a generic error page, but your users will prefer something a bit more
// thoughtful. =)

// Ensures that production builds do not include the error page
let RedwoodDevFatalErrorPage = undefined
if (process.env.NODE_ENV === 'development') {
RedwoodDevFatalErrorPage =
require('@redwoodjs/web/dist/components/DevFatalErrorPage').DevFatalErrorPage
}

export default RedwoodDevFatalErrorPage ||
(() => (
<main>
<style
dangerouslySetInnerHTML={{
__html: `
html, body {
margin: 0;
}
html * {
box-sizing: border-box;
}
main {
display: flex;
align-items: center;
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, "Noto Sans", sans-serif;
text-align: center;
background-color: #E2E8F0;
height: 100vh;
}
section {
background-color: white;
border-radius: 0.25rem;
width: 32rem;
padding: 1rem;
margin: 0 auto;
box-shadow: 0 1px 3px 0 rgba(0, 0, 0, 0.1), 0 1px 2px 0 rgba(0, 0, 0, 0.06);
}
h1 {
font-size: 2rem;
margin: 0;
font-weight: 500;
line-height: 1;
color: #2D3748;
}
`,
}}
/>

<section>
<h1>
<span>Something went wrong</span>
</h1>
</section>
</main>
))
Loading