vitejs / vite

Next generation frontend tooling. It's fast!
http://vite.dev
MIT License
68.66k stars 6.2k forks source link

Support vue-class-component #1476

Closed pavellzubkov closed 3 years ago

pavellzubkov commented 3 years ago

Is your feature request related to a problem? Please describe. vite can't build component when using class component. error - expected class, but get const ...

Djaler commented 3 years ago

I think this is a problem with https://github.com/underfin/vite-plugin-vue2

pavellzubkov commented 3 years ago

i've been trying with vue3 and oficial vite vue plugin.

Djaler commented 3 years ago

Ok, I just thought the problem is inside vue2 plugin because I saw it in stacktrace

pavellzubkov commented 3 years ago

May be class api and vue-class-component have type issues and not planned to be supported in vite...

C0Nd3Mnd commented 3 years ago

Full error (I truncated the paths):

[vite] Internal server error: Transform failed with 1 error:
/truncated_path/src/App.vue:10:0: error: Expected "class" but found "const"
  Plugin: vite:vue
  File: /truncated_path/src/App.vue

  Expected "class" but found "const"
  7  |      HelloWorld
  8  |    }
  9  |  })
     |     ^
  10 |  const _sfc_main = class App extends Vue {}
     |  ^
  11 |

      at failureErrorWithLog (C:\truncated_path\node_modules\esbuild\lib\main.js:969:15)
      at C:\truncated_path\node_modules\esbuild\lib\main.js:852:33
      at handleIncomingPacket (C:\truncated_path\node_modules\esbuild\lib\main.js:566:9)
      at Socket.readFromStdout (C:\truncated_path\node_modules\esbuild\lib\main.js:482:7)
      at Socket.emit (events.js:315:20)
      at addChunk (internal/streams/readable.js:309:12)
      at readableAddChunk (internal/streams/readable.js:284:9)
      at Socket.Readable.push (internal/streams/readable.js:223:10)
      at Pipe.onStreamRead (internal/stream_base_commons.js:188:23)

package.json:

{
  "name": "project_name",
  "version": "0.0.0",
  "scripts": {
    "dev": "vite",
    "build": "vuedx-typecheck . && vite build"
  },
  "dependencies": {
    "vue": "^3.0.5",
    "vue-class-component": "^8.0.0-rc.1"
  },
  "devDependencies": {
    "@vitejs/plugin-vue": "^1.0.4",
    "@vue/compiler-sfc": "^3.0.5",
    "@vuedx/typecheck": "^0.4.1",
    "@vuedx/typescript-plugin-vue": "^0.4.1",
    "autoprefixer": "^10.2.1",
    "postcss": "^8.2.4",
    "tailwindcss": "^2.0.2",
    "typescript": "^4.1.3",
    "vite": "^2.0.0-beta.12"
  }
}

App.vue:

<template>
  <img alt="Vue logo" src="./assets/logo.png" />
  <HelloWorld msg="Hello Vue 3 + Vite" />
</template>

<script lang="ts">
import { Vue, Options } from 'vue-class-component'
import HelloWorld from './components/HelloWorld.vue'

@Options({
  components: {
    HelloWorld
  }
})
export default class App extends Vue {}
</script>

It works if you remove the @Options decorator. That's not a solution though.

alykamb commented 3 years ago

The issue is that vite changes the "export default class" to const _sfc_main, and its not possible to decorate a variable. Thus a workaround is to create the class and export it in another line of code:

@Options({
    components: {HelloWorld}
})
class App extends Vue {}

export default App
n1kk commented 3 years ago

Had same issue, tried re-exporting class and now I have this error:

> vite

Optimizable dependencies detected:
vue, vue-class-component, vue-property-decorator
Pre-bundling them to speed up dev server page load...
(this will be run only when your dependencies or config have changed)
 > node_modules/vue-class-component/dist/vue-class-component.esm.js: error: No matching export for import
