fix: evaluate __can__ before branch conditions

This commit is contained in:
stanig2106
2025-11-14 19:36:48 +01:00
parent 7d823307fa
commit 29d9c23800
6 changed files with 17 additions and 12 deletions

View File

@@ -5,6 +5,10 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
## [1.1.1] - 2025-11-15
### Changed
- Evaluate `__can__` guards before business expressions when merging into `v-if` / `v-else-if` branches to keep short-circuit order predictable.
## [1.1.0] - 2025-11-15
### Added
- Automatically mirror `v-can` guards across `v-else-if` / `v-else` branches and surface the inferred expression in documentation and fixtures.
@@ -35,6 +39,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- English README describing usage, playground, and contribution guide.
- Roadmap and release prep guidance.
[1.1.1]: https://github.com/eduvia-app/nuxt-can/releases/tag/v1.1.1
[1.1.0]: https://github.com/eduvia-app/nuxt-can/releases/tag/v1.1.0
[1.0.1]: https://github.com/eduvia-app/nuxt-can/releases/tag/v1.0.1
[1.0.0]: https://github.com/eduvia-app/nuxt-can/releases/tag/v1.0.0

View File

@@ -73,7 +73,7 @@ Now you can write directives that stay type-safe:
```vue
<button v-if="__can__('employee', 'view')">View profile</button>
<button v-if="(isReady) && __can__('employee', 'edit')">Edit profile</button>
<button v-if="__can__('employee', 'edit') && (isReady)">Edit profile</button>
<p v-if="!(__can__('employee', 'edit'))">Access denied</p>
```
@@ -101,10 +101,10 @@ Once the first branch of a conditional chain carries `v-can`, the transformer au
Transforms into:
```vue
<div v-if="(status === 'draft') && __can__('foo', 'bar')">
<div v-if="__can__('foo', 'bar') && (status === 'draft')">
Draft state
</div>
<div v-else-if="(status === 'pending') && __can__('foo', 'bar')">
<div v-else-if="__can__('foo', 'bar') && (status === 'pending')">
Pending state
</div>
<div v-else-if="__can__('foo', 'bar')">

View File

@@ -59,7 +59,7 @@ Ce document sert de guide de travail pour construire le plugin Nuxt et son trans
### 🚀 Phase 4 — Transformation des nœuds `v-can`
- Convertir lexpression en chemin `['segment1','segment2',…]`.
- Chercher un `v-if` existant :
- `v-if="expr"``v-if="(expr) && __can__(path)"`.
- `v-if="expr"``v-if="__can__(path) && (expr)"`.
- Sans `v-if` → ajouter `v-if="__can__(path)"`.
- Interdire `v-can` sur des éléments possédant déjà `v-else` / `v-else-if` (lever une erreur compilateur).
@@ -125,7 +125,7 @@ Avant :
Après compilation :
```vue
<div v-if="(isReady) && __can__('contract', 'edit')">
<div v-if="__can__('contract', 'edit') && (isReady)">
Modifier le contrat
</div>
```
@@ -141,7 +141,7 @@ Avant :
Après compilation :
```vue
<button v-if="(ctaVisible) && __can__('employee', 'view')">Voir</button>
<button v-if="__can__('employee', 'view') && (ctaVisible)">Voir</button>
<p v-if="!__can__('employee', 'view')">Acces refuse</p>
```

View File

@@ -1,6 +1,6 @@
{
"name": "@eduvia-app/nuxt-can",
"version": "1.1.0",
"version": "1.1.1",
"description": "Nuxt directives (`v-can`, `v-cannot`) to layer permissions without touching business v-ifs.",
"author": "Eduvia <engineering@eduvia.app>",
"homepage": "https://github.com/eduvia-app/nuxt-can#readme",

View File

@@ -317,7 +317,7 @@ function mergeGuardIntoConditional(params: {
ctx.patches.push({
start: ctx.templateStart + conditionDirective.loc.start.offset,
end: ctx.templateStart + conditionDirective.loc.end.offset,
text: `v-if="(${conditionExpression}) && ${canInvocation}"`,
text: `v-if="${canInvocation} && (${conditionExpression})"`,
})
return
@@ -333,7 +333,7 @@ function mergeGuardIntoConditional(params: {
ctx.patches.push({
start: ctx.templateStart + conditionDirective.loc.start.offset,
end: ctx.templateStart + conditionDirective.loc.end.offset,
text: `v-else-if="(${conditionExpression}) && ${canInvocation}"`,
text: `v-else-if="${canInvocation} && (${conditionExpression})"`,
})
return

View File

@@ -27,7 +27,7 @@ describe('transformCan', () => {
<div v-if="isReady" v-can="can.contract.create" />
`)
expect(code).toContain(`v-if="(isReady) && __can__('contract', 'create')"`)
expect(code).toContain(`v-if="__can__('contract', 'create') && (isReady)"`)
})
it('throws when v-cannot is used without a preceding v-can', () => {
@@ -56,8 +56,8 @@ describe('transformCan', () => {
<p v-cannot>Denied</p>
`)
expect(code).toContain(`v-if="(ready) && __can__('employee', 'view')"`)
expect(code).toContain(`v-else-if="(later) && __can__('employee', 'view')"`)
expect(code).toContain(`v-if="__can__('employee', 'view') && (ready)"`)
expect(code).toContain(`v-else-if="__can__('employee', 'view') && (later)"`)
expect(code).toContain(`v-else-if="__can__('employee', 'view')"`)
expect(code).toContain(`v-if="!(__can__('employee', 'view'))"`)
})