Closed pspeter3 closed 6 years ago
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.
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.
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
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 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.
🤦♂️ 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):
Unfortunately some of the solutions to have TypeScript config files break with Gatsby v3 including gatsby-plugin-ts-config
(https://github.com/Js-Brecht/gatsby-plugin-ts-config/issues/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?
Totally supporting @openscript on having core-supported .ts
- .tsx
file extension support for gatsby-*.*
files!
Come on!
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.
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.
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
@qhj The error you faced is due to this https://github.com/gatsbyjs/gatsby/blob/3651e932c507bb783e4385820a9ce1065843a908/packages/gatsby/src/utils/webpack.config.js#L798-L813
gatsby tries to cache gatsby-node.js
with an assumption that gatsby-node
must be in a .js
extension. In this case, obviously, not.
@alvis I get it. Thanks for your reply.
@qhj The error you faced is due to this https://github.com/gatsbyjs/gatsby/blob/3651e932c507bb783e4385820a9ce1065843a908/packages/gatsby/src/utils/webpack.config.js#L798-L813
gatsby tries to cache
gatsby-node.js
with an assumption thatgatsby-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.
I used this way. It works fine during
yarn develop
but two lines of warning occurs when runningyarn 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)
By the way, I wonder why there is require('source-map-support').install()
? How is it related to the topic? Thanks
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} */
Please go to https://github.com/gatsbyjs/gatsby/discussions/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.
Future googlers: This is available in 4.9. https://www.gatsbyjs.com/docs/reference/release-notes/v4.9/#support-for-typescript-in-gatsby-config-and-gatsby-node
Assuming I have ts-node, it would be nice to write the config file in TypeScript.