Next.js: import customized Document from library causes 'Constructor invoked without 'new'' error

Created on 1 Aug 2019  路  18Comments  路  Source: vercel/next.js

Bug report

  1. Make a library which included a customized Document(_document.js).
  2. import the customized Document into another project. throws TypeError: Class constructor Document cannot be invoked without 'new' error.

There's no error before next9

To Reproduce

https://github.com/thundermiracle/next-lib-bug

Screenshots

image

System information

  • OS: [Windows]
  • Browser (if applies) [chrome,]
  • Version of Next.js: [9.0.2 and canary]

Most helpful comment

Note: I had this error this morning when upgrading to 9.0.3, but removed @babel/preset-env from presets in my .babelrc file and I am no longer receiving this error.

{
  "presets": [
-    "@babel/preset-env",
    "next/babel",
  ],
  "compact": true,
...(other config options for styled components, etc)

In retrospect, not really sure why we had @babel/preset-env in there.
EDIT: Our project has a custom _document and custom _app, but we don't export it to other projects.

All 18 comments

Note: I had this error this morning when upgrading to 9.0.3, but removed @babel/preset-env from presets in my .babelrc file and I am no longer receiving this error.

{
  "presets": [
-    "@babel/preset-env",
    "next/babel",
  ],
  "compact": true,
...(other config options for styled components, etc)

In retrospect, not really sure why we had @babel/preset-env in there.
EDIT: Our project has a custom _document and custom _app, but we don't export it to other projects.

@thundermiracle is this working in 9.0.3?

FYI: I was unable to get it working on 9.0.3 even after removing @babel/preset-env from my babel rc

@jaslakson Thank you for your advice. I have nothing in babel.presets except "next/babel" but still get the same error.
The problem is that import from node_modules throws error, but if you import it by relative path, everything works fine.

@Timer 9.0.3 got the same error.

I just took a deeper look at this.

The reason this issue is happening is because next must be compiled by webpack.

When you import it indirectly in next-mui-helper (which is not bundled, as it's in node_modules), webpack doesn't get the chance to bundle or compile next/**.
This package then just gets handled with typical Node imports.

To solve this, you'll need to customize your webpack configuration to ensure that next-mui-helper is compiled by webpack and not externalized, such that next imports are properly seen by the build.

Do note that this extension of Document is not a supported behavior and is very prone to break release-by-release.

but removed @babel/preset-env from presets in my .babelrc file and I am no longer receiving this error.

I too am not longer receiving this error . when I remove @babel/env. This seemed to happen specifically when I upgraded to v9.0.3. v9.0.2 worked fine for me. It also threw the error on App as well as Document.
TypeError: Class constructor App cannot be invoked without 'new' at new What3wordsApp (/Users/marc/Documents/Websites/corp-site/frontend/.next/server/static/development/pages/_app.js:3955:240) at processChild (/Users/marc/Documents/Websites/corp-site/frontend/node_modules/react-dom/cjs/react-dom-server.node.development.js:2829:14)

Can I ask why users had @babel/preset-env in your .babelrc in the first place? This behavior has never been supported.

Looking at the history for our .babelrc, we made that change in a testing/feature branch at the same time that we were going from:

-    "next": "7.0.2-canary.28",
+    "next": "7.0.2-canary.31",

We have a custom express server implementation, and I remember at the time there was an issue with the server-side code compiling properly (es6 not being transpiled). Whatever the issue, it was resolved (probably very quickly) and we neglected to remove the change once it was no longer needed.

So - not a great explanation, but that's the way it happened for me. Thanks for all the awesome work!

@Timer Thank you very much for your explanation!
Would please tell me which PR causes this problem?
I can't find out it in the changelog.
By the way, I don't know bundle modules in node_modules is a good idea or not.

@thundermiracle it wasn't any specific PR, these changes have been made across many PRs as we optimize Next.js' core.

I'm facing the same issue. Please share the workaround fix?

I'm also running into this issue; not quite sure how I'm supposed to resolve it other than reverting to v8 for now.

@baoduy @mherodev Remove @babel/preset-env from your .babelrc. That worked for me.

@quantafire That doesn't quite cut it for our build. The jump from 9.0.2 to 9.0.3 introduced something that breaks how we're extending the document.

babel.config.js

const env = require('./env-config.js');

module.exports = {
  babelrc: false,
  plugins: [
    ['emotion', { autoLabel: true, sourceMap: true }],
    'inline-react-svg',
    ['transform-define', env],
    ['lodash'],
  ],
  env: {
    development: {
      presets: ['next/babel'],
      compact: false,
    },
    production: {
      presets: ['next/babel'],
      plugins: [['emotion', { autoLabel: true, sourceMap: false }]],
    },
    test: {
      presets: [
        [
          'next/babel',
          {
            'preset-env': { modules: 'commonjs' },
            'transform-runtime': { corejs: false }, // https://github.com/zeit/next.js/issues/7050
          },
        ],
      ],
    },
  },
};

next.config.js

const withBundleAnalyzer = require('@next/bundle-analyzer')({ enabled: process.env.ANALYZE === 'true' });
const withOffline = require('next-offline');
const withPlugins = require('next-compose-plugins');
const withProgressBar = require('next-progressbar');
const withTranspileModules = require('next-transpile-modules');
const serviceWorkerConfig = require('./src/lib/serviceWorker/config');

const plugins = [
  [
    withBundleAnalyzer,
    {
      analyzeServer: ['server', 'both'].includes(process.env.BUNDLE_ANALYZE),
      analyzeBrowser: ['browser', 'both'].includes(process.env.BUNDLE_ANALYZE),
      bundleAnalyzerConfig: {
        server: {
          analyzerMode: 'static',
          reportFilename: '../../bundles/server.html',
        },
        browser: {
          analyzerMode: 'static',
          reportFilename: '../bundles/client.html',
        },
      },
    },
  ],
  [withOffline, { workboxOpts: serviceWorkerConfig }],
  withProgressBar,
  [withTranspileModules, { transpileModules: ['debug'] }],
];

const nextConfig = {
  assetPrefix: '',
  useFileSystemPublicRoutes: false,
  poweredByHeader: false,
  webpack: config => {
    config.module.rules.push(
      {
        test: /\.css$/,
        use: ['to-string-loader', 'css-loader'],
      },
      {
        test: /\.js$/,
        exclude: /(node_modules)/,
        use: {
          loader: 'babel-loader',
        },
      }
    );
    return config;
  },
};

module.exports = withPlugins([...plugins], nextConfig);

_document.js

import React from 'react';
import Document, { Head, Main, NextScript } from 'next/document';
import { extractCritical } from 'emotion-server';

class RootDocument extends Document {
  static getInitialProps({ renderPage }) {
    const page = renderPage();
    const styles = extractCritical(page.html);
    return { ...page, ...styles };
  }

  constructor(props) {
    super(props);

    const { __NEXT_DATA__, ids } = props;
    if (ids) {
      __NEXT_DATA__.ids = ids;
    }
  }

  render() {
    return (
      <html lang="en-US">
        <Head>
          {/* eslint-disable-next-line react/no-danger */}
          <style dangerouslySetInnerHTML={{ __html: this.props.css }} />
        </Head>
        <body>
          <Main />
          <NextScript />
        </body>
      </html>
    );
  }
}

