Nuxt.js: "SyntaxError: Unexpected token <" on external Vue component

Created on 25 Mar 2017  路  15Comments  路  Source: nuxt/nuxt.js

I've followed the instructions in the documentation, and I run into the same issue as #380. I've done some testing, but can't nail down what the issue is (the error is exactly the same as the closed issue).

Here is the steps I've taken:

  1. vue init nuxt/starter test
  2. cd test
  3. npm install
  4. npm install --save bulma font-awesome node-sass sass-loader vue-bulma-breadcrumb (verified pre-processor instructions from https://nuxtjs.org/faq/pre-processors)
  5. Added { src: 'bulma', lang: 'sass' } as shown in https://nuxtjs.org/api/configuration-css
  6. npm run dev Everything works fine.
  7. Modify default layout to import Breadcrumb from 'vue-bulma-breadcrumb' and added Breadcrumb under components.

At this point, I get the same error as in #380.

Unexpected character '@' (1:0)
You may need an appropriate loader to handle this file type.
| @import '~bulma/sass/utilities/functions';
| @import '~bulma/sass/utilities/variables';

However, if I go and copy the vue file from the module into a component, and load that instead of vue-bulma-breadcrumb it works just fine. Modifying the template to actually reference the Breadcrumb component, it works just fine.

My conclusion is there is something wrong with the configuration that is preventing it from loading from node_modules properly. I'm digging around and exploring for a resolution. In the mean time, I figure someone else may find the fix quicker. This happens with vue-bulma-tooltip as well, but I didn't explore any other packages.

Is there a step that is being missed or is this a webpack configuration issue?

This feature request is available on Nuxt.js community (#c382)
enhancement

Most helpful comment

@SharadKumar

Apparently whitelisting by using the module name isn't enough, you need to use a regex for it to bundle subpaths as well. Here's wow I fixed that issue with vue-awesome:

const nodeExternals = require('webpack-node-externals');

module.exports = {
   ...
  extend(config, { isServer }) {
    if (isServer) {
      config.externals = [
        nodeExternals({
          whitelist: [/\.(?!(?:js|json)$).{1,5}$/i, /^vue-awesome/]
        })
      ]
    }
  },
  ...
}

Note: the first regex comes from the base nuxt server config.

All 15 comments

Hi @BigForNothing

You error is due to vue-bulma-tooltip and not from vue-bulma-breadcrumb.

vue-bulma-tooltip use a .scss file, to tell nuxt.js how to handle this file type, you need to extend the webpack config in your nuxt.config.js:

module.exports = {
  css: [
    { src: 'bulma', lang: 'sass' }
  ],
  build: {
    extend (config) {
      config.module.rules.push({
        test: /\.scss$/,
        loader: 'vue-style-loader!css-loader!sass-loader'
      })
    }
  }
}

I am going to handle these file types directly in nuxt.js for the upcoming release (0.10.1).

Hi @Atinux, thanks for responding. I actually wasn't even loading vue-bulma-tooltip at first.

I went ahead and added in what you responded with, and here is what's being produced, loading vue-bulma-breadcrumb only

nuxt:render Rendering url / +10s
F:\Projects\test\node_modules\vue-bulma-breadcrumb\src\Breadcrumb.vue:1
(function (exports, require, module, __filename, __dirname) { <template>
                                                              ^
SyntaxError: Unexpected token <
    at createScript (vm.js:53:10)
    at Object.runInThisContext (vm.js:95:10)
    at Module._compile (module.js:543:28)
    at Object.Module._extensions..js (module.js:580:10)
    at Module.load (module.js:488:32)
    at tryModuleLoad (module.js:447:12)
    at Function.Module._load (module.js:439:3)
    at Module.require (module.js:498:17)
    at require (internal/module.js:20:19)
    at r (F:\Projects\test\node_modules\vue-server-renderer\build.js:6947:16)
    at Object.<anonymous> (server-bundle.js:1851:18)
    at __webpack_require__ (server-bundle.js:27:30)
    at Object.49 (0.server-bundle.js:37:79)
    at __webpack_require__ (server-bundle.js:27:30)
    at Object.46 (0.server-bundle.js:13:3)
    at __webpack_require__ (server-bundle.js:27:30)
  nuxt:render Data fetching /: 17ms +50ms

It's like it's not using vue-loader properly.

Loading Vue-bulma-tooltip only

nuxt:render Rendering url / +4m
F:\Projects\test\node_modules\vue-bulma-tooltip\src\index.js:1
(function (exports, require, module, __filename, __dirname) { import classNames from 'classnames'
                                                              ^^^^^^
SyntaxError: Unexpected token import
    at createScript (vm.js:53:10)
    at Object.runInThisContext (vm.js:95:10)
    at Module._compile (module.js:543:28)
    at Object.Module._extensions..js (module.js:580:10)
    at Module.load (module.js:488:32)
    at tryModuleLoad (module.js:447:12)
    at Function.Module._load (module.js:439:3)
    at Module.require (module.js:498:17)
    at require (internal/module.js:20:19)
    at r (F:\Projects\test\node_modules\vue-server-renderer\build.js:6947:16)
    at Object.<anonymous> (server-bundle.js:1852:18)
    at __webpack_require__ (server-bundle.js:27:30)
    at Object.49 (0.server-bundle.js:37:76)
    at __webpack_require__ (server-bundle.js:27:30)
    at Object.46 (0.server-bundle.js:13:3)
    at __webpack_require__ (server-bundle.js:27:30)
  nuxt:render Data fetching /: 4ms +19ms

It appears to not understand import. Looking at the webpack configuration, node_modules is being excluded. Comparing it to vue-admin (which is the dashboard incorporating all the vue-bulma elements), they have

        test: /\.js$/,
        loader: 'babel-loader',
        include: projectRoot,
        // /node_modules\/(?!vue-bulma-.*)/
        exclude: [new RegExp(`node_modules\\${path.sep}(?!vue-bulma-.*)`)]
      },

while nuxt.js's is

{
          test: /\.js$/,
          loader: 'babel-loader',
          exclude: /node_modules/,
          query: defaults(this.options.build.babel, {
            presets: ['vue-app'],
            cacheDirectory: !!this.dev
          })
        },

Apologies if I'm going down the wrong rabbit hole here, but appears if it's being loaded from node_modules, it isn't being processed correctly in either situation.

@BigForNothing can you upgrade nuxt.js to its latest version (0.10.1) and try again? (you can remove build.extend in your nuxt.config.js)

@Atinux I seem to be getting the same issue, but I'm doing everything over to verify.

@Atinux Same issue. I went ahead and pushed it to https://github.com/BigForNothing/nuxt-buma-issue so you can see exactly what was changed. I have only edited 3 lines (the bulma line under css, importing breadcrumb into default.vue, and placing it under the components).

The error remains:

F:\Projects\test\node_modules\vue-bulma-breadcrumb\src\Breadcrumb.vue:1
(function (exports, require, module, __filename, __dirname) { <template>
                                                              ^
SyntaxError: Unexpected token <
    at createScript (vm.js:53:10)
    at Object.runInThisContext (vm.js:95:10)
    at Module._compile (module.js:543:28)
    at Object.Module._extensions..js (module.js:580:10)
    at Module.load (module.js:488:32)
    at tryModuleLoad (module.js:447:12)
    at Function.Module._load (module.js:439:3)
    at Module.require (module.js:498:17)
    at require (internal/module.js:20:19)
    at r (F:\Projects\test\node_modules\vue-server-renderer\build.js:6947:16)
    at Object.<anonymous> (server-bundle.js:1787:18)
    at __webpack_require__ (server-bundle.js:27:30)
    at Object.41 (0.server-bundle.js:37:79)
    at __webpack_require__ (server-bundle.js:27:30)
    at Object.38 (0.server-bundle.js:13:3)
    at __webpack_require__ (server-bundle.js:27:30)
  nuxt:render Data fetching /: 9ms +36ms

@BigForNothing see my PR (https://github.com/BigForNothing/nuxt-buma-issue/pull/2) to fix your issue.

Well that's a little embarrassing. Appreciate that, I doubt I would have tried the full path. Sorry to have taken up your time on that.

So maybe you can clarify something for me. Why is it that webpack configuration has:

 {
          test: /\.vue$/,
          loader: 'vue-loader',
          query: vueLoaderConfig.call(this, { isClient, isServer })
        },
        {
          test: /\.js$/,
          loader: 'babel-loader',
          exclude: /node_modules/,
          query: defaults(this.options.build.babel, {
            presets: ['vue-app'],
            cacheDirectory: !!this.dev
          })
        },

and it isn't loading the correct loader?

  • Breadcrumb is a .vue file. I would assume the test: /\.vue$/ regexp would load that appropriately. especially since there isn't an exclude.
  • While the /\.js$/ does have an exclude, this should only apply to vue-bulma-tooltip, right?

@BigForNothing I have no idea 馃槃

You need to give full path for the vue-bulma-* plugins since they are not compiled to ES2015 JS files.

@Atinux I managed to track down the issue on what is causing this. Unless I'm misunderstanding what I've gathered from Webpack, it's actually very simple. All of this should be verifiable using the vue-hackernews-2.0 repo.

In Webpack, it's recommended to use something similar to this line (line 19 in webpack.server.config.js):

  externals: Object.keys(require('../package.json').dependencies),

According to the Webpack documentation on externals:

The externals configuration option provides a way of excluding dependencies from the output bundles. Instead, the created bundle relies on that dependency to be present in the consumer's environment.

Also according to the [resolve.mainFields[(https://webpack.js.org/configuration/resolve/#resolve-mainfields) documentation:

When importing from an npm package, e.g. import * as D3 from "d3", this option will determine which fields in it's package.json are checked. The default values will vary based upon the target specified in your webpack configuration.

  • It works on the client-side, because it's not specified as an external. This mean's it correctly resolves the file to load by using the appropriate field in package.json. I tested with vue-bulma-breadcrumb, and it's listed appropriately under the main property.
  • On the server-side, it is listed as an external. So while the external will still find the appropriate file if it's used without an extension (or the src/Breadcrumb.vue in this case), it's expecting it to be a loadable module in one of these formats: CommonJS, AMD, global and ES2015 modules. Obviously the single file components are not in any of those formats prior to being ran through vue-loader.
  • Still on the server-side, if a file extension is provided (or the src/Breadcrumb.vue), it will detect that it needs to go through the appropriately loader as determined by module.rules within the Webpack configuration.
  • Since the full name resolution is happening AFTER the tests, there isn't any loader to run it through. I could be wrong on this part, it could be that it's skipping the step since it's expecting it to be pre-compiled. Debugging Webpack is a complete pain.
  • Moving "vue-bulma-breadcrumb": "^1.0.1", from dependencies to devDependencies solves this issue all together.

TL;DR - make sure non-compiled modules like vue-bulma-* (and most likely other non compiled Vue components) are installed using npm install --save-dev so they are saved to devDependencies within package.json.

This is not a nuxt.js issue BTW. I checked several projects that included server rendering for Vue. I didn't realize vue-hackernews-2.0 used it until today, and that allowed me to exclude any other modifications that differed from the recommendations. However, I would add a note about it to the FAQ because this is one bug that won't make sense to anyone until they dive in and start to get an understanding of all the different parts of the ecosystem.

To also shame myself, the npm documentation says this under the devDependencies section:

For build steps that are not platform-specific, such as compiling CoffeeScript or other languages to JavaScript, use the prepare script to do this, and make the required package a devDependency.

Although, If I would have done that from the get go, I wouldn't have got to know Webpack internals better. Thanks again for your patience and help.

@BigForNothing wow, thank you for this comment, it's a really good research you made.

You can also keep vue-bulma-breadcrumb in the dependencies and use build.extend to remove it from the list of externals.

There is really few module that are not compiled to ES2015 because there was not best-practices to create Vue.js plugin.

@Atinux @BigForNothing I face similar issue with vue-awesome:

/node_modules/vue-awesome/icons/globe.js:1
(function (exports, require, module, __filename, __dirname) { import Icon from '../components/Icon.vue'
                                                              ^^^^^^
SyntaxError: Unexpected token import

So taking tip from above and this issue from coosto-hackathon project:
https://github.com/esclapes/coosto-hackathon/commit/d055b83cab6a99d3955ffff58c1ae6459bc817d4

I updated my package.json (devDependencies) and nuxt.js:

const nodeExternals = require('webpack-node-externals');

module.exports = {
   ...
   build: {
      vendor: ['vue-awesome']
      ...
   extend(config, ctx) {
      if (ctx.isClient) {
        config.module.rules.push({
          enforce: 'pre',
          test: /\.(js|vue)$/,
          loader: 'eslint-loader',
          exclude: /(node_modules)/
        })
      } else {
        config.externals = [ nodeExternals({
          whitelist: ['vue-awesome']
        })]
      }
   }
   ...
}

I started getting this after upgrading to alpha3 from v0.10.7. Again, this works fine on client but fails on server render. What did I miss?

Just to add, I'm having the same issue importing modules from material-components-web. If I figure anything out I'll contribute something more.

Scrub that, I've literally just got it working by adding the following to my nuxt.config.js:

if (ctx.isClient) {
    ...
} else {
    config.externals = [nodeExternals({
        whitelist: [
            '@material/base', '@material/drawer', <etc>
        ]
    })]
}

I realise it doesn't help those for whom it isn't working, but I thought I'd mention it in case someone else is trying to integrate material-components-web and running into this.

@SharadKumar

Apparently whitelisting by using the module name isn't enough, you need to use a regex for it to bundle subpaths as well. Here's wow I fixed that issue with vue-awesome:

const nodeExternals = require('webpack-node-externals');

module.exports = {
   ...
  extend(config, { isServer }) {
    if (isServer) {
      config.externals = [
        nodeExternals({
          whitelist: [/\.(?!(?:js|json)$).{1,5}$/i, /^vue-awesome/]
        })
      ]
    }
  },
  ...
}

Note: the first regex comes from the base nuxt server config.

This thread has been automatically locked since there has not been any recent activity after it was closed. Please open a new issue for related bugs.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

vadimsg picture vadimsg  路  3Comments

VincentLoy picture VincentLoy  路  3Comments

bimohxh picture bimohxh  路  3Comments

maicong picture maicong  路  3Comments

surmon-china picture surmon-china  路  3Comments