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

Error when accessing Typescript enum defined outside Next root in Next 9.4 #13045

Closed
majelbstoat opened this issue May 18, 2020 · 23 comments
Closed

Comments

@majelbstoat
Copy link

majelbstoat commented May 18, 2020

Bug report

Accessing a typescript enum property when the file defining the enum is outside Next's directory causes issues in Next 9.4.0 and Next 9.4.1, but not in Next 9.3.6.

Describe the bug

Defining a Typescript enum in a .ts file outside the root causes issues running next if any property is accessed on the enum.

The error implies that the file was not parsed correctly:

../lib/common/types.ts 1:7
Module parse failed: Unexpected token (1:7)
You may need an appropriate loader to handle this file type, currently no loaders are configured to process this file. See https://webpack.js.org/concepts#loaders
> export enum MyEnum {
|   OPTION_ONE = 1,
|   OPTION_TWO = 2,

This was not a problem in Next 9.3.6.

It's hard to describe, so I put together a minimal reproduction: https://github.com/majelbstoat/nextjs-broken-enums

Expected behavior

Typescript is compiled correctly, and I can access properties on enums.

System information

  • OS: [macOS]
  • Version of Next.js: [9.4.0, 9.4.1]
  • Version of Node.js: [12.16.1]

Additional context

This broke in 9.4.0, and I originally thought it was the definition itself that caused the problem because that's what the error implied (#12786) but that was a symptom, not the root issue.

@majelbstoat majelbstoat changed the title baseUrl works differently for yarn dev than yarn build in Next 9.4 Compilation errors on yarn dev, but not yarn build in Next 9.4 May 18, 2020
@majelbstoat majelbstoat changed the title Compilation errors on yarn dev, but not yarn build in Next 9.4 Compilation errors on 'next', but not 'next build' in Next 9.4 May 18, 2020
@majelbstoat majelbstoat changed the title Compilation errors on 'next', but not 'next build' in Next 9.4 Error when accessing Typescript enum defined outside Next root in Next 9.4 May 18, 2020
@Timer
Copy link
Member

Timer commented May 18, 2020

This is the expected behavior because you're importing a file outside of your application root. TypeScript files are not processed there.

You'll need to override your webpack configuration to compile the top-level lib, or move the files into your app root.

@Timer Timer closed this as completed May 18, 2020
@majelbstoat
Copy link
Author

This worked without issue, in exactly the same manner, in 9.3.6.

They can't be moved inside because they are shared files with other projects.

@Timer
Copy link
Member

Timer commented May 18, 2020

Hmm, nothing should've changed in that regard. Do you have a next.config.js in your project?

Note your provided example also fails with 9.3.6.

@majelbstoat
Copy link
Author

Ah, yes, I do! It has this in it to support symlinks:

function supportSymlinkedFilesInNextBabelLoader(config) {
  config.module.rules.forEach(({ use }, i) => {
    const isBabelLoader = use && use.loader === "next-babel-loader";
    if (isBabelLoader) {
      delete config.module.rules[i].include;
    }
  });
}

module.exports = {
  webpack: function (config, { isServer }) {
    // Add support symlinks for dev.
    supportSymlinkedFilesInNextBabelLoader(config);
    if (!config.resolve) {
      config.resolve = {};
    }
    config.resolve.symlinks = false;
  },
};

Any chance next-babel-loader is not the name of the loader any more?

@Timer
Copy link
Member

Timer commented May 18, 2020

use could be an array, so you need to search deeper.

@majelbstoat
Copy link
Author

majelbstoat commented May 18, 2020

The problem actually appears to be that next-babel-loader is no longer used for the client?

With 9.3.6, use-babel-loader is used twice, once each for server and client:

{
  loader: 'next-babel-loader',
  options: {
    isServer: false, // Client
    distDir: '/dev/nextjs-broken-enums/app/.next',
    pagesDir: '/dev/nextjs-broken-enums/app/pages',
    cwd: '/dev/nextjs-broken-enums/app',
    cache: true,
    babelPresetPlugins: [],
    hasModern: false,
    development: true,
    hasReactRefresh: false
  }
}
{
  loader: 'next-babel-loader',
  options: {
    isServer: true, // Server
    distDir: '/dev/nextjs-broken-enums/app/.next',
    pagesDir: '/dev/nextjs-broken-enums/app/pages',
    cwd: '/dev/nextjs-broken-enums/app',
    cache: true,
    babelPresetPlugins: [],
    hasModern: false,
    development: true,
    hasReactRefresh: false
  }
}

But for 9.4.1, it's only used for the server:

{
  loader: 'next-babel-loader',
  options: {
    isServer: true, //Server
    distDir: '/dev/nextjs-broken-enums/app/.next',
    pagesDir: '/dev/nextjs-broken-enums/app/pages',
    cwd: '/dev/nextjs-broken-enums/app',
    cache: true,
    babelPresetPlugins: [],
    hasModern: false,
    development: true,
    hasReactRefresh: false
  }
}

Is that a deliberate change?

(Because it's not called on the client, I can't update the include path to make it handle Typescript outside the root)

@Timer
Copy link
Member

Timer commented May 18, 2020

It's still used for the client, but you need to handle the case when use is an array.

@majelbstoat
Copy link
Author

Ah, I see. Yeah, awesome, that got it, thanks.

For those who have a similar issue, or come here from (#3898), my updated config is:

function supportSymlinkedFilesInNextBabelLoader(config) {
  config.module.rules.forEach(({ use }, i) => {
    if (!use) return
    const isBabelLoader = Array.isArray(use)
      ? use.findIndex((item) => item && item.loader && item.loader === 'next-babel-loader') !== -1
      : use.loader === 'next-babel-loader'
    if (isBabelLoader) {
      delete config.module.rules[i].include
    }
  })
}

@DayBr3ak
Copy link

@majelbstoat can we have the full config? Do you have any babel dependencies? I can't get it to work.

I'm stumped nextjs can't parse an enum correctly.

@Timer
Copy link
Member

Timer commented Jun 22, 2020

@DayBr3ak Next.js can parse enums just fine. This is a bug with your custom configuration or code, so you'll need to provide a full demo for us to find the issue in your project.

@majelbstoat
Copy link
Author

Yeah, @Timer's right, it does work if configured correctly. For me, I had code that was a) outside the next root, and b) symlinked in from elsewhere in a monorepo. This is my next config in its entirety:

const withBundleAnalyzer = require('@next/bundle-analyzer')({
  enabled: process.env.ANALYZE === 'true',
})

function supportSymlinkedFilesInNextBabelLoader(config) {
  config.module.rules.forEach(({ use }, i) => {
    if (!use) return
    const isBabelLoader = Array.isArray(use)
      ? use.findIndex((item) => item && item.loader && item.loader === 'next-babel-loader') !== -1
      : use.loader === 'next-babel-loader'
    if (isBabelLoader) {
      delete config.module.rules[i].include
    }
  })
}

module.exports = withBundleAnalyzer({
  typescript: {
    ignoreDevErrors: true,
  },
  webpack: function(config, { isServer }) {
    // Add support symlinks for dev.
    supportSymlinkedFilesInNextBabelLoader(config)
    if (!config.resolve) {
      config.resolve = {}
    }
    config.resolve.symlinks = false

    return config
  },
})

@benwinding
Copy link

Here's what worked for me, (based on @majelbstoat), using the function version of next.config.js

// This uses phases as outlined here: https://nextjs.org/docs/api-reference/next.config.js/introduction
module.exports = (phase, { defaultConfig }) => {
  return {
    ...defaultConfig, // This is important
    webpack: (config, { buildId, dev, isServer, defaultLoaders, webpack }) => {
      fixEnums(config); // This is important
      return config
    },
  }
}

function fixEnums(config) {
  config.module.rules.forEach(({ use }, i) => {
    if (!use) return
    const isBabelLoader = Array.isArray(use)
      ? use.findIndex((item) => item && item.loader && item.loader === 'next-babel-loader') !== -1
      : use.loader === 'next-babel-loader'
    if (isBabelLoader) {
      delete config.module.rules[i].include
    }
  })
}

@hadnet
Copy link

hadnet commented Dec 4, 2020

Why Next.js 10 does not support const enums?

@rokinsky
Copy link
Contributor

rokinsky commented Dec 6, 2020

Why Next.js 10 does not support const enums?

@hadnet, see babel/babel#8741

@flolu
Copy link

flolu commented Apr 9, 2021

@benwinding Your next.config.js works perfectly! 👏
Can you shortly explain what the fixEnums function does?

@benwinding
Copy link

@flolu what fixEnums does is remove the next-babel-loader from the webpack config, during the webpack compilation. This prevents that loader from parsing the enum declarations and throwing an error.

@flolu
Copy link

flolu commented Apr 14, 2021

@benwinding Alright thank you. Why is the next-babel-loader in the webpack config in the first place?

@benwinding
Copy link

From what I understand, nextjs do a bunch of preprocessing to files (using webpack). next-babel-loader is a version of the babel-loader which is used to transform modern javascript to work on older browsers.

@flolu
Copy link

flolu commented Apr 14, 2021

But is it safe to remove it then?

@flolu
Copy link

flolu commented Jun 18, 2021

@benwinding Your fixEnum method doesn't seem to work with webpack5.
Do you have a workaround?

@flolu
Copy link

flolu commented Jul 30, 2021

It seems as if this experimental configuration in next.config.js fixes the problem:

module.exports = {
  experimental: {
    externalDir: true,
  },
}

@landsman
Copy link
Contributor

landsman commented Jan 5, 2022

Hmm I still have a problem and I don't know why...

  • Next.js version: 11.1.3
  • active experimental externalDir option

@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 Feb 4, 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

9 participants