export default RootDocument;

Just got back from vacation and tried our 9.0.2 build against 9.0.6 and the current issue still exists:

TypeError: Class constructor Document cannot be invoked without 'new'
    at new RootDocument (/Users/mherodev/test-web/build/.next/server/static/qd3L6z_dmzM4eCXRKPYiJ/pages/_document.js:173:256)
    at c (/Users/mherodev/test-web/node_modules/react-dom/cjs/react-dom-server.node.production.min.js:33:323)
    at Sa (/Users/mherodev/test-web/node_modules/react-dom/cjs/react-dom-server.node.production.min.js:36:1)
    at a.render (/Users/mherodev/test-web/node_modules/react-dom/cjs/react-dom-server.node.production.min.js:41:467)
    at a.read (/Users/mherodev/test-web/node_modules/react-dom/cjs/react-dom-server.node.production.min.js:41:58)
    at Object.renderToStaticMarkup (/Users/mherodev/test-web/node_modules/react-dom/cjs/react-dom-server.node.production.min.js:53:181)
    at renderDocument (/Users/mherodev/test-web/node_modules/next/dist/next-server/server/render.js:88:18)
    at Object.renderToHTML (/Users/mherodev/test-web/node_modules/next/dist/next-server/server/render.js:295:16)
    at process._tickCallback (internal/process/next_tick.js:68:7)

I don't have the bandwidth right now to create a repo for reproduction, but I'll throw a ticket in our backlog to dig further in.

+1 I don't use @babel/env and I experience this issue. I had to downgrade to 9.0.2.

@mherodev I fixed this issue by removing 'preset-env' inside next/babel. It looks like this was causing an issue into my project. I hope it helps.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

rauchg picture rauchg  路  3Comments

kenji4569 picture kenji4569  路  3Comments

jesselee34 picture jesselee34  路  3Comments

knipferrc picture knipferrc  路  3Comments

YarivGilad picture YarivGilad  路  3Comments