Next.js: [Next.js 9.0.2] Shallow rendering issue: App cannot be invoked without 'new'

Created on 30 Jul 2019  Â·  18Comments  Â·  Source: vercel/next.js

Bug report

Describe the bug

After updating our app from Next.js 7.0.2 to 9.0.2 enzyme’s shallow rendering (same issue with enzyme’s mount) for the custom <App> (_app.is) not working anymore. The local development server is serving the app perfectly fine, though.

For experimental purposes we also tried running our tests with Next.js 8.1.0 and the rendering caused no problems.

To Reproduce

Clone the issue repository:
https://github.com/rzschoch/next-9-issues/tree/shallow-issue

  1. Run npm install
  2. Run npm run test
  3. See error: TypeError: Class constructor App cannot be invoked without 'new'

Expected behavior

The test in /src/pagesSpecs/_app.spec.js should be rendered without issues and compared against the existing snapshot (from Next.js 7.0.2).

Screenshots

image

System information

  • OS: MacOS 10.14.5 / Windows 10.0.17763 Build 17763
  • Version of Next.js: 9.0.2

Most helpful comment

Never mind, removing "@babel/preset-env" from "presets" solve this issue.

after i removed the preset-env in presets, all i had in .babelrc was

   "presets": [
    "next/babel"
  ] 

and it didn't work

After that i changed to

"presets": [
    [
      "next/babel",
      {
        "preset-env": { "targets": { "node": "current" } }
      }
    ]
  ]

and it works :+1:

All 18 comments

Hey @rzschoch, could you test it with the latest canary? There should be a fix for it.

Hi @huv1k 9.0.3-canary.5 does not fix it, unfortunately.

