Subscribe on changes!

Using custom script languages (such as coffeescript) no longer works since vue 3.3 (works in 3.2)

avatar
May 18th 2023

Vue version

3.3.4

Link to minimal reproduction

https://github.com/Boux/vite-coffee-bug

Steps to reproduce

clone my example repo by running

git clone https://github.com/Boux/vite-coffee-bug.git
cd vite-coffee-bug
npm install
npm run build

This is a simple vue project that uses vite to compile everything, it uses 2 plugins in the vite.config.js:

  • @vitejs/plugin-vue
  • a custom plugin that compiles coffeescript in .coffee files and in <script lang="coffee"> in .vue files
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import CoffeeScript from "coffeescript"

// https://vitejs.dev/config/
export default defineConfig({
  plugins: [
    vue(),
    {
      name: 'coffee_compile',
      transform: function(src, id) {

        // compile coffee files to js
        if (/\.coffee$/.test(id)) {
          var {js, sourceMap} = CoffeeScript.compile(src, { sourceMap: true })
          return { code: js, map: sourceMap }
        }

      }
    }
  ],
})

What is expected?

The sfc compiler called from the @vitejs/plugin-vue plugin should output something like this:

import _sfc_main from "/Users/boux/repo/vite_coffee_bug/src/App.vue?vue&type=script&lang.coffee"
export * from "/Users/boux/repo/vite_coffee_bug/src/App.vue?vue&type=script&lang.coffee"
import { toDisplayString as _toDisplayString, createElementVNode as _createElementVNode, openBlock as _openBlock, createElementBlock as _createElementBlock } from "vue"

function _sfc_render(_ctx, _cache) {
  return (_openBlock(), _createElementBlock("div", null, [
    _createElementVNode("button", {
      type: "button",
      onClick: _cache[0] || (_cache[0] = $event => (_ctx.count++))
    }, "count is " + _toDisplayString(_ctx.count), 1)
  ]))
}


import _export_sfc from 'plugin-vue:export-helper'
export default /*#__PURE__*/_export_sfc(_sfc_main, [['render',_sfc_render]])

which I can then use in the coffee_compile plugin to compile the contents of import _sfc_main from "/Users/boux/repo/vite_coffee_bug/src/App.vue?vue&type=script&lang.coffee" from coffeescript to javascript

What is actually happening?

The src compiler crashes with syntax errors as if it's trying to parse the code as typescript

# npm run build

> vite-coffee-bug@0.0.0 build
> vite build

vite v4.3.8 building for production...
✓ 3 modules transformed.
✓ built in 79ms
[vite:vue] [vue/compiler-sfc] Missing semicolon. (5:6)

/Users/boux/repo/vite-coffee-bug/src/App.vue
3  |  
4  |  export default
5  |    data: -> { count: ref(0) }
   |        ^
6  |  </script>
7  |  
file: /Users/boux/repo/vite-coffee-bug/src/App.vue:5:6
error during build:
SyntaxError: [vue/compiler-sfc] Missing semicolon. (5:6)

/Users/boux/repo/vite-coffee-bug/src/App.vue
3  |  
4  |  export default
5  |    data: -> { count: ref(0) }
   |        ^