"default"
    6 │ import Vue from 'vue';
      ╵        ~~~

 > node_modules/vue-property-decorator/lib/index.js: error: No matching export for import "default"
    3 │ import Vue from 'vue';
      ╵        ~~~
yyx990803 commented 3 years ago

@n1kk Vue 3 doesn't have a default export. Looks like you are using vue-class-component v7 which is for Vue 2.

n1kk commented 3 years ago

Thanks @yyx990803, updating to beta v8 of vue-class-component helped with first error, the issue vue-property-decorator is still there. They don't have a vue3 version ready yet, so I guess it's not possible to use property decorators for now.

C0Nd3Mnd commented 3 years ago

They don't have a vue3 version ready yet, so I guess it's not possible to use property decorators for now.

I thought most (if not all) of the functionality of vue-property-decorator is available in vue-class-component now, making it no longer necessary.

n1kk commented 3 years ago

I thought most (if not all) of the functionality of vue-property-decorator is available in vue-class-component now, making it no longer necessary.

Is it? They don't have the docs for the new version yet. At least I couldn't find it.

C0Nd3Mnd commented 3 years ago

@n1kk This is part of the vue-class-component.d.ts file (8.0.0-rc1):

// ...
export declare function prop<T>(options: PropOptionsWithDefault<T>): WithDefault<T>;

export declare function prop<T>(options: PropOptionsWithRequired<T>): T;

export declare function prop<T>(options: Prop<T>): T | undefined;

export declare interface PropOptions<T = any, D = T> {
    type?: PropType<T> | true | null;
    required?: boolean;
    default?: D | DefaultFactory<D> | null | undefined | object;
    validator?(value: unknown): boolean;
}

export declare interface PropOptionsWithDefault<T, D = T> extends PropOptions<T, D> {
    default: PropOptions<T, D>['default'];
}

export declare interface PropOptionsWithRequired<T, D = T> extends PropOptions<T, D> {
    required: true;
}

export declare type PublicProps = VNodeProps & AllowedComponentProps & ComponentCustomProps;
// ...

It doesn't contain any comments and I haven't played around with it a lot yet, but I'd imagine you'd use it somewhat like this:

import { Vue, prop } from 'vue-class-component'

class MyComponent extends Vue {
  @prop({
    type: String,
    required: false,
    default: ''
  })
  private example!: string
}

However, when using it with vite, it compiles fine, but I get this error in Chromium:

Uncaught TypeError: decorator is not a function
    at __decorate (MyComponent.vue:7)
    at MyComponent.vue:23

Not sure if I'm using it completely wrong or if vite just doesn't support it properly yet.

alykamb commented 3 years ago

@n1kk This is part of the vue-class-component.d.ts file (8.0.0-rc1):

// ...
export declare function prop<T>(options: PropOptionsWithDefault<T>): WithDefault<T>;

export declare function prop<T>(options: PropOptionsWithRequired<T>): T;

export declare function prop<T>(options: Prop<T>): T | undefined;

export declare interface PropOptions<T = any, D = T> {
    type?: PropType<T> | true | null;
    required?: boolean;
    default?: D | DefaultFactory<D> | null | undefined | object;
    validator?(value: unknown): boolean;
}

export declare interface PropOptionsWithDefault<T, D = T> extends PropOptions<T, D> {
    default: PropOptions<T, D>['default'];
}

export declare interface PropOptionsWithRequired<T, D = T> extends PropOptions<T, D> {
    required: true;
}

export declare type PublicProps = VNodeProps & AllowedComponentProps & ComponentCustomProps;
// ...

It doesn't contain any comments and I haven't played around with it a lot yet, but I'd imagine you'd use it somewhat like this:

import { Vue, prop } from 'vue-class-component'

class MyComponent extends Vue {
  @prop({
    type: String,
    required: false,
    default: ''
  })
  private example!: string
}

However, when using it with vite, it compiles fine, but I get this error in Chromium:

Uncaught TypeError: decorator is not a function
    at __decorate (MyComponent.vue:7)
    at MyComponent.vue:23

