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

Using transpileDependencies throws runtime error #2637

Closed
diachedelic opened this issue Sep 28, 2018 · 11 comments
Closed

Using transpileDependencies throws runtime error #2637

diachedelic opened this issue Sep 28, 2018 · 11 comments

Comments

@diachedelic
Copy link

diachedelic commented Sep 28, 2018

Version

3.0.4

Reproduction link

https://github.com/diachedelic/vue-cli-transpile-dependencies-repro

Node and OS info

Node 8.9.0 / yarn 1.9.4 / MacOS 10.13.6 / Google Chrome 69.0.3497.100 (Official Build) (64-bit)

Steps to reproduce

git clone git@github.com:diachedelic/vue-cli-transpile-dependencies-repro.git
cd vue-cli-transpile-dependencies-repro
yarn

git checkout transpile-dependencies-simple
yarn add file:dep # refresh dep
yarn serve --open
# see compiler warning
# see error in browser console

git checkout transpile-dependencies-obj-spread
yarn add file:dep # refresh dep
yarn serve --open
# see error in browser console

What is expected?

transpileDependencies should transpile any listed dependencies to ES5 without compiler warnings or runtime errors.

What is actually happening?

I am seeing two distinct runtime errors:

Uncaught TypeError: Cannot assign to read only property 'exports' of object '#<Object>'
TypeError: _objectSpread is not a function

See README.md for full stack traces etc.


