All jobs

Convert a Gatsby Site From JavaScript to TypeScript

Yes. An agent converts a Gatsby site's config, pages, templates and components to TypeScript with typed props and query data, builds it and opens a pull request.

Run this on RushIn a Project that has your repository.

What the agent does

The agent clones the site into its own cloud sandbox and counts what has to change. In a site like gatsbyjs/gatsby-starter-blog there are four config files at the root, gatsby-config.js, gatsby-node.js, gatsby-browser.js and gatsby-ssr.js, and under src three components (bio.js, layout.js, seo.js), two pages (404.js, index.js) and one template, templates/blog-post.js. One file is already TypeScript: src/pages/using-typescript.tsx imports PageProps, HeadFC and graphql from gatsby and types its query result with a DataProps type, which is the pattern the rest of the site should follow. package.json pins gatsby ^5.14.6 with React ^18.1.0, has only prettier ^2.8.8 as a dev dependency, uses package-lock.json, and its test script exits 1 with a reminder to write tests, so the build is the only check.

Gatsby 5 compiles .ts and .tsx files, and gatsby-config.ts and gatsby-node.ts, without a plugin, so the conversion needs no bundler change. The agent adds typescript as a dev dependency and a tsconfig.json with strict, jsx set for React, esModuleInterop, skipLibCheck and noEmit, plus a typecheck script that runs tsc --noEmit, because Gatsby's own compile step does not type-check and a pull request that only renames files would prove nothing. src/pages/using-typescript.tsx compiles under that config as is, which confirms the settings before anything else moves.

The config files go first because the source files depend on them. gatsby-config.js becomes gatsby-config.ts: its module.exports with a JSDoc @type {import('gatsby').GatsbyConfig} becomes an import type { GatsbyConfig } and a typed const config exported as default, with the siteMetadata, the two gatsby-source-filesystem entries, the remark plugins and the gatsby-plugin-feed serializer typed on its query argument. gatsby-node.ts types createPages and onCreateNode as GatsbyNode["createPages"] and GatsbyNode["onCreateNode"], which the JSDoc already named, and gives the allMarkdownRemark result a type so result.data is no longer any. gatsby-ssr.js becomes gatsby-ssr.tsx with GatsbySSR["onRenderBody"], and gatsby-browser.js becomes a .ts file of imports.

Then the React files, each renamed to .tsx. layout.js takes location, title and children, and reads the __PATH_PREFIX__ global Gatsby injects, so the agent declares that global in a src/global.d.ts rather than casting it. bio.js and seo.js call useStaticQuery and get a type for their site.siteMetadata shape; index.js and blog-post.js become React.FC<PageProps<DataProps>> with DataProps written from each file's graphql query, and their Head exports become HeadFC<DataProps>. The prop that blog-post.js destructures as { previous, next, site, markdownRemark: post } gets nullable types, since gatsby-node.ts passes null for the first and last post. Finally the agent runs npm run typecheck, npm run build and npx prettier --check with the repository's .prettierrc (semi: false, arrowParens: avoid) until all three pass, and opens the pull request.

Start it with one API call

POST https://api.prix.dev/o/{org}/sessions with this body:

{
  "agent": "claude",
  "input": "Convert this Gatsby site from JavaScript to TypeScript. Read package.json, gatsby-config.js, gatsby-node.js, gatsby-browser.js, gatsby-ssr.js, every file under src, and src/pages/using-typescript.tsx, which already shows the typing style to follow. Add typescript as a dev dependency with npm, a strict tsconfig.json with noEmit, and a typecheck script that runs tsc --noEmit. Rename the config files to gatsby-config.ts, gatsby-node.ts, gatsby-ssr.tsx and gatsby-browser.ts, typing them with GatsbyConfig, GatsbyNode and GatsbySSR and replacing module.exports with ES exports. Rename the components, pages and template to .tsx, type page components with PageProps and a DataProps type written from each file's graphql query, type Head exports with HeadFC, give useStaticQuery results a type, and declare the __PATH_PREFIX__ global in a .d.ts file. Do not use any; where a value can be null, say so in the type. Run npm run typecheck, npm run build and prettier --check with the repository's .prettierrc until all pass. Open a pull request that lists every renamed file and each real type error the conversion found.",
  "resources": [
    {
      "url": "https://github.com/your-org/your-app"
    }
  ]
}
  1. Get an API key. In the console, open Settings, then API keys, and create a key for your org. Send it in the Authorization header as a Bearer token.
  2. Send the request. POST the body in request.json to https://api.prix.dev/o/{org}/sessions, with your repository as the resources URL. The run starts in its own cloud sandbox.
  3. Read what came back. Follow the run on its live stream or wait for the run.completed webhook, then read the result and its trajectory.