Not sure if I'm using it completely wrong or if vite just doesn't support it properly yet.

I could be wrong, but I don't think those prop types are for decorators. I managed to get it working with https://github.com/calebeaires/vue-decorator

C0Nd3Mnd commented 3 years ago

I was talking nonsense then, I was just assuming that's how it might work.

Thanks for the heads-up.

yyx990803 commented 3 years ago

As of @vitejs/plugin-vue@1.1.3 the following should work as expected:

<template>
  <div>{{ count }}</div>
  <button @click="increment">+1</button>
</template>

<script lang="ts">
import { Vue, Options } from 'vue-class-component'

// Component definition
@Options({
  // Define component options
  watch: {
    count: value => {
      console.log(value)
    }
  }
})
export default class Counter extends Vue {
  // The behavior in class is the same as the current
  count = 0

  increment() {
    this.count++
  }
}
</script>
biggestcookie commented 3 years ago

As of @vitejs/plugin-vue@1.1.3 the following should work as expected:

Using the example, I seem to be running into an error if I don't include @Options.

<template>
  <div>{{ count }}</div>
  <button @click="increment">+1</button>
</template>

<script lang="ts">
import { Vue } from "vue-class-component";

export default class App extends Vue {
  count = 0;

  increment() {
    this.count++;
  }
}
</script>
[plugin:vite:vue] Transform failed with 1 error:
/src/App.vue:2:41: error: Expected ";" but found "class"
/src/App.vue:2:41
Expected ";" but found "class"
1  |  
2  |  import { Vue } from "vue-class-component"class App extends Vue {
   |                                           ^
3  |    count = 0;

package.json:

"dependencies": {
    "vue": "^3.0.5",
    "vue-class-component": "^8.0.0-rc.1",
    "vue-router": "^4.0.4"
},
"devDependencies": {
    "@types/node": "^14.14.32",
    "@typescript-eslint/eslint-plugin": "^4.17.0",
    "@typescript-eslint/parser": "^4.17.0",
    "@vitejs/plugin-vue": "^1.1.5",
    "@vue/compiler-sfc": "^3.0.7",
    "@vue/eslint-config-prettier": "^6.0.0",
    "@vue/eslint-config-typescript": "^7.0.0",
    "@vuedx/typecheck": "^0.6.0",
    "@vuedx/typescript-plugin-vue": "^0.6.0",
    "eslint": "^7.21.0",
    "eslint-plugin-prettier": "^3.3.1",
    "eslint-plugin-vue": "^7.6.0",
    "prettier": "^2.2.1",
    "sass": "^1.32.8",
    "typescript": "^4.2.3",
    "vite": "^2.0.5"
}
alykamb commented 3 years ago

It also happens if extending mixins instead of Vue directly:

import { mixins } from 'vue-class-component'

import { Input } from './input'

export default class FormDatepicker extends mixins(Input) {}

The workaround still works, move the export default to another line of code:

import { mixins } from 'vue-class-component'

import { Input } from './input'

class FormDatepicker extends mixins(Input) {}
export default FormDatepicker

The issue was not happening in version 2.0.0-beta.69, but happens in 2.0.5

charles-allen commented 3 years ago

Likewise it seems Vite clashes with vue-class-component's MyClass extends Vue.with(Props) syntax.

Perhaps a different issue because it doesn't work on any of 2.0.0-beta.69, 2.0.5, 2.3.6, and sadly splitting export default doesn't seem to fix it:

Works (all versions):

@Options({ props: { slug: String } })
class Event extends Vue {
  slug!: string
  ...
}
export default Event

Doesn't work (all versions) -- does work with Webpack

class Event extends Vue.with(class {
  slug!: string
}) {
  ...
}
export default Event
github-actions[bot] commented 3 years ago

This issue has been locked since it has been closed for more than 14 days.

If you have found a concrete bug or regression related to it, please open a new bug report with a reproduction against the latest Vite version. If you have any other comments you should join the chat at Vite Land or create a new discussion.