Next.js: Files outside of app directory will not be transpiled

Created on 13 Nov 2018  路  13Comments  路  Source: vercel/next.js

Bug report

Describe the bug

I have a monorepo usecase, where I want to share code between the next app and other modules that are located outside of the app folder.

It will end up with this error:

{ Error: (client) ../foo.ts 1:0
Module parse failed: The keyword 'interface' is reserved (1:0)
You may need an appropriate loader to handle this file type.
> interface Foo {
|   prop: string;
| }
 @ ./pages/index.tsx 4:0-28 8:5-8
 @ multi ./pages/index.tsx

To Reproduce

I have set up a simple repo here, based on the next-typescript example:

https://github.com/Swatinem/next-monorepo/tree/master

Expected behavior

Whatever I import should be transpiled like everything else, period.

System information

Additional context

Related issue (possibly a duplicate?): https://github.com/zeit/next-plugins/issues/234
Also related maybe:

Most helpful comment

Thanks @Swatinem! Made it slightly more resilient by searching for the relevant loader rather than relying on index:

config.module.rules.forEach((rule) => {
    const ruleContainsTs = rule.test.toString().includes('ts|tsx');

    if (ruleContainsTs && rule.use && rule.use.loader === 'next-babel-loader') {
      rule.include = undefined;
    }
  });

All 13 comments

Sounds like this is a duplicate of https://github.com/zeit/next.js/issues/706

Just for reference, this is the extremely ugly workaround I found for this:

    // NOTE(swatinem): we just assume the typescript loader is configured last
    const tsxRules = config.module.rules[config.module.rules.length - 1]

    // By default next-typescript only includes things in the app root for no real reason -_-
    // See: https://github.com/zeit/next-plugins/blob/be21851f63e82845387e576f5f2ed3e5c448cb97/packages/next-typescript/index.js#L51
    // See: https://github.com/zeit/next-plugins/issues/234
    tsxRules.include = undefined

    // Also, apparently babel does not search for the `.babelrc` based on the `root`
    // option correctly when the file to transpile is outside of the root.
    // See: https://babeljs.io/docs/en/options#root
    // So we just pass a path to the babelrc manually here
    defaultLoaders.babel.options.configFile = path.join(__dirname, '.babelrc')

Thanks @Swatinem! Made it slightly more resilient by searching for the relevant loader rather than relying on index:

config.module.rules.forEach((rule) => {
    const ruleContainsTs = rule.test.toString().includes('ts|tsx');

    if (ruleContainsTs && rule.use && rule.use.loader === 'next-babel-loader') {
      rule.include = undefined;
    }
  });

@Swatinem, Thanx for the workaround I've spent few hours till found this thread.

For easier debug, you can console.log the config and find out the exact ts-loader, then modify it.

Going to track this in #706

Apparently someone changed the regex in the rules to /\.(tsx|ts|js|mjs|jsx)$/, thus the fix suggested by @jakemmarsh only works if you check for .includes("tsx|ts") ...

I feel like this should be re-opened because it's a different issue. This one is for files outside of the nextjs root, and #706 is for files within the nextjs root, but in the node_modules folder.

I seems that transpileModules works for me.

module.exports = withBundleAnalyzer(
  withTranspileModules({
    // To force Next.js to transpile code from other workspace packages.
    // https://github.com/martpie/next-transpile-modules
    transpileModules: ['evolu'],
    // We need server for local next export for integration tests.
    // We can detects ZEIT Now runtime via AWS_REGION.
    target: process.env.AWS_REGION ? 'serverless' : 'server',
    // Enforce relative paths for integration tests.
    // https://github.com/zeit/next.js/issues/2581
    assetPrefix: './',
  }),
);

You can also specify a path to an external folder.

config.module.rules.forEach((rule) => {
    const isTsRule = rule.test && rule.test.toString().includes('tsx|ts');
    if (isTsRule && rule.use && rule.use.loader === 'next-babel-loader') {
        rule.include = [
            path.join(__dirname, 'path', 'to', 'external', 'folder'),
            ...rule.include
        ];
    }
});

@jakemmarsh This works for me:

const ruleString = rule.test ? rule.test.toString() : ''
const ruleContainsTs = /tsx?/.test(ruleString)

if (ruleContainsTs && rule.use && rule.use.loader === 'next-babel-loader') {
  rule.include = undefined
}

Does Next dev mode rebuilds on change of the external files?

We made this library to do this - it's not 100% reliable but it's pretty good.

https://github.com/hashicorp/next-remote-watch

Was this page helpful?
0 / 5 - 0 ratings