What comes back

What comes back is a pull request on its own branch in which every .js file at the root and under src has been renamed to .ts or .tsx, with tsconfig.json, src/global.d.ts, the typescript dev dependency and a typecheck script added to package.json and package-lock.json. The renames are recorded as renames, so the diff shows the typing changes in each file rather than a deletion and an addition. The description lists the files in the order they were converted and quotes the final tsc --noEmit and gatsby build output from the sandbox.

The description also lists what the compiler found that JavaScript had hidden, with file and line: a prop that can be null where the template assumed an object, a siteMetadata field read with optional chaining that the query always returns, or a plugin option typed more narrowly than the config passed. Each is either fixed in the same commit or called out as left for review, never silenced with a cast.

Before merging, a reviewer checks that gatsby build still generates the same pages, that the gatsby-plugin-feed serializer in gatsby-config.ts still produces /rss.xml with the same fields, and that no any or @ts-ignore was introduced. The transcript at GET /o/{org}/sessions/{id}/messages holds every tsc run, including the failing early ones, so the review can follow how each error was resolved.

Run it on every repository still on JavaScript

A platform team with several Gatsby sites sends this request once per repository, from a script that loops over the list with resources set to each one and a request_id per repository, so a retried call returns the run already started rather than opening a second pull request. Each run has its own sandbox and its own branch.

Set a webhook URL on the key and Rush POSTs run.completed when each turn finishes, with data.session_url to read the pull request from. A review comment that asks for a different type on one page becomes a follow-up message on that session, which wakes the parked sandbox with the converted tree still in place.

The use case this job follows, with its automation and trigger: Hand off from your terminal.

Why run it on Rush?

A conversion is a loop of rename, type-check, fix and build, and on a Gatsby site the build step pulls in Sharp image processing and a GraphQL schema build that take a while on a laptop. The agent runs that loop in its own sandbox with its own node_modules and .cache, and your working tree stays on the branch you were on.

The result is checkable without trusting the description. The pull request carries the exact tsc --noEmit and gatsby build output, the transcript keeps every intermediate failure, and the rename-aware diff shows only the type annotations that changed. Several sites convert at once, each in its own sandbox.

Frequently asked questions

Does the site need a plugin or a bundler change to use TypeScript?

No. Gatsby 5 compiles .ts and .tsx sources and the gatsby-config.ts and gatsby-node.ts files itself. The agent adds typescript only so tsc --noEmit can type-check, because Gatsby's compile step strips types without checking them.

Will it use any to make the compiler pass?

No. The prompt forbids it, and the places where a JavaScript file was loose get real types: a DataProps type per page query, nullable previous and next props on the post template, and a declared __PATH_PREFIX__ global. An error the agent cannot type honestly is listed in the pull request instead.

What about generated GraphQL types?

The conversion writes the query types by hand from each file's graphql tag, which keeps the diff small. Gatsby can generate them instead by setting graphqlTypegen in gatsby-config.ts, which emits a src/gatsby-types.d.ts; ask for that in a follow-up and the agent switches the pages to the generated Queries namespace.

Will it change the formatting?

Only in the files it touches, and following the repository's .prettierrc, which turns semicolons off and avoids arrow parentheses. The format script already matches .ts and .tsx files, so the existing Prettier setup covers the converted tree without a config change.