It'd be cool to add support for this. Perhaps the existing Typescript plugin can just add require('ts-node/register')
That would be a good solution.
Closing this as probably won't ever support it in core.
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
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()
.
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 errorTSError: โจฏ 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:12index.ts:367 getOutput
[new-adaptavist-docs]/[ts-node]/src/index.ts:367:40index.ts:558 Object.compile
[new-adaptavist-docs]/[ts-node]/src/index.ts:558:11index.ts:439 Module.m._compile
[new-adaptavist-docs]/[ts-node]/src/index.ts:439:43index.ts:439 Module.m._compile
[new-adaptavist-docs]/[ts-node]/src/index.ts:439:23index.ts:442 require.extensions.(anonymous function)
[new-adaptavist-docs]/[ts-node]/src/index.ts:442:12index.ts:442 Object.require.extensions.(anonymous function) [as .ts]
[new-adaptavist-docs]/[ts-node]/src/index.ts:442:12v8-compile-cache.js:159 require
[new-adaptavist-docs]/[v8-compile-cache]/v8-compile-cache.js:159:20gatsby-node.js:4 Object.
/Users/jonny/work/new-adaptavist-docs/gatsby-node.js:4:23v8-compile-cache.js:178 Module._compile
[new-adaptavist-docs]/[v8-compile-cache]/v8-compile-cache.js:178:30v8-compile-cache.js:159 require
[new-adaptavist-docs]/[v8-compile-cache]/v8-compile-cache.js:159:20api-runner-node.js:61 require
[new-adaptavist-docs]/[gatsby]/src/utils/api-runner-node.js:61:22success 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.
๐คฆโโ๏ธ 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.
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.
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: https://github.com/mdluo/blog-gatsby/commit/68eb2693a67fbafc869fd55272d04bc3e96f4021
@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
@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
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.
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.
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.
If you want I made a TypeScript starter (including Gatsby node hooks and config):
Most helpful comment
Quick note for anyone looking to do this - it's very straightforward. Just add
ts-node
yourself and add the hook ingatsby-node.js
before loading your TS files: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