TypeScript : conflicting target/module compilerOptions in tsconfig.json

352 views Asked by At

I've recently started to write a little graphql server on node.js based on fastify and mercurius. But being forced to learn many new stuff at the same time, I chose to start in javascript rather than typescript in order to avoid having to learn one more thing. But now I feel quite comfortable with most of those stuff, I would like to rewrite my code in typescript as long as I'm still in early stage.

But I'm getting to a conflict that I don't know which solution is the best to pick. The only thing I'm sure is the problem is around target and module compilerOptions from tsconfig.json.

So I went to mercurius documentation and found this page about typescript

so here's my index.ts which is kinda hybriding between their doc and my previous JS code (always targeting ES2017 or later)

import Fastify, { FastifyRequest, FastifyReply } from 'fastify'
import fastifyCookie from "@fastify/cookie"
import fastifyWebsocket from "@fastify/websocket"
import mercurius, { IResolvers } from 'mercurius' // Move IResolvers from resolvers file
import mercuriusCodegen from 'mercurius-codegen'

import { PubSub } from 'graphql-subscriptions'

const pubsub = new PubSub()

/******************************************************************************/

//import ms from "ms"

import { neo4jschema, dataModel } from "./neo4j/neo4j.mjs"

/******************************************************************************/
//(req: FastifyRequest, res: FastifyReply) => 
// { return { req, res, pubsub, redisSubscriptions, eventEmitter, dataModel, redisClient, serverUrl } }
const buildContext = async (req: FastifyRequest, res: FastifyReply) => 
{
    return { req, res, dataModel, }
}

const subscriptionContext = async (req: FastifyRequest, res: FastifyReply) =>
{
    return { req, res, }
}

type PromiseType<T> = T extends PromiseLike<infer U> ? U : T

declare module 'mercurius'
{
    interface MercuriusContext extends PromiseType<ReturnType<typeof buildContext>> {}
}

/******************************************************************************/
/******************************************************************************/
/******************************************************************************/

const fastify = Fastify (/* { serverFactory } */
{
    // logger: true,
    // https: null,
})

fastify.register(fastifyCookie, 
{
    secret: process.env.COOKIE_SIGNATURE_KEY, // for cookies signature
    hook: 'onRequest', // set to false to disable cookie autoparsing or set autoparsing on any of the following hooks: 'onRequest', 'preParsing', 'preHandler', 'preValidation'. default: 'onRequest'
    parseOptions: {}  // options for parsing cookies
})

// fastify.get ("/verify_email", verifyEmail)

fastify.register(fastifyWebsocket,
    {
        options:
        {
            maxPayload: 1048576,
        }
    })

fastify.register(mercurius,
    {
        schema: neo4jschema,
        subscription: 
        { 
            pubsub,
            // context: subscriptionContext,
            // context: (res: FastifyRequest, req: FastifyReply) => ({ res, req })
        },
        graphiql: process.env.NODE_ENV==='dev',
        context: buildContext,
    })
//(req: FastifyRequest, res: FastifyReply) => { return { req, res, /*pubsub, redisSubscriptions, eventEmitter,*/ dataModel, /*redisClient, serverUrl*/ } }

const url = await fastify.listen( { port: process.env.NODE_ENV==='dev' ? 80 : 443, host: "::" } )

// console.log(' Server ready at ' + serverUrl)

mercuriusCodegen(fastify, {
    // Commonly relative to your root package.json
    targetPath: './src/graphql/generated.ts'
  }).catch(console.error)

console.log (url)

