relay: Can't resolve graphl file when using Webpack 5 (and nextjs)

Hi.

Using:

  • Next.js
  • Babel relay plugin
  • typescript (projects)
  • Default webpack config (which is 4)
  • Yarn workspaces everything runs fine.

But when I upgrade to webpack 5, which is supported by nextjs I get the following error while starting development server or building:

Module not found: Can't resolve '..\..\__generated__\relay\someMutation.graphql' this is the graphql file added by babel relay plugin.

I’ve been trying to debug and understand better babel’s work but I confess I do not have much knowledge to debug it further by myself.

babel.config.json

{
  "presets": [
    "next/babel"
  ],
  "plugins": [
    [
      "relay",
      {
        "artifactDirectory": "./src/__generated__/relay/"
      }
    ],
    "@babel/plugin-transform-runtime",
    "babel-plugin-macros",
    [
      "styled-components",
      {
        "ssr": true,
        "displayName": true
      }
    ]
  ]
}

Note that I use babel plugin macros for tailwind’s macro, not relay plugin macro. Although I tried using it and I got the same error too…

My generated files are indeed correctly generated at '.src/__generated__/relay'

It does work with webpack 4, and not with webpack 5.

I appreciate any help to try debugging it, I didn’t find any public repo that uses typescript, relay and webpack 5, that’s why I’m not sure I’m the one doing something wrong.

About this issue

  • Original URL
  • State: closed
  • Created 4 years ago
  • Reactions: 4
  • Comments: 26 (15 by maintainers)

Commits related to this issue

Most upvoted comments

If someone experience this problem, might be interested to use this plugin that I did, just add it to your babel.config.js after the relay plugin. It is in the meantime, until this problem is fixed and a new babel-plugin-relay is published.

  • babel-config-js
...
plugins: [
  ['babel-plugin-relay', { ... }],
  ['./pathNormaliser'],
  ...
],
...
  • pathNormaliser.js
'use strict';

const replacer = (filepath) => filepath.replace(/\\/g, '/');

/**
 * Normalise path to POSIX (only for `win32` using `require`).
 */
const PathNormaliserPlugin = function (api) {
  if (process.platform === 'win32') {
    const t = api.types;

    return {
      name: 'path-normaliser',

      visitor: {
        CallExpression: {
          enter: function (nodePath) {
            const callee = nodePath.get('callee');

            if (callee.isIdentifier() && callee.equals('name', 'require')) {
              const arg = nodePath.get('arguments.0');

              if (arg && arg.isStringLiteral()) {
                const sourcePath = arg.node.value;
                const targetPath = replacer(sourcePath);

                if (sourcePath !== targetPath) {
                  arg.replaceWith(t.stringLiteral(targetPath));
                }
              }
            }
          },
        },
      },
    };
  }

  return {
    name: 'path-normaliser',
  };
};

module.exports = PathNormaliserPlugin;

@alunyov This happens even with v13 since the code has not changed in the involved lines mentioned above:

https://github.com/facebook/relay/blame/b5add1e0369ee38150d810c5c677c57d0952670e/packages/babel-plugin-relay/compileGraphQLTag.js

I do appreciate the advise of @sokra that confirms the bug:

https://github.com/facebook/relay/issues/3272#issuecomment-912781359

This is just affecting non posix (Windows) and the required fix it is just a few characters long, should I create a PR for this or are you thinking to apply a different solution?

For example, in place of a regex replacement, a more resilient solution will be to use = require('path').posix; and all the methods will use the right separator. This is better because even if it might be strange to use the character \ within the file/folder names, in posix it is so valid like “a”.

https://github.com/facebook/relay/blob/35e0c2bd4fd020a1bd5ac467e50adb5ad5f57d40/packages/babel-plugin-relay/compileGraphQLTag.js#L27

I run into the same issue:

Module not found: Can't resolve '..\..\.relay\someQuery.graphql'
Did you mean './..\..\.relay\someQuery.graphql'?
Requests that should resolve in the current directory need to start with './'.

