I have a Vue.js project which was working normally until a few minutes ago. I was in the middle of trying to add a new small piece of functionality when npm run build
stopped working, with the Syntax Error
you see below. I spent time reading it over, but it seems to me that there are no clues given as to where the Unexpected token
actually is, other than that it's in App.vue.
I suspect I can probably eventually get it working by just taking out code until I figure out what it's choking on, but what's the preferred way to deal with these error messages? They seem not helpful, and pulling out code every time I get a Syntax Error
is going to be much slower than knowing where the error is immediately.
| building for production...
Starting to optimize CSS...
Processing css/app.b78de1b1e52d045850f3f8ef25eab789.css...
Processed css/app.b78de1b1e52d045850f3f8ef25eab789.css, before: 2077, after: 2009, ratio: 96.73%
Hash: 463db738399a4df0bc89
Version: webpack 2.6.1
Time: 7211ms
Asset Size Chunks Chunk Names
js/app.46d986e313fa2c015cd0.js 5.62 kB 0 [emitted] app
js/vendor.bf7e840e23d2fcdbebe0.js 141 kB 1 [emitted] vendor
js/manifest.c323c0be7462a1c8da87.js 1.51 kB 2 [emitted] manifest
css/app.b78de1b1e52d045850f3f8ef25eab789.css 2.01 kB 0 [emitted] app
js/app.46d986e313fa2c015cd0.js.map 26 kB 0 [emitted] app
css/app.b78de1b1e52d045850f3f8ef25eab789.css.map 4.13 kB 0 [emitted] app
js/vendor.bf7e840e23d2fcdbebe0.js.map 974 kB 1 [emitted] vendor
js/manifest.c323c0be7462a1c8da87.js.map 14.6 kB 2 [emitted] manifest
..\..\templates\app.html 598 bytes [emitted]
ERROR in ./~/vue-loader/lib/template-compiler?{"id":"data-v-59c95520"}!./~/vue-loader/lib/selector.js?type=template&index=0!./src/App.vue
Module build failed: SyntaxError: Unexpected token (72:13)
at Parser.pp$4.raise (C:\path_to_project\client\node_modules\vue-template-es2015-compiler\buble.js:2231:15)
at Parser.pp.unexpected (C:\path_to_project\client\node_modules\vue-template-es2015-compiler\buble.js:613:10)
at Parser.pp.expect (C:\path_to_project\client\node_modules\vue-template-es2015-compiler\buble.js:607:28)
at Parser.parseObj (C:\path_to_project\client\node_modules\vue-template-es2015-compiler\buble.js:3871:16)
at Parser.pp$3.parseExprAtom (C:\path_to_project\client\node_modules\vue-template-es2015-compiler\buble.js:1815:19)
at Parser.parseExprAtom (C:\path_to_project\client\node_modules\vue-template-es2015-compiler\buble.js:3800:24)
at Parser.pp$3.parseExprSubscripts (C:\path_to_project\client\node_modules\vue-template-es2015-compiler\buble.js:1725:21)
at Parser.pp$3.parseMaybeUnary (C:\path_to_project\client\node_modules\vue-template-es2015-compiler\buble.js:1702:19)
at Parser.pp$3.parseExprOps (C:\path_to_project\client\node_modules\vue-template-es2015-compiler\buble.js:1647:21)
at Parser.pp$3.parseMaybeConditional (C:\path_to_project\client\node_modules\vue-template-es2015-compiler\buble.js:1630:21)
at Parser.pp$3.parseMaybeAssign (C:\path_to_project\client\node_modules\vue-template-es2015-compiler\buble.js:1607:21)
at Parser.pp$3.parsePropertyValue (C:\path_to_project\client\node_modules\vue-template-es2015-compiler\buble.js:2008:89)
at Parser.parseObj (C:\path_to_project\client\node_modules\vue-template-es2015-compiler\buble.js:3895:14)
at Parser.pp$3.parseExprAtom (C:\path_to_project\client\node_modules\vue-template-es2015-compiler\buble.js:1815:19)
at Parser.parseExprAtom (C:\path_to_project\client\node_modules\vue-template-es2015-compiler\buble.js:3800:24)
at Parser.pp$3.parseExprSubscripts (C:\path_to_project\client\node_modules\vue-template-es2015-compiler\buble.js:1725:21)
at Parser.pp$3.parseMaybeUnary (C:\path_to_project\client\node_modules\vue-template-es2015-compiler\buble.js:1702:19)
at Parser.pp$3.parseExprOps (C:\path_to_project\client\node_modules\vue-template-es2015-compiler\buble.js:1647:21)
at Parser.pp$3.parseMaybeConditional (C:\path_to_project\client\node_modules\vue-template-es2015-compiler\buble.js:1630:21)
at Parser.pp$3.parseMaybeAssign (C:\path_to_project\client\node_modules\vue-template-es2015-compiler\buble.js:1607:21)
at Parser.pp$3.parseExprList (C:\path_to_project\client\node_modules\vue-template-es2015-compiler\buble.js:2175:22)
at Parser.pp$3.parseSubscripts (C:\path_to_project\client\node_modules\vue-template-es2015-compiler\buble.js:1751:35)
@ ./src/App.vue 8:2-170
@ ./src/main.js
Here is App.vue:
<template>
<div id="app">
<div id="toolbar" v-on-clickaway="disableSongToolbar">
<div id="toolbar__left">
<a class="ui-text toolbar__option" href="/">Rhymecraft</a>
<div class="ui-text toolbar__option"
:class="{ toolbar__option_active: songToolbarActive }"
@click="toggleSongToolbarDropdown()">
Song
</div>
<div class="ui-text toolbar__option">Line</div>
<div class="ui-text toolbar__option">Help</div>
</div>
<div id="toolbar__right">
<a class="ui-text toolbar__option" href="/logout">Log out</a>
</div>
</div>
<div id="toolbar-dropdowns">
<div id="toolbar-dropdowns__song" v-show="songToolbarActive">
<div class="ui-text toolbar__option toolbar-dropdown-option toolbar-dropdown-not-last-option"
@click="createNewSong">Create new song</div>
<div class="ui-text toolbar__option toolbar-dropdown-option"
@click="displayListOfUsersSongs">Load song</div>
<div class="ui-text toolbar__option toolbar-dropdown-option"
v-if="song.hasOwnProperty('id')"
@click="displayConfirmDeleteSong = true">Delete current song</div>
</div>
</div>
<div id="modals" :class="{ modals-active: modalActive }">
<div id="song-loader" v-show="displaySongLoader">
<div id="song-loader-header" class="ui-text">Load Song</div>
<div class="ui-text song-loader__item song-loader__selectable-item" v-for="song in songs" @click="loadSong(song.id)">{{ song.name }}</div>
<div class="ui-text song-loader__item" v-if="songs.length === 0 || !songs[0].hasOwnProperty('id')">You do not have any songs.</div>
<div id="song-loader__cancel" class="ui-text" @click="displaySongLoader = false">Cancel</div>
</div>
<div id="confirm-delete-song" v-show="displayConfirmDeleteSong">
<div id="confirm-delete-song__header" class="ui-text">Delete Song</div>
<div id="confirm-delete-song__cancel" class="ui-text" @click="displayConfirmDeleteSong = false">Cancel</div>
<div id="confirm-delete-song__spacer"></div>
<div id="confirm-delete-song__confirm" class="ui-text" @click="deleteCurrentSong">Confirm</div>
</div>
</div>
<div id="song-area">
<div id="song">
<div v-for="line in song.lines" class="song-line">
<div v-for="spanOfTime in line.spansOfTime"
class="ui-text span-of-time span-of-time--4"
:id="spanOfTime.id"
@click="currentlySelectedSpanOfTime = spanOfTime.id; console.log(spanOfTime.id)"> </div>
</div>
</div>
</div>
<router-view></router-view>
</div>
</template>
<script>
import { mixin as clickaway } from 'vue-clickaway'
export default {
name: 'app',
mixins: [ clickaway ],
data: function () {
return {
songToolbarActive: false,
displaySongLoader: false,
song: [],
songs: [],
displayConfirmDeleteSong: false,
currentlySelectedSpanOfTime: -1
}
},
computed: {
modalActive: function () {
if (this.displaySongLoader) {
return true
}
}
},
methods: {
toggleSongToolbarDropdown: function () {
this.songToolbarActive = !this.songToolbarActive
},
disableSongToolbar: function () {
if (this.songToolbarActive) {
this.songToolbarActive = false
}
},
createNewSong: function () {
this.$http.post('/song/create').then(response => {
this.song = response.data.song
}, response => {
// error callback
})
},
displayListOfUsersSongs: function () {
this.$http.get('/songs').then(response => {
this.songs = response.data.songs
this.displaySongLoader = true
}, response => {
// error callback
})
},
loadSong: function (songId) {
this.displaySongLoader = false
this.$http.get('/song/' + songId).then(response => {
this.song = response.data.song
}, response => {
this.song = []
})
},
deleteCurrentSong: function () {
this.displayConfirmDeleteSong = false
this.$http.post('/song/' + this.song.id + '/delete').then(response => {
this.song = []
}, response => {
})
}
}
}
</script>
<style>
html, body {
height: 100%;
}
div#modals {
position: absolute;
text-align: center;
width: 100%;
height: 100%;
display: flex;
}
div.modals-active {
z-index: 1;
}
div#song-loader {
width: 300px;
display: flex;
flex-direction: column;
justify-content: center;
margin: auto;
border: 1px solid rgb(85, 85, 85);
}
div#song-loader-header {
padding: 5px 0px;
background-color:rgb(60,63,65);
}
div.song-loader__item {
background-color: beige;
padding: 10px;
color: rgb(60, 60, 60);
}
div.song-loader__selectable-item:hover {
background-color: rgb(230, 230, 190);
}
div#song-loader__cancel {
padding: 5px 0px;
}
div#song-area {
height: 100%;
}
div#song {
width: 608px;
height: 30px;
margin: auto;
display: block;
position: absolute;
top: 0;
left: 0;
bottom: 0;
right: 0;
}
div.span-of-time {
background-color: white;
min-height: 29px;
display: inline-block;
border-right: 1px solid rgb(200,200,200);
}
div.span-of-time--4 {
width: 37px;
}
div#app {
height: 100%
}
div#toolbar {
border-bottom: 1px solid #282828;
box-shadow: 0px 1px 1px rgb(85,85,85);
}
div#toolbar__left {
display: inline-block;
}
div#toolbar__right {
display: inline-block;
float: right;
}
.ui-text {
color: #BBBBBB;
font-size: 13px;
text-decoration: none;
font-family: 'Martel Sans', sans-serif;
cursor: default;
/* noselect */
-webkit-touch-callout: none; /* iOS Safari */
-webkit-user-select: none; /* Safari */
-khtml-user-select: none; /* Konqueror HTML */
-moz-user-select: none; /* Firefox */
-ms-user-select: none; /* Internet Explorer/Edge */
user-select: none; /* Non-prefixed version, currently
supported by Chrome and Opera */
}
div#toolbar-dropdowns {
position: absolute;
z-index: 2;
}
.toolbar__option {
display: inline-block;
height: 25px;
padding: 2px 5px 0px 5px;
}
.toolbar__option:hover {
color: rgb(225,225,225);
cursor: pointer;
}
div.toolbar__option_active {
background-color: rgb(75,110,175);
}
div#toolbar-dropdowns__song {
margin-left: 86px;
width: 190px;
border: 1px solid dimgray;
}
div.toolbar-dropdown-not-last-option {
border-bottom: 1px solid dimgray;
}
div.toolbar-dropdown-option {
cursor: default;
width: 181px;
display: inline-block;
font-size: 12px;
}
div.toolbar-dropdown-option:hover {
background-color: rgb(75,110,175);
}
.noselect {
-webkit-touch-callout: none; /* iOS Safari */
-webkit-user-select: none; /* Safari */
-khtml-user-select: none; /* Konqueror HTML */
-moz-user-select: none; /* Firefox */
-ms-user-select: none; /* Internet Explorer/Edge */
user-select: none; /* Non-prefixed version, currently
supported by Chrome and Opera */
}
</style>
Update: I found the error by cutting out code until npm run build
worked again. The problem was that I had created a conditional class with a dash in its name, but hadn't surrounded the class name with single-quotes. So instead of :class="{ 'modals-active': modalActive }"
I had :class="{ modals-active: modalActive }"
.
However, I didn't ask this question to solve this particular problem, but to know if there's any way to use these error messages to get to the problem quicker. The actual problem was on line 29 of the .vue
template file, but the error message seems to indicate that the error is on line 72.
I found the error by cutting out code until npm run build
worked again. The problem was that I had created a conditional class with a dash in its name, but hadn't surrounded the class name with single-quotes. So instead of :class="{ 'modals-active': modalActive }"
I had :class="{ modals-active: modalActive }"
.