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

Typescript: import fail: Cannot find module 'query.gql' #59

Closed
estaub opened this issue Mar 11, 2017 · 33 comments
Closed

Typescript: import fail: Cannot find module 'query.gql' #59

estaub opened this issue Mar 11, 2017 · 33 comments
Labels

Comments

@estaub
Copy link

estaub commented Mar 11, 2017

This is a reopen of #42 .

Nothing has changed, it's just that the presence of a workaround is not a fix.

As described before, attempts to import a graphql module result in Cannot find module.... Using require instead of import, eg:

const query = require('gql/Query.gql');

works fine... up to a point. Somewhere, the Apollo graphql decorator performs a deep copy of the query The query DocumentNode returned above includes a default element that has itself as a property. As a result, the deep copy blows out with a soft stack overflow. So the full workaround is:

const query = require('gql/Query.gql'); delete query['default']

It's possible that the default problem is dependent on the tsconfig.json setting:

"allowSyntheticDefaultImports": true

I need it for other reasons, and was unable to easily test with it turned off.

@jnwng jnwng added the bug label Mar 14, 2017
@gungorkocak
Copy link

I am also having the same issue. I have also tried workarounds mentioned in #42 and here #59 .
Also tried adding custom module declaration for gql files and playing around with alternatives.

Workarounds and outputs are like these:

  1. Naive approach: Try direct import query.gql
import query from './query.gql'

/* =>

Ts output: `error TS2307: Cannot find module './query.gql'.`
Browser console output: -

*/
  1. Add wildcard custom declaration to index.t.ds
// index.d.ts
declare module "*.gql" {
  const content: any;
  export default content;
}

// component.tsx
import query from './query.gql'

/* =>

Ts output: Fine / No error
Browser console output: browser.js:40 Uncaught Error: Argument of undefined passed to parser was not a valid GraphQL DocumentNode. You may need to use 'graphql-tag' or another method to convert your operation into a document

*/
  1. Try with import = require syntax.
// index.d.ts
// // ...same as 2

// component.tsx
import query = require('./query.gql')

/* =>

Ts output: `error TS2345: Argument of type 'typeof "*.gql"' is not assignable to parameter of type 'DocumentNode'.
  Property 'kind' is missing in type 'typeof "*.gql"'.`
Browser console output: Fine / No error (actually works)

*/
  1. Try adding type declaration DocumentNode from graphql typings.
// index.d.ts
import {
  DocumentNode
} from 'graphql'

declare module "*.gql" {
  const content: DocumentNode;
  export default content;
}

// component.tsx
import query = require('./query.gql')

/* =>

Ts output: `error TS2307: Cannot find module './query.gql'.`
Browser output: -

*/

I can live with typescript error Argument of type 'typeof "*.gql"' is not assignable since it does not break actual usage, but it would be great to fix that in some way.

I have tried both "allowSyntheticDefaultImports": true and delete query['default'] workarounds, could not get an errorless flow for both typescript and browser usage.

@ddetkovs
Copy link

I found that this works:

// graphql.d.ts file
declare module '*.graphql' {
    import {DocumentNode} from 'graphql';

    const value: DocumentNode;
    export = value;
}

then, when I need to import *.graphql

/// <reference path="./graphql.d.ts" />
import schema = require('./schema.graphql');

@ScallyGames
Copy link

ScallyGames commented May 23, 2017

Changing the solution by @ddetkovs to

// graphql.d.ts file
declare module '*.graphql' {
    import {DocumentNode} from 'graphql';

    const value: DocumentNode;
    export default value;
}
/// <reference path="./graphql.d.ts" />
import schema from './schema.graphql';

works for ES2015 import.

@ddetkovs
Copy link

@Aides359 graphql loader exports schema like this:
module.exports = doc;

This module is then resolved with webpack and since you're using es2015 import syntax it tries to access its default member, which is undefined, thus causing a runtime error.

I get the following generated code:

var graphql_tools_1 = __webpack_require__(6);
var schema_graphql_1 = __webpack_require__(4);
var resolvers_1 = __webpack_require__(1);
exports.default = graphql_tools_1.makeExecutableSchema({ typeDefs: [schema_graphql_1.default], resolvers: resolvers_1.default });

Also, typescript handbook says this:

When importing a module using export =, TypeScript-specific import module = require("module") must be used to import the module.

