Skip to content

Commit

Permalink
GraphQL Codegen for generating gql types (#2485)
Browse files Browse the repository at this point in the history
* Add generate types cli function | Automatically trigger in dev

* Setup watcher for generate types

* Add commented out code for makeMergeSchema

Remove unused imports

* Forgot to add codegen deps

* Add types to tsconfig

* Remove prebuild tasks
All type generation now under rw g types
Move watcher one level up, so it can be reused more easily for other actions

* Progress: query operation and mutation operation results are globals

* Generify graphqlHooksProvider

* Fix createCell types to avoid hacking OperationResult

* Allow passing through of apollo error

* Update cell generator and template to have types

* Transform to js, when not a typescript project

* Bit of cleanup

* Update cell generator snapshots

* Merge template changes

* Update cell list template

* Get tests working | add compute for readablity

* Update formatting on cell template

* PR Comments

* Lint

* Sometimes vscode just needs a refresh 🤷
  • Loading branch information
dac09 authored and pull[bot] committed Jan 21, 2024
1 parent aa162b1 commit 85cc2bd
Show file tree
Hide file tree
Showing 24 changed files with 1,465 additions and 242 deletions.
7 changes: 6 additions & 1 deletion packages/cli/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,12 @@
"prompts": "^2.4.1",
"rimraf": "^3.0.2",
"terminal-link": "^2.1.1",
"yargs": "^16.0.3"
"yargs": "^16.0.3",
"@graphql-codegen/cli": "^1.21.4",
"@graphql-codegen/typescript": "^1.22.0",
"@graphql-codegen/typescript-operations": "^1.17.16",
"@graphql-codegen/typescript-react-apollo": "^2.2.4",
"@graphql-codegen/typescript-resolvers": "^1.19.1"
},
"devDependencies": {
"@types/listr": "^0.14.3",
Expand Down
17 changes: 11 additions & 6 deletions packages/cli/src/commands/dev.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,12 @@ import terminalLink from 'terminal-link'

import { getConfig } from '@redwoodjs/internal'
import { shutdownPort } from '@redwoodjs/internal/devtools'
import { getProject } from '@redwoodjs/structure'
const project = getProject()

import { getPaths } from 'src/lib'
import c from 'src/lib/colors'
import { generatePrismaClient } from 'src/lib/generatePrismaClient'
import runPreBuildTasks from 'src/lib/runPreBuildTasks'

export const command = 'dev [side..]'
export const description = 'Start development servers for api, and web'
Expand Down Expand Up @@ -53,9 +54,6 @@ export const handler = async ({
const API_DIR_SRC = getPaths().api.src
const WEB_DIR_SRC = getPaths().web.src

// Run tasks like type generate, etc.
runPreBuildTasks()

if (side.includes('api')) {
try {
await generatePrismaClient({
Expand Down Expand Up @@ -86,6 +84,7 @@ export const handler = async ({
}
}

/** @type {Record<string, import('concurrently').CommandObj>} */
const jobs = {
api: {
name: 'api',
Expand All @@ -101,10 +100,16 @@ export const handler = async ({
command: `cd "${path.join(
BASE_DIR,
'web'
)}" && yarn webpack-dev-server --config ../node_modules/@redwoodjs/core/config/webpack.development.js ${forward}`,
)}" && yarn cross-env NODE_ENV=development webpack-dev-server --config ../node_modules/@redwoodjs/core/config/webpack.development.js ${forward}`,
prefixColor: 'blue',
runWhen: () => fs.existsSync(WEB_DIR_SRC),
},
typeGenerator: {
name: 'typeGenerator',
command: 'yarn rw generate types --watch',
prefixColor: 'green',
runWhen: () => project.isTypeScriptProject,
},
}

if (esbuild) {
Expand All @@ -118,7 +123,7 @@ export const handler = async ({

concurrently(
Object.keys(jobs)
.map((n) => side.includes(n) && jobs[n])
.map((n) => (side.includes(n) || n === 'typeGenerator') && jobs[n])
.filter((job) => job && job.runWhen()),
{
prefix: '{name} |',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -169,7 +169,7 @@ export const Success = ({ userProfile }) => {

exports[`creates a cell mock with a camelCase word name 1`] = `
"// Define your own mock data here:
export const standard = (/* vars, { ctx, req } */) => ({
export const standard = () => /* vars, { ctx, req } */ ({
userProfile: {
id: 42,
},
Expand All @@ -179,7 +179,7 @@ export const standard = (/* vars, { ctx, req } */) => ({

exports[`creates a cell mock with a kebabCase word name 1`] = `
"// Define your own mock data here:
export const standard = (/* vars, { ctx, req } */) => ({
export const standard = () => /* vars, { ctx, req } */ ({
userProfile: {
id: 42,
},
Expand All @@ -189,7 +189,7 @@ export const standard = (/* vars, { ctx, req } */) => ({

exports[`creates a cell mock with a multi word name 1`] = `
"// Define your own mock data here:
export const standard = (/* vars, { ctx, req } */) => ({
export const standard = () => /* vars, { ctx, req } */ ({
userProfile: {
id: 42,
},
Expand All @@ -199,7 +199,7 @@ export const standard = (/* vars, { ctx, req } */) => ({

exports[`creates a cell mock with a single word name 1`] = `
"// Define your own mock data here:
export const standard = (/* vars, { ctx, req } */) => ({
export const standard = () => /* vars, { ctx, req } */ ({
user: {
id: 42,
},
Expand All @@ -209,7 +209,7 @@ export const standard = (/* vars, { ctx, req } */) => ({

exports[`creates a cell mock with a snakeCase word name 1`] = `
"// Define your own mock data here:
export const standard = (/* vars, { ctx, req } */) => ({
export const standard = () => /* vars, { ctx, req } */ ({
userProfile: {
id: 42,
},
Expand Down Expand Up @@ -338,7 +338,7 @@ export default { title: 'Cells/UserProfileCell' }
`;

exports[`creates a cell test with a camelCase word name 1`] = `
"import { render, screen } from '@redwoodjs/testing'
"import { render } from '@redwoodjs/testing'
import { Loading, Empty, Failure, Success } from './UserProfileCell'
import { standard } from './UserProfileCell.mock'
Expand All @@ -364,7 +364,8 @@ describe('UserProfileCell', () => {
// When you're ready to test the actual output of your component render
// you could test that, for example, certain text is present:
//
// expect(screen.getByText('Hello, world')).toBeInTheDocument()
// 1. import { screen } from '@redwoodjs/testing'
// 2. Add test: expect(screen.getByText('Hello, world')).toBeInTheDocument()
it('renders Success successfully', async () => {
expect(() => {
Expand All @@ -376,7 +377,7 @@ describe('UserProfileCell', () => {
`;

exports[`creates a cell test with a kebabCase word name 1`] = `
"import { render, screen } from '@redwoodjs/testing'
"import { render } from '@redwoodjs/testing'
import { Loading, Empty, Failure, Success } from './UserProfileCell'
import { standard } from './UserProfileCell.mock'
Expand All @@ -402,7 +403,8 @@ describe('UserProfileCell', () => {
// When you're ready to test the actual output of your component render
// you could test that, for example, certain text is present:
//
// expect(screen.getByText('Hello, world')).toBeInTheDocument()
// 1. import { screen } from '@redwoodjs/testing'
// 2. Add test: expect(screen.getByText('Hello, world')).toBeInTheDocument()
it('renders Success successfully', async () => {
expect(() => {
Expand All @@ -414,7 +416,7 @@ describe('UserProfileCell', () => {
`;

exports[`creates a cell test with a multi word name 1`] = `
"import { render, screen } from '@redwoodjs/testing'
"import { render } from '@redwoodjs/testing'
import { Loading, Empty, Failure, Success } from './UserProfileCell'
import { standard } from './UserProfileCell.mock'
Expand All @@ -440,7 +442,8 @@ describe('UserProfileCell', () => {
// When you're ready to test the actual output of your component render
// you could test that, for example, certain text is present:
//
// expect(screen.getByText('Hello, world')).toBeInTheDocument()
// 1. import { screen } from '@redwoodjs/testing'
// 2. Add test: expect(screen.getByText('Hello, world')).toBeInTheDocument()
it('renders Success successfully', async () => {
expect(() => {
Expand All @@ -452,7 +455,7 @@ describe('UserProfileCell', () => {
`;

exports[`creates a cell test with a single word name 1`] = `
"import { render, screen } from '@redwoodjs/testing'
"import { render } from '@redwoodjs/testing'
import { Loading, Empty, Failure, Success } from './UserCell'
import { standard } from './UserCell.mock'
Expand All @@ -478,7 +481,8 @@ describe('UserCell', () => {
// When you're ready to test the actual output of your component render
// you could test that, for example, certain text is present:
//
// expect(screen.getByText('Hello, world')).toBeInTheDocument()
// 1. import { screen } from '@redwoodjs/testing'
// 2. Add test: expect(screen.getByText('Hello, world')).toBeInTheDocument()
it('renders Success successfully', async () => {
expect(() => {
Expand All @@ -490,7 +494,7 @@ describe('UserCell', () => {
`;

exports[`creates a cell test with a snakeCase word name 1`] = `
"import { render, screen } from '@redwoodjs/testing'
"import { render } from '@redwoodjs/testing'
import { Loading, Empty, Failure, Success } from './UserProfileCell'
import { standard } from './UserProfileCell.mock'
Expand All @@ -516,7 +520,8 @@ describe('UserProfileCell', () => {
// When you're ready to test the actual output of your component render
// you could test that, for example, certain text is present:
//
// expect(screen.getByText('Hello, world')).toBeInTheDocument()
// 1. import { screen } from '@redwoodjs/testing'
// 2. Add test: expect(screen.getByText('Hello, world')).toBeInTheDocument()
it('renders Success successfully', async () => {
expect(() => {
Expand Down
10 changes: 8 additions & 2 deletions packages/cli/src/commands/generate/cell/cell.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import pascalcase from 'pascalcase'
import pluralize from 'pluralize'

import { transformTSToJS } from 'src/lib'
import { getSchema } from 'src/lib'

import { yargsDefaults } from '../../generate'
Expand Down Expand Up @@ -87,7 +88,7 @@ export const files = async ({
extension: generateTypescript ? '.tsx' : '.js',
webPathSection: REDWOOD_WEB_PATH_NAME,
generator: 'cell',
templatePath: `cell${templateNameSuffix}.js.template`,
templatePath: `cell${templateNameSuffix}.tsx.template`,
templateVars: {
operationName,
idType,
Expand Down Expand Up @@ -141,8 +142,12 @@ export const files = async ({
// "path/to/fileB": "<<<template>>>",
// }
return files.reduce((acc, [outputPath, content]) => {
const template = generateTypescript
? content
: transformTSToJS(outputPath, content)

return {
[outputPath]: content,
[outputPath]: template,
...acc,
}
}, {})
Expand All @@ -152,6 +157,7 @@ export const { command, description, builder, handler } =
createYargsForComponentGeneration({
componentName: 'cell',
filesFn: files,
generateTypes: true,
optionsObj: {
...yargsDefaults,
list: {
Expand Down
17 changes: 0 additions & 17 deletions packages/cli/src/commands/generate/cell/templates/cell.js.template

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import type { ${operationName} } from 'types/gql-types'
import type { CellSuccessProps, CellFailureProps } from '@redwoodjs/web'

export const QUERY = gql`
query ${operationName}($id: ${idType}!) {
${camelName}: ${camelName}(id: $id) {
id
}
}
`

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

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

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

export const Success = ({ ${camelName} }: CellSuccessProps<${operationName}>) => {
return <div>{JSON.stringify(${camelName})}</div>
}

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import type { ${operationName} } from 'types/gql-types'
import type { CellSuccessProps, CellFailureProps } from '@redwoodjs/web'


export const QUERY = gql`
query ${operationName} {
${camelName} {
id
}
}
`

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

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

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

export const Success = ({ ${camelName} }: CellSuccessProps<${operationName}>) => {
return (
<ul>
{${camelName}.map((item) => {
return <li key={item.id}>{JSON.stringify(item)}</li>
})}
</ul>
)
}
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,8 @@ describe('${pascalName}Cell', () => {
// When you're ready to test the actual output of your component render
// you could test that, for example, certain text is present:
//
// expect(screen.getByText('Hello, world')).toBeInTheDocument()
// 1. import { screen } from '@redwoodjs/testing'
// 2. Add test: expect(screen.getByText('Hello, world')).toBeInTheDocument()

it('renders Success successfully', async () => {
expect(() => {
Expand Down
Loading

0 comments on commit 85cc2bd

Please sign in to comment.