The problem is that this code seems to be made for module=CommonJS (according to github examples linked by mercurius page) while I make use of top-level await calls (once in previous code, twice in imported "neo4j.mjs", which syntax is not accepted in module=CommonJS.

If I turn module to Node16 or NodeNext, I get these two error messages

src/index.ts:64:18 - error TS2769: No overload matches this call.
  Overload 1 of 3, '(plugin: FastifyPluginCallback<{ schema: GraphQLSchema; subscription: { pubsub: PubSub; }; graphiql: boolean; context: (req: FastifyRequest<RouteGenericInterface, RawServerDefault, ... 5 more ..., ResolveFastifyRequestType<...>>, res: FastifyReply<...>) => Promise<...>; }, RawServerDefault, FastifyTypeProvider, FastifyBaseLogger>, opts?: FastifyRegisterOptions<...> | undefined): FastifyInstance<...> & PromiseLike<...>', gave the following error.
    Argument of type 'typeof import("/Users/ankou29666/Insync/[email protected]/GoogleDrive/dev/GSC-graphql-server/node_modules/mercurius/index")' is not assignable to parameter of type 'FastifyPluginCallback<{ schema: GraphQLSchema; subscription: { pubsub: PubSub; }; graphiql: boolean; context: (req: FastifyRequest<RouteGenericInterface, RawServerDefault, ... 5 more ..., ResolveFastifyRequestType<...>>, res: FastifyReply<...>) => Promise<...>; }, RawServerDefault, FastifyTypeProvider, FastifyBaseLo...'.
      Type 'typeof import("/Users/ankou29666/Insync/[email protected]/GoogleDrive/dev/GSC-graphql-server/node_modules/mercurius/index")' provides no match for the signature '(instance: FastifyInstance<RawServerDefault, IncomingMessage, ServerResponse<IncomingMessage>, FastifyBaseLogger, FastifyTypeProvider>, opts: { ...; }, done: (err?: Error | undefined) => void): void'.
  Overload 2 of 3, '(plugin: FastifyPluginAsync<{ schema: GraphQLSchema; subscription: { pubsub: PubSub; }; graphiql: boolean; context: (req: FastifyRequest<RouteGenericInterface, RawServerDefault, ... 5 more ..., ResolveFastifyRequestType<...>>, res: FastifyReply<...>) => Promise<...>; }, RawServerDefault, FastifyTypeProvider, FastifyBaseLogger>, opts?: FastifyRegisterOptions<...> | undefined): FastifyInstance<...> & PromiseLike<...>', gave the following error.
    Argument of type 'typeof import("/Users/ankou29666/Insync/[email protected]/GoogleDrive/dev/GSC-graphql-server/node_modules/mercurius/index")' is not assignable to parameter of type 'FastifyPluginAsync<{ schema: GraphQLSchema; subscription: { pubsub: PubSub; }; graphiql: boolean; context: (req: FastifyRequest<RouteGenericInterface, RawServerDefault, ... 5 more ..., ResolveFastifyRequestType<...>>, res: FastifyReply<...>) => Promise<...>; }, RawServerDefault, FastifyTypeProvider, FastifyBaseLogger>'.
      Type 'typeof import("/Users/ankou29666/Insync/[email protected]/GoogleDrive/dev/GSC-graphql-server/node_modules/mercurius/index")' provides no match for the signature '(instance: FastifyInstance<RawServerDefault, IncomingMessage, ServerResponse<IncomingMessage>, FastifyBaseLogger, FastifyTypeProvider>, opts: { ...; }): Promise<...>'.
  Overload 3 of 3, '(plugin: FastifyPluginCallback<{ schema: GraphQLSchema; subscription: { pubsub: PubSub; }; graphiql: boolean; context: (req: FastifyRequest<RouteGenericInterface, RawServerDefault, ... 5 more ..., ResolveFastifyRequestType<...>>, res: FastifyReply<...>) => Promise<...>; }, RawServerDefault, FastifyTypeProvider, FastifyBaseLogger> | FastifyPluginAsync<...> | Promise<...> | Promise<...>, opts?: FastifyRegisterOptions<...> | undefined): FastifyInstance<...> & PromiseLike<...>', gave the following error.
    Argument of type 'typeof import("/Users/ankou29666/Insync/[email protected]/GoogleDrive/dev/GSC-graphql-server/node_modules/mercurius/index")' is not assignable to parameter of type 'FastifyPluginCallback<{ schema: GraphQLSchema; subscription: { pubsub: PubSub; }; graphiql: boolean; context: (req: FastifyRequest<RouteGenericInterface, RawServerDefault, ... 5 more ..., ResolveFastifyRequestType<...>>, res: FastifyReply<...>) => Promise<...>; }, RawServerDefault, FastifyTypeProvider, FastifyBaseLo...'.

64 fastify.register(mercurius,
                    ~~~~~~~~~


src/index.ts:82:1 - error TS2349: This expression is not callable.
  Type 'typeof import("/Users/ankou29666/Insync/[email protected]/GoogleDrive/dev/GSC-graphql-server/node_modules/mercurius-codegen/dist/index")' has no call signatures.

82 mercuriusCodegen(fastify, {
   ~~~~~~~~~~~~~~~~

[17:52:56] Found 2 errors. Watching for file changes.

If turning module to CommonJS, there is three times this obvious one

src/index.ts:78:13 - error TS1378: Top-level 'await' expressions are only allowed when the 'module' option is set to 'es2022', 'esnext', 'system', 'node16', or 'nodenext', and the 'target' option is set to 'es2017' or higher.

78 const url = await fastify.listen( { port: process.env.NODE_ENV==='dev' ? 80 : 443, host: "::" } )

There's no longer any error related to mercurius, but it doesn't like the top level awaits. And I really prefer the await syntax over the .then one. In my neo4j module file, I have two top level awaits prior to export variables.

It seems possible to import mercurius with module=ESNext or ES2022, that is ok for toplevel awaits, but this time the error is about imports.

src/index.ts:1:55 - error TS2792: Cannot find module 'fastify'. Did you mean to set the 'moduleResolution' option to 'nodenext', or to add aliases to the 'paths' option?

1 import Fastify, { FastifyRequest, FastifyReply } from 'fastify'

at every import excepted one that is different

src/index.ts:32:16 - error TS2664: Invalid module name in augmentation, module 'mercurius' cannot be found.

32 declare module 'mercurius'

If I add moduleResolution to nodenext as suggested, I get back to the same first error.

What would be the best tsconfig you could recommend me ? Here's my package.json file with all packages I already had imported in my JS project. I'd go for CommonJS

{
  "name": "gsc-graphql-server",
  "version": "1.0.0",
  "description": "",
  "type": "module",
  "main": "index.js",
  "scripts": {
    "start": "nodemon -e js,mjs,json,graphql,env src/index.js"
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
  "dependencies": {
    "@fastify/cookie": "^8.3.0",
    "@fastify/websocket": "^7.1.3",
    "@graphql-tools/apollo-engine-loader": "^7.3.26",
    "@neo4j/graphql-ogm": "^3.17.1",
    "@neo4j/graphql-plugin-auth": "^2.1.0",
    "@redis/json": "^1.0.4",
    "bcrypt": "^5.1.0",
    "crypto-js": "^4.1.1",
    "dotenv": "^16.0.3",
    "fastify": "^4.14.1",
    "fastify-express": "^0.4.0",
    "fs": "^0.0.1-security",
    "graphiql": "^2.4.0",
    "graphql": "^16.6.0",
    "graphql-middleware": "^6.1.33",
    "graphql-redis-subscriptions": "^2.6.0",
    "graphql-scalars": "^1.21.3",
    "graphql-shield": "^7.6.5",
    "graphql-subscriptions": "^2.0.0",
    "graphql-tag": "^2.12.6",
    "graphql-tools": "^8.3.19",
    "graphql-type-bigint": "^1.0.0",
    "http": "^0.0.1-security",
    "json": "^11.0.0",
    "json-bigint-patch": "^0.0.8",
    "mercurius": "^12.2.0",
    "mercurius-codegen": "^5.0.2",
    "microtime": "^3.1.1",
    "ms": "^2.1.3",
    "neo4j-driver": "^5.6.0",
    "nodemailer": "^6.9.1",
    "nodemon": "^2.0.21",
    "pubsub": "^3.2.1",
    "redis": "^4.6.5",
    "uuid": "^9.0.0"
  },
  "devDependencies": {
    "@types/ms": "^0.7.31",
    "@types/semver": "^7.3.13",
    "@types/ws": "^8.5.4",
    "typescript": "^5.0.2"
  }
}
``
1

There are 1 answers

0
ankou29666 On

Thanks Divama, this solved the problem, but however leads to a new one.

There's an import that i have to write as

import pkg from "@neo4j/graphql-ogm"
const { OGM } = pkg

because import { OGM } from "@neo4j/graphql-ogm" doesn't work in node18.14.2 and it replies me to use first syntax instead. But typescript doesn't want that first syntax. What could I do ?

so in tsconfig I currently have module and target both ES2022 and moduleResolution to node.