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

@casl/vue - Vue 3 support #396

Closed
rak-phillip opened this issue Sep 23, 2020 · 22 comments
Closed

@casl/vue - Vue 3 support #396

rak-phillip opened this issue Sep 23, 2020 · 22 comments

Comments

@rak-phillip
Copy link

Is your feature request related to a problem? Please describe.
Our team is migrating our projects to Vue 3 and I would like to migrate @casl/vue to Vue 3 as well 😁.

Describe the solution you'd like
There aren't many changes that would need to be made, but they would break backwards compatibility:

I'm thinking that we might need to create a new branch to support Vue 3 and maintain compatibility with Vue 2 and am open to ideas about how the project would best support this change.

@stalniy
Copy link
Owner

stalniy commented Sep 24, 2020

Cool. From the history point of view, there were almost no changes in package as soon as it was finished.

thats why I think there is no need to support both versions simultaneously. Let’s just change the code and release a new major version. For Vue2, people will need to use previous version

@rak-phillip
Copy link
Author

Sounds like a plan! I should be able to get started next week.

@stalniy
Copy link
Owner

stalniy commented Sep 25, 2020

Cool @rak-phillip looking forward to your PR!

@rak-phillip
Copy link
Author

@stalniy I'm still trying to wrap my head around TypeScript and Vue 3 🤔🤔🤔

Seeing if I can't figure out how to update patch.d.ts and src/types.ts. After doing some digging around, it looks like ComponentPublicInstance might be way forward now that the Global Vue API is no more.

The good news is that I was able to work through the plugin fairly easily, but we won't know how successful it is until we can run tests.

@stalniy
Copy link
Owner

stalniy commented Oct 1, 2020

Check how they did this for vue-router - https://github.com/vuejs/vue-router-next/blob/master/src/globalExtensions.ts

@rak-phillip
Copy link
Author

That was a really good find. Thanks for pointing me in the right direction, I think I'm getting it 😁😁😁

This is taking a lot longer than expected 😤. I hope to have a PR in the coming week, barring any other unexpected developments.

@stalniy
Copy link
Owner

stalniy commented Oct 9, 2020

Waiting for it :) If you have any other blockers let me know and I'll try to help

@stalniy
Copy link
Owner

stalniy commented Oct 20, 2020

Hi @rak-phillip

Do you have any news or blockers on your side?

@codespikey
Copy link

Here is my rough implementation of casl for vue 3

waiting for official package release

[plugin.ts]

import {App, reactive } from 'vue'
import {Ability} from "@casl/ability"

const WATCHERS = new WeakMap()

function renderingDependencyFor(app: App, ability: Ability) {
    if (WATCHERS.has(ability)) {
        return WATCHERS.get(ability)
    }

    const data = { _touch: true }
    const watcher = reactive(data)

    ability.on('updated', () => {
        watcher._touch = !watcher._touch
    });
    WATCHERS.set(ability, watcher)

    return watcher
}

function abilityDescriptor(ability?: Ability) {
    if (ability) {
        return ability
    }
    return {
        get() {
            throw new Error('Please provide `Ability` instance either in `abilitiesPlugin` or in ComponentOptions')
        }
    }
}

function install(app: App, defaultAbility: Ability){

    app.config.globalProperties.$ability = abilityDescriptor(defaultAbility)

    app.mixin({
        beforeCreate() {

            const {ability} = this.$options
            const parent = this.$parent

            const localAbility = ability || (parent ? parent.$ability : null)
            if (localAbility) {
                Object.defineProperty(this, '$ability', { value: localAbility })
            }
        },
        methods: {
            $can(...args: any): boolean {
                const dep = renderingDependencyFor(app, this.$ability)
                dep._touch = dep._touch
                return this.$ability.can(...args)
            }
        }
    })
}


export default {
    install
}

[types.d.ts]

import {ComponentCustomProperties, ComponentCustomOptions} from '@vue/runtime-core'
import { AnyAbility } from '@casl/ability'

module '@vue/runtime-core' {

    export interface ComponentCustomProperties {
        $ability: AnyAbility
        $can(this: this, ...args: Parameters<this['$ability']['can']>): boolean
    }

    export interface ComponentCustomOptions {
        ability?: AnyAbility
    }
}

@rak-phillip
Copy link
Author

