@casl/vue - Vue 3 support
rak-phillip opened this issue Β· 22 comments
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:
- update plugin to use an application instance (detailed under Global API in migration guide)
- update can component to use
createComponent
(detailed under Vue 3 TypeScript support) - update lifecycle hooks, namely update
beforeCreate
tosetup
(detailed under lifecycle hooks) - any other unforseen issues that might arise while debugging the plugin
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.
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
Sounds like a plan! I should be able to get started next week.
Cool @rak-phillip looking forward to your PR!
@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.
Check how they did this for vue-router - https://github.com/vuejs/vue-router-next/blob/master/src/globalExtensions.ts
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.
Waiting for it :) If you have any other blockers let me know and I'll try to help
Hi @rak-phillip
Do you have any news or blockers on your side?
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
}
}
@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.
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:
-
Provide ability instance through vue inject/provide hooks, instead of passing it to the plugin and doing magic in mixin
-
create a vue hook (e.g.,
useAbility
), just proxy forinject(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 } }
-
Do not expose
$can
function globally and let's force users to use either hook orinject
api in their components. Maybe allow users to injectcan
directly, as a method -
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
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.
Pushed more changes to the forked project. Only a few type errors to work through at this point.
@stalniy I'm able to generate a working demo after upgrading CASL Vue todo to Vue 3. Moving onto unit tests.
@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.
@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.
@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
That does the trick, thanks for working through that with me!!!
Do we have any new updates for this issue? Looking forward for Vue 3 support and updated docs. π
I picked up the work in a separate branch and finalizing tests. So, very soon Iβll release a new version
π This issue has been resolved in version 2.0.0 π
The release is available on:
Your semantic-release bot π¦π
breaking changes can be found in related changelog