That’s @huv1k solution for now (thank you):
“We updated our build to use modern javascript with esmodules in this PR (https://github.com/zeit/next.js/pull/7014). There is a problem that node doesn't support it yet so you need to use for your tests current node. You can achieve this with this .babelrc:“

{
  "presets": ["next/babel"],
  "plugins": ["inline-react-svg"],
  "env": {
    "test": {
      "presets": [
        ["next/babel", { "preset-env": { "targets": { "node": "current" } } }]
      ],
      "plugins": ["dynamic-import-node"]
    }
  }
}

Now it should work just with canary @rzschoch https://github.com/zeit/next.js/pull/8181

It is now working fine with the latest canary (9.0.3-canary.6 in my case) and a .babelrc like so:

{
  "presets": ["next/babel"],
  "plugins": ["inline-react-svg"]
}

Same issue in 9.0.3
not happen in 9.0.2

I have the same problem with 9.0.3 on a HOC of the custom _app.ts. I get the same error at super(props) in constructor of HOC.

compiled line:

_this = Object(_babel_runtime_corejs2_helpers_esm_possibleConstructorReturn__WEBPACK_IMPORTED_MODULE_16__["default"])(this, Object(_babel_runtime_corejs2_helpers_esm_getPrototypeOf__WEBPACK_IMPORTED_MODULE_17__["default"])(WithI18next).call(this, props));

source:

const withI18next = (WrappedApp: typeof App) => {
  const WrappedComponentWithSSR = withSSR()(WrappedApp)
  ...
  class WithI18next extends App<WithI18nextProps> {
    public static displayName = `WithI18next(${WrappedApp.name || 'App'})`
    ...
    public constructor(props: WithI18nextProps) {
      super(props)
      ...
    }

The latest canary didn't work for me, but the following did:

{
  "plugins": [],
  "presets": [
    [
      "next/babel",
      {
        "preset-env": { "targets": { "node": "current" } }
      }
    ]
  ]
}

Just a data point, I was able to resolve this issue with unit testing a custom _app.js by giving my App subclass a no-op constructor:

  // This no-op constructor helps with an error:
  // TypeError: Class constructor App cannot be invoked without 'new'
  constructor(props) {
    super(props);
  }

Still same problem in [email protected], my _app.js and _document.js files are exactly same as this example project about styled-component.
here is my .babelrc -

{
  "presets": [
    "@babel/preset-env",
    "@babel/preset-react",
    "@babel/preset-flow",
    [
      "next/babel",
      {
        "preset-env": { "targets": { "node": "current" } }
      }
    ]
  ],
  "plugins": [
    [
      "@babel/plugin-proposal-decorators",
      { "legacy": true }
    ],
    [
      "styled-components",
      { "ssr": true, "displayName": true, "preprocess": false }
    ],
    [
      "babel-plugin-root-import",
      {
        "rootPathPrefix": "~",
        "rootPathSuffix": "./src/app"
      }
    ]
  ]
}

It workes when removing both _app.js and _document.js.

Never mind, removing "@babel/preset-env" from "presets" solve this issue.

I have the same issue. This is mine next.config.js:

/* eslint no-param-reassign: 0 */
const appConfig = require("./config");
const path = require("path");
const webpack = require("webpack");

module.exports = {
  publicRuntimeConfig: {
    restRootEndpointUrl: appConfig.REST_ROOT_ENDPOINT_URL,
    adminAuthenticationUrl: appConfig.ADMIN_AUTHENTICATION_URL,
  },

  distDir: "../build/app",
  webpack: (config, { buildId, dev, isServer, defaultLoaders }) => {
    // Fixes npm packages that depend on `fs` module
    config.node = {
      fs: "empty"
    };

    config.resolve.alias["../../storefront-ui/config"] = path.resolve(
      __dirname,
      "storefront-ui/config.js"
    );

    config.resolve.alias["storefront-ui"] = `@r00dy/storefront-ui/`;

    config.module.rules.push({
      test: /\.(graphql|gql)$/,
      exclude: /node_modules/,
      loader: "graphql-tag/loader"
    });

    config.module.rules.push({
      test: /\.svg$/,
      use: [
        {
          loader: "@svgr/webpack",
          options: {
            svgoConfig: {
              plugins: {
                removeViewBox: false
              }
            },
            titleProp: true
          }
        }
      ]
    });

    // Variables required by storefront-ui to be defined globally. The following line makes them visible in bundle.
    config.plugins.push(
      new webpack.DefinePlugin({
        __DEV__: dev,
        __BROWSER__: !isServer
      })
    );

    return config;
  }
};

and this is babel.rc:

{
  "presets": ["next", "@babel/preset-react", "@emotion/babel-preset-css-prop"],
  "plugins": [
    ["@babel/plugin-proposal-decorators", { "legacy": true }],
    ["@babel/plugin-proposal-class-properties", { "loose": true }]
  ]
}

I tried with calling: constructor(props) { super(props); } but it didn't work. And it started after upgrading to 9.0.5.

I'm also facing the same issue after updating from 7 to 9.0.5.
Here is my .babelrc:

{
    "presets": [
      "@babel/preset-env",
      "@babel/preset-react",
      ["next/babel", { "preset-env": { "targets": { "node": "current" } } }]
    ],
    "plugins": ["@babel/plugin-proposal-class-properties", "inline-react-svg"],
    "env": {
      "development" : {
        "compact": false
      }
    }
}

Also my next.config.js:

const withSass = require('@zeit/next-sass')
const eslintFormatter = require('react-dev-utils/eslintFormatter')
const alias = require('./config/alias')
const StyleLintPlugin = require('stylelint-webpack-plugin')
const FilterPlugin = require('./config/filter.plugin')
const withOffline = require('next-offline')
const withSize = require('next-size')
const nextBuildId = require('next-build-id')

module.exports = withOffline(withSize(withSass({
  useFileSystemPublicRoutes: false,
  cssModules: true,

  cssLoaderOptions: {
    localIdentName: '[local]_-[hash:base64:5]'
  },

  sassLoaderOptions: {
    includePaths: ['static/style']
  },

  publicRuntimeConfig: {
    API_URL: JSON.stringify(process.env.API_URL),
    PUBLIC_URL: JSON.stringify(process.env.PUBLIC_URL)
  },

  generateBuildId: () => nextBuildId({ dir: __dirname }),

  webpack(configuration) {
    const config = configuration

    config.node = {
      fs: 'empty'
    }

    config.resolve = {
      alias: { ...(config.resolve.alias || {}), ...alias },
      extensions: ['.web.js', '.mjs', '.js', '.json', '.web.jsx', '.jsx']
    }

    config.plugins.push(
      // Silence mini-css-extract-plugin generating lots of warnings for CSS ordering.
      // We use CSS modules that should not care for the order of CSS imports, so we
      // should be safe to ignore these.
      new FilterPlugin({ filter: /chunk styles \[mini-css-extract-plugin]\nConflicting order between:/ }),
    )

    config.module.rules.push(
      {
        test: /\.(js|jsx|mjs)$/,
        enforce: 'pre',
        use: [
          {
            options: {
              formatter: eslintFormatter,
              eslintPath: require.resolve('eslint')
            },
            loader: require.resolve('eslint-loader')
          }
        ],
        include: alias['app-root']
      },
      {
        test: /\.(ttf|eot|woff|woff2|otf|txt|jpg|png|svg|ico)$/,
        use: [
          {
            loader: 'url-loader',
            options: {
              limit: 10000,
              name: '[path][name].[hash].[ext]',
              publicPath: '/_next'
            }
          }
        ]
      }
    )

    config.plugins.push(
      new StyleLintPlugin({
        context: alias['app-root'],
        files: '**/*.scss',
        emitErrors: true
      })
    )

    return config
  }
})))

Never mind, removing "@babel/preset-env" from "presets" solve this issue.

after i removed the preset-env in presets, all i had in .babelrc was

   "presets": [
    "next/babel"
  ] 

and it didn't work

After that i changed to

"presets": [
    [
      "next/babel",
      {
        "preset-env": { "targets": { "node": "current" } }
      }
    ]
  ]

and it works :+1:

Never mind, removing "@babel/preset-env" from "presets" solve this issue.

This also worked for me, I replaced

["@babel/preset-env", { "targets": "> 0.5% in US" } ]

with

["next/babel", { "preset-env": { "targets": "> 0.5% in US" } }]

I'm running into this:

TypeError: Class constructor cannot be invoked without 'new'

with the following tsconfig.json:

{
  "compilerOptions": {
    "declaration": false,
    "target": "es6",
    "lib": [
      "dom",
      "dom.iterable",
      "esnext"
    ],
    "allowJs": true,
    "skipLibCheck": true,
    "strict": false,
    "forceConsistentCasingInFileNames": true,
    "noEmit": true,
    "esModuleInterop": true,
    "module": "esnext",
    "moduleResolution": "node",
    "resolveJsonModule": true,
    "isolatedModules": true,
    "jsx": "preserve"
  },
  "exclude": [
    "node_modules"
  ],
  "include": [
    "next-env.d.ts",
    "**/*.ts",
    "**/*.tsx"
  ]
}

Any ideas?

In my case, I had a custom webpack config which was using babel loader like this:

      {
        // for all files except for node_modules, but including @scoped within node_modules
        test: /\.(js|tsx)$/,
        exclude: /node_modules(?!\/@scoped\/)/,
        use: {
          loader: 'babel-loader',
        },
      },

and the babel.config.js was like this:

module.exports = {
presets: [
 'next/babel',
  ],
  plugins: [
    [
      'react-css-modules', {
        generateScopedName: '[name]__[local]',
        webpackHotModuleReloading: true,
        handleMissingStyleName: 'warn',
      },
    ],
    'dynamic-import-node',
    [
      '@babel/plugin-proposal-decorators', {
        legacy: true,
      },
    ],
  ],
};

This was giving me an error 'App cannot be invoked without new'

The error was resolved by either of these:

  1. remove the custom webpack config which uses babel-loader. (not an option as we need it to include some files from inside node _modules for babel transpilation)
  2. by changing babel.config.js :
    ```
    presets: [
    [
    '@babel/preset-env',
    ],
    'next/babel',
    ]
Did not choose this option as nextjs docs specifically suggest not to add the presets which are already included in 'next/babel'
https://nextjs.org/docs/advanced-features/customizing-babel-config

3. by changing babel.config.js:

presets: [
[
'next/babel',
{
'preset-env': { targets: { node: 'current' } },
},
],
],
```

This was the option we went ahead with, although it is still not clear why this solves the issue.

What's the working solution for lerna, nextjs, and babel these days?

I'm trying to avoid pre-transpiling my libraries before import, and I've hit a brick wall with every solution here. Here's what I can't get working:

  • next-transpile-modules.
  • resetting the preset-env settings.
  • updating the next-babel-loader include and exclude rules to pass/fail respectively.
Was this page helpful?
0 / 5 - 0 ratings

Related issues

jesselee34 picture jesselee34  Â·  3Comments

formula349 picture formula349  Â·  3Comments

olifante picture olifante  Â·  3Comments

swrdfish picture swrdfish  Â·  3Comments

havefive picture havefive  Â·  3Comments