@stalniy time commitment is my biggest blocker at this point. I've pushed my changes to the vue-3 branch in my fork of the project if you would like to reference.

I last left off seeing if we could implement a setup function for can.ts because render functions no longer receive any arguments in Vue 3. One caveat to this approach is that setup accepts props, attrs, slots, & emit as arguments, so we don't have direct access to the parent component. I last left off looking at [getCurrentInstance](https://v3.vuejs.org/api/composition-api.html#getcurrentinstance) as a way to reference $can and $ability in the parent.

After the can component is finished, we need to update tests and documentation.

@stalniy
Copy link
Owner

stalniy commented Oct 21, 2020

I see, thanks for the update and reference to the branch. I'll take a look as soon as I finish with docs for v5.

What I have in mind for this integration is:

  1. Provide ability instance through vue inject/provide hooks, instead of passing it to the plugin and doing magic in mixin

  2. create a vue hook (e.g., useAbility), just proxy for inject(Ability) or something like this:

     // Can.js
     import { useAbility } from '@casl/vue';
     export default {
        setup() {
          const { can } = useAbility();
          return () => can(....) ? slots.default() : null  // don't know the API completely of Vue 3
        }
     }
  3. Do not expose $can function globally and let's force users to use either hook or inject api in their components. Maybe allow users to inject can directly, as a method

  4. For those who want to restore old functionality, can use globalProperties by yourself:

app.config.globalProperties.$can = ability.can.bind(ability);

what do you think?

PROS:

  • no global pollution
  • simpler typescript definitions
  • explicit dependency on can

Update: I also think whether it make sense to keep <Can> component as can function and hooks will be easier to use and is more expressive in my opinion

@rak-phillip
Copy link
Author

I think everything that you propose sounds great and more like idiomatic Vue 3, but I realize that I'm over complicating things after typing my original response 🦆.

Just because render functions no longer receive arguments doesn't mean that they don't have access to this. We should be able to implement the upgrade without changing too much. I'll see if I can another update out later today in an effort to keep moving things along.

@rak-phillip
Copy link
Author

Pushed more changes to the forked project. Only a few type errors to work through at this point.

@rak-phillip
Copy link
Author

@stalniy I'm able to generate a working demo after upgrading CASL Vue todo to Vue 3. Moving onto unit tests.

@stalniy
Copy link
Owner

stalniy commented Nov 11, 2020

@rak-phillip how do you run tests? What package manager do u use?

According to the deps babel-jest is in dev deps, so everything should be fine. Wh

P.s.:
According the guidelines, the correct way to setup repo is to use pnpm because it’s pnpm monorepo with a lock file in root of the repo.

@rak-phillip
Copy link
Author

rak-phillip commented Nov 17, 2020

@stalniy thanks for pointing me to the correct guidelines, I was originally using npm and switching to pnpm resolves the original Cannot find module 'babel-jest' issue that I was experiencing. This is what I find when I try to run tests for casl-vue

 FAIL  spec/can.spec.js
  ● Test suite failed to run

    Cannot find module '@casl/ability' from 'spec/can.spec.js'

      1 | import { createLocalVue, mount } from '@vue/test-utils'
    > 2 | import { defineAbility } from '@casl/ability'
        | ^
      3 | import { abilitiesPlugin } from '../src'
      4 | import Can from '../src/component/can'
      5 | 

      at Resolver.resolveModule (../../node_modules/.pnpm/jest-resolve@26.4.0/node_modules/jest-resolve/build/index.js:307:11)
      at Object.<anonymous> (spec/can.spec.js:2:1)

Which might make sense because the contribution guidelines mention that I should be able to run all test suites with pnpm test, but I get  ERROR  Missing script: test when I try to run from the casl dir. Maybe this is the source of my woes? I'm trying to run the tests against an unmodified master branch btw.

@stalniy
Copy link
Owner

stalniy commented Nov 17, 2020

@rak-phillip thanks for following. This showed the missed parts in guidelines!

So, you need to run pnpm run -r build, so locally linked packages can be interconnected. You don’t need to run all tests in the repo because you change only Vue related package. So, run pnpm test in casl-vue folder to run local tests or pnpm run -r test to run all tests in the repo

@rak-phillip
Copy link
Author

That does the trick, thanks for working through that with me!!!

