Next.js: Error during build: Cannot read property 'file' of undefined

Created on 11 Jul 2017  路  19Comments  路  Source: vercel/next.js

I'm stuck on an odd error during build, where a certain source file is unable to be transpiled. The file in question just exports a named class: export class ...

The issue seems to be caused by styled-jsx and its moduleExportsVisitor. The file where the error occurs does not use React or styled-jsx.

Any advice/help would be appreciated. For now, I'm working around it by creating a separate build for that module without going through the Next build pipeline. Here's the stack trace:

Module build failed: TypeError: ~/dev/mooz/vex/src/accidental.js: Cannot read property 'file' of undefined
    at NodePath.getSource (~/dev/node_modules/babel-traverse/lib/path/introspection.js:193:20)
    at moduleExportsVisitor (~/dev/node_modules/styled-jsx/dist/babel-external.js:171:24)
    at callExternalVisitor (~/dev/node_modules/styled-jsx/dist/babel.js:346:3)
    at PluginPass.AssignmentExpression (~/dev/node_modules/styled-jsx/dist/babel.js:308:9)
    at newFn (~/dev/node_modules/babel-traverse/lib/visitors.js:276:21)
    at NodePath._call (~/dev/node_modules/babel-traverse/lib/path/context.js:76:18)
    at NodePath.call (~/dev/node_modules/babel-traverse/lib/path/context.js:48:17)
    at NodePath.visit (~/dev/node_modules/babel-traverse/lib/path/context.js:105:12)
    at TraversalContext.visitQueue (~/dev/node_modules/babel-traverse/lib/context.js:150:16)
    at TraversalContext.visitSingle (~/dev/node_modules/babel-traverse/lib/context.js:108:19)
    at TraversalContext.visit (~/dev/node_modules/babel-traverse/lib/context.js:192:19)
    at Function.traverse.node (~/dev/node_modules/babel-traverse/lib/index.js:114:17)

  • [x] I have searched the issues of this repository and believe that this is not a duplicate.

Expected Behavior


The build task completes successfully.

Current Behavior


The build task fails with an error mentioned in summary.

Steps to Reproduce


Unknown

Your Environment


| Tech | Version |
|---------|---------|
| next | @beta |

All 19 comments

The exact place where the error is thrown is: babel-traverse/lib/path/introspection.js:193:20. This is what's there:

function getSource() {
  var node = this.node;
  if (node.end) {
    return this.hub.file.code.slice(node.start, node.end); // <-- *here*
  } else {
    return "";
  }
}

I'm not familiar enough with Babel to know why this.hub is undefined.

Following who called getSource() at: styled-jsx/dist/babel-external.js:171:24..

function moduleExportsVisitor(path, opts) {
  if (path.get('left').getSource() !== 'module.exports') { // <-- *here*
    return;
  }
  defaultExports(path, path.get('right'), opts);
};

path.get('left') is returning a this without this.hub. If I understand correctly, getSource expects to have this.node and this.hub - as well as this.hub.file, which is throwing the undefined error.

As a try, I added a check for path.hub at this line:

