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

Is it possible to write the gatsby config in TypeScript? #1457

Closed
pspeter3 opened this issue Jul 10, 2017 · 29 comments
Closed

Is it possible to write the gatsby config in TypeScript? #1457

pspeter3 opened this issue Jul 10, 2017 · 29 comments

Comments

@pspeter3
Copy link

Assuming I have ts-node, it would be nice to write the config file in TypeScript.

@KyleAMathews
Copy link
Contributor

It'd be cool to add support for this. Perhaps the existing Typescript plugin can just add require('ts-node/register')

@pspeter3
Copy link
Author

That would be a good solution.

@KyleAMathews
Copy link
Contributor

KyleAMathews commented Dec 18, 2017

Edit: This comment was written years ago when Typescript was much less popular. Most of the framework code is now written in Typescript. We're very open to community PRs adding better support for Typescript to core files.


Closing this as probably won't ever support it in core.

@clarkdave
Copy link

clarkdave commented Apr 15, 2018

Edit: Before you attempt the solution below, please update to 4.9 (https://www.gatsbyjs.com/docs/reference/release-notes/v4.9/#support-for-typescript-in-gatsby-config-and-gatsby-node) and use the built-in way of using TypeScript files.

Quick note for anyone looking to do this - it's very straightforward. Just add ts-node yourself and add the hook in gatsby-node.js before loading your TS files:

require('source-map-support').install()
require('ts-node').register({
  compilerOptions: {
    module: 'commonjs',
    target: 'es2017',
  },
})

// typescript files
exports.createPages = require('./lib/createPages')
exports.onCreateNode = require('./lib/onCreateNode')

There aren't any TS typings for the gatsby node API, but it's pretty easy to create some to cover your own app's surface area and provide some safety. Full example: https://gist.github.com/clarkdave/53cc050fa58d9a70418f8a76982dd6c8#file-types-ts

@jonnybot0
Copy link

Small follow-up to @clarkdave's most helpful comment; ts-node automagically loads tsconfig.json, so if you've already got one in your project, you needn't import it when you register ts-node. You should be able to just call require('ts-node').register().

@jonnybot0
Copy link

jonnybot0 commented Dec 21, 2018

So, since the above, I've been trying to start a fresh project utilizing this approach. I'm now having newfound problems. Whenever gatsby-node.js tries to load the file, I get an error like this:

  TSError: ⨯ Unable to compile TypeScript:
  src/util/createPages.ts(6,33): error TS7006: Parameter '_a' implicitly has an 'any' type.
  src/util/createPages.ts(11,50): error TS7006: Parameter 'result' implicitly has an 'any' type.
  src/util/createPages.ts(16,57): error TS7006: Parameter '_a' implicitly has an 'any' type.

Will update when I find a solution, but wanted to leave this here in case anyone else was having the same trouble & pulling their hair out.

The createPages.ts file I'm using definitely doesn't have any functions with a parameter called _a. Any functions in the file that do use any have explicitly declared the parameter type using : any. tsc compiles the file just fine, too, so it's only ts-node that seems to have the problem. I thought perhaps this was just part of ts-node's normal behaviour, but even changing my registration call to
require('ts-node').register({files: true}) hasn't helped.

Full error:

success open and validate gatsby-configs — 0.241 s
success load plugins — 0.120 s
error gatsby-node.js returned an error

TSError: ⨯ Unable to compile TypeScript:
src/util/createPages.ts(6,33): error TS7006: Parameter '_a' implicitly has an 'any' type.
src/util/createPages.ts(11,50): error TS7006: Parameter 'result' implicitly has an 'any' type.
src/util/createPages.ts(16,57): error TS7006: Parameter '_a' implicitly has an 'any' type.

  • index.ts:261 createTSError
    [new-adaptavist-docs]/[ts-node]/src/index.ts:261:12

  • index.ts:367 getOutput
    [new-adaptavist-docs]/[ts-node]/src/index.ts:367:40

  • index.ts:558 Object.compile
    [new-adaptavist-docs]/[ts-node]/src/index.ts:558:11

  • index.ts:439 Module.m._compile
    [new-adaptavist-docs]/[ts-node]/src/index.ts:439:43

  • index.ts:439 Module.m._compile
    [new-adaptavist-docs]/[ts-node]/src/index.ts:439:23

  • index.ts:442 require.extensions.(anonymous function)
    [new-adaptavist-docs]/[ts-node]/src/index.ts:442:12

  • index.ts:442 Object.require.extensions.(anonymous function) [as .ts]
    [new-adaptavist-docs]/[ts-node]/src/index.ts:442:12

  • v8-compile-cache.js:159 require
    [new-adaptavist-docs]/[v8-compile-cache]/v8-compile-cache.js:159:20

  • gatsby-node.js:4 Object.
    /Users/jonny/work/new-adaptavist-docs/gatsby-node.js:4:23

  • v8-compile-cache.js:178 Module._compile
    [new-adaptavist-docs]/[v8-compile-cache]/v8-compile-cache.js:178:30

  • v8-compile-cache.js:159 require
    [new-adaptavist-docs]/[v8-compile-cache]/v8-compile-cache.js:159:20

  • api-runner-node.js:61 require
    [new-adaptavist-docs]/[gatsby]/src/utils/api-runner-node.js:61:22

success onPreInit — 1.235 s
success delete html and css files from previous builds — 0.010 s
success initialize cache — 0.007 s
success copy gatsby files — 0.056 s

It does appear that the un-typed parameter of _a actually exists in the compiled version of my lib/createNode.ts file; that is, it looks like ts-node is trying parse the resultant js file from an initial compile.

@jonnybot0
Copy link

jonnybot0 commented Dec 28, 2018

🤦‍♂️ It was as simple as this: I was calling require('ts-node').register({files: true}) in both gatsby-config.js and gatsby-node.js. That accounts for the second attempt to compile an already compiled file.

Once I simply made it require ts-node once in gatsby-config.js, it worked.

@jonnybot0
Copy link

Pardon the spam... trying to decide the most appropriate place to leave this comment. If you've read this far looking for answers to the above problem, just skip this note! Everything you need to know is above. :)

I've been toying around with gatsby themes. I created a theme, and installed it into my project via yarn (that is, as a dependency in my package.json). My theme used the above solution for pulling typescript files into gatsby-config.js. While that works inside the theme's repo, it throws errors when trying to require the theme. The initial error is a bit confusing:

error ENOENT: no such file or directory, scandir 'adaptavist-docs-gatsby-theme'


  Error: ENOENT: no such file or directory, scandir 'my-docs-gatsby-theme'

error UNHANDLED REJECTION


  Error: ENOENT: no such file or directory, scandir 'my-docs-gatsby-theme'

error Command failed with exit code 1.

my-docs-gatsby-theme is the name of my custom theme. Debugging into the gatsby code, I found that the problem came when gatsby was trying to require the theme's gatsby-config.js file. The real, underlying error was this:

/path/to/my/project/node_modules/my-docs-gatsby-theme/siteMap.ts:1
(function (exports, require, module, __filename, __dirname) { import {SiteMap} from "./index";
                                                              ^^^^^^

SyntaxError: Unexpected token import
    at NativeCompileCache._moduleCompile (/path/to/my/project/node_modules/v8-compile-cache/v8-compile-cache.js:226:18)
    at Module._compile (/path/to/my/project/node_modules/v8-compile-cache/v8-compile-cache.js:172:36)
    at Module._extensions..js (module.js:663:10)
    at Object.require.extensions.(anonymous function) [as .ts] (/path/to/my/project/node_modules/ts-node/src/index.ts:431:14)
    at Module.load (module.js:565:32)
    at tryModuleLoad (module.js:505:12)
    at Function.Module._load (module.js:497:3)
    at Module.require (module.js:596:17)
    at require (/path/to/my/project/node_modules/v8-compile-cache/v8-compile-cache.js:159:20)
    at Object.<anonymous> (/path/to/my/project/node_modules/my-docs-gatsby-theme/gatsby-config.js:2:19)
    at Module._compile (/path/to/my/project/node_modules/v8-compile-cache/v8-compile-cache.js:178:30)
    at Object.Module._extensions..js (module.js:663:10)
    at Module.load (module.js:565:32)
    at tryModuleLoad (module.js:505:12)
    at Function.Module._load (module.js:497:3)
    at Module.require (module.js:596:17)

The typescript file I was importing had an import statement at the top. That's fine for typescript, not so fine for a vanilla node JS loader. I'm not sure why registering ts-node doesn't help in this case.

Only workaround I can think of is to compile my typescript files into JS for redistribution.

samselikoff added a commit to miragejs/site that referenced this issue Sep 2, 2019
Using [this comment](gatsbyjs/gatsby#1457 (comment)) to get TS wired up, so we could import our TS Router in gatsby-node.
samselikoff added a commit to miragejs/site that referenced this issue Sep 2, 2019
* SSR guides pages using router.pages

Using [this comment](gatsbyjs/gatsby#1457 (comment)) to get TS wired up, so we could import our TS Router in gatsby-node.

* Dont SSR dynamic pages
@mdluo
Copy link

mdluo commented Nov 16, 2019

I found a perfect (almost) solution: use gatsby-plugin-typegen to generate types from GraphQL schema so that there will be:

In both gatsby-node configs and tsx components.

Example: mdluo/blog-gatsby@68eb269

image

image

@ecklf
Copy link
Contributor

ecklf commented Dec 16, 2019

@mdluo I really like the solution you have come up with but it only seems to generate type definitions when using the useStaticQuery hook for me. I am also exporting the graphql schema in createPages.ts as in your example commit. Do you mind to explain how you got that working?

Solved: got my files outside of src folder (which the plugin scans).
https://github.com/cometkim/gatsby-plugin-typegen/blob/master/gatsby-node.ts#L19

@assainov
Copy link

@clarkdave 's solution is awesome. I actually took it a bit further and converted all Gatsby api files to TypeScript. There are multiple ways to achieve that.

Check out my blogpost: Converting Gatsby Config and Node API to TypeScript

@Ieuanoh
Copy link

Ieuanoh commented Jan 16, 2020

I was able to fix the issue raised by @jonnybot0 regarding the unexpected token in an import statement by moving where the ts-node().register was called.

I had this error as a result of an import into gatsby-config when calling register in gatsby-node:

(function (exports, require, module, __filename, __dirname) { import { LanguageCode } from "types/localisation"
                                                                       ^
  SyntaxError: Unexpected token {

Calling register() in gatsby-config instead of gatsby-node fixed this without having to do any extra transpiling.

@mrmartineau
Copy link
Contributor

FWIW I achieved this by moving any Gatsby config files, that I wanted written in TypeScript, to my src directory (e.g. ./src/gatsby). I then just created a prebuild step to compile those files with tsc (TypeScript's compiler). gatsby-plugin-ts was used instead of the gatsby-plugin-typescript because it checks your types while it compiles and autogenerates your Graphql schema too.

The prebuild step runs tsc using a different tsconfig.json (tsconfig.gatsby.json), like so:

"scripts": {
    "build:gatsby": "tsc --project tsconfig.gatsby.json",
    "prebuild": "yarn clean && yarn build:gatsby",
    "build": "gatsby build",
   ...
}

gatsby-plugin-ts requires some specific tsconfig changes which I needed because there was an issue with some Graphql queries being added to the compiled code. Below you can see what I used, which I took directly from the gatsby-plugin-ts readme

"compilerOptions": {
  "target": "ES2018",    /* or at least ES2015 */
  "module": "ESNext",    /* or at least ES2015 */
  "lib": ["dom"],             /* <-- required! */
  "jsx": "preserve",          /* <-- required! */
  "moduleResolution": "node", /* <-- required! */
  /* other options... */
}

My full tsconfig.gatsby.json looks like this:

{
  "compilerOptions": {
    "allowJs": true,
    "esModuleInterop": true,
    "forceConsistentCasingInFileNames": true,
    "isolatedModules": true,
    "jsx": "preserve",
    "lib": ["dom", "dom.iterable", "esnext"],
    "module": "esnext",
    "moduleResolution": "node",
    "noEmit": true,
    "resolveJsonModule": true,
    "skipLibCheck": true,
    "strict": false,
    "target": "es5"
    "module": "commonjs",
    "outDir": ".gatsby",
    "target": "es2017",
    "isolatedModules": false,
    "noEmit": false
  },
  "include": ["src/gatsby/**/*.ts"]
}

If you see the "outDir": ".gatsby", that is where the code is compiled to. I added this directory to my .gitignore file. What you will notice is the output from running tsc in this way does not bundle the files, it just converts them to .js file in CommonJS format, so if you are referencing other files that aren't .ts, you may have to use path.resolve to ensure you get the correct relative path.

Here's an example of that:

createPage({
  path: `/${node.node_locale.toLowerCase()}/${BLOG_PATH}/${node.slug}`,
  component: nodePath.resolve('./src/templates/Article/Article.tsx'),
  context: {
    id: node.contentful_id,
    locale: node.node_locale,
  },
})

gatsby-config.js is still needed by Gatsby, but now all it does is require the newly compiled file that tsc generates:

/**
 * DO NOT EDIT THIS FILE DIRECTLY
 * Edit the source file at `./src/gatsby/gatsby-config.ts`
 */
module.exports = require('./.gatsby/src/gatsby/gatsby-config')

I do the same for gatsby-node.js, and you can see the folder structure here:

❯ exa -T -L 2
.
├── createPages
│  ├── createContentfulArticlePages.ts
│  ├── createContentfulLegalPages.ts
│  └── createContentfulMarketingPages.ts
├── createPages.ts
├── gatsby-config.ts
├── gatsby-node.ts
├── paths.ts
└── README.md

Hope this helps some people. Let me know if it can be improved.

@dandv
Copy link
Contributor

dandv commented May 9, 2020

This gist by @JohnAlbin worked great as of the current Gatsby version, and was very easy to follow and set up. No changes were required in the build or develop commands.

@antoinerousseau
Copy link
Contributor

antoinerousseau commented Jul 26, 2020

If you want I made a TypeScript starter (including Gatsby node hooks and config):

https://github.com/antoinerousseau/gatsby-starter-antoine

@openscript
Copy link
Contributor

openscript commented Mar 8, 2021

Unfortunately some of the solutions to have TypeScript config files break with Gatsby v3 including gatsby-plugin-ts-config(Js-Brecht/gatsby-plugin-ts-config#29).

I would love to see Gatsby having full fledged TypeScript support, also for the config files. I guess the best would be if it's somehow integrated into gatsby-plugin-typescript. It seems to be the right place for it. What would be the necessary steps?

@dvakatsiienko
Copy link

Totally supporting @openscript on having core-supported .ts - .tsx file extension support for gatsby-*.* files!

@dustinlacewell
Copy link

Come on!

@samwightt
Copy link

It's really embarrassing that Gatsby's Typescript support is half-assed like this. Everything inside the src directory works perfectly fine, but gatsby-* don't for reasons that aren't understandable to beginners. This should really be re-opened until its resolved.

@KyleAMathews
Copy link
Contributor

This issue was closed years ago when Typescript was much less popular. We're very open to PRs adding better support for Typescript to core files. If you'd like to discuss how to help make that happen, please open a new issue and let's make it happen.

@qhj
Copy link

qhj commented Jul 11, 2021

I used this way. It works fine during yarn develop but two lines of warning occurs when running yarn build:

<w> [webpack.cache.PackFileCacheStrategy] Caching failed for pack: Error: Can't resolve 'path/to/my-gatsby-project/gatsby-node.js' in 'path/to/my-gatsby-project'

gatsby-config.js:

require('ts-node').register()

module.exports = require('./gatsby-config.ts')

project files:

$ exa -T -L 1
.
├── content
├── gatsby-browser.ts
├── gatsby-config.js
├── gatsby-config.ts
├── gatsby-node.ts
├── node_modules
├── package.json
├── public
├── README.md
├── src
└── yarn.lock

@alvis
Copy link
Contributor

alvis commented Jul 27, 2021

@qhj The error you faced is due to this

const cacheConfig = {
type: `filesystem`,
name: stage,
cacheLocation,
buildDependencies: {
config: [
__filename,
...store
.getState()
.flattenedPlugins.filter(plugin =>
plugin.nodeAPIs.includes(`onCreateWebpackConfig`)
)
.map(plugin => path.join(plugin.resolve, `gatsby-node.js`)),
],
},
}

gatsby tries to cache gatsby-node.js with an assumption that gatsby-node must be in a .js extension. In this case, obviously, not.

@qhj
Copy link

qhj commented Jul 27, 2021

@alvis I get it. Thanks for your reply.

@roffelsaurus
Copy link

roffelsaurus commented Jul 28, 2021

@qhj The error you faced is due to this

const cacheConfig = {
type: `filesystem`,
name: stage,
cacheLocation,
buildDependencies: {
config: [
__filename,
...store
.getState()
.flattenedPlugins.filter(plugin =>
plugin.nodeAPIs.includes(`onCreateWebpackConfig`)
)
.map(plugin => path.join(plugin.resolve, `gatsby-node.js`)),
],
},
}

gatsby tries to cache gatsby-node.js with an assumption that gatsby-node must be in a .js extension. In this case, obviously, not.

This is an unfortunate hardcoding which conflicts with the ts-node hack me and many use.
However, I avoided it by changing just this file to .js.

What is not mentioned on this issue is that it's not really the core files which is interesting to get typescript support for; it's plugins and other code in the build pipeline which use Gatsby APIs. They can grow quite complex and need defs to be maintainable.

@rynoV
Copy link

rynoV commented Dec 5, 2021

I used this way. It works fine during yarn develop but two lines of warning occurs when running yarn build:

<w> [webpack.cache.PackFileCacheStrategy] Caching failed for pack: Error: Can't resolve 'path/to/my-gatsby-project/gatsby-node.js' in 'path/to/my-gatsby-project'

gatsby-config.js:

require('ts-node').register()

module.exports = require('./gatsby-config.ts')

For those using this setup running into this issue, I kept my gatsby-node.ts as it was and added a gatsby-node.js with just the line:

module.exports = require('./gatsby-node.ts')

and this seems to have gotten rid of the warning. Although now I get

<w> [webpack.cache.PackFileCacheStrategy] Serializing big strings (1888kiB) impacts deserialization performance (consider using Buffer instead and decode when needed)

@fzyzcjy
Copy link

fzyzcjy commented Feb 4, 2022

By the way, I wonder why there is require('source-map-support').install()? How is it related to the topic? Thanks

@decanTyme
Copy link

decanTyme commented Feb 5, 2022

For anyone wondering how they could use the GatsbyConfig interface without rewriting to TS or using any other configuration/deps just for intellisense, just paste this at the top of your gatsby-config.js:

/** @type {import("gatsby").GatsbyConfig} */

@LekoArts
Copy link
Contributor

LekoArts commented Feb 7, 2022

Please go to #34613 and you can use TypeScript with Gatsby. By the time you're reading this it's also possible that the feature already was shipped in Gatsby 4. If that is the case the RFC will be marked as done.

@gatsbyjs gatsbyjs locked as resolved and limited conversation to collaborators Feb 7, 2022
@LekoArts
Copy link
Contributor

LekoArts commented Mar 2, 2022

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

No branches or pull requests