@cerinoligutom
Copy link
Contributor

Do we have any new updates for this issue? Looking forward for Vue 3 support and updated docs. 🙂

@stalniy
Copy link
Owner

stalniy commented Jan 19, 2021

I picked up the work in a separate branch and finalizing tests. So, very soon I’ll release a new version

stalniy added a commit that referenced this issue Jan 20, 2021
stalniy added a commit that referenced this issue Jan 22, 2021
Fixes #396

BREAKING CHANGE: refactor to use Vue 3 what introduces a bunch of breaking changes:

  * `Ability` instance is not a required plugin parameter. Previously, we could decide whether to pass ability as plugin parameter or as root component option. Now, the only way is to pass it in plugin:

    **Before**

    ```js
    import { abilitiesPlugin } from '@casl/vue';
    import Vue from 'vue';
    import { ability } from './services/AppAbility';

    Vue.use(abilitiesPlugin);
    new Vue({
      ability
    }).$mount('#app')
    ```

    **After**

    ```js
    import { abilitiesPlugin } from '@casl/vue';
    import { createApp } from 'vue';
    import { ability } from './services/AppAbility';

    createApp()
     .use(abilitiesPlugin, ability)
     .mount('#app');
    ```

  * `abilitiesPlugin` no more define global `$ability` and `$can` properties, instead a recommended way to get `AppAbility` instance is by injecting it through [provide/inject API](https://v3.vuejs.org/guide/component-provide-inject.html). To get previous behavior, pass `useGlobalProperties: true` option:

    **Before**

    ```js
    import { abilitiesPlugin } from '@casl/vue';
    import Vue from 'vue';
    import { ability } from './services/AppAbility';

    Vue.use(abilitiesPlugin);
    const root = new Vue({
      ability
    }).$mount('#app')

    console.log(root.$ability)
    ```

    **After**

    Recommended way:

    ```js
    import { abilitiesPlugin, ABILITY_TOKEN } from '@casl/vue';
    import { createApp } from 'vue';
    import { ability } from './services/AppAbility';

    const App = {
      name: 'App',
      inject: {
        $ability: { from: ABILITY_TOKEN }
      }
    };

    const root = createApp(App)
      .use(abilitiesPlugin, ability, {
        useGlobalProperties: true
      })
      .mount('#app');

    console.log(root.$ability)
    ```

    Backward compatible way:

    ```js
    import { abilitiesPlugin } from '@casl/vue';
    import { createApp } from 'vue';
    import { ability } from './services/AppAbility';

    const root = createApp()
      .use(abilitiesPlugin, ability, {
        useGlobalProperties: true
      })
      .mount('#app');

    console.log(root.$ability)
    ```

  * `AllCanProps<TAbility>` type was renamed to `CanProps<TAbility>`

  * `@casl/vue` no more augment vue types, so if you decide to use global properties, you will need to augment types by yourself

     **Before**

     @casl/vue augments type of `$ability` to `AnyAbility` and `$can` to `typeof $ability['can']`

     **After**

     create a separate file `src/ability-shim.d.ts` with the next content:

     ```ts
     import { AppAbility } from './AppAbility'

     declare module 'vue' {
       interface ComponentCustomProperties {
         $ability: AppAbility;
         $can(this: this, ...args: Parameters<this['$ability']['can']>): boolean;
       }
     }
     ```
stalniy added a commit that referenced this issue Jan 22, 2021
Fixes #396

BREAKING CHANGE: refactor to use Vue 3 what introduces a bunch of breaking changes:

  * `Ability` instance is not a required plugin parameter. Previously, we could decide whether to pass ability as plugin parameter or as root component option. Now, the only way is to pass it in plugin:

    **Before**

    ```js
    import { abilitiesPlugin } from '@casl/vue';
    import Vue from 'vue';
    import { ability } from './services/AppAbility';

    Vue.use(abilitiesPlugin);
    new Vue({
      ability
    }).$mount('#app')
    ```

    **After**

    ```js
    import { abilitiesPlugin } from '@casl/vue';
    import { createApp } from 'vue';
    import { ability } from './services/AppAbility';

    createApp()
     .use(abilitiesPlugin, ability)
     .mount('#app');
    ```

  * `abilitiesPlugin` no more define global `$ability` and `$can` properties, instead a recommended way to get `AppAbility` instance is by injecting it through [provide/inject API](https://v3.vuejs.org/guide/component-provide-inject.html). To get previous behavior, pass `useGlobalProperties: true` option:

    **Before**

    ```js
    import { abilitiesPlugin } from '@casl/vue';
    import Vue from 'vue';
    import { ability } from './services/AppAbility';

    Vue.use(abilitiesPlugin);
    const root = new Vue({
      ability
    }).$mount('#app')

    console.log(root.$ability)
    ```

    **After**

    Recommended way:

    ```js
    import { abilitiesPlugin, ABILITY_TOKEN } from '@casl/vue';
    import { createApp } from 'vue';
    import { ability } from './services/AppAbility';

    const App = {
      name: 'App',
      inject: {
        $ability: { from: ABILITY_TOKEN }
      }
    };

    const root = createApp(App)
      .use(abilitiesPlugin, ability, {
        useGlobalProperties: true
      })
      .mount('#app');

    console.log(root.$ability)
    ```

    Backward compatible way:

    ```js
    import { abilitiesPlugin } from '@casl/vue';
    import { createApp } from 'vue';
    import { ability } from './services/AppAbility';

    const root = createApp()
      .use(abilitiesPlugin, ability, {
        useGlobalProperties: true
      })
      .mount('#app');

    console.log(root.$ability)
    ```

  * `AllCanProps<TAbility>` type was renamed to `CanProps<TAbility>`

  * `@casl/vue` no more augment vue types, so if you decide to use global properties, you will need to augment types by yourself

     **Before**

     @casl/vue augments type of `$ability` to `AnyAbility` and `$can` to `typeof $ability['can']`

     **After**

     create a separate file `src/ability-shim.d.ts` with the next content:

     ```ts
     import { AppAbility } from './AppAbility'

     declare module 'vue' {
       interface ComponentCustomProperties {
         $ability: AppAbility;
         $can(this: this, ...args: Parameters<this['$ability']['can']>): boolean;
       }
     }
     ```
stalniy added a commit that referenced this issue Jan 22, 2021
Fixes #396

BREAKING CHANGE: refactor to use Vue 3 what introduces a bunch of breaking changes:

  * `Ability` instance is not a required plugin parameter. Previously, we could decide whether to pass ability as plugin parameter or as root component option. Now, the only way is to pass it in plugin:

    **Before**

    ```js
    import { abilitiesPlugin } from '@casl/vue';
    import Vue from 'vue';
    import { ability } from './services/AppAbility';

    Vue.use(abilitiesPlugin);
    new Vue({
      ability
    }).$mount('#app')
    ```

    **After**

    ```js
    import { abilitiesPlugin } from '@casl/vue';
    import { createApp } from 'vue';
    import { ability } from './services/AppAbility';

    createApp()
     .use(abilitiesPlugin, ability)
     .mount('#app');
    ```

  * `abilitiesPlugin` no more define global `$ability` and `$can` properties, instead a recommended way to get `AppAbility` instance is by injecting it through [provide/inject API](https://v3.vuejs.org/guide/component-provide-inject.html). To get previous behavior, pass `useGlobalProperties: true` option:

    **Before**

    ```js
    import { abilitiesPlugin } from '@casl/vue';
    import Vue from 'vue';
    import { ability } from './services/AppAbility';

    Vue.use(abilitiesPlugin);
    const root = new Vue({
      ability
    }).$mount('#app')

    console.log(root.$ability)
    ```

    **After**

    Recommended way:

    ```js
    import { abilitiesPlugin, ABILITY_TOKEN } from '@casl/vue';
    import { createApp } from 'vue';
    import { ability } from './services/AppAbility';

    const App = {
      name: 'App',
      inject: {
        $ability: { from: ABILITY_TOKEN }
      }
    };

    const root = createApp(App)
      .use(abilitiesPlugin, ability, {
        useGlobalProperties: true
      })
      .mount('#app');

    console.log(root.$ability)
    ```

    Backward compatible way:

    ```js
    import { abilitiesPlugin } from '@casl/vue';
    import { createApp } from 'vue';
    import { ability } from './services/AppAbility';

    const root = createApp()
      .use(abilitiesPlugin, ability, {
        useGlobalProperties: true
      })
      .mount('#app');

    console.log(root.$ability)
    ```

  * `AllCanProps<TAbility>` type was renamed to `CanProps<TAbility>`

  * `@casl/vue` no more augment vue types, so if you decide to use global properties, you will need to augment types by yourself

     **Before**

     @casl/vue augments type of `$ability` to `AnyAbility` and `$can` to `typeof $ability['can']`

     **After**

     create a separate file `src/ability-shim.d.ts` with the next content:

     ```ts
     import { AppAbility } from './AppAbility'

     declare module 'vue' {
       interface ComponentCustomProperties {
         $ability: AppAbility;
         $can(this: this, ...args: Parameters<this['$ability']['can']>): boolean;
       }
     }
     ```
stalniy added a commit that referenced this issue Jan 22, 2021
Fixes #396

BREAKING CHANGE: refactor to use Vue 3 what introduces a bunch of breaking changes:

  * `Ability` instance is not a required plugin parameter. Previously, we could decide whether to pass ability as plugin parameter or as root component option. Now, the only way is to pass it in plugin:

    **Before**

    ```js
    import { abilitiesPlugin } from '@casl/vue';
    import Vue from 'vue';
    import { ability } from './services/AppAbility';

    Vue.use(abilitiesPlugin);
    new Vue({
      ability
    }).$mount('#app')
    ```

    **After**

    ```js
    import { abilitiesPlugin } from '@casl/vue';
    import { createApp } from 'vue';
    import { ability } from './services/AppAbility';

    createApp()
     .use(abilitiesPlugin, ability)
     .mount('#app');
    ```

  * `abilitiesPlugin` no more define global `$ability` and `$can` properties, instead a recommended way to get `AppAbility` instance is by injecting it through [provide/inject API](https://v3.vuejs.org/guide/component-provide-inject.html). To get previous behavior, pass `useGlobalProperties: true` option:

    **Before**

    ```js
    import { abilitiesPlugin } from '@casl/vue';
    import Vue from 'vue';
    import { ability } from './services/AppAbility';

    Vue.use(abilitiesPlugin);
    const root = new Vue({
      ability
    }).$mount('#app')

    console.log(root.$ability)
    ```

    **After**

    Recommended way:

    ```js
    import { abilitiesPlugin, ABILITY_TOKEN } from '@casl/vue';
    import { createApp } from 'vue';
    import { ability } from './services/AppAbility';

    const App = {
      name: 'App',
      inject: {
        $ability: { from: ABILITY_TOKEN }
      }
    };

    const root = createApp(App)
      .use(abilitiesPlugin, ability, {
        useGlobalProperties: true
      })
      .mount('#app');

    console.log(root.$ability)
    ```

    Backward compatible way:

    ```js
    import { abilitiesPlugin } from '@casl/vue';
    import { createApp } from 'vue';
    import { ability } from './services/AppAbility';

    const root = createApp()
      .use(abilitiesPlugin, ability, {
        useGlobalProperties: true
      })
      .mount('#app');

    console.log(root.$ability)
    ```

  * `AllCanProps<TAbility>` type was renamed to `CanProps<TAbility>`

  * `@casl/vue` no more augment vue types, so if you decide to use global properties, you will need to augment types by yourself

     **Before**

     @casl/vue augments type of `$ability` to `AnyAbility` and `$can` to `typeof $ability['can']`

     **After**

     create a separate file `src/ability-shim.d.ts` with the next content:

     ```ts
     import { AppAbility } from './AppAbility'

     declare module 'vue' {
       interface ComponentCustomProperties {
         $ability: AppAbility;
         $can(this: this, ...args: Parameters<this['$ability']['can']>): boolean;
       }
     }
     ```
stalniy added a commit that referenced this issue Jan 22, 2021
Fixes #396

BREAKING CHANGE: refactor to use Vue 3 what introduces a bunch of breaking changes:

  * `Ability` instance is not a required plugin parameter. Previously, we could decide whether to pass ability as plugin parameter or as root component option. Now, the only way is to pass it in plugin:

    **Before**

    ```js
    import { abilitiesPlugin } from '@casl/vue';
    import Vue from 'vue';
    import { ability } from './services/AppAbility';

    Vue.use(abilitiesPlugin);
    new Vue({
      ability
    }).$mount('#app')
    ```

    **After**

    ```js
    import { abilitiesPlugin } from '@casl/vue';
    import { createApp } from 'vue';
    import { ability } from './services/AppAbility';

    createApp()
     .use(abilitiesPlugin, ability)
     .mount('#app');
    ```

  * `abilitiesPlugin` no more define global `$ability` and `$can` properties, instead a recommended way to get `AppAbility` instance is by injecting it through [provide/inject API](https://v3.vuejs.org/guide/component-provide-inject.html). To get previous behavior, pass `useGlobalProperties: true` option:

    **Before**

    ```js
    import { abilitiesPlugin } from '@casl/vue';
    import Vue from 'vue';
    import { ability } from './services/AppAbility';

    Vue.use(abilitiesPlugin);
    const root = new Vue({
      ability
    }).$mount('#app')

    console.log(root.$ability)
    ```

    **After**

    Recommended way:

    ```js
    import { abilitiesPlugin, ABILITY_TOKEN } from '@casl/vue';
    import { createApp } from 'vue';
    import { ability } from './services/AppAbility';

    const App = {
      name: 'App',
      inject: {
        $ability: { from: ABILITY_TOKEN }
      }
    };

    const root = createApp(App)
      .use(abilitiesPlugin, ability, {
        useGlobalProperties: true
      })
      .mount('#app');

    console.log(root.$ability)
    ```

    Backward compatible way:

    ```js
    import { abilitiesPlugin } from '@casl/vue';
    import { createApp } from 'vue';
    import { ability } from './services/AppAbility';

    const root = createApp()
      .use(abilitiesPlugin, ability, {
        useGlobalProperties: true
      })
      .mount('#app');

    console.log(root.$ability)
    ```

  * `AllCanProps<TAbility>` type was renamed to `CanProps<TAbility>`

  * `@casl/vue` no more augment vue types, so if you decide to use global properties, you will need to augment types by yourself

     **Before**

     @casl/vue augments type of `$ability` to `AnyAbility` and `$can` to `typeof $ability['can']`

     **After**

     create a separate file `src/ability-shim.d.ts` with the next content:

     ```ts
     import { AppAbility } from './AppAbility'

     declare module 'vue' {
       interface ComponentCustomProperties {
         $ability: AppAbility;
         $can(this: this, ...args: Parameters<this['$ability']['can']>): boolean;
       }
     }
     ```
stalniy added a commit that referenced this issue Jan 22, 2021
Fixes #396

BREAKING CHANGE: refactor to use Vue 3 what introduces a bunch of breaking changes:

  * `Ability` instance is not a required plugin parameter. Previously, we could decide whether to pass ability as plugin parameter or as root component option. Now, the only way is to pass it in plugin:

    **Before**

    ```js
    import { abilitiesPlugin } from '@casl/vue';
    import Vue from 'vue';
    import { ability } from './services/AppAbility';

    Vue.use(abilitiesPlugin);
    new Vue({
      ability
    }).$mount('#app')
    ```

    **After**

    ```js
    import { abilitiesPlugin } from '@casl/vue';
    import { createApp } from 'vue';
    import { ability } from './services/AppAbility';

    createApp()
     .use(abilitiesPlugin, ability)
     .mount('#app');
    ```

  * `abilitiesPlugin` no more define global `$ability` and `$can` properties, instead a recommended way to get `AppAbility` instance is by injecting it through [provide/inject API](https://v3.vuejs.org/guide/component-provide-inject.html). To get previous behavior, pass `useGlobalProperties: true` option:

    **Before**

    ```js
    import { abilitiesPlugin } from '@casl/vue';
    import Vue from 'vue';
    import { ability } from './services/AppAbility';

    Vue.use(abilitiesPlugin);
    const root = new Vue({
      ability
    }).$mount('#app')

    console.log(root.$ability)
    ```

    **After**

    Recommended way:

    ```js
    import { abilitiesPlugin, ABILITY_TOKEN } from '@casl/vue';
    import { createApp } from 'vue';
    import { ability } from './services/AppAbility';

    const App = {
      name: 'App',
      inject: {
        $ability: { from: ABILITY_TOKEN }
      }
    };

    const root = createApp(App)
      .use(abilitiesPlugin, ability, {
        useGlobalProperties: true
      })
      .mount('#app');

    console.log(root.$ability)
    ```

    Backward compatible way:

    ```js
    import { abilitiesPlugin } from '@casl/vue';
    import { createApp } from 'vue';
    import { ability } from './services/AppAbility';

    const root = createApp()
      .use(abilitiesPlugin, ability, {
        useGlobalProperties: true
      })
      .mount('#app');

    console.log(root.$ability)
    ```

  * `AllCanProps<TAbility>` type was renamed to `CanProps<TAbility>`

  * `@casl/vue` no more augment vue types, so if you decide to use global properties, you will need to augment types by yourself

     **Before**

     @casl/vue augments type of `$ability` to `AnyAbility` and `$can` to `typeof $ability['can']`

     **After**

     create a separate file `src/ability-shim.d.ts` with the next content:

     ```ts
     import { AppAbility } from './AppAbility'

     declare module 'vue' {
       interface ComponentCustomProperties {
         $ability: AppAbility;
         $can(this: this, ...args: Parameters<this['$ability']['can']>): boolean;
       }
     }
     ```
stalniy added a commit that referenced this issue Jan 22, 2021
Fixes #396

BREAKING CHANGE: refactor to use Vue 3 what introduces a bunch of breaking changes:

  * `Ability` instance is not a required plugin parameter. Previously, we could decide whether to pass ability as plugin parameter or as root component option. Now, the only way is to pass it in plugin:

    **Before**

    ```js
    import { abilitiesPlugin } from '@casl/vue';
    import Vue from 'vue';
    import { ability } from './services/AppAbility';

    Vue.use(abilitiesPlugin);
    new Vue({
      ability
    }).$mount('#app')
    ```

    **After**

    ```js
    import { abilitiesPlugin } from '@casl/vue';
    import { createApp } from 'vue';
    import { ability } from './services/AppAbility';

    createApp()
     .use(abilitiesPlugin, ability)
     .mount('#app');
    ```

  * `abilitiesPlugin` no more define global `$ability` and `$can` properties, instead a recommended way to get `AppAbility` instance is by injecting it through [provide/inject API](https://v3.vuejs.org/guide/component-provide-inject.html). To get previous behavior, pass `useGlobalProperties: true` option:

    **Before**

    ```js
    import { abilitiesPlugin } from '@casl/vue';
    import Vue from 'vue';
    import { ability } from './services/AppAbility';

    Vue.use(abilitiesPlugin);
    const root = new Vue({
      ability
    }).$mount('#app')

    console.log(root.$ability)
    ```

    **After**

    Recommended way:

    ```js
    import { abilitiesPlugin, ABILITY_TOKEN } from '@casl/vue';
    import { createApp } from 'vue';
    import { ability } from './services/AppAbility';

    const App = {
      name: 'App',
      inject: {
        $ability: { from: ABILITY_TOKEN }
      }
    };

    const root = createApp(App)
      .use(abilitiesPlugin, ability, {
        useGlobalProperties: true
      })
      .mount('#app');

    console.log(root.$ability)
    ```

    Backward compatible way:

    ```js
    import { abilitiesPlugin } from '@casl/vue';
    import { createApp } from 'vue';
    import { ability } from './services/AppAbility';

    const root = createApp()
      .use(abilitiesPlugin, ability, {
        useGlobalProperties: true
      })
      .mount('#app');

    console.log(root.$ability)
    ```

  * `AllCanProps<TAbility>` type was renamed to `CanProps<TAbility>`

  * `@casl/vue` no more augment vue types, so if you decide to use global properties, you will need to augment types by yourself

     **Before**

     @casl/vue augments type of `$ability` to `AnyAbility` and `$can` to `typeof $ability['can']`

     **After**

     create a separate file `src/ability-shim.d.ts` with the next content:

     ```ts
     import { AppAbility } from './AppAbility'

     declare module 'vue' {
       interface ComponentCustomProperties {
         $ability: AppAbility;
         $can(this: this, ...args: Parameters<this['$ability']['can']>): boolean;
       }
     }
     ```
@stalniy
Copy link
Owner

stalniy commented Jan 22, 2021

🎉 This issue has been resolved in version 2.0.0 🎉

The release is available on:

Your semantic-release bot 📦🚀

@stalniy
Copy link
Owner

stalniy commented Jan 22, 2021

breaking changes can be found in related changelog

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

No branches or pull requests

4 participants