I am upgrading a couple of PWAs from their modified vue-cli v2 templates, to use vue-cli v3 (so I don't have to maintain a webpack config). I had dependencies transpiling ok using my own webpack config, and I want to get that behaviour back.

Check out README.md on repro's master branch for a full walkthrough.

@haoqunjiang
Copy link
Member

You cannot mix commonjs & ES modules in webpack 4.

Now that it's a third party dependency, the only way to solve this problem is to let babel take care of the module interop issues, by changing babel.config.js's content to:

// babel.config.js
module.exports = {
  presets: [
    ["@vue/app", {
      modules: "commonjs"
    }]
  ]
};

All your ES module code will be transpiled to commonjs and then pass to webpack for further compilation.

@diachedelic
Copy link
Author

Looks like that option has been removed, I get this compile error:

Module build failed (from ./node_modules/babel-loader/lib/index.js):
ReferenceError: [BABEL] /private/tmp/vue-cli-transpile-dependencies-repro/src/main.js: Using removed Babel 5 option: .modules - Use the corresponding module transform plugin in the `plugins` option. Check out http://babeljs.io/docs/plugins/#modules

@diachedelic
Copy link
Author

Using the plugin mentioned in the error message, I get another runtime error:

main.js?56d7:1 Uncaught TypeError: _interopRequireDefault is not a function
    at eval (main.js?56d7:1)
    at Object../src/main.js (app.js:2106)
    at __webpack_require__ (app.js:725)
    at fn (app.js:102)
    at Object.0 (app.js:2119)
    at __webpack_require__ (app.js:725)
    at app.js:792
    at app.js:795
module.exports = {
  presets: ['@vue/app'],
  plugins: ['@babel/plugin-transform-modules-commonjs']
}

You cannot mix commonjs & ES modules in webpack 4.

@sodatea This was not mentioned in Webpack's migration guide from v3 to v4 - could you please cite a source?

All your ES module code will be transpiled to commonjs and then pass to webpack for further compilation.

My external dependency actually is a CommonJS module already, how would this help me?

@diachedelic
Copy link
Author

I rewrote my external dep as an ES6 module, and while the runtime error disappears, it is not longer transpiled to es5. To reproduce:

git clone git@github.com:diachedelic/vue-cli-transpile-dependencies-repro.git
cd vue-cli-transpile-dependencies-repro
git checkout es6-dep
yarn && yarn add file:dep
yarn build && npx es-check es5 'dist/js/*.js'

An error is thrown because the fat arrow function in dep/index.mjs was not transpiled.

@diachedelic
Copy link
Author

Renaming dep/index.mjs to dep/index.js fixes the issue. So if my deps are ES6 modules, with a .js file extension, and are listed in transpileDependencies, things work as expected.

However, rewriting all of my CommonJS deps (most of which I do not control) as es6 modules will be a real pain, especially as some must support both Node.js and the browser. Is that really the only solution?

@haoqunjiang
Copy link
Member

  1. Don't forget the extra [] when configuring a babel preset.
// correct
presets: [
    ["@vue/app", {
      modules: "commonjs"
    }]
  ]

// incorrect
presets: [
    "@vue/app", {
      modules: "commonjs"
    }
  ]

  1. It is always the case since webpack 2.

webpack/webpack#4039
You can't mix import and module.exports.

(import statements might be added by @babel/plugin-transform-runtime to include polyfills & helpers)


  1. In Vue CLI, we don't support .mjs extensions by default, for the same reason as Create React App v2

It was removed because of inconsistent support from underlying tools. We will add it back after it stops being experimental, and Jest gets built-in support for it.

To add it back, configure it in vue.config.js

module.exports = {
  chainWebpack: config => config.resolve.extensions.prepend('.mjs')
}

  1. The recommended solution is provided in the first comment. Just configure babel with the correct configurations.

@diachedelic
Copy link
Author

diachedelic commented Sep 28, 2018

Thanks for your help - I fixed up my preset config, but I still get a runtime error:

Uncaught TypeError: _interopRequireDefault is not a function
    ...

To reproduce:

git clone git@github.com:diachedelic/vue-cli-transpile-dependencies-repro.git
cd vue-cli-transpile-dependencies-repro
git checkout module-commonjs
yarn && yarn add file:dep
yarn serve --open
# see runtime error in browser console

However, I think I have found a working solution (from this stackoverflow question). By setting VUE_CLI_BABEL_TRANSPILE_MODULES=true, all my problems are solved. To see it working:

git clone git@github.com:diachedelic/vue-cli-transpile-dependencies-repro.git
cd vue-cli-transpile-dependencies-repro
git checkout babel-transpile
yarn && yarn add file:dep
yarn build && npx es-check es5 'dist/js/*.js' # pure es5
yarn serve --open # no runtime errors
// babel.config.js
process.env.VUE_CLI_BABEL_TRANSPILE_MODULES = true

module.exports = {
  presets: [
    '@vue/app'
  ],
}

This appears to work because internally it not only sets modules: "commonjs", but also sets useESModules: false on the transform-runtime plugin:

// inside @vue/babel-preset-app/index.js
  plugins.push([require('@babel/plugin-transform-runtime'), {
    useESModules: !process.env.VUE_CLI_BABEL_TRANSPILE_MODULES,
  }])

Could you please explain why this works @sodatea, but is not the default behaviour? Thanks.

@haoqunjiang
Copy link
Member

Ummm……
More reasoning on the default behavior can be found in this thread: #1568

After rereading that thread I think my first comment might not be the best solution. Sorry for my oversight.

Also, there are several tips on dependencies that needs polyfills in the doc:
https://github.com/vuejs/vue-cli/tree/dev/packages/%40vue/babel-preset-app#usebuiltins

If one of your dependencies need polyfills, you have a few options:

  1. If the dependency is written in an ES version that your target environments do not support: Add that dependency to the transpileDependencies option in vue.config.js. This would enable both syntax transforms and usage-based polyfill detection for that dependency.

  2. If the dependency ships ES5 code and explicitly lists the polyfills needed: you can pre-include the needed polyfills using the polyfills option for this preset.

  3. If the dependency ships ES5 code, but uses ES6+ features without explicitly listing polyfill requirements (e.g. Vuetify): Use useBuiltIns: 'entry' and then add import '@babel/polyfill' to your entry file. This will import ALL polyfills based on your browserslist targets so that you don't need to worry about dependency polyfills anymore, but will likely increase your final bundle size with some unused polyfills.

Setting VUE_CLI_BABEL_TRANSPILE_MODULES to true is a valid escape hatch but that's not what we intended.

In your case it is the helpers that inserts import statements and we didn't take that into consideration. I think it should be identified as a bug in @vue/babel-preset-app. A better solution could be automatically setting helpers option of transform-runtime to inline babel helper functions based on the useBulitIns option. Will do that in the next release.

@diachedelic
Copy link
Author

Thanks, I'm glad - I didn't want to rely on what felt like a hack of vue-cli's internals.

@diachedelic
Copy link
Author

Just a note for anyone who runs into this - using process.env.VUE_CLI_BABEL_TRANSPILE_MODULES = true breaks code splitting (if you are wondering why you can't get your routes to lazy load). Furthermore, this workaround is not longer required to fix the above issue as of vue-cli@3.12.1.

@drdilyor
Copy link

drdilyor commented Jul 6, 2021

The above answers didn't help me, but setting transpileDependencies: [] in vue.config.js solved it

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants