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

Edge babel regenerator runtime usage can break client/server specific requires #9298

Closed
meyer9 opened this issue Nov 3, 2019 · 8 comments
Closed
Milestone

Comments

@meyer9
Copy link

meyer9 commented Nov 3, 2019

Bug report

Describe the bug

An async getInitialProps with a require statement requiring server-side code includes the server side code on the client side, crashing the application.

static async getInitialProps () {
  if (typeof window === 'undefined') {
    // this shouldn't be called on the client but it is
    require('../asdf')
    return { asdf: 'test1' }
  }
}

To Reproduce

Example repository: https://github.com/meyer9/next-async-import-bug

Expected behavior

It should properly split the server code and not include or run the module on the client.

Log

ModuleNotFoundError: Module not found: Error: Can't resolve 'fs' in '/home/meyer9/development/bug-report'                                                                                                                                     
    at factory.create (/home/meyer9/development/bug-report/node_modules/webpack/lib/Compilation.js:888:10)                                                                                                                                    
    at factory (/home/meyer9/development/bug-report/node_modules/webpack/lib/NormalModuleFactory.js:401:22)                                                                                                                                   
    at resolver (/home/meyer9/development/bug-report/node_modules/webpack/lib/NormalModuleFactory.js:130:21)           
    at asyncLib.parallel (/home/meyer9/development/bug-report/node_modules/webpack/lib/NormalModuleFactory.js:224:22)                                                                                                                         
    at /home/meyer9/development/bug-report/node_modules/neo-async/async.js:2830:7                                                                                                                                                             
    at /home/meyer9/development/bug-report/node_modules/neo-async/async.js:6877:13                                     
    at normalResolver.resolve (/home/meyer9/development/bug-report/node_modules/webpack/lib/NormalModuleFactory.js:214:25)                                                                                                                    
    at doResolve (/home/meyer9/development/bug-report/node_modules/enhanced-resolve/lib/Resolver.js:213:14)            
    at hook.callAsync (/home/meyer9/development/bug-report/node_modules/enhanced-resolve/lib/Resolver.js:285:5)                                                                                                                               
    at _fn0 (eval at create (/home/meyer9/development/bug-report/node_modules/tapable/lib/HookCodeFactory.js:33:10), <anonymous>:15:1)                                                                                                        
    at resolver.doResolve (/home/meyer9/development/bug-report/node_modules/enhanced-resolve/lib/UnsafeCachePlugin.js:44:7)                                                                                                                   
    at hook.callAsync (/home/meyer9/development/bug-report/node_modules/enhanced-resolve/lib/Resolver.js:285:5)        
    at _fn0 (eval at create (/home/meyer9/development/bug-report/node_modules/tapable/lib/HookCodeFactory.js:33:10), <anonymous>:15:1)                                                                                                        
    at hook.callAsync (/home/meyer9/development/bug-report/node_modules/enhanced-resolve/lib/Resolver.js:285:5)        
    at _fn0 (eval at create (/home/meyer9/development/bug-report/node_modules/tapable/lib/HookCodeFactory.js:33:10), <anonymous>:27:1)                                                                                                        
    at resolver.doResolve (/home/meyer9/development/bug-report/node_modules/enhanced-resolve/lib/DescriptionFilePlugin.js:67:43)
@meyer9
Copy link
Author

meyer9 commented Nov 3, 2019

I found a temporary workaround for now:

static getInitialProps () {
  if (typeof window === 'undefined') {
    return (async () => {
      require('../asdf')
      // do async stuff with asdf
      return { asdf: 'test1' }
    })()
  }
}

@BRKalow
Copy link
Contributor

BRKalow commented Nov 4, 2019

It might be possible to use process.browser for this.

static async getInitialProps () {
  if (!process.browser) {
    require('../asdf')
    return { asdf: 'test1' }
  }
}

This is defined at build time so it should exclude it from the client build. See here: https://github.com/zeit/next.js/blob/21f0db8181ed1fd10b5b6ea92ca7b9415fcb2242/packages/next/build/webpack-config.ts#L781

@meyer9
Copy link
Author