http://www.typescriptlang.org/docs/handbook/modules.html

@ScallyGames
Copy link

@ddetkovs odd, my described setup works in my project, without any runtime errors 🤔
I'll check the generated code and see how it behaves, however I probably won't find time to do so before next monday.

@jasonzoladz
Copy link

Any updates on this?

@jnwng
Copy link
Contributor

jnwng commented Jun 20, 2017

unfortunately i dont know enough about typescript to know what the solution space for this issue might be like. if there's any chance someone can send me a repo (or just some code) that exhibits exactly this issue, i can look into figuring out how to resolve this

@ScallyGames
Copy link

@ddetkovs this fell of my radar, however I have now taken a look at my generated output.

While I don't know about makeExecutableSchema as I am currently only using it to write queries for me the generated code is

/* harmony import */ var __WEBPACK_IMPORTED_MODULE_4__request_foo_graphql__ = __webpack_require__(5);
/* harmony import */ var __WEBPACK_IMPORTED_MODULE_4__request_foo_graphql___default = __webpack_require__.n(__WEBPACK_IMPORTED_MODULE_4__request_foo_graphql__);

apollo.watchQuery({
   query:  __WEBPACK_IMPORTED_MODULE_4__request_foo_graphql___default
})

which works just fine.

@ddetkovs
Copy link

@Aides359 what I meant is when graphql-tag/loader compiles your query, it exports it as:
module.exports = doc here

when you do: import schema from './schema.graphql';, you import default property of the module, but exported doc doesn't have it, so it fails.

This works too:
import * as schema from './schema.graphql';

@buggy
Copy link

buggy commented Jul 21, 2017

This solution works for me. It's similar to what @ddetkovs posted earlier but allows you to use the import syntax and doesn't require using /// <reference path="..." />

All of my .graphql files are in a single folder so I could add an index.d.ts in that folder with the following

declare module '*.graphql' {
    import {DocumentNode} from 'graphql';

    const value: DocumentNode;
    export = value;
}

Having all of the .graphql files in a single directory and naming it index.d.ts avoids the need to add /// <reference path="./graphql.d.ts" /> in other files.

I can then import using import * as MyQuery from "../graphql/MyQuery.graphql"

Thanks to @ddetkovs, @Aides359

@gustavoalfaro
Copy link

Any updates here?

@nitingupta910
Copy link

@gustavoalfaro Solution by @Aides359 works for me.

@gustavoalfaro
Copy link

gustavoalfaro commented May 11, 2018

@nitingupta910 Changing this: import MyQuery from "../graphql/MyQuery.graphql"
for this: import * as MyQuery from "../graphql/MyQuery.graphql"
doesn't work for me

@estaub
Copy link
Author

estaub commented May 11, 2018

@gustavoalfaro

Did you include the declare module '*.graphql'... declaration suggested above? I figure that with 20 up-votes, it's probably worth a shot!

What error do you see?

@rajeshkumarcc
Copy link

Facing the same issue. Any update on this?

@jnwng
Copy link
Contributor

jnwng commented May 28, 2018

again, unfortunately i dont know enough about typescript in order to figure out what i can do here. please open a PR to provide better documentation about TypeScript usage if that is the general consensus on how to avoid other folks getting hit by this.

closing for now, but please re-open if there's more to discuss.

@jnwng jnwng closed this as completed May 28, 2018
@suciuvlad
Copy link

+1

@eug48
Copy link

eug48 commented Jun 7, 2018

Could anyone having issues with this please post the error message they're getting? I had trouble implementing the above solution because I didn't have the import { DocumentNode } from "graphql" inside the declare module..

@Falieson
Copy link

Could someone upload a reproducible example of it working? I am getting this error:

[server] Module build failed (from ./node_modules/graphql-tag/loader.js):
[server] GraphQLError: Syntax Error: Unexpected Name "module"
// Module.tsx
/// <reference path="../../types/graphql.d.ts" />
import getStatus from './NetworkQueries.graphql'
// const  getStatus = require('./NetworkQueries.gql')
// NetworkQueries.graphql
query {
  networkStatus @client {
    isConnected
  }
}

@pabl-o-ce
Copy link

pabl-o-ce commented Aug 24, 2018

what is the current status on this issue?
webpack ts loader compile without any problem my import of graphql files using like

