@blueking/bkui-knowledge 0.0.1-beta.7 → 0.0.1-beta.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +33 -1
- package/bin/bkui-knowledge.js +61 -2
- package/knowledge/manifest.json +10 -1
- package/knowledge/skills/bkui-quick-start/SKILL.md +21 -37
- package/knowledge/skills/bkui-quick-start/references/components-list.md +17 -0
- package/knowledge/skills/bkui-quick-start/references/skills-index.md +26 -0
- package/knowledge/skills/external/vue-skills/LICENSE +21 -0
- package/knowledge/skills/external/vue-skills/README.md +69 -0
- package/knowledge/skills/external/vue-skills/skills/vue-best-practices/SKILL.md +42 -0
- package/knowledge/skills/external/vue-skills/skills/vue-best-practices/rules/codeactions-save-performance.md +79 -0
- package/knowledge/skills/external/vue-skills/skills/vue-best-practices/rules/data-attributes-config.md +74 -0
- package/knowledge/skills/external/vue-skills/skills/vue-best-practices/rules/deep-watch-numeric.md +102 -0
- package/knowledge/skills/external/vue-skills/skills/vue-best-practices/rules/define-model-update-event.md +79 -0
- package/knowledge/skills/external/vue-skills/skills/vue-best-practices/rules/duplicate-plugin-detection.md +102 -0
- package/knowledge/skills/external/vue-skills/skills/vue-best-practices/rules/fallthrough-attributes.md +63 -0
- package/knowledge/skills/external/vue-skills/skills/vue-best-practices/rules/hmr-vue-ssr.md +124 -0
- package/knowledge/skills/external/vue-skills/skills/vue-best-practices/rules/module-resolution-bundler.md +81 -0
- package/knowledge/skills/external/vue-skills/skills/vue-best-practices/rules/pinia-store-mocking.md +159 -0
- package/knowledge/skills/external/vue-skills/skills/vue-best-practices/rules/script-setup-jsdoc.md +85 -0
- package/knowledge/skills/external/vue-skills/skills/vue-best-practices/rules/strict-css-modules.md +68 -0
- package/knowledge/skills/external/vue-skills/skills/vue-best-practices/rules/unplugin-auto-import-conflicts.md +97 -0
- package/knowledge/skills/external/vue-skills/skills/vue-best-practices/rules/volar-3-breaking-changes.md +66 -0
- package/knowledge/skills/external/vue-skills/skills/vue-best-practices/rules/vue-directive-comments.md +73 -0
- package/knowledge/skills/external/vue-skills/skills/vue-best-practices/rules/vue-router-typed-params.md +81 -0
- package/knowledge/skills/external/vue-skills/skills/vue-best-practices/rules/vue-tsc-strict-templates.md +69 -0
- package/knowledge/skills/external/vue-skills/skills/vue-best-practices/rules/with-defaults-union-types.md +102 -0
- package/package.json +1 -1
- package/server/mcp-core.js +38 -8
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: defineModel Fires Update Event with Undefined
|
|
3
|
+
impact: MEDIUM
|
|
4
|
+
impactDescription: fixes runtime errors from unexpected undefined in model updates
|
|
5
|
+
type: capability
|
|
6
|
+
tags: defineModel, v-model, update-event, undefined, vue-3.5
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
# defineModel Fires Update Event with Undefined
|
|
10
|
+
|
|
11
|
+
**Impact: MEDIUM** - fixes runtime errors from unexpected undefined in model updates
|
|
12
|
+
|
|
13
|
+
> **Version Note (2025):** This issue may be resolved in Vue 3.5+. Testing with Vue 3.5.26 could not reproduce the double emission with `undefined`. If you're on Vue 3.5+, verify the issue exists in your specific scenario before applying workarounds.
|
|
14
|
+
|
|
15
|
+
Components using `defineModel` may fire the `@update:model-value` event with `undefined` in certain edge cases. TypeScript types don't always reflect this behavior, potentially causing runtime errors when the parent expects a non-nullable value.
|
|
16
|
+
|
|
17
|
+
## Symptoms
|
|
18
|
+
|
|
19
|
+
- Parent component receives `undefined` unexpectedly
|
|
20
|
+
- Runtime error: "Cannot read property of undefined"
|
|
21
|
+
- Type mismatch between expected `T` and received `T | undefined`
|
|
22
|
+
- Issue appears when clearing/resetting the model value
|
|
23
|
+
|
|
24
|
+
## Root Cause
|
|
25
|
+
|
|
26
|
+
`defineModel` returns `Ref<T | undefined>` by default, even when `T` is non-nullable. The update event can fire with `undefined` when:
|
|
27
|
+
- Component unmounts
|
|
28
|
+
- Model is explicitly cleared
|
|
29
|
+
- Internal state resets
|
|
30
|
+
|
|
31
|
+
## Fix
|
|
32
|
+
|
|
33
|
+
**Option 1: Use required option (Vue 3.5+)**
|
|
34
|
+
```typescript
|
|
35
|
+
// Returns Ref<Item> instead of Ref<Item | undefined>
|
|
36
|
+
const model = defineModel<Item>({ required: true })
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
**Option 2: Type parent handler to accept undefined**
|
|
40
|
+
```vue
|
|
41
|
+
<template>
|
|
42
|
+
<MyComponent
|
|
43
|
+
v-model="item"
|
|
44
|
+
@update:model-value="handleUpdate"
|
|
45
|
+
/>
|
|
46
|
+
</template>
|
|
47
|
+
|
|
48
|
+
<script setup lang="ts">
|
|
49
|
+
// Handle both value and undefined
|
|
50
|
+
const handleUpdate = (value: Item | undefined) => {
|
|
51
|
+
if (value !== undefined) {
|
|
52
|
+
item.value = value
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
</script>
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
**Option 3: Use default value in defineModel**
|
|
59
|
+
```typescript
|
|
60
|
+
const model = defineModel<string>({ default: '' })
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
## Type Declaration Pattern
|
|
64
|
+
|
|
65
|
+
```typescript
|
|
66
|
+
// In child component
|
|
67
|
+
interface Props {
|
|
68
|
+
modelValue: Item
|
|
69
|
+
}
|
|
70
|
+
const model = defineModel<Item>({ required: true })
|
|
71
|
+
|
|
72
|
+
// Emits will be typed as (value: Item) not (value: Item | undefined)
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
## Reference
|
|
76
|
+
|
|
77
|
+
- [vuejs/core#12817](https://github.com/vuejs/core/issues/12817)
|
|
78
|
+
- [vuejs/core#10103](https://github.com/vuejs/core/issues/10103)
|
|
79
|
+
- [defineModel docs](https://vuejs.org/api/sfc-script-setup.html#definemodel)
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Duplicate Vue Plugin Detection
|
|
3
|
+
impact: MEDIUM
|
|
4
|
+
impactDescription: fixes cryptic build errors from Vue plugin registered twice
|
|
5
|
+
type: capability
|
|
6
|
+
tags: vite, plugin, vue, duplicate, config, inline
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
# Duplicate Vue Plugin Detection
|
|
10
|
+
|
|
11
|
+
**Impact: MEDIUM** - fixes cryptic build errors from Vue plugin registered twice
|
|
12
|
+
|
|
13
|
+
When using Vite's JavaScript API, if the Vue plugin is loaded in `vite.config.js` and specified again in `inlineConfig`, it gets registered twice, causing cryptic build errors.
|
|
14
|
+
|
|
15
|
+
## Symptoms
|
|
16
|
+
|
|
17
|
+
- Build produces unexpected output or fails silently
|
|
18
|
+
- "Cannot read property of undefined" during build
|
|
19
|
+
- Different build behavior between CLI and JavaScript API
|
|
20
|
+
- Vue components render incorrectly after build
|
|
21
|
+
|
|
22
|
+
## Root Cause
|
|
23
|
+
|
|
24
|
+
Vite doesn't deduplicate plugins by name when merging configs. The Vue plugin's internal state gets corrupted when registered twice.
|
|
25
|
+
|
|
26
|
+
## Fix
|
|
27
|
+
|
|
28
|
+
**Option 1: Use configFile: false with inline plugins**
|
|
29
|
+
```typescript
|
|
30
|
+
import { build } from 'vite'
|
|
31
|
+
import vue from '@vitejs/plugin-vue'
|
|
32
|
+
|
|
33
|
+
await build({
|
|
34
|
+
configFile: false, // Don't load vite.config.js
|
|
35
|
+
plugins: [vue()],
|
|
36
|
+
// ... rest of config
|
|
37
|
+
})
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
**Option 2: Don't specify plugins in inlineConfig**
|
|
41
|
+
```typescript
|
|
42
|
+
// vite.config.js already has vue plugin
|
|
43
|
+
import { build } from 'vite'
|
|
44
|
+
|
|
45
|
+
await build({
|
|
46
|
+
// Don't add vue plugin here - it's in vite.config.js
|
|
47
|
+
root: './src',
|
|
48
|
+
build: { outDir: '../dist' }
|
|
49
|
+
})
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
**Option 3: Filter out Vue plugin before merging**
|
|
53
|
+
```typescript
|
|
54
|
+
import { build, loadConfigFromFile } from 'vite'
|
|
55
|
+
import vue from '@vitejs/plugin-vue'
|
|
56
|
+
|
|
57
|
+
const { config } = await loadConfigFromFile({ command: 'build', mode: 'production' })
|
|
58
|
+
|
|
59
|
+
// Remove existing Vue plugin
|
|
60
|
+
const filteredPlugins = config.plugins?.filter(
|
|
61
|
+
p => !p || (Array.isArray(p) ? false : p.name !== 'vite:vue')
|
|
62
|
+
) || []
|
|
63
|
+
|
|
64
|
+
await build({
|
|
65
|
+
...config,
|
|
66
|
+
plugins: [...filteredPlugins, vue({ /* your options */ })]
|
|
67
|
+
})
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
## Detection Script
|
|
71
|
+
|
|
72
|
+
Add this to debug plugin registration:
|
|
73
|
+
```typescript
|
|
74
|
+
// vite.config.ts
|
|
75
|
+
export default defineConfig({
|
|
76
|
+
plugins: [
|
|
77
|
+
vue(),
|
|
78
|
+
{
|
|
79
|
+
name: 'debug-plugins',
|
|
80
|
+
configResolved(config) {
|
|
81
|
+
const vuePlugins = config.plugins.filter(p => p.name?.includes('vue'))
|
|
82
|
+
if (vuePlugins.length > 1) {
|
|
83
|
+
console.warn('WARNING: Multiple Vue plugins detected:', vuePlugins.map(p => p.name))
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
]
|
|
88
|
+
})
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
## Common Scenarios
|
|
92
|
+
|
|
93
|
+
| Scenario | Solution |
|
|
94
|
+
|----------|----------|
|
|
95
|
+
| Using `vite.createServer()` | Use `configFile: false` |
|
|
96
|
+
| Build script with custom config | Don't duplicate plugins |
|
|
97
|
+
| Monorepo with shared config | Check for plugin inheritance |
|
|
98
|
+
|
|
99
|
+
## Reference
|
|
100
|
+
|
|
101
|
+
- [Vite Issue #5335](https://github.com/vitejs/vite/issues/5335)
|
|
102
|
+
- [Vite JavaScript API](https://vite.dev/guide/api-javascript.html)
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Enable Fallthrough Attributes Type Checking
|
|
3
|
+
impact: HIGH
|
|
4
|
+
impactDescription: enables type-safe fallthrough attributes in component libraries
|
|
5
|
+
type: capability
|
|
6
|
+
tags: fallthroughAttributes, vueCompilerOptions, component-library, wrapper-components
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
# Enable Fallthrough Attributes Type Checking
|
|
10
|
+
|
|
11
|
+
**Impact: MEDIUM** - enables type-aware attribute forwarding in component libraries
|
|
12
|
+
|
|
13
|
+
When building component libraries with wrapper components, enable `fallthroughAttributes` to get IDE autocomplete for attributes that will be forwarded to child elements.
|
|
14
|
+
|
|
15
|
+
## What It Does
|
|
16
|
+
|
|
17
|
+
Wrapper components that pass attributes to child elements can benefit from type-aware completion:
|
|
18
|
+
|
|
19
|
+
```vue
|
|
20
|
+
<!-- MyButton.vue - wrapper around native button -->
|
|
21
|
+
<template>
|
|
22
|
+
<button v-bind="$attrs"><slot /></button>
|
|
23
|
+
</template>
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
## Solution
|
|
27
|
+
|
|
28
|
+
Enable `fallthroughAttributes` in your tsconfig:
|
|
29
|
+
|
|
30
|
+
```json
|
|
31
|
+
// tsconfig.json or tsconfig.app.json
|
|
32
|
+
{
|
|
33
|
+
"vueCompilerOptions": {
|
|
34
|
+
"fallthroughAttributes": true
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
## How It Works
|
|
40
|
+
|
|
41
|
+
When `fallthroughAttributes: true`:
|
|
42
|
+
- Vue Language Server analyzes which element receives `$attrs`
|
|
43
|
+
- IDE autocomplete suggests valid attributes for the target element
|
|
44
|
+
- Helps developers discover available attributes
|
|
45
|
+
|
|
46
|
+
> **Note:** This primarily enables IDE autocomplete for valid fallthrough attributes. It does NOT reject invalid attributes as type errors - arbitrary attributes are still allowed.
|
|
47
|
+
|
|
48
|
+
## Related Options
|
|
49
|
+
|
|
50
|
+
Combine with `strictTemplates` for comprehensive checking:
|
|
51
|
+
|
|
52
|
+
```json
|
|
53
|
+
{
|
|
54
|
+
"vueCompilerOptions": {
|
|
55
|
+
"strictTemplates": true,
|
|
56
|
+
"fallthroughAttributes": true
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
## Reference
|
|
62
|
+
|
|
63
|
+
- [Vue Language Tools Wiki - Vue Compiler Options](https://github.com/vuejs/language-tools/wiki/Vue-Compiler-Options)
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: HMR Debugging for Vue SSR
|
|
3
|
+
impact: MEDIUM
|
|
4
|
+
impactDescription: fixes Hot Module Replacement breaking in Vue SSR applications
|
|
5
|
+
type: efficiency
|
|
6
|
+
tags: vite, hmr, ssr, vue, hot-reload, server-side-rendering
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
# HMR Debugging for Vue SSR
|
|
10
|
+
|
|
11
|
+
**Impact: MEDIUM** - fixes Hot Module Replacement breaking in Vue SSR applications
|
|
12
|
+
|
|
13
|
+
Hot Module Replacement breaks when modifying Vue component `<script setup>` sections in SSR applications. Changes cause errors instead of smooth updates, requiring full page reloads.
|
|
14
|
+
|
|
15
|
+
## Symptoms
|
|
16
|
+
|
|
17
|
+
- HMR works for `<template>` changes but breaks for `<script setup>`
|
|
18
|
+
- "Cannot read property of undefined" after saving
|
|
19
|
+
- Full page reload required after script changes
|
|
20
|
+
- HMR works in dev:client but not dev:ssr
|
|
21
|
+
|
|
22
|
+
## Root Cause
|
|
23
|
+
|
|
24
|
+
SSR mode has a different transformation pipeline. The Vue plugin's HMR boundary detection doesn't handle SSR modules the same way as client modules.
|
|
25
|
+
|
|
26
|
+
## Fix
|
|
27
|
+
|
|
28
|
+
**Step 1: Ensure correct SSR plugin configuration**
|
|
29
|
+
```typescript
|
|
30
|
+
// vite.config.ts
|
|
31
|
+
import { defineConfig } from 'vite'
|
|
32
|
+
import vue from '@vitejs/plugin-vue'
|
|
33
|
+
|
|
34
|
+
export default defineConfig({
|
|
35
|
+
plugins: [vue()],
|
|
36
|
+
ssr: {
|
|
37
|
+
// Don't externalize these for HMR to work
|
|
38
|
+
noExternal: ['vue', '@vue/runtime-core', '@vue/runtime-dom']
|
|
39
|
+
}
|
|
40
|
+
})
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
**Step 2: Configure dev server for SSR HMR**
|
|
44
|
+
```typescript
|
|
45
|
+
// server.ts
|
|
46
|
+
import { createServer } from 'vite'
|
|
47
|
+
|
|
48
|
+
const vite = await createServer({
|
|
49
|
+
server: { middlewareMode: true },
|
|
50
|
+
appType: 'custom'
|
|
51
|
+
})
|
|
52
|
+
|
|
53
|
+
// Use vite.ssrLoadModule for server-side imports
|
|
54
|
+
const { render } = await vite.ssrLoadModule('/src/entry-server.ts')
|
|
55
|
+
|
|
56
|
+
// Handle HMR
|
|
57
|
+
vite.watcher.on('change', async (file) => {
|
|
58
|
+
if (file.endsWith('.vue')) {
|
|
59
|
+
// Invalidate the module
|
|
60
|
+
const mod = vite.moduleGraph.getModuleById(file)
|
|
61
|
+
if (mod) {
|
|
62
|
+
vite.moduleGraph.invalidateModule(mod)
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
})
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
**Step 3: Add HMR acceptance in entry-server**
|
|
69
|
+
```typescript
|
|
70
|
+
// entry-server.ts
|
|
71
|
+
import { createApp } from './main'
|
|
72
|
+
|
|
73
|
+
export async function render(url: string) {
|
|
74
|
+
const app = createApp()
|
|
75
|
+
// ... render logic
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
// Accept HMR updates
|
|
79
|
+
if (import.meta.hot) {
|
|
80
|
+
import.meta.hot.accept()
|
|
81
|
+
}
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
## Framework-Specific Solutions
|
|
85
|
+
|
|
86
|
+
### Nuxt 3
|
|
87
|
+
HMR should work out of the box. If not:
|
|
88
|
+
```bash
|
|
89
|
+
rm -rf .nuxt node_modules/.vite
|
|
90
|
+
npm install
|
|
91
|
+
npm run dev
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
### Vite SSR Template
|
|
95
|
+
Ensure you're using the latest `@vitejs/plugin-vue`:
|
|
96
|
+
```bash
|
|
97
|
+
npm install @vitejs/plugin-vue@latest
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
## Debugging
|
|
101
|
+
|
|
102
|
+
Enable verbose HMR logging:
|
|
103
|
+
```typescript
|
|
104
|
+
// vite.config.ts
|
|
105
|
+
export default defineConfig({
|
|
106
|
+
server: {
|
|
107
|
+
hmr: {
|
|
108
|
+
overlay: true
|
|
109
|
+
}
|
|
110
|
+
},
|
|
111
|
+
logLevel: 'info' // Shows HMR updates
|
|
112
|
+
})
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
## Known Limitations
|
|
116
|
+
|
|
117
|
+
- HMR for `<script>` (not `<script setup>`) may require full reload
|
|
118
|
+
- SSR components with external dependencies may not hot-reload
|
|
119
|
+
- State is not preserved for SSR components (expected behavior)
|
|
120
|
+
|
|
121
|
+
## Reference
|
|
122
|
+
|
|
123
|
+
- [vite-plugin-vue#525](https://github.com/vitejs/vite-plugin-vue/issues/525)
|
|
124
|
+
- [Vite SSR Guide](https://vite.dev/guide/ssr.html)
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: moduleResolution Bundler Migration Issues
|
|
3
|
+
impact: HIGH
|
|
4
|
+
impactDescription: fixes "Cannot find module" errors after @vue/tsconfig upgrade
|
|
5
|
+
type: capability
|
|
6
|
+
tags: moduleResolution, bundler, tsconfig, vue-tsconfig, node, esm
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
# moduleResolution Bundler Migration Issues
|
|
10
|
+
|
|
11
|
+
**Impact: HIGH** - fixes "Cannot find module" errors after @vue/tsconfig upgrade
|
|
12
|
+
|
|
13
|
+
Recent versions of `@vue/tsconfig` changed `moduleResolution` from `"node"` to `"bundler"`. This can break existing projects with errors like "Cannot find module 'vue'" or issues with `resolveJsonModule`.
|
|
14
|
+
|
|
15
|
+
## Symptoms
|
|
16
|
+
|
|
17
|
+
- `Cannot find module 'vue'` or other packages
|
|
18
|
+
- `Option '--resolveJsonModule' cannot be specified without 'node' module resolution`
|
|
19
|
+
- Errors appear after updating `@vue/tsconfig`
|
|
20
|
+
- Some third-party packages no longer resolve
|
|
21
|
+
|
|
22
|
+
## Root Cause
|
|
23
|
+
|
|
24
|
+
`moduleResolution: "bundler"` requires:
|
|
25
|
+
1. TypeScript 5.0+
|
|
26
|
+
2. Packages to have proper `exports` field in package.json
|
|
27
|
+
3. Different resolution rules than Node.js classic resolution
|
|
28
|
+
|
|
29
|
+
## Fix
|
|
30
|
+
|
|
31
|
+
**Option 1: Ensure TypeScript 5.0+ everywhere**
|
|
32
|
+
```bash
|
|
33
|
+
npm install -D typescript@^5.0.0
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
In monorepos, ALL packages must use TypeScript 5.0+.
|
|
37
|
+
|
|
38
|
+
**Option 2: Add compatibility workaround**
|
|
39
|
+
```json
|
|
40
|
+
{
|
|
41
|
+
"compilerOptions": {
|
|
42
|
+
"module": "ESNext",
|
|
43
|
+
"moduleResolution": "bundler",
|
|
44
|
+
"resolvePackageJsonExports": false
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
Setting `resolvePackageJsonExports: false` restores compatibility with packages that don't have proper exports.
|
|
50
|
+
|
|
51
|
+
**Option 3: Revert to Node resolution**
|
|
52
|
+
```json
|
|
53
|
+
{
|
|
54
|
+
"compilerOptions": {
|
|
55
|
+
"moduleResolution": "node"
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
## Which Packages Break?
|
|
61
|
+
|
|
62
|
+
Packages break if they:
|
|
63
|
+
- Lack `exports` field in package.json
|
|
64
|
+
- Have incorrect `exports` configuration
|
|
65
|
+
- Rely on Node.js-specific resolution behavior
|
|
66
|
+
|
|
67
|
+
## Diagnosis
|
|
68
|
+
|
|
69
|
+
```bash
|
|
70
|
+
# Check which resolution is being used
|
|
71
|
+
cat tsconfig.json | grep moduleResolution
|
|
72
|
+
|
|
73
|
+
# Test if a specific module resolves
|
|
74
|
+
npx tsc --traceResolution 2>&1 | grep "module-name"
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
## Reference
|
|
78
|
+
|
|
79
|
+
- [vuejs/tsconfig#8](https://github.com/vuejs/tsconfig/issues/8)
|
|
80
|
+
- [TypeScript moduleResolution docs](https://www.typescriptlang.org/tsconfig#moduleResolution)
|
|
81
|
+
- [Vite discussion#14001](https://github.com/vitejs/vite/discussions/14001)
|
package/knowledge/skills/external/vue-skills/skills/vue-best-practices/rules/pinia-store-mocking.md
ADDED
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Mocking Pinia Stores with Vitest
|
|
3
|
+
impact: HIGH
|
|
4
|
+
impactDescription: properly mocks Pinia stores in component tests
|
|
5
|
+
type: efficiency
|
|
6
|
+
tags: pinia, vitest, testing, mock, createTestingPinia, store
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
# Mocking Pinia Stores with Vitest
|
|
10
|
+
|
|
11
|
+
**Impact: HIGH** - properly mocks Pinia stores in component tests
|
|
12
|
+
|
|
13
|
+
Developers struggle to properly mock Pinia stores: `createTestingPinia` requires explicit `createSpy` configuration, and "injection Symbol(pinia) not found" errors occur without proper setup.
|
|
14
|
+
|
|
15
|
+
> **Important (@pinia/testing 1.0+):** The `createSpy` option is **REQUIRED**, not optional. Omitting it throws an error: "You must configure the `createSpy` option."
|
|
16
|
+
|
|
17
|
+
## Symptoms
|
|
18
|
+
|
|
19
|
+
- "injection Symbol(pinia) not found" error
|
|
20
|
+
- "You must configure the `createSpy` option" error
|
|
21
|
+
- Actions not properly mocked
|
|
22
|
+
- Store state not reset between tests
|
|
23
|
+
|
|
24
|
+
## Fix
|
|
25
|
+
|
|
26
|
+
**Pattern 1: Basic setup with createTestingPinia**
|
|
27
|
+
```typescript
|
|
28
|
+
import { mount } from '@vue/test-utils'
|
|
29
|
+
import { createTestingPinia } from '@pinia/testing'
|
|
30
|
+
import { vi } from 'vitest'
|
|
31
|
+
import MyComponent from './MyComponent.vue'
|
|
32
|
+
import { useCounterStore } from '@/stores/counter'
|
|
33
|
+
|
|
34
|
+
test('component uses store', async () => {
|
|
35
|
+
const wrapper = mount(MyComponent, {
|
|
36
|
+
global: {
|
|
37
|
+
plugins: [
|
|
38
|
+
createTestingPinia({
|
|
39
|
+
createSpy: vi.fn, // REQUIRED in @pinia/testing 1.0+
|
|
40
|
+
initialState: {
|
|
41
|
+
counter: { count: 10 } // Set initial state
|
|
42
|
+
}
|
|
43
|
+
})
|
|
44
|
+
]
|
|
45
|
+
}
|
|
46
|
+
})
|
|
47
|
+
|
|
48
|
+
// Get the store instance AFTER mounting
|
|
49
|
+
const store = useCounterStore()
|
|
50
|
+
|
|
51
|
+
// Actions are automatically stubbed
|
|
52
|
+
await wrapper.find('button').trigger('click')
|
|
53
|
+
expect(store.increment).toHaveBeenCalled()
|
|
54
|
+
})
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
**Pattern 2: Customize action behavior**
|
|
58
|
+
```typescript
|
|
59
|
+
test('component handles async action', async () => {
|
|
60
|
+
const wrapper = mount(MyComponent, {
|
|
61
|
+
global: {
|
|
62
|
+
plugins: [
|
|
63
|
+
createTestingPinia({
|
|
64
|
+
createSpy: vi.fn,
|
|
65
|
+
stubActions: false // Don't stub, use real actions
|
|
66
|
+
})
|
|
67
|
+
]
|
|
68
|
+
}
|
|
69
|
+
})
|
|
70
|
+
|
|
71
|
+
const store = useCounterStore()
|
|
72
|
+
|
|
73
|
+
// Override specific action
|
|
74
|
+
store.fetchData = vi.fn().mockResolvedValue({ items: [] })
|
|
75
|
+
|
|
76
|
+
await wrapper.find('.load-button').trigger('click')
|
|
77
|
+
expect(store.fetchData).toHaveBeenCalled()
|
|
78
|
+
})
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
**Pattern 3: Testing store directly**
|
|
82
|
+
```typescript
|
|
83
|
+
import { setActivePinia, createPinia } from 'pinia'
|
|
84
|
+
import { useCounterStore } from '@/stores/counter'
|
|
85
|
+
|
|
86
|
+
describe('Counter Store', () => {
|
|
87
|
+
beforeEach(() => {
|
|
88
|
+
setActivePinia(createPinia())
|
|
89
|
+
})
|
|
90
|
+
|
|
91
|
+
test('increments count', () => {
|
|
92
|
+
const store = useCounterStore()
|
|
93
|
+
expect(store.count).toBe(0)
|
|
94
|
+
|
|
95
|
+
store.increment()
|
|
96
|
+
expect(store.count).toBe(1)
|
|
97
|
+
})
|
|
98
|
+
})
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
## Setup Store with Vitest
|
|
102
|
+
|
|
103
|
+
```typescript
|
|
104
|
+
// stores/counter.ts - Setup store syntax
|
|
105
|
+
export const useCounterStore = defineStore('counter', () => {
|
|
106
|
+
const count = ref(0)
|
|
107
|
+
const doubleCount = computed(() => count.value * 2)
|
|
108
|
+
|
|
109
|
+
function increment() {
|
|
110
|
+
count.value++
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
return { count, doubleCount, increment }
|
|
114
|
+
})
|
|
115
|
+
|
|
116
|
+
// Test file
|
|
117
|
+
test('setup store works', async () => {
|
|
118
|
+
const pinia = createTestingPinia({
|
|
119
|
+
createSpy: vi.fn,
|
|
120
|
+
initialState: {
|
|
121
|
+
counter: { count: 5 }
|
|
122
|
+
}
|
|
123
|
+
})
|
|
124
|
+
|
|
125
|
+
const wrapper = mount(MyComponent, {
|
|
126
|
+
global: { plugins: [pinia] }
|
|
127
|
+
})
|
|
128
|
+
|
|
129
|
+
const store = useCounterStore()
|
|
130
|
+
expect(store.count).toBe(5)
|
|
131
|
+
expect(store.doubleCount).toBe(10)
|
|
132
|
+
})
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
## Reset Between Tests
|
|
136
|
+
|
|
137
|
+
```typescript
|
|
138
|
+
describe('Store Tests', () => {
|
|
139
|
+
let pinia: Pinia
|
|
140
|
+
|
|
141
|
+
beforeEach(() => {
|
|
142
|
+
pinia = createTestingPinia({
|
|
143
|
+
createSpy: vi.fn
|
|
144
|
+
})
|
|
145
|
+
})
|
|
146
|
+
|
|
147
|
+
afterEach(() => {
|
|
148
|
+
vi.clearAllMocks()
|
|
149
|
+
})
|
|
150
|
+
|
|
151
|
+
test('test 1', () => { /* ... */ })
|
|
152
|
+
test('test 2', () => { /* ... */ })
|
|
153
|
+
})
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
## Reference
|
|
157
|
+
|
|
158
|
+
- [Pinia Testing Guide](https://pinia.vuejs.org/cookbook/testing.html)
|
|
159
|
+
- [Pinia Discussion #2092](https://github.com/vuejs/pinia/discussions/2092)
|
package/knowledge/skills/external/vue-skills/skills/vue-best-practices/rules/script-setup-jsdoc.md
ADDED
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: JSDoc Documentation for Script Setup Components
|
|
3
|
+
impact: MEDIUM
|
|
4
|
+
impactDescription: enables proper documentation for composition API components
|
|
5
|
+
type: capability
|
|
6
|
+
tags: jsdoc, script-setup, documentation, composition-api, component
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
# JSDoc Documentation for Script Setup Components
|
|
10
|
+
|
|
11
|
+
**Impact: MEDIUM** - enables proper documentation for composition API components
|
|
12
|
+
|
|
13
|
+
`<script setup>` doesn't have an obvious place to attach JSDoc comments for the component itself. Use a dual-script pattern.
|
|
14
|
+
|
|
15
|
+
## Problem
|
|
16
|
+
|
|
17
|
+
**Incorrect:**
|
|
18
|
+
```vue
|
|
19
|
+
<script setup lang="ts">
|
|
20
|
+
/**
|
|
21
|
+
* This comment doesn't appear in IDE hover or docs
|
|
22
|
+
* @component
|
|
23
|
+
*/
|
|
24
|
+
import { ref } from 'vue'
|
|
25
|
+
|
|
26
|
+
const count = ref(0)
|
|
27
|
+
</script>
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
JSDoc comments inside `<script setup>` don't attach to the component export because there's no explicit export statement.
|
|
31
|
+
|
|
32
|
+
## Solution
|
|
33
|
+
|
|
34
|
+
Use both `<script>` and `<script setup>` blocks:
|
|
35
|
+
|
|
36
|
+
**Correct:**
|
|
37
|
+
```vue
|
|
38
|
+
<script lang="ts">
|
|
39
|
+
/**
|
|
40
|
+
* A counter component that displays and increments a value.
|
|
41
|
+
*
|
|
42
|
+
* @example
|
|
43
|
+
* ```vue
|
|
44
|
+
* <Counter :initial="5" @update="handleUpdate" />
|
|
45
|
+
* ```
|
|
46
|
+
*
|
|
47
|
+
* @component
|
|
48
|
+
*/
|
|
49
|
+
export default {}
|
|
50
|
+
</script>
|
|
51
|
+
|
|
52
|
+
<script setup lang="ts">
|
|
53
|
+
import { ref } from 'vue'
|
|
54
|
+
|
|
55
|
+
const props = defineProps<{
|
|
56
|
+
/** Starting value for the counter */
|
|
57
|
+
initial?: number
|
|
58
|
+
}>()
|
|
59
|
+
|
|
60
|
+
const emit = defineEmits<{
|
|
61
|
+
/** Emitted when counter value changes */
|
|
62
|
+
update: [value: number]
|
|
63
|
+
}>()
|
|
64
|
+
|
|
65
|
+
const count = ref(props.initial ?? 0)
|
|
66
|
+
</script>
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
## How It Works
|
|
70
|
+
|
|
71
|
+
- The regular `<script>` block's default export is merged with `<script setup>`
|
|
72
|
+
- JSDoc on `export default {}` attaches to the component
|
|
73
|
+
- Props and emits JSDoc in `<script setup>` still work normally
|
|
74
|
+
|
|
75
|
+
## What Gets Documented
|
|
76
|
+
|
|
77
|
+
| Location | Shows In |
|
|
78
|
+
|----------|----------|
|
|
79
|
+
| `export default {}` JSDoc | Component import hover |
|
|
80
|
+
| `defineProps` JSDoc | Prop hover in templates |
|
|
81
|
+
| `defineEmits` JSDoc | Event handler hover |
|
|
82
|
+
|
|
83
|
+
## Reference
|
|
84
|
+
|
|
85
|
+
- [Vue Language Tools Discussion #5932](https://github.com/vuejs/language-tools/discussions/5932)
|