meyer9 commented Nov 4, 2019

I tested it and that works the same as typeof window === 'undefined'. non-async getInitialProps works, but async getInitialProps does not.

@ijjk
Copy link
Member

ijjk commented Nov 4, 2019

Hi, it looks like this is due to the way the babel regenerator runtime transforms the client side code. If you move the return outside of the check it works correctly.

if (typeof window !== 'undefined') {
  require('../asdf')
}
return {}

This is what the regenerator runtime transforms the static async getInitialProps to if you have the return inside of the if. Notice the require is no longer inside of the if which causes webpack to try to bundle fs and fail for the client build since you haven't added handling for it with the node webpack option;

var _getInitialProps = _asyncToGenerator(
  /*#__PURE__*/
  regeneratorRuntime.mark(function _callee() {
    return regeneratorRuntime.wrap(function _callee$(_context) {
      while (1) {
        switch (_context.prev = _context.next) {
          case 0:
            if (!(typeof window === 'undefined')) {
              _context.next = 3;
              break;
            }

            require('hello');

            return _context.abrupt("return", {
              asdf: ''
            });

          case 3:
          case "end":
            return _context.stop();
        }
      }
    }, _callee);
  }));

From the above investigating I'm not sure this would be considered a Next.js bug 🤔

@ijjk ijjk changed the title Async getInitialProps does not properly remove server-side code from client Edge babel regenerator runtime usage can break client/server specific requires Nov 4, 2019
@meyer9
Copy link
Author

meyer9 commented Nov 4, 2019

Interesting, thanks for the workaround. I'll look into it a bit more and maybe submit it as a bug to regenerator-runtime if needed.

@meyer9
Copy link
Author

meyer9 commented Nov 4, 2019

It seems to be a little more complex than just moving the return statement outside of the if statement. Even this code causes an error:

static async getInitialProps(ctx: NextPageContext) {
    if (typeof window === 'undefined') {
      require('fs')
    } else {
      try {
        user = await axios.get('/api/user')
      } catch (e) {
        if (e && e.response && e.response.data && e.response.data.error) {
          Router.push('/login')
        }
      }
    }

    return {}
  }

With this generated:

var _getInitialProps = Object(_babel_runtime_corejs2_helpers_esm_asyncToGenerator__WEBPACK_IMPORTED_MODULE_1__["default"])(
      /*#__PURE__*/
      _babel_runtime_corejs2_regenerator__WEBPACK_IMPORTED_MODULE_0___default.a.mark(function _callee(ctx) {
        var user;
        return _babel_runtime_corejs2_regenerator__WEBPACK_IMPORTED_MODULE_0___default.a.wrap(function _callee$(_context) {
          while (1) {
            switch (_context.prev = _context.next) {
              case 0:
                if (true) {
                  _context.next = 4;
                  break;
                }

                __webpack_require__(/*! axios */ "./node_modules/axios/index.js");


                _context.next = 13;
                break;

              case 4:
                _context.prev = 4;
                _context.next = 7;
                return axios__WEBPACK_IMPORTED_MODULE_8___default.a.get('/api/user');

              case 7:
                user = _context.sent;
                _context.next = 13;
                break;

              case 10:
                _context.prev = 10;
                _context.t0 = _context["catch"](4);

                if (_context.t0 && _context.t0.response && _context.t0.response.data && _context.t0.response.data.error) {
                  next_router__WEBPACK_IMPORTED_MODULE_9___default.a.push('/login');
                }

              case 13:
                return _context.abrupt("return", {
                  u: user
                });

              case 14:
              case "end":
                return _context.stop();
            }
          }
        }, _callee, null, [[4, 10]]);
      }));

@timneutkens
Copy link
Member

Closing as this is upstream and only one user ran into it: facebook/regenerator#380

@balazsorban44
Copy link
Member

This issue has been automatically locked due to no recent activity. If you are running into a similar issue, please create a new issue with the steps to reproduce. Thank you.

@vercel vercel locked as resolved and limited conversation to collaborators Jan 30, 2022
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

No branches or pull requests

6 participants