I'm attempting to add Foundation for Sites to my first Vue.js project that is setup with the Vue CLI.
The website runs however the Karma+Phantomjs unit test suite is emitting this error:
PhantomJS 2.1.1 (Mac OS X 0.0.0) ERROR
SyntaxError: Invalid character: '`'
at webpack:///~/foundation-sites/js/foundation.util.core.js:24:0 <- index.js:10362
I believe this is related to webpack/babel and the ES module loading of Foundation. I'm not too sure how to further diagnose and resolve the problem.
Here's an overview of the code changes I've made...
Main.js
import jQuery from 'jquery'
window.jQuery = jQuery
window.$ = jQuery
import Foundation from 'foundation-sites'
window.Foundation = Foundation
Hello.vue
<template> ... </template>
<script>
export default {
name: 'hello',
mounted () {
this.dropdownMenu = new Foundation.DropdownMenu($('#dropdown-menu'), {
// These options can be declarative using the data attributes
hoverDelay: 300
})
},
data () {
return {
msg: 'Welcome to Your Vue.js App'
}
},
destroyed () {
this.dropdownMenu.destroy()
}
}
</script>
test/unit/index.js
import Vue from 'vue'
import Foundation from 'foundation-sites'
window.Foundation = Foundation
// ... auto-generated unit tests
test/unit/karma.conf.js
// This is a karma config file. For more details see
// http://karma-runner.github.io/0.13/config/configuration-file.html
// we are also using it with karma-webpack
// https://github.com/webpack/karma-webpack
var webpackConfig = require('../../build/webpack.test.conf')
module.exports = function (config) {
config.set({
// to run in additional browsers:
// 1. install corresponding karma launcher
// http://karma-runner.github.io/0.13/config/browsers.html
// 2. add it to the `browsers` array below.
browsers: ['PhantomJS'],
frameworks: ['mocha', 'sinon-chai', 'phantomjs-shim'],
reporters: ['spec', 'coverage'],
files: ['./index.js'],
preprocessors: {
'./index.js': ['webpack', 'sourcemap']
},
webpack: webpackConfig,
webpackMiddleware: {
noInfo: true
},
coverageReporter: {
dir: './coverage',
reporters: [
{ type: 'lcov', subdir: '.' },
{ type: 'text-summary' }
]
}
})
}
webpack.base.conf.js
var path = require('path')
var utils = require('./utils')
var config = require('../config')
var vueLoaderConfig = require('./vue-loader.conf')
function resolve (dir) {
return path.join(__dirname, '..', dir)
}
module.exports = {
entry: {
app: './src/main.js'
},
output: {
path: config.build.assetsRoot,
filename: '[name].js',
publicPath: process.env.NODE_ENV === 'production'
? config.build.assetsPublicPath
: config.dev.assetsPublicPath
},
resolve: {
extensions: ['.js', '.vue', '.json'],
alias: {
'vue$': 'vue/dist/vue.esm.js',
'@': resolve('src'),
}
},
module: {
rules: [
{
test: /\.(js|vue)$/,
loader: 'eslint-loader',
enforce: 'pre',
include: [resolve('src'), resolve('test')],
options: {
formatter: require('eslint-friendly-formatter')
}
},
{
test: /\.vue$/,
loader: 'vue-loader',
options: vueLoaderConfig
},
{
test: /\.js$/,
loader: 'babel-loader',
include: [resolve('src'), resolve('test')]
},
{
test: /\.(png|jpe?g|gif|svg)(\?.*)?$/,
loader: 'url-loader',
options: {
limit: 10000,
name: utils.assetsPath('img/[name].[hash:7].[ext]')
}
},
{
test: /\.(mp4|webm|ogg|mp3|wav|flac|aac)(\?.*)?$/,
loader: 'url-loader',
options: {
limit: 10000,
name: utils.assetsPath('media/[name].[hash:7].[ext]')
}
},
{
test: /\.(woff2?|eot|ttf|otf)(\?.*)?$/,
loader: 'url-loader',
options: {
limit: 10000,
name: utils.assetsPath('fonts/[name].[hash:7].[ext]')
}
}
]
}
}
The solution I've found is to load Foundation via Babel.
webpack.base.conf.js
The main reason for this is actually explained in the Foundation docs:
The Foundation docs go on to recommend including and setting the babel
"presets": ["es2015"]
however the Vue CLI is already setup slightly differently.Vue CLI already uses the
babel-preset-env
andbabel-preset-stage2
plugins which I've since learned takes care of the ES2015 > ES5 transpilation. Vue CLI sets up webpack with several loaders so seems to be it's necessary to add Foundation to thebabel-loader
config.Currently I'm importing Foundation and adding it to the global
window
from within Vue'smain.js
entry point. Next up I'll probably see if I can expose it using the Expose loader.