import * as type from 'type.grpahql';
// it get me the files like I want
// but gets my this error on console and vscode [ts] Cannot find module './type.graphql'

I think this is more in tsconfig problem if anyone can get me some guide I will be great

Solved
just add to tsconfig includes the d.ts definition of @buggy
thanks

@mrdulin
Copy link

mrdulin commented Sep 17, 2018

@buggy thanks. After add d.ts,tslint still throw some error:

My case is:

import * as Q from 'gqlMod/queries/library.gql';
import * as M from 'gqlMod/mutations/library.gql';

//...
//...

export default compose(
  graphql(Q.CART, {
    props: ({ data: { cart } }) => {
      return {
        cart
      };
    }
  }),
  graphql(M.REMOVE_ALL_FROM_COUNT, { name: 'removeAllFromCart' }),
  graphql(M.ADD_TO_CART, { name: 'addToCart' }),
  graphql(M.REMOVE_FROM_CART, { name: 'removeFromCart' }),
  graphql(M.REMOVE_COUNT_FROM_CART, { name: 'removeCountFromCart' })
)(Cart);

tslint give me some errors:

[ts] Property 'CART' does not exist on type 'DocumentNode'.
[ts] Property 'REMOVE_ALL_FROM_COUNT' does not exist on type 'DocumentNode'.
...

So I change the d.ts like this:

declare module '*.gql' {
  import { DocumentNode } from 'graphql';

  const value: {
    CART: DocumentNode;
    REMOVE_ALL_FROM_COUNT: DocumentNode;
    ADD_TO_CART: DocumentNode;
    REMOVE_FROM_CART: DocumentNode;
    REMOVE_COUNT_FROM_CART: DocumentNode;
  };
  export = value;
}

It works fine. tslint error gone. It's a little more complex for adding definition for each query.

So change d.ts again like this:

declare module '*.gql' {
  import { DocumentNode } from 'graphql';

  const value: {
    [key: string]: DocumentNode;
  };
  export = value;
}

@annguyen0505
Copy link

@gustavoalfaro

Did you include the declare module '*.graphql'... declaration suggested above? I figure that with 20 up-votes, it's probably worth a shot!

What error do you see?

i got the imported module is just a string? just like that "/static/media/postsByUser.79323037.graphql"

@sneko
Copy link

sneko commented Sep 26, 2018

@buggy thank you for the solution!

I'm still having an issue because my GraphQL client (Apollo) is expecting a object typed GraphQLSchema by using your method I just get a DocumentNode.

ts]
Argument of type '{ schema: DocumentNode; }' is not assignable to parameter of type 'Options'.
  Types of property 'schema' are incompatible.
    Type 'DocumentNode' is not assignable to type 'GraphQLSchema'.
      Property 'astNode' is missing in type 'DocumentNode'.
(property) schema: DocumentNode

Do you know how to fit to this requirement? Thank you!

Note: I tried to set GraphQLSchema instead of DocumentNode in the index.d.ts but it doesn't work

@nemcek
Copy link

nemcek commented Oct 3, 2018

@gustavoalfaro
Did you include the declare module '*.graphql'... declaration suggested above? I figure that with 20 up-votes, it's probably worth a shot!
What error do you see?

i got the imported module is just a string? just like that "/static/media/postsByUser.79323037.graphql"

@annguyen0505
I got the same problem and you probably aren't using graphql-tag/loader in your webpack configuration.

Check out react-app-rewired with react-app-rewire-graphql-tag so you don't need to eject your react app (assuming you have a project created by create-react-app).

@irace
Copy link

irace commented Nov 10, 2018

@nemcek Can you elaborate on “you probably aren't using graphql-tag/loader in your webpack configuration”?

My webpack configuration looks just like:

{
  test: /\.(graphql|gql)$/,
  exclude: /node_modules/,
  loader: 'graphql-tag/loader',
}

I’ve added the following as per this comment:

declare module '*.graphql' {
    import {DocumentNode} from 'graphql';

    const value: DocumentNode;
    export = value;
}

Which does get rid of my compile-time errors, but I still get hit with the following at runtime:

GraphQLError: Syntax Error: Unexpected Name "module"

Any thoughts would be appreciated, I really have no idea how to get past this.

@switz
Copy link

switz commented Nov 14, 2018

The solution works for me, but does anyone have a solution that works across many directories? My codebase is based on a module structure so the graphql tags are not flat in a single directory.

@jbroadice
Copy link

jbroadice commented Nov 17, 2018

I've tried the solutions listed here and I simply cannot get this to work. I receive the following error, no matter what:

src/modules/auth/index.ts(2,27): error TS2307: Cannot find module './schema.graphql'.``

I have tried placing the graphql.d.ts file within the same directory as the .graphql file, at the src level, at the project root. None have altered a single thing. It's as if TS isn't even seeing the .d.ts file.

Here is the tsconfig:

  "compilerOptions": {
    "lib": ["es2017", "esnext.asynciterable", "dom"],
    "target": "es2017",
    "module": "commonjs",
    "moduleResolution": "node",
    "sourceMap": true,
    "experimentalDecorators": true,
    "emitDecoratorMetadata": true,
    "outDir": "dist",
    "typeRoots": ["node_modules/@types"],
    "paths": {
      "@lib/*": ["./lib/*"],
      "@models/*": ["./models/*"],
      "@modules/*": ["./modules/*"]
    },
    "baseUrl": "./src/",
    "allowSyntheticDefaultImports": true
  },
  "include": ["src/**/*"],
  "exclude": ["node_modules"]
}

@eug48
Copy link

eug48 commented Nov 17, 2018

@JChapple it would help if you post a link to your file structure (e.g. output of find .) to a pastebin, and also mention your TypeScript version. But anyway, I think the typeRoots line in your tsconfig might be the source of this problem.

@defrex
Copy link

defrex commented Mar 17, 2019

I don't see it mentioned here, and this is the top google result for this problem. So FYI, graphql-code-generator has a plugin to generate these module definitions. It solved the problem for me.
https://graphql-code-generator.com/docs/plugins/typescript-graphql-files-modules

@yakovlevkll
Copy link

yakovlevkll commented Sep 14, 2019

Faced the problem with importing .gql across many directories (same as @switz has mentioned).

Solved it with global.d.ts. For example, let it be placed in src folder

// src/global.d.ts
declare module '*.gql' {
    import { DocumentNode } from 'graphql';

    const value: DocumentNode;
    export = value;
}

Be sure that src folder is in include section of your tsconfig.json

// tsconfig.json
{
    // ...
    "include": ["src/**/*.ts"]
}

Thanks to @buggy and others.

@khaledosman
Copy link

I'm facing the same issue, any ideas?

tsconfig.json

{
  "compilerOptions": {
    "lib": ["es2017"],
    "moduleResolution": "node",
    "noUnusedLocals": false,
    "noUnusedParameters": true,
    "sourceMap": true,
    "target": "es2017",
    "outDir": "lib"
  },
  "exclude": ["node_modules"]
}

webpack.config.js

const path = require('path')
const slsw = require('serverless-webpack')

module.exports = {
  mode: slsw.lib.webpack.isLocal ? 'development' : 'production',
  entry: slsw.lib.entries,
  devtool: 'source-map',
  resolve: {
    extensions: ['.js', '.jsx', '.json', '.ts', '.tsx', '.mjs', '.gql', '.graphql']
  },
  output: {
    libraryTarget: 'commonjs',
    path: path.join(__dirname, '.webpack'),
    filename: '[name].js'
  },
  target: 'node',
  module: {
    rules: [
      // all files with a `.ts` or `.tsx` extension will be handled by `ts-loader`
      { test: /\.(tsx?|mjs)$/, loader: 'ts-loader' },
      {
        test: /\.(graphql|gql)$/,
        exclude: /node_modules/,
        loader: 'graphql-tag/loader'
      }
    ]
  }
}

usage:

import typeDefs from './schema.graphql'

@ecomachio
Copy link

if you having trouble importing gql files with jest I solved by using @ddetkovs solution plus adding this to jest config.
transform: { '^.+\\.(gql|graphql)$': 'jest-transform-graphql', ... }
more info here https://github.com/remind101/jest-transform-graphql

Hope this helps.

@aleksanderfret
Copy link

I used code from @buggy:

declare module '*.graphql' {
  import { DocumentNode } from 'graphql';

  const value: DocumentNode;
  export = value;
}

I put this code into index.d.ts file into my src/@types folder. Then I add this folder to my typeRoots setting in tsconfing.json file:

"typeRoots": ["./src/@types"]

This solution works for me for all .graphql files inside my src folder.

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

No branches or pull requests