Not sure where that error is coming from but at least I found the reason why the path doesn’t start with './':

https://github.com/facebook/relay/blob/521ca914ec756d8c8d6f291dde93c3eccf1ae56f/packages/babel-plugin-relay/compileGraphQLTag.js#L236-L237

Apparently that was fine in Webpack 4 but not in Webpack 5? If the error comes from Webpack that is. By the way forcing a './' here did work around the issue. Shouldn’t be needed for paths already starting with '../' if you ask me.

At least you can tell Webpack 5 to prefer relative; in the case of NextJS:

// next.config.js
module.exports = {
  webpack: config => {
    config.resolve.preferRelative = true
    return config
  },
}

If someone needs it, here’s a plugin that replaces the slashes, now updated to support eagerEsModules. It serves as a workaround; however, we should submit a proper fix as explained above.

'use strict';

/**
 * Normalise path to POSIX (only for `win32`).
 */
const PathNormaliserPlugin = function (api) {
  if (process.platform === 'win32') {
    const t = api.types;

    const normalizePath = (pathNode) => {
      if (pathNode && pathNode.isStringLiteral()) {
        const sourcePath = pathNode.node.value;
        const targetPath = sourcePath.replace(/\\/g, '/');

        if (sourcePath !== targetPath) {
          pathNode.replaceWith(t.stringLiteral(targetPath));
        }
      }
    };

    return {
      name: 'path-normaliser',

      visitor: {
        CallExpression: {
          enter(nodePath) {
            const callee = nodePath.get('callee');

            if (callee.isIdentifier({name: 'require'})) {
              normalizePath(nodePath.get('arguments.0'));
            }
          },
        },
        ImportDeclaration: {
          enter(nodePath) {
            normalizePath(nodePath.get('source'));
          },
        },
      },
    };
  }

  return {
    name: 'path-normaliser',
  };
};

module.exports = PathNormaliserPlugin;

@artola I checked usage of require('path').posix and it doesn’t fix backslashes in my case. So I think replace(/\\/g, '/') is the best solution, but we need to apply it only for windows

Fix getting relative import path in windows for babel-plugin-relay #3830

@wKich Thanks for the PR. From my point of view, this is the simplest solution and also Windows’ compatible.

@JUNKYASS you shouldn’t change directly file compileGraphQLTag.js. It will reset after reinstall. You should follow this comment instead

#3272 (comment)

Yes, you’re right. Got it, thanks!

@JUNKYASS you shouldn’t change directly file compileGraphQLTag.js. It will reset after reinstall. You should follow this comment instead

https://github.com/facebook/relay/issues/3272#issuecomment-912950844

I confirm that for us also in Windows we see this error (except with "module": "commonjs" where it works).

It seems related to the joinPath (alias for path.join) because it is environment sensitive.

If we “patch” the code (by replacing the slashes) in the following line to make it POSIX … then it works everywhere, independently of the settings in tsconfig.

https://github.com/facebook/relay/blob/ac930cd215152057114933c20d5fe1b02e2bc3bb/packages/babel-plugin-relay/compileGraphQLTag.js#L239

return relativeReference + joinPath(relative, fileToRequire).replace(/\\/g, '/');

@alloy @kassens Do you have some idea to define if it a problem of this plugin or next in the chain? (for example some change in webpack API between v4 and v5 regarding normalisation of the paths)

See: Tobias’ comment https://blog.johnnyreilly.com/2021/04/20/ts-loader-goes-webpack-5/

Note that asset names should have only / as they are filenames and not paths. Only absolute paths have \.

But what about test environment ? Then, might be better if webpack normalise the filenames as somehow was happening in v4. It would be great to have their team participating in this issue.

strangely, a workaround is to remove artifactDirectory option from both react-compiler options and babel-relay-plugin… then it generates a __generated__ folder on each file that uses a graphql tag. It messes up my codebase but it works until we figure out how to get it to work like before on webpack 5