if (path.hub && path.get('left').getSource() !== 'module.exports') {

Now the build completes. OK, getting closer to a solution..

@giuseppeg

@eliot-akira can I see an example of the source code that causes the failure? Or maybe can you submit a PR to styled-jsx with a failing test?

OK, I will see if I can create a failing test case for styled-jsx.

So I created a number of tests for styled-jsx, all of which passed. I tested named class export, importing a named class export, and also just the whole source file from the problematic build.

Then, going back to the Next build process, I started removing parts of the source file where the error is happening. Remove, test, remove, test..until I narrowed it down to the smallest chunk that still fails to build:

export default function fn() {
  for (let i = 0; i < 1; i++) {
    const g = () => {}
    const b = () => g()
    i = 2
  }
}

I also removed extraneous details like variable names and what the original function did. This chunk as a test for styled-jsx passes, and the transpiled snapshot looks good.

The multiple occurrences of => could mean something. I'll keep following up..

OK, I think I've found a way to reliably reproduce this error:

  • Start a new project: next init, yarn
  • Replace page/index.js with the following:

    for (let i = 0; i < 1; i++) {
    const g = () => {}
    const b = () => g()
    i = 2
    }
    
  • Run next build

Strange thing about this chunk is that if I remove any one part of it, the build error is gone.

Odd, do you have a custom .babelrc or additional babel plugins?
Can you put together a sample Next.js app which fails on build?

It happens on a brand new project with no babel config.

As additional info, the build completes if I move the variable declaration outside the for loop:

let i

for (i=0; ...

page/index.js or pages?

Here's an example I put together: https://github.com/eliot-akira/next-build-debug

Edit: I meant pages.

@eliot-akira yeah thank you! My guess is that by the time we do the check

if (path.get('left').getSource() !== 'module.exports')

the ast has been transformed by another plugin already and since getSource works on the original source code probably it is trying to stringify the wrong path/node.

Instead of being lazy and checking for the source to be module.exports maybe we should do something like this in styled-jsx http://astexplorer.net/#/gist/2a0110cea332771706ebd10c7d6dab68/41f6f8d1a4b981d2f018b69445610777a7e1d7ec

want to give it a try and see if that fixes it?

I tried the MemberExpression visitor in place of ExportDefaultDeclaration visitor in babel-external.js. It results in: Cannot read property 'name' of undefined. I see that path.get('object') and path.get('property') are returning instances of NodePath without the expected .node property.

It seems the issue is further up the build pipeline, and styled-jsx is receiving "corrupted" syntax tree..? Here's the stack trace:

Module build failed: TypeError: ~/build-test/pages/index.js?entry: Cannot read property 'name' of undefined
    at moduleExportsVisitor (~/build-test/node_modules/styled-jsx/dist/babel-external.js:171:31)
    at callExternalVisitor (~/build-test/node_modules/styled-jsx/dist/babel.js:346:3)
    at PluginPass.AssignmentExpression (~/build-test/node_modules/styled-jsx/dist/babel.js:308:9)
    at newFn (~/build-test/node_modules/babel-traverse/lib/visitors.js:276:21)
    at NodePath._call (~/build-test/node_modules/babel-traverse/lib/path/context.js:76:18)
    at NodePath.call (~/build-test/node_modules/babel-traverse/lib/path/context.js:48:17)
    at NodePath.visit (~/build-test/node_modules/babel-traverse/lib/path/context.js:105:12)
    at TraversalContext.visitQueue (~/build-test/node_modules/babel-traverse/lib/context.js:150:16)
    at TraversalContext.visitSingle (~/build-test/node_modules/babel-traverse/lib/context.js:108:19)
    at TraversalContext.visit (~/build-test/node_modules/babel-traverse/lib/context.js:192:19)
 @ multi ./pages?entry
    at ~/build-test/node_modules/next/dist/server/build/index.js:182:21
    at ~/build-test/node_modules/webpack/lib/Compiler.js:274:15
    at Compiler.emitRecords (~/build-test/node_modules/webpack/lib/Compiler.js:369:37)
    at ~/build-test/node_modules/webpack/lib/Compiler.js:267:12
    at ~/build-test/node_modules/webpack/lib/Compiler.js:362:11
    at next (~/build-test/node_modules/tapable/lib/Tapable.js:154:11)
    at Compiler.compiler.plugin (~/build-test/node_modules/webpack/lib/performance/SizeLimitsPlugin.js:99:4)
    at Compiler.applyPluginsAsyncSeries1 (~/build-test/node_modules/tapable/lib/Tapable.js:158:13)
    at Compiler.afterEmit (~/build-test/node_modules/webpack/lib/Compiler.js:359:8)
    at Compiler.<anonymous> (~/build-test/node_modules/webpack/lib/Compiler.js:354:14)

You need to replace AssignmentExpression with MemberExpression in babel.js.
Also why in place of ExportDefaultDeclaration visitor?

Ah OK, I didn't know what I was doing there. So I did as you suggested: replace AssignmentExpression with MemberExpression in babel.js, and then modified moduleExportsVisitor in babel-external.js to the following:

function moduleExportsVisitor(path, opts) {
  if (path.get('object').node.name !== 'module') {
    return
  }
  if (path.get('property').node.name !== 'exports') {
    return
  }
  console.log('yep module export')
  const parentPath = path.parentPath
  defaultExports(path, parentPath, opts)
}

Yes, the build completes successfully. I also tried defining module.exports in the problematic snippet, and it recognizes it.

I tried the above changes in styled-jsx source, and it fails 4 tests with Cannot read property 'name' of undefined.

Awesome, thank you for your collaboration! I can put together a patch in the following days, but if that's urgent feel free to submit a PR to styled-jsx. One thing to check before using my proposed fix is that there aren't edge cases and my code doesn't break other kind of MemberExpressions or stuff in general.

btw defaultExports(path, path.get('right'), opts) should be parentPath not sure how it worked.

Yes I noticed that mistake.. It works when passing parentPath.

If I add if (!path.get('object').node) return then all tests pass except 1.

It's not urgent, as I found a workaround for now in the problematic source file - to declare the variable before the for loop - so it compiles.

Thank you for looking into it, I'm a big fan of Zeit and Next.js.

Seems like this was fixed in styled-jsx

Was this page helpful?
0 / 5 - 0 ratings