6  |  </script>
7  |  
    at instantiate (/Users/boux/repo/vite-coffee-bug/node_modules/@babel/parser/lib/index.js:653:32)
    at constructor (/Users/boux/repo/vite-coffee-bug/node_modules/@babel/parser/lib/index.js:946:12)
    at Parser.raise (/Users/boux/repo/vite-coffee-bug/node_modules/@babel/parser/lib/index.js:3270:19)
    at Parser.semicolon (/Users/boux/repo/vite-coffee-bug/node_modules/@babel/parser/lib/index.js:3637:10)
    at Parser.parseExportDefaultExpression (/Users/boux/repo/vite-coffee-bug/node_modules/@babel/parser/lib/index.js:13759:10)
    at Parser.parseExport (/Users/boux/repo/vite-coffee-bug/node_modules/@babel/parser/lib/index.js:13663:25)
    at Parser.parseStatementContent (/Users/boux/repo/vite-coffee-bug/node_modules/@babel/parser/lib/index.js:12661:27)
    at Parser.parseStatementLike (/Users/boux/repo/vite-coffee-bug/node_modules/@babel/parser/lib/index.js:12549:17)
    at Parser.parseModuleItem (/Users/boux/repo/vite-coffee-bug/node_modules/@babel/parser/lib/index.js:12526:17)
    at Parser.parseBlockOrModuleBlockBody (/Users/boux/repo/vite-coffee-bug/node_modules/@babel/parser/lib/index.js:13121:36)
    at Parser.parseBlockBody (/Users/boux/repo/vite-coffee-bug/node_modules/@babel/parser/lib/index.js:13114:10)
    at Parser.parseProgram (/Users/boux/repo/vite-coffee-bug/node_modules/@babel/parser/lib/index.js:12437:10)
    at Parser.parseTopLevel (/Users/boux/repo/vite-coffee-bug/node_modules/@babel/parser/lib/index.js:12427:25)
    at Parser.parse (/Users/boux/repo/vite-coffee-bug/node_modules/@babel/parser/lib/index.js:14245:10)
    at Object.parse (/Users/boux/repo/vite-coffee-bug/node_modules/@babel/parser/lib/index.js:14286:38)
    at parse (/Users/boux/repo/vite-coffee-bug/node_modules/@vue/compiler-sfc/dist/compiler-sfc.cjs.js:15812:25)
    at new ScriptCompileContext (/Users/boux/repo/vite-coffee-bug/node_modules/@vue/compiler-sfc/dist/compiler-sfc.cjs.js:15828:43)
    at Object.compileScript (/Users/boux/repo/vite-coffee-bug/node_modules/@vue/compiler-sfc/dist/compiler-sfc.cjs.js:19817:15)
    at resolveScript (file:///Users/boux/repo/vite-coffee-bug/node_modules/@vitejs/plugin-vue/dist/index.mjs:283:31)
    at genScriptCode (file:///Users/boux/repo/vite-coffee-bug/node_modules/@vitejs/plugin-vue/dist/index.mjs:2469:18)
    at transformMain (file:///Users/boux/repo/vite-coffee-bug/node_modules/@vitejs/plugin-vue/dist/index.mjs:2282:54)
    at Object.transform (file:///Users/boux/repo/vite-coffee-bug/node_modules/@vitejs/plugin-vue/dist/index.mjs:2794:16)
    at file:///Users/boux/repo/vite-coffee-bug/node_modules/rollup/dist/es/shared/node-entry.js:24592:40

System Info

Tested on both `Mac OS 13.3.1` and up-to-date `Arch Linux`

Both on `node v16.16.0 (npm v8.11.0)` and `node v20.2.0 (npm v9.6.6)`


  System:
    OS: macOS 13.3.1
    CPU: (8) arm64 Apple M1
    Memory: 62.44 MB / 8.00 GB
    Shell: 5.9 - /bin/zsh
  Binaries:
    Node: 16.16.0 - ~/.nvm/versions/node/v16.16.0/bin/node
    Yarn: 1.22.19 - ~/.nvm/versions/node/v16.16.0/bin/yarn
    npm: 8.11.0 - ~/.nvm/versions/node/v16.16.0/bin/npm
  Browsers:
    Brave Browser: 113.1.51.110
    Chrome: 113.0.5672.92
    Firefox: 112.0.2
    Safari: 16.4
  npmPackages:
    vue: 3.3 => 3.3.4 

Any additional comments?

I am not sure if something has to be changed in @vitejs/plugin-vue when it calls the compiler, or if the compiler itself has a bug, or if this intended. It doesn't seem intended after reading the vue 3.3 blog post: https://blog.vuejs.org/posts/vue-3-3#script-setup-typescript-dx-improvements. They talk about fixes in their compiler for typescript when the code is in a <script setup>, but I'm not using a <script setup> and much less typescript. Nothing about restricting custom script languages

avatar
May 19th 2023

This doesn't seem to be related to anything in Vue core - the error comes from the Vite scanning phase, so it's more of a Vite issue.

However, I'm not able to get your repro to work in any recent versions of Vite, so I'm not even sure if this is a regression. What is the version of Vue / Vite / @vitejs/plugin-vue in your previous working setup?

avatar
May 19th 2023

I understand, I will try posting this in the @vitejs/plugin-vue issues page EDIT: I posted it here: https://github.com/vitejs/vite-plugin-vue/issues/178

Here's the version numbers just in case @vitejs/plugin-vue: 4.2.3 (works with vue 3.2, but not 3.3) vite: 4.3.8 (works with vue 3.2, but not 3.3)

vue: 3.3.4 (doesnt work) vue: 3.2.47 (works)

if you downgrade the vue version to 3.2, are you still not able to get it to work?

npm install vue@3.2
npm run build

This is my build output after running the above commands in my example project

> vite-coffee-bug@0.0.0 build
> vite build

vite v4.3.8 building for production...
✓ 12 modules transformed.
dist/index.html                  0.45 kB │ gzip:  0.30 kB
dist/assets/index-78058d01.css   1.03 kB │ gzip:  0.56 kB
dist/assets/index-0fe7b7d9.js   53.24 kB │ gzip: 21.45 kB
✓ built in 492ms
avatar
May 19th 2023

@yyx990803 I did a little digging, comparing both vue 3.2 and 3.3's compileScript function. Both of them tries to parse the script's content with babel's parser, with a try/catch around it, but in 3.2, the catch simply fails silently and returns the script, and in 3.3 it throws the error

version 3.2 https://github.com/vuejs/core/blob/3.2/packages/compiler-sfc/src/compileScript.ts line: 202

    } catch (e) {
      // silently fallback if parse fails since user may be using custom
      // babel syntax
      return script
    }

version 3.3 https://github.com/vuejs/core/blob/main/packages/compiler-sfc/src/script/context.ts in ScriptCompileContext's constructor, line: 103

    function parse(input: string, offset: number): Program {
      try {
        return babelParse(input, {
          plugins,
          sourceType: 'module'
        }).program
      } catch (e: any) {
        e.message = `[vue/compiler-sfc] ${e.message}\n\n${
          descriptor.filename
        }\n${generateCodeFrame(
          descriptor.source,
          e.pos + offset,
          e.pos + offset + 1
        )}`
        throw e
      }
    }

    this.scriptAst =
      descriptor.script &&
      parse(descriptor.script.content, descriptor.script.loc.start.offset)

I've never used babel before, I don't really know what it does, but if I comment out the line:

    this.scriptAst =
      descriptor.script &&
      parse(descriptor.script.content, descriptor.script.loc.start.offset)

in the node_modules/@vue/compiler-sfc/dist/compiler-sfc.cjs.js and I call npm run build again, everything works in vue 3.3

Is there a way to make it fail silently like it did in 3.2?

avatar
Aug 18th 2023

I can confirm that this issue is affecting my webpack setup as well. vue on version 3.3.4 fails as though pre-processing is never run, whereas vue v3.2.47 works perfectly.

Here are the errors I receive when trying to build with v3.3:

ERROR in ./views/home.vue?vue&type=script&lang=coffee (./node_modules/coffee-loader/dist/cjs.js!./node_modules/vue-loader/dist/index.js??ruleSet[1].rules[9].use[0]!./views/home.vue?vue&type=script&lang=coffee)
Module Error (from ./node_modules/vue-loader/dist/index.js):
[vue/compiler-sfc] Missing semicolon. (5:12)

/path/to/project/views/home.vue
47 |  
48 |  export default
49 |    components: {
   |              ^
50 |      MyComponent,
51 |    }
 @ ./views/home.vue?vue&type=script&lang=coffee 1:0-178 1:0-178 1:179-346 1:179-346
 @ ./views/home.vue 2:0-59 3:0-54 3:0-54 8:49-55

ERROR in ./views/home.vue?vue&type=script&lang=coffee (./node_modules/coffee-loader/dist/cjs.js!./node_modules/vue-loader/dist/index.js??ruleSet[1].rules[9].use[0]!./views/home.vue?vue&type=script&lang=coffee)
Module build failed (from ./node_modules/vue-loader/dist/index.js):
TypeError: Cannot read properties of null (reading 'content')
    at selectBlock (/path/to/project/node_modules/vue-loader/dist/select.js:23:45)
    at Object.loader (/path/to/project/node_modules/vue-loader/dist/index.js:92:41)
 @ ./views/home.vue?vue&type=script&lang=coffee 1:0-178 1:0-178 1:179-346 1:179-346
 @ ./views/home.vue 2:0-59 3:0-54 3:0-54 8:49-55

ERROR in ./views/home.vue?vue&type=template&id=36e0b024&scoped=true&lang=pug (./node_modules/vue-loader/dist/templateLoader.js??ruleSet[1].rules[1]!./node_modules/vue-pug-loader/index.js!./node_modules/vue-loader/dist/index.js??ruleSet[1].rules[9].use[0]!./views/home.vue?vue&type=template&id=36e0b024&scoped=true&lang=pug)
Module Error (from ./node_modules/vue-loader/dist/templateLoader.js):
[vue/compiler-sfc] Missing semicolon. (5:12)

/path/to/project/views/home.vue
47 |  
48 |  export default
49 |    components: {
   |              ^
50 |      MyComponent,
51 |    }
 @ ./views/home.vue?vue&type=template&id=36e0b024&scoped=true&lang=pug 1:0-259 1:0-259
 @ ./views/home.vue 1:0-86 8:68-74

2 errors have detailed information that is not shown.
Use 'stats.errorDetails: true' resp. '--stats-error-details' to show it.

webpack 5.88.2 compiled with 3 errors in 1023 ms
error Command failed with exit code 1.
avatar
Aug 29th 2023

I currently have a (bad) workaround for this. I simply split up my script from my vue file and just import it in the vue file, for example:

MyComponent.vue

<script>
import MyComponent from "./MyComponent.coffee"
export default MyComponent
</script>

<template>
<p @click='onClick'>{{thing}}</p>
</template>

MyComponent.coffee

export default
  data: -> thing: 123
  methods:
    onClick: -> console.log "hello"

I ported one of my small projects over to vue 3.3 with this method, but I just gave up on coffeescript for my new projects

avatar
Sep 28th 2023

I currently have a (bad) workaround for this. I simply split up my script from my vue file and just import it in the vue file, for example:

MyComponent.vue

<script>
import MyComponent from "./MyComponent.coffee"
export default MyComponent
</script>

<template>
<p @click='onClick'>{{thing}}</p>
</template>

MyComponent.coffee

export default
  data: -> thing: 123
  methods:
    onClick: -> console.log "hello"

I ported one of my small projects over to vue 3.3 with this method, but I just gave up on coffeescript for my new projects

Yeah, I'm spending almost few days to make it works unfortunately.. hope there will be a better workaround available..