uilint-semantic 0.2.136
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 +134 -0
- package/dist/chunk-LATUVFI5.js +523 -0
- package/dist/chunk-LATUVFI5.js.map +1 -0
- package/dist/chunk-QZKD24XZ.js +1 -0
- package/dist/chunk-QZKD24XZ.js.map +1 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +8 -0
- package/dist/index.js.map +1 -0
- package/dist/node.d.ts +2 -0
- package/dist/node.js +8 -0
- package/dist/node.js.map +1 -0
- package/dist/plugin/index.d.ts +207 -0
- package/dist/plugin/index.js +9 -0
- package/dist/plugin/index.js.map +1 -0
- package/package.json +54 -0
package/README.md
ADDED
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
# uilint-semantic
|
|
2
|
+
|
|
3
|
+
Semantic analysis plugin for UILint. Provides duplicate code detection and semantic similarity analysis.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
pnpm add uilint-semantic
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## Usage
|
|
12
|
+
|
|
13
|
+
### Plugin System
|
|
14
|
+
|
|
15
|
+
The semantic plugin auto-registers with the UILint plugin registry:
|
|
16
|
+
|
|
17
|
+
```typescript
|
|
18
|
+
import "uilint-semantic"; // Auto-registers plugin
|
|
19
|
+
|
|
20
|
+
import { pluginRegistry } from "uilint-core";
|
|
21
|
+
const semanticPlugin = pluginRegistry.get("semantic");
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
### Types
|
|
25
|
+
|
|
26
|
+
```typescript
|
|
27
|
+
import type {
|
|
28
|
+
DuplicateMatch,
|
|
29
|
+
DuplicateGroup,
|
|
30
|
+
Chunk,
|
|
31
|
+
IndexStats,
|
|
32
|
+
} from "uilint-semantic";
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
## Features
|
|
36
|
+
|
|
37
|
+
- **Duplicate Detection**: Find semantically similar code patterns
|
|
38
|
+
- **Code Indexing**: Build searchable index of code chunks
|
|
39
|
+
- **Similarity Scoring**: Configurable similarity thresholds
|
|
40
|
+
- **Issue Reporting**: ESLint rule for duplicate detection
|
|
41
|
+
|
|
42
|
+
## Plugin Definition
|
|
43
|
+
|
|
44
|
+
The semantic plugin provides:
|
|
45
|
+
|
|
46
|
+
### Commands
|
|
47
|
+
- `semantic:rebuild-index` - Rebuild the duplicates index
|
|
48
|
+
- `semantic:clear-filter` - Clear heatmap filter
|
|
49
|
+
|
|
50
|
+
### Panels
|
|
51
|
+
- **Duplicates Panel**: Side-by-side code comparison with diff highlighting
|
|
52
|
+
- **Index Status Panel**: Shows indexing progress and statistics
|
|
53
|
+
|
|
54
|
+
### Rules
|
|
55
|
+
- `no-semantic-duplicates` - ESLint rule to detect duplicate code
|
|
56
|
+
|
|
57
|
+
### State
|
|
58
|
+
```typescript
|
|
59
|
+
interface SemanticState {
|
|
60
|
+
indexStatus: "idle" | "indexing" | "ready" | "error";
|
|
61
|
+
indexProgress: IndexProgress | null;
|
|
62
|
+
indexStats: IndexStats | null;
|
|
63
|
+
lastIndexError: string | null;
|
|
64
|
+
selectedDuplicate: SelectedDuplicate | null;
|
|
65
|
+
}
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
## Types
|
|
69
|
+
|
|
70
|
+
### DuplicateMatch
|
|
71
|
+
```typescript
|
|
72
|
+
interface DuplicateMatch {
|
|
73
|
+
sourceDataLoc: string;
|
|
74
|
+
targetDataLoc: string;
|
|
75
|
+
similarity: number;
|
|
76
|
+
sourceCode?: string;
|
|
77
|
+
targetCode?: string;
|
|
78
|
+
}
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
### IndexStats
|
|
82
|
+
```typescript
|
|
83
|
+
interface IndexStats {
|
|
84
|
+
totalChunks: number;
|
|
85
|
+
added: number;
|
|
86
|
+
modified: number;
|
|
87
|
+
deleted: number;
|
|
88
|
+
duration: number;
|
|
89
|
+
}
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
### Chunk
|
|
93
|
+
```typescript
|
|
94
|
+
interface Chunk {
|
|
95
|
+
id: string;
|
|
96
|
+
kind: ChunkKind;
|
|
97
|
+
filePath: string;
|
|
98
|
+
startLine: number;
|
|
99
|
+
endLine: number;
|
|
100
|
+
code: string;
|
|
101
|
+
hash: string;
|
|
102
|
+
}
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
## Configuration
|
|
106
|
+
|
|
107
|
+
The `no-semantic-duplicates` rule accepts options:
|
|
108
|
+
|
|
109
|
+
```javascript
|
|
110
|
+
// .eslintrc.js
|
|
111
|
+
{
|
|
112
|
+
rules: {
|
|
113
|
+
"uilint/no-semantic-duplicates": ["warn", {
|
|
114
|
+
threshold: 0.75, // Similarity threshold (0-1)
|
|
115
|
+
}]
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
## WebSocket Messages
|
|
121
|
+
|
|
122
|
+
The plugin handles these WebSocket message types:
|
|
123
|
+
|
|
124
|
+
| Message | Direction | Description |
|
|
125
|
+
|---------|-----------|-------------|
|
|
126
|
+
| `duplicates:index` | Client→Server | Request indexing |
|
|
127
|
+
| `duplicates:indexing:start` | Server→Client | Indexing started |
|
|
128
|
+
| `duplicates:indexing:progress` | Server→Client | Progress update |
|
|
129
|
+
| `duplicates:indexing:complete` | Server→Client | Indexing finished |
|
|
130
|
+
| `duplicates:indexing:error` | Server→Client | Indexing error |
|
|
131
|
+
|
|
132
|
+
## License
|
|
133
|
+
|
|
134
|
+
MIT
|
|
@@ -0,0 +1,523 @@
|
|
|
1
|
+
// src/plugin/index.ts
|
|
2
|
+
import { pluginRegistry } from "uilint-core";
|
|
3
|
+
|
|
4
|
+
// src/plugin/state.ts
|
|
5
|
+
var semanticInitialState = {
|
|
6
|
+
indexStatus: "idle",
|
|
7
|
+
indexProgress: null,
|
|
8
|
+
indexStats: null,
|
|
9
|
+
lastIndexError: null,
|
|
10
|
+
selectedDuplicate: null
|
|
11
|
+
};
|
|
12
|
+
var semanticStateDefinition = {
|
|
13
|
+
initialState: semanticInitialState,
|
|
14
|
+
computed: {
|
|
15
|
+
/** Whether index is ready for queries */
|
|
16
|
+
isIndexReady: (state) => state.indexStatus === "ready",
|
|
17
|
+
/** Whether indexing is in progress */
|
|
18
|
+
isIndexing: (state) => state.indexStatus === "indexing",
|
|
19
|
+
/** Whether there was an error */
|
|
20
|
+
hasError: (state) => state.indexStatus === "error" || state.lastIndexError !== null,
|
|
21
|
+
/** Progress percentage (0-100) */
|
|
22
|
+
progressPercent: (state) => {
|
|
23
|
+
if (!state.indexProgress) return 0;
|
|
24
|
+
if (state.indexProgress.total === 0) return 0;
|
|
25
|
+
return Math.round(state.indexProgress.current / state.indexProgress.total * 100);
|
|
26
|
+
}
|
|
27
|
+
},
|
|
28
|
+
persist: {
|
|
29
|
+
key: "uilint-semantic",
|
|
30
|
+
include: []
|
|
31
|
+
// Don't persist anything for now
|
|
32
|
+
}
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
// src/plugin/actions.ts
|
|
36
|
+
var h = (fn) => fn;
|
|
37
|
+
var semanticActionHandlers = {
|
|
38
|
+
/**
|
|
39
|
+
* Start indexing
|
|
40
|
+
*/
|
|
41
|
+
"start-indexing": h((ctx) => {
|
|
42
|
+
ctx.setState({
|
|
43
|
+
indexStatus: "indexing",
|
|
44
|
+
indexProgress: { current: 0, total: 0 },
|
|
45
|
+
lastIndexError: null
|
|
46
|
+
});
|
|
47
|
+
ctx.websocket.send({ type: "duplicates:index" });
|
|
48
|
+
}),
|
|
49
|
+
/**
|
|
50
|
+
* Handle indexing started
|
|
51
|
+
*/
|
|
52
|
+
"handle-indexing-start": h((ctx) => {
|
|
53
|
+
ctx.setState({
|
|
54
|
+
indexStatus: "indexing",
|
|
55
|
+
indexProgress: { current: 0, total: 0, message: "Starting..." },
|
|
56
|
+
lastIndexError: null
|
|
57
|
+
});
|
|
58
|
+
}),
|
|
59
|
+
/**
|
|
60
|
+
* Handle indexing progress
|
|
61
|
+
*/
|
|
62
|
+
"handle-indexing-progress": h(
|
|
63
|
+
(ctx, payload) => {
|
|
64
|
+
ctx.setState({
|
|
65
|
+
indexProgress: {
|
|
66
|
+
current: payload.current ?? 0,
|
|
67
|
+
total: payload.total ?? 0,
|
|
68
|
+
message: payload.message
|
|
69
|
+
}
|
|
70
|
+
});
|
|
71
|
+
}
|
|
72
|
+
),
|
|
73
|
+
/**
|
|
74
|
+
* Handle indexing complete
|
|
75
|
+
*/
|
|
76
|
+
"handle-indexing-complete": h((ctx, payload) => {
|
|
77
|
+
ctx.setState({
|
|
78
|
+
indexStatus: "ready",
|
|
79
|
+
indexProgress: null,
|
|
80
|
+
indexStats: payload,
|
|
81
|
+
lastIndexError: null
|
|
82
|
+
});
|
|
83
|
+
}),
|
|
84
|
+
/**
|
|
85
|
+
* Handle indexing error
|
|
86
|
+
*/
|
|
87
|
+
"handle-indexing-error": h((ctx, payload) => {
|
|
88
|
+
ctx.setState({
|
|
89
|
+
indexStatus: "error",
|
|
90
|
+
indexProgress: null,
|
|
91
|
+
lastIndexError: payload.error
|
|
92
|
+
});
|
|
93
|
+
}),
|
|
94
|
+
/**
|
|
95
|
+
* Select a duplicate for viewing in inspector
|
|
96
|
+
*/
|
|
97
|
+
"select-duplicate": h(
|
|
98
|
+
(ctx, payload) => {
|
|
99
|
+
ctx.setState({ selectedDuplicate: payload });
|
|
100
|
+
ctx.openInspector("duplicates", payload);
|
|
101
|
+
}
|
|
102
|
+
),
|
|
103
|
+
/**
|
|
104
|
+
* Clear selected duplicate
|
|
105
|
+
*/
|
|
106
|
+
"clear-selected-duplicate": h((ctx) => {
|
|
107
|
+
ctx.setState({ selectedDuplicate: null });
|
|
108
|
+
}),
|
|
109
|
+
/**
|
|
110
|
+
* Toggle heatmap filter for duplicates
|
|
111
|
+
*/
|
|
112
|
+
"toggle-heatmap-filter": h(
|
|
113
|
+
(ctx, payload) => {
|
|
114
|
+
ctx.setHeatmapFilter(
|
|
115
|
+
[payload.sourceDataLoc, payload.targetDataLoc],
|
|
116
|
+
"Duplicate Code"
|
|
117
|
+
);
|
|
118
|
+
}
|
|
119
|
+
),
|
|
120
|
+
/**
|
|
121
|
+
* Clear heatmap filter
|
|
122
|
+
*/
|
|
123
|
+
"clear-heatmap-filter": h((ctx) => {
|
|
124
|
+
ctx.clearHeatmapFilter();
|
|
125
|
+
}),
|
|
126
|
+
/**
|
|
127
|
+
* Open file in editor
|
|
128
|
+
*/
|
|
129
|
+
"open-editor": h((ctx, payload) => {
|
|
130
|
+
ctx.openInEditor(payload.dataLoc);
|
|
131
|
+
})
|
|
132
|
+
};
|
|
133
|
+
|
|
134
|
+
// src/plugin/commands.ts
|
|
135
|
+
var semanticCommands = [
|
|
136
|
+
{
|
|
137
|
+
id: "semantic:rebuild-index",
|
|
138
|
+
title: "Rebuild Duplicates Index",
|
|
139
|
+
keywords: ["semantic", "duplicates", "index", "rebuild", "scan"],
|
|
140
|
+
category: "Semantic",
|
|
141
|
+
subtitle: "Rebuild the semantic code index for duplicate detection",
|
|
142
|
+
icon: "refresh",
|
|
143
|
+
action: { type: "start-indexing" }
|
|
144
|
+
},
|
|
145
|
+
{
|
|
146
|
+
id: "semantic:clear-filter",
|
|
147
|
+
title: "Clear Duplicate Filter",
|
|
148
|
+
keywords: ["semantic", "duplicates", "clear", "filter", "heatmap"],
|
|
149
|
+
category: "Semantic",
|
|
150
|
+
subtitle: "Clear the heatmap filter for duplicates",
|
|
151
|
+
icon: "x",
|
|
152
|
+
action: { type: "clear-heatmap-filter" }
|
|
153
|
+
}
|
|
154
|
+
];
|
|
155
|
+
|
|
156
|
+
// src/plugin/panels.ts
|
|
157
|
+
var duplicatesPanelDefinition = {
|
|
158
|
+
id: "duplicates",
|
|
159
|
+
title: "Duplicate Code",
|
|
160
|
+
priority: 10,
|
|
161
|
+
loading: {
|
|
162
|
+
when: { binding: "isLoading" },
|
|
163
|
+
message: "Loading code comparison..."
|
|
164
|
+
},
|
|
165
|
+
empty: {
|
|
166
|
+
when: { expression: "!sourceCode && !targetCode" },
|
|
167
|
+
message: "No duplicate information available.",
|
|
168
|
+
icon: "search"
|
|
169
|
+
},
|
|
170
|
+
layout: [
|
|
171
|
+
// Similarity badge
|
|
172
|
+
{
|
|
173
|
+
type: "badge",
|
|
174
|
+
variant: "similarity",
|
|
175
|
+
value: { binding: "similarity" },
|
|
176
|
+
centered: true
|
|
177
|
+
},
|
|
178
|
+
// Source code section
|
|
179
|
+
{
|
|
180
|
+
type: "code-viewer",
|
|
181
|
+
label: "This Code",
|
|
182
|
+
icon: "target",
|
|
183
|
+
code: {
|
|
184
|
+
fetch: {
|
|
185
|
+
type: "source-code",
|
|
186
|
+
params: {
|
|
187
|
+
filePath: { binding: "sourceLocation.filePath" },
|
|
188
|
+
line: { binding: "sourceLocation.startLine" },
|
|
189
|
+
contextAbove: 10,
|
|
190
|
+
contextBelow: 10
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
},
|
|
194
|
+
location: { binding: "sourceLocation" },
|
|
195
|
+
diffHighlighting: true,
|
|
196
|
+
maxHeight: 250,
|
|
197
|
+
onNavigate: {
|
|
198
|
+
type: "open-editor",
|
|
199
|
+
payloadBindings: { dataLoc: "sourceDataLoc" }
|
|
200
|
+
}
|
|
201
|
+
},
|
|
202
|
+
// Target code section
|
|
203
|
+
{
|
|
204
|
+
type: "code-viewer",
|
|
205
|
+
label: "Similar Code",
|
|
206
|
+
icon: "link",
|
|
207
|
+
code: {
|
|
208
|
+
fetch: {
|
|
209
|
+
type: "source-code",
|
|
210
|
+
params: {
|
|
211
|
+
filePath: { binding: "targetLocation.filePath" },
|
|
212
|
+
line: { binding: "targetLocation.startLine" },
|
|
213
|
+
contextAbove: 10,
|
|
214
|
+
contextBelow: 10
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
},
|
|
218
|
+
location: { binding: "targetLocation" },
|
|
219
|
+
diffHighlighting: true,
|
|
220
|
+
maxHeight: 250,
|
|
221
|
+
onNavigate: {
|
|
222
|
+
type: "open-editor",
|
|
223
|
+
payloadBindings: { dataLoc: "targetDataLoc" }
|
|
224
|
+
}
|
|
225
|
+
},
|
|
226
|
+
{ type: "divider", spacing: "medium" },
|
|
227
|
+
// Actions
|
|
228
|
+
{
|
|
229
|
+
type: "actions",
|
|
230
|
+
direction: "row",
|
|
231
|
+
actions: [
|
|
232
|
+
{
|
|
233
|
+
id: "toggle-heatmap",
|
|
234
|
+
label: {
|
|
235
|
+
condition: { binding: "heatmapFilterActive" },
|
|
236
|
+
true: "Clear Heatmap Filter",
|
|
237
|
+
false: "Focus in Heatmap"
|
|
238
|
+
},
|
|
239
|
+
icon: "filter",
|
|
240
|
+
variant: "primary",
|
|
241
|
+
action: {
|
|
242
|
+
type: "toggle-heatmap-filter",
|
|
243
|
+
payloadBindings: {
|
|
244
|
+
sourceDataLoc: "sourceDataLoc",
|
|
245
|
+
targetDataLoc: "targetDataLoc"
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
]
|
|
250
|
+
}
|
|
251
|
+
]
|
|
252
|
+
};
|
|
253
|
+
var indexStatusPanelDefinition = {
|
|
254
|
+
id: "semantic-index-status",
|
|
255
|
+
title: "Index Status",
|
|
256
|
+
priority: 5,
|
|
257
|
+
layout: [
|
|
258
|
+
// Status badge
|
|
259
|
+
{
|
|
260
|
+
type: "badge",
|
|
261
|
+
variant: "status",
|
|
262
|
+
value: { binding: "indexStatus" },
|
|
263
|
+
centered: true
|
|
264
|
+
},
|
|
265
|
+
// Progress (when indexing)
|
|
266
|
+
{
|
|
267
|
+
type: "conditional",
|
|
268
|
+
condition: { expression: "indexStatus === 'indexing'" },
|
|
269
|
+
then: [
|
|
270
|
+
{
|
|
271
|
+
type: "progress",
|
|
272
|
+
value: { binding: "progressPercent" },
|
|
273
|
+
label: { binding: "indexProgress.message" }
|
|
274
|
+
}
|
|
275
|
+
]
|
|
276
|
+
},
|
|
277
|
+
// Stats (when ready)
|
|
278
|
+
{
|
|
279
|
+
type: "conditional",
|
|
280
|
+
condition: { expression: "indexStatus === 'ready' && indexStats" },
|
|
281
|
+
then: [
|
|
282
|
+
{
|
|
283
|
+
type: "text",
|
|
284
|
+
content: { binding: "indexStats.totalChunks" },
|
|
285
|
+
variant: "body"
|
|
286
|
+
}
|
|
287
|
+
]
|
|
288
|
+
},
|
|
289
|
+
// Error (when error)
|
|
290
|
+
{
|
|
291
|
+
type: "conditional",
|
|
292
|
+
condition: { expression: "indexStatus === 'error'" },
|
|
293
|
+
then: [
|
|
294
|
+
{
|
|
295
|
+
type: "text",
|
|
296
|
+
content: { binding: "lastIndexError" },
|
|
297
|
+
variant: "error"
|
|
298
|
+
}
|
|
299
|
+
]
|
|
300
|
+
},
|
|
301
|
+
{ type: "divider" },
|
|
302
|
+
// Rebuild action
|
|
303
|
+
{
|
|
304
|
+
type: "actions",
|
|
305
|
+
direction: "column",
|
|
306
|
+
actions: [
|
|
307
|
+
{
|
|
308
|
+
id: "rebuild-index",
|
|
309
|
+
label: "Rebuild Index",
|
|
310
|
+
icon: "refresh",
|
|
311
|
+
variant: "secondary",
|
|
312
|
+
action: { type: "start-indexing" },
|
|
313
|
+
disabled: { binding: "isIndexing" }
|
|
314
|
+
}
|
|
315
|
+
]
|
|
316
|
+
}
|
|
317
|
+
]
|
|
318
|
+
};
|
|
319
|
+
var semanticPanelDefinitions = [
|
|
320
|
+
duplicatesPanelDefinition,
|
|
321
|
+
indexStatusPanelDefinition
|
|
322
|
+
];
|
|
323
|
+
|
|
324
|
+
// src/plugin/rules.ts
|
|
325
|
+
var noSemanticDuplicatesRuleDefinition = {
|
|
326
|
+
id: "no-semantic-duplicates",
|
|
327
|
+
name: "No Semantic Duplicates",
|
|
328
|
+
description: "Warns when code is semantically similar to existing code",
|
|
329
|
+
category: "semantic",
|
|
330
|
+
icon: "search",
|
|
331
|
+
defaultSeverity: "warn",
|
|
332
|
+
defaultEnabled: false,
|
|
333
|
+
heatmapColor: "#f59e0b",
|
|
334
|
+
// Amber
|
|
335
|
+
customInspectorPanel: "duplicates",
|
|
336
|
+
requirements: [
|
|
337
|
+
{
|
|
338
|
+
type: "semantic-index",
|
|
339
|
+
description: "Requires semantic index for duplicate detection",
|
|
340
|
+
setupHint: "Run: uilint duplicates index"
|
|
341
|
+
}
|
|
342
|
+
],
|
|
343
|
+
defaultOptions: [
|
|
344
|
+
{
|
|
345
|
+
threshold: 0.75,
|
|
346
|
+
indexPath: ".uilint/.duplicates-index",
|
|
347
|
+
minLines: 3,
|
|
348
|
+
confidenceLevel: "low",
|
|
349
|
+
useStructuralBoost: true,
|
|
350
|
+
includeSameFile: false,
|
|
351
|
+
kind: "all"
|
|
352
|
+
}
|
|
353
|
+
],
|
|
354
|
+
optionSchema: {
|
|
355
|
+
fields: [
|
|
356
|
+
{
|
|
357
|
+
key: "threshold",
|
|
358
|
+
label: "Similarity Threshold",
|
|
359
|
+
type: "number",
|
|
360
|
+
defaultValue: 0.75,
|
|
361
|
+
description: "Minimum similarity score (0-1) to report as duplicate."
|
|
362
|
+
},
|
|
363
|
+
{
|
|
364
|
+
key: "confidenceLevel",
|
|
365
|
+
label: "Minimum Confidence",
|
|
366
|
+
type: "select",
|
|
367
|
+
defaultValue: "low",
|
|
368
|
+
options: [
|
|
369
|
+
{ value: "high", label: "High (>90%) - Likely copy-paste" },
|
|
370
|
+
{ value: "medium", label: "Medium (>75%) - Semantically similar" },
|
|
371
|
+
{ value: "low", label: "Low (>60%) - Possibly related" }
|
|
372
|
+
],
|
|
373
|
+
description: "Minimum confidence level for reported duplicates."
|
|
374
|
+
},
|
|
375
|
+
{
|
|
376
|
+
key: "kind",
|
|
377
|
+
label: "Chunk Type",
|
|
378
|
+
type: "select",
|
|
379
|
+
defaultValue: "all",
|
|
380
|
+
options: [
|
|
381
|
+
{ value: "all", label: "All" },
|
|
382
|
+
{ value: "component", label: "Components" },
|
|
383
|
+
{ value: "hook", label: "Hooks" },
|
|
384
|
+
{ value: "function", label: "Functions" }
|
|
385
|
+
],
|
|
386
|
+
description: "Filter duplicates by code type."
|
|
387
|
+
},
|
|
388
|
+
{
|
|
389
|
+
key: "useStructuralBoost",
|
|
390
|
+
label: "Use Structural Analysis",
|
|
391
|
+
type: "boolean",
|
|
392
|
+
defaultValue: true,
|
|
393
|
+
description: "Include structural similarity (props, JSX, hooks) in scoring."
|
|
394
|
+
},
|
|
395
|
+
{
|
|
396
|
+
key: "includeSameFile",
|
|
397
|
+
label: "Include Same-File Duplicates",
|
|
398
|
+
type: "boolean",
|
|
399
|
+
defaultValue: false,
|
|
400
|
+
description: "Report duplicates within the same file."
|
|
401
|
+
},
|
|
402
|
+
{
|
|
403
|
+
key: "minLines",
|
|
404
|
+
label: "Minimum Lines",
|
|
405
|
+
type: "number",
|
|
406
|
+
defaultValue: 3,
|
|
407
|
+
description: "Minimum number of lines for a code chunk."
|
|
408
|
+
}
|
|
409
|
+
]
|
|
410
|
+
},
|
|
411
|
+
docs: `
|
|
412
|
+
## No Semantic Duplicates Rule
|
|
413
|
+
|
|
414
|
+
This rule detects code that is semantically similar to existing code in your codebase.
|
|
415
|
+
|
|
416
|
+
### How it Works
|
|
417
|
+
|
|
418
|
+
1. The codebase is indexed to extract "chunks" (components, hooks, functions)
|
|
419
|
+
2. Each chunk is embedded using an AI model to capture semantic meaning
|
|
420
|
+
3. During linting, chunks are compared against the index
|
|
421
|
+
4. Similar chunks above the threshold are reported
|
|
422
|
+
|
|
423
|
+
### Confidence Levels
|
|
424
|
+
|
|
425
|
+
- **High (>90%)**: Very likely copy-paste or near-identical code
|
|
426
|
+
- **Medium (>75%)**: Semantically similar, possibly refactorable
|
|
427
|
+
- **Low (>60%)**: Possibly related, worth reviewing
|
|
428
|
+
|
|
429
|
+
### Setup
|
|
430
|
+
|
|
431
|
+
1. Run \`uilint duplicates index\` to build the semantic index
|
|
432
|
+
2. Enable this rule in your ESLint config
|
|
433
|
+
3. The rule will report duplicates during linting
|
|
434
|
+
|
|
435
|
+
### Performance
|
|
436
|
+
|
|
437
|
+
The rule uses a pre-built index, so it's fast during linting.
|
|
438
|
+
Re-index periodically to keep the index up to date.
|
|
439
|
+
`.trim()
|
|
440
|
+
};
|
|
441
|
+
var semanticRuleDefinitions = [
|
|
442
|
+
noSemanticDuplicatesRuleDefinition
|
|
443
|
+
];
|
|
444
|
+
|
|
445
|
+
// src/plugin/messages.ts
|
|
446
|
+
var semanticMessageHandlers = {
|
|
447
|
+
/**
|
|
448
|
+
* Handle indexing started
|
|
449
|
+
*/
|
|
450
|
+
"duplicates:indexing:start": (ctx) => {
|
|
451
|
+
ctx.dispatch("handle-indexing-start");
|
|
452
|
+
},
|
|
453
|
+
/**
|
|
454
|
+
* Handle indexing progress
|
|
455
|
+
*/
|
|
456
|
+
"duplicates:indexing:progress": (ctx, message) => {
|
|
457
|
+
const msg = message;
|
|
458
|
+
ctx.dispatch("handle-indexing-progress", {
|
|
459
|
+
message: msg.message,
|
|
460
|
+
current: msg.current,
|
|
461
|
+
total: msg.total
|
|
462
|
+
});
|
|
463
|
+
},
|
|
464
|
+
/**
|
|
465
|
+
* Handle indexing complete
|
|
466
|
+
*/
|
|
467
|
+
"duplicates:indexing:complete": (ctx, message) => {
|
|
468
|
+
const msg = message;
|
|
469
|
+
ctx.dispatch("handle-indexing-complete", {
|
|
470
|
+
added: msg.added,
|
|
471
|
+
modified: msg.modified,
|
|
472
|
+
deleted: msg.deleted,
|
|
473
|
+
totalChunks: msg.totalChunks,
|
|
474
|
+
duration: msg.duration
|
|
475
|
+
});
|
|
476
|
+
},
|
|
477
|
+
/**
|
|
478
|
+
* Handle indexing error
|
|
479
|
+
*/
|
|
480
|
+
"duplicates:indexing:error": (ctx, message) => {
|
|
481
|
+
const msg = message;
|
|
482
|
+
ctx.dispatch("handle-indexing-error", { error: msg.error });
|
|
483
|
+
}
|
|
484
|
+
};
|
|
485
|
+
|
|
486
|
+
// src/plugin/index.ts
|
|
487
|
+
var semanticPlugin = {
|
|
488
|
+
// === Metadata ===
|
|
489
|
+
id: "semantic",
|
|
490
|
+
name: "Semantic Analysis",
|
|
491
|
+
version: "1.0.0",
|
|
492
|
+
description: "Semantic code analysis and duplicate detection",
|
|
493
|
+
icon: "brain",
|
|
494
|
+
// === State ===
|
|
495
|
+
state: semanticStateDefinition,
|
|
496
|
+
actions: semanticActionHandlers,
|
|
497
|
+
// === UI Contributions (Declarative) ===
|
|
498
|
+
commands: semanticCommands,
|
|
499
|
+
panels: semanticPanelDefinitions,
|
|
500
|
+
// No toolbar groups for semantic plugin
|
|
501
|
+
// === Rules ===
|
|
502
|
+
rules: semanticRuleDefinitions,
|
|
503
|
+
handlesRuleCategories: ["semantic"],
|
|
504
|
+
// === WebSocket ===
|
|
505
|
+
messageHandlers: semanticMessageHandlers,
|
|
506
|
+
// === Issue Aggregation ===
|
|
507
|
+
// Note: Issues come from ESLint rule, not directly from plugin state
|
|
508
|
+
// This is here for completeness but semantic issues flow through ESLint
|
|
509
|
+
getIssues: (_state) => {
|
|
510
|
+
return { pluginId: "semantic", issues: /* @__PURE__ */ new Map() };
|
|
511
|
+
},
|
|
512
|
+
// === Browser Actions ===
|
|
513
|
+
// Semantic plugin doesn't need browser actions
|
|
514
|
+
browserActions: []
|
|
515
|
+
};
|
|
516
|
+
pluginRegistry.register(semanticPlugin);
|
|
517
|
+
var plugin_default = semanticPlugin;
|
|
518
|
+
|
|
519
|
+
export {
|
|
520
|
+
semanticPlugin,
|
|
521
|
+
plugin_default
|
|
522
|
+
};
|
|
523
|
+
//# sourceMappingURL=chunk-LATUVFI5.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/plugin/index.ts","../src/plugin/state.ts","../src/plugin/actions.ts","../src/plugin/commands.ts","../src/plugin/panels.ts","../src/plugin/rules.ts","../src/plugin/messages.ts"],"sourcesContent":["/**\n * Semantic Plugin Definition\n *\n * Complete plugin export - NO REACT.\n * This is the main plugin definition that gets registered with pluginRegistry.\n */\n\nimport type { PluginWithHandlers, PluginIssue, IssueContribution } from \"uilint-core\";\nimport { pluginRegistry } from \"uilint-core\";\n\nimport { semanticStateDefinition, type SemanticState } from \"./state.js\";\nimport { semanticActionHandlers } from \"./actions.js\";\nimport { semanticCommands } from \"./commands.js\";\nimport { semanticPanelDefinitions } from \"./panels.js\";\nimport { semanticRuleDefinitions } from \"./rules.js\";\nimport { semanticMessageHandlers } from \"./messages.js\";\n\n/**\n * Semantic plugin definition\n *\n * Contains everything needed for semantic analysis EXCEPT React components.\n * The host (uilint-react) imports this and renders the appropriate UI.\n */\nexport const semanticPlugin: PluginWithHandlers<SemanticState> = {\n // === Metadata ===\n id: \"semantic\",\n name: \"Semantic Analysis\",\n version: \"1.0.0\",\n description: \"Semantic code analysis and duplicate detection\",\n icon: \"brain\",\n\n // === State ===\n state: semanticStateDefinition,\n actions: semanticActionHandlers,\n\n // === UI Contributions (Declarative) ===\n commands: semanticCommands,\n panels: semanticPanelDefinitions,\n // No toolbar groups for semantic plugin\n\n // === Rules ===\n rules: semanticRuleDefinitions,\n handlesRuleCategories: [\"semantic\"],\n\n // === WebSocket ===\n messageHandlers: semanticMessageHandlers,\n\n // === Issue Aggregation ===\n // Note: Issues come from ESLint rule, not directly from plugin state\n // This is here for completeness but semantic issues flow through ESLint\n getIssues: (_state: SemanticState): IssueContribution => {\n // Semantic issues are reported by the no-semantic-duplicates ESLint rule\n // They don't come from plugin state, so we return empty\n return { pluginId: \"semantic\", issues: new Map() };\n },\n\n // === Browser Actions ===\n // Semantic plugin doesn't need browser actions\n browserActions: [],\n};\n\n// Auto-register with plugin registry on import\npluginRegistry.register(semanticPlugin);\n\nexport default semanticPlugin;\n","/**\n * Semantic Plugin State\n *\n * State shape and initial state for the semantic plugin.\n * No React - pure TypeScript.\n */\n\nimport type { StateDefinition } from \"uilint-core\";\nimport type { IndexStatus, IndexProgress, IndexStats } from \"../types.js\";\n\n/**\n * Semantic plugin state shape\n */\nexport interface SemanticState {\n // === Index Status ===\n /** Current index status */\n indexStatus: IndexStatus;\n /** Indexing progress */\n indexProgress: IndexProgress | null;\n /** Index statistics from last build */\n indexStats: IndexStats | null;\n\n // === Error State ===\n /** Last indexing error */\n lastIndexError: string | null;\n\n // === UI State ===\n /** Currently selected duplicate for inspector */\n selectedDuplicate: {\n sourceDataLoc: string;\n targetDataLoc: string;\n similarity: number;\n } | null;\n}\n\n/**\n * Initial state for the semantic plugin\n */\nexport const semanticInitialState: SemanticState = {\n indexStatus: \"idle\",\n indexProgress: null,\n indexStats: null,\n lastIndexError: null,\n selectedDuplicate: null,\n};\n\n/**\n * State definition for the plugin system\n */\nexport const semanticStateDefinition: StateDefinition<SemanticState> = {\n initialState: semanticInitialState,\n\n computed: {\n /** Whether index is ready for queries */\n isIndexReady: (state) => state.indexStatus === \"ready\",\n\n /** Whether indexing is in progress */\n isIndexing: (state) => state.indexStatus === \"indexing\",\n\n /** Whether there was an error */\n hasError: (state) => state.indexStatus === \"error\" || state.lastIndexError !== null,\n\n /** Progress percentage (0-100) */\n progressPercent: (state) => {\n if (!state.indexProgress) return 0;\n if (state.indexProgress.total === 0) return 0;\n return Math.round((state.indexProgress.current / state.indexProgress.total) * 100);\n },\n },\n\n persist: {\n key: \"uilint-semantic\",\n include: [], // Don't persist anything for now\n },\n};\n","/**\n * Semantic Plugin Action Handlers\n *\n * Plain functions that handle plugin actions.\n * No React - uses PluginContext for state management.\n */\n\nimport type { ActionHandlers, PluginContext } from \"uilint-core\";\nimport type { SemanticState } from \"./state.js\";\nimport type { IndexStats } from \"../types.js\";\n\n// Helper type for cleaner action handler definitions\ntype Handler<TPayload = void> = (\n ctx: PluginContext<SemanticState>,\n payload: TPayload\n) => void | Promise<void>;\n\n// Cast helper for proper typing - wraps typed handlers to satisfy ActionHandlers\nconst h = <TPayload = void>(fn: Handler<TPayload>): Handler<unknown> =>\n fn as Handler<unknown>;\n\n/**\n * Action handlers for the semantic plugin\n */\nexport const semanticActionHandlers: ActionHandlers<SemanticState> = {\n /**\n * Start indexing\n */\n \"start-indexing\": h((ctx) => {\n ctx.setState({\n indexStatus: \"indexing\",\n indexProgress: { current: 0, total: 0 },\n lastIndexError: null,\n });\n\n // Request server to start indexing\n ctx.websocket.send({ type: \"duplicates:index\" });\n }),\n\n /**\n * Handle indexing started\n */\n \"handle-indexing-start\": h((ctx) => {\n ctx.setState({\n indexStatus: \"indexing\",\n indexProgress: { current: 0, total: 0, message: \"Starting...\" },\n lastIndexError: null,\n });\n }),\n\n /**\n * Handle indexing progress\n */\n \"handle-indexing-progress\": h<{ message: string; current?: number; total?: number }>(\n (ctx, payload) => {\n ctx.setState({\n indexProgress: {\n current: payload.current ?? 0,\n total: payload.total ?? 0,\n message: payload.message,\n },\n });\n }\n ),\n\n /**\n * Handle indexing complete\n */\n \"handle-indexing-complete\": h<IndexStats>((ctx, payload) => {\n ctx.setState({\n indexStatus: \"ready\",\n indexProgress: null,\n indexStats: payload,\n lastIndexError: null,\n });\n }),\n\n /**\n * Handle indexing error\n */\n \"handle-indexing-error\": h<{ error: string }>((ctx, payload) => {\n ctx.setState({\n indexStatus: \"error\",\n indexProgress: null,\n lastIndexError: payload.error,\n });\n }),\n\n /**\n * Select a duplicate for viewing in inspector\n */\n \"select-duplicate\": h<{ sourceDataLoc: string; targetDataLoc: string; similarity: number }>(\n (ctx, payload) => {\n ctx.setState({ selectedDuplicate: payload });\n ctx.openInspector(\"duplicates\", payload);\n }\n ),\n\n /**\n * Clear selected duplicate\n */\n \"clear-selected-duplicate\": h((ctx) => {\n ctx.setState({ selectedDuplicate: null });\n }),\n\n /**\n * Toggle heatmap filter for duplicates\n */\n \"toggle-heatmap-filter\": h<{ sourceDataLoc: string; targetDataLoc: string }>(\n (ctx, payload) => {\n ctx.setHeatmapFilter(\n [payload.sourceDataLoc, payload.targetDataLoc],\n \"Duplicate Code\"\n );\n }\n ),\n\n /**\n * Clear heatmap filter\n */\n \"clear-heatmap-filter\": h((ctx) => {\n ctx.clearHeatmapFilter();\n }),\n\n /**\n * Open file in editor\n */\n \"open-editor\": h<{ dataLoc: string }>((ctx, payload) => {\n ctx.openInEditor(payload.dataLoc);\n }),\n};\n","/**\n * Semantic Plugin Commands\n *\n * Command palette commands for the semantic plugin.\n * Declarative - no React.\n */\n\nimport type { CommandDefinition } from \"uilint-core\";\n\n/**\n * Semantic plugin commands\n */\nexport const semanticCommands: CommandDefinition[] = [\n {\n id: \"semantic:rebuild-index\",\n title: \"Rebuild Duplicates Index\",\n keywords: [\"semantic\", \"duplicates\", \"index\", \"rebuild\", \"scan\"],\n category: \"Semantic\",\n subtitle: \"Rebuild the semantic code index for duplicate detection\",\n icon: \"refresh\",\n action: { type: \"start-indexing\" },\n },\n {\n id: \"semantic:clear-filter\",\n title: \"Clear Duplicate Filter\",\n keywords: [\"semantic\", \"duplicates\", \"clear\", \"filter\", \"heatmap\"],\n category: \"Semantic\",\n subtitle: \"Clear the heatmap filter for duplicates\",\n icon: \"x\",\n action: { type: \"clear-heatmap-filter\" },\n },\n];\n","/**\n * Semantic Plugin Panels\n *\n * Inspector panel definitions for the semantic plugin.\n * Declarative - no React.\n */\n\nimport type { PanelDefinition } from \"uilint-core\";\n\n/**\n * Duplicates inspector panel\n */\nexport const duplicatesPanelDefinition: PanelDefinition = {\n id: \"duplicates\",\n title: \"Duplicate Code\",\n priority: 10,\n\n loading: {\n when: { binding: \"isLoading\" },\n message: \"Loading code comparison...\",\n },\n\n empty: {\n when: { expression: \"!sourceCode && !targetCode\" },\n message: \"No duplicate information available.\",\n icon: \"search\",\n },\n\n layout: [\n // Similarity badge\n {\n type: \"badge\",\n variant: \"similarity\",\n value: { binding: \"similarity\" },\n centered: true,\n },\n\n // Source code section\n {\n type: \"code-viewer\",\n label: \"This Code\",\n icon: \"target\",\n code: {\n fetch: {\n type: \"source-code\",\n params: {\n filePath: { binding: \"sourceLocation.filePath\" },\n line: { binding: \"sourceLocation.startLine\" },\n contextAbove: 10,\n contextBelow: 10,\n },\n },\n },\n location: { binding: \"sourceLocation\" },\n diffHighlighting: true,\n maxHeight: 250,\n onNavigate: {\n type: \"open-editor\",\n payloadBindings: { dataLoc: \"sourceDataLoc\" },\n },\n },\n\n // Target code section\n {\n type: \"code-viewer\",\n label: \"Similar Code\",\n icon: \"link\",\n code: {\n fetch: {\n type: \"source-code\",\n params: {\n filePath: { binding: \"targetLocation.filePath\" },\n line: { binding: \"targetLocation.startLine\" },\n contextAbove: 10,\n contextBelow: 10,\n },\n },\n },\n location: { binding: \"targetLocation\" },\n diffHighlighting: true,\n maxHeight: 250,\n onNavigate: {\n type: \"open-editor\",\n payloadBindings: { dataLoc: \"targetDataLoc\" },\n },\n },\n\n { type: \"divider\", spacing: \"medium\" },\n\n // Actions\n {\n type: \"actions\",\n direction: \"row\",\n actions: [\n {\n id: \"toggle-heatmap\",\n label: {\n condition: { binding: \"heatmapFilterActive\" },\n true: \"Clear Heatmap Filter\",\n false: \"Focus in Heatmap\",\n },\n icon: \"filter\",\n variant: \"primary\",\n action: {\n type: \"toggle-heatmap-filter\",\n payloadBindings: {\n sourceDataLoc: \"sourceDataLoc\",\n targetDataLoc: \"targetDataLoc\",\n },\n },\n },\n ],\n },\n ],\n};\n\n/**\n * Index status panel\n */\nexport const indexStatusPanelDefinition: PanelDefinition = {\n id: \"semantic-index-status\",\n title: \"Index Status\",\n priority: 5,\n\n layout: [\n // Status badge\n {\n type: \"badge\",\n variant: \"status\",\n value: { binding: \"indexStatus\" },\n centered: true,\n },\n\n // Progress (when indexing)\n {\n type: \"conditional\",\n condition: { expression: \"indexStatus === 'indexing'\" },\n then: [\n {\n type: \"progress\",\n value: { binding: \"progressPercent\" },\n label: { binding: \"indexProgress.message\" },\n },\n ],\n },\n\n // Stats (when ready)\n {\n type: \"conditional\",\n condition: { expression: \"indexStatus === 'ready' && indexStats\" },\n then: [\n {\n type: \"text\",\n content: { binding: \"indexStats.totalChunks\" },\n variant: \"body\",\n },\n ],\n },\n\n // Error (when error)\n {\n type: \"conditional\",\n condition: { expression: \"indexStatus === 'error'\" },\n then: [\n {\n type: \"text\",\n content: { binding: \"lastIndexError\" },\n variant: \"error\",\n },\n ],\n },\n\n { type: \"divider\" },\n\n // Rebuild action\n {\n type: \"actions\",\n direction: \"column\",\n actions: [\n {\n id: \"rebuild-index\",\n label: \"Rebuild Index\",\n icon: \"refresh\",\n variant: \"secondary\",\n action: { type: \"start-indexing\" },\n disabled: { binding: \"isIndexing\" },\n },\n ],\n },\n ],\n};\n\n/**\n * All semantic panel definitions\n */\nexport const semanticPanelDefinitions: PanelDefinition[] = [\n duplicatesPanelDefinition,\n indexStatusPanelDefinition,\n];\n","/**\n * Semantic Plugin Rules\n *\n * Rule definitions for the semantic plugin.\n * Declarative - no React.\n */\n\nimport type { RuleDefinition } from \"uilint-core\";\n\n/**\n * no-semantic-duplicates rule definition\n */\nexport const noSemanticDuplicatesRuleDefinition: RuleDefinition = {\n id: \"no-semantic-duplicates\",\n name: \"No Semantic Duplicates\",\n description: \"Warns when code is semantically similar to existing code\",\n category: \"semantic\",\n icon: \"search\",\n defaultSeverity: \"warn\",\n defaultEnabled: false,\n heatmapColor: \"#f59e0b\", // Amber\n customInspectorPanel: \"duplicates\",\n requirements: [\n {\n type: \"semantic-index\",\n description: \"Requires semantic index for duplicate detection\",\n setupHint: \"Run: uilint duplicates index\",\n },\n ],\n defaultOptions: [\n {\n threshold: 0.75,\n indexPath: \".uilint/.duplicates-index\",\n minLines: 3,\n confidenceLevel: \"low\",\n useStructuralBoost: true,\n includeSameFile: false,\n kind: \"all\",\n },\n ],\n optionSchema: {\n fields: [\n {\n key: \"threshold\",\n label: \"Similarity Threshold\",\n type: \"number\",\n defaultValue: 0.75,\n description: \"Minimum similarity score (0-1) to report as duplicate.\",\n },\n {\n key: \"confidenceLevel\",\n label: \"Minimum Confidence\",\n type: \"select\",\n defaultValue: \"low\",\n options: [\n { value: \"high\", label: \"High (>90%) - Likely copy-paste\" },\n { value: \"medium\", label: \"Medium (>75%) - Semantically similar\" },\n { value: \"low\", label: \"Low (>60%) - Possibly related\" },\n ],\n description: \"Minimum confidence level for reported duplicates.\",\n },\n {\n key: \"kind\",\n label: \"Chunk Type\",\n type: \"select\",\n defaultValue: \"all\",\n options: [\n { value: \"all\", label: \"All\" },\n { value: \"component\", label: \"Components\" },\n { value: \"hook\", label: \"Hooks\" },\n { value: \"function\", label: \"Functions\" },\n ],\n description: \"Filter duplicates by code type.\",\n },\n {\n key: \"useStructuralBoost\",\n label: \"Use Structural Analysis\",\n type: \"boolean\",\n defaultValue: true,\n description: \"Include structural similarity (props, JSX, hooks) in scoring.\",\n },\n {\n key: \"includeSameFile\",\n label: \"Include Same-File Duplicates\",\n type: \"boolean\",\n defaultValue: false,\n description: \"Report duplicates within the same file.\",\n },\n {\n key: \"minLines\",\n label: \"Minimum Lines\",\n type: \"number\",\n defaultValue: 3,\n description: \"Minimum number of lines for a code chunk.\",\n },\n ],\n },\n docs: `\n## No Semantic Duplicates Rule\n\nThis rule detects code that is semantically similar to existing code in your codebase.\n\n### How it Works\n\n1. The codebase is indexed to extract \"chunks\" (components, hooks, functions)\n2. Each chunk is embedded using an AI model to capture semantic meaning\n3. During linting, chunks are compared against the index\n4. Similar chunks above the threshold are reported\n\n### Confidence Levels\n\n- **High (>90%)**: Very likely copy-paste or near-identical code\n- **Medium (>75%)**: Semantically similar, possibly refactorable\n- **Low (>60%)**: Possibly related, worth reviewing\n\n### Setup\n\n1. Run \\`uilint duplicates index\\` to build the semantic index\n2. Enable this rule in your ESLint config\n3. The rule will report duplicates during linting\n\n### Performance\n\nThe rule uses a pre-built index, so it's fast during linting.\nRe-index periodically to keep the index up to date.\n `.trim(),\n};\n\n/**\n * All semantic rule definitions\n */\nexport const semanticRuleDefinitions: RuleDefinition[] = [\n noSemanticDuplicatesRuleDefinition,\n];\n","/**\n * Semantic Plugin Message Handlers\n *\n * WebSocket message handlers for the semantic plugin.\n * Plain functions - no React.\n */\n\nimport type { MessageHandlers, PluginContext } from \"uilint-core\";\nimport type { SemanticState } from \"./state.js\";\nimport type {\n DuplicatesIndexingStartMessage,\n DuplicatesIndexingProgressMessage,\n DuplicatesIndexingCompleteMessage,\n DuplicatesIndexingErrorMessage,\n} from \"../types.js\";\n\n/**\n * Message handlers for the semantic plugin\n */\nexport const semanticMessageHandlers: MessageHandlers<SemanticState> = {\n /**\n * Handle indexing started\n */\n \"duplicates:indexing:start\": (ctx: PluginContext<SemanticState>) => {\n ctx.dispatch(\"handle-indexing-start\");\n },\n\n /**\n * Handle indexing progress\n */\n \"duplicates:indexing:progress\": (\n ctx: PluginContext<SemanticState>,\n message: unknown\n ) => {\n const msg = message as DuplicatesIndexingProgressMessage;\n ctx.dispatch(\"handle-indexing-progress\", {\n message: msg.message,\n current: msg.current,\n total: msg.total,\n });\n },\n\n /**\n * Handle indexing complete\n */\n \"duplicates:indexing:complete\": (\n ctx: PluginContext<SemanticState>,\n message: unknown\n ) => {\n const msg = message as DuplicatesIndexingCompleteMessage;\n ctx.dispatch(\"handle-indexing-complete\", {\n added: msg.added,\n modified: msg.modified,\n deleted: msg.deleted,\n totalChunks: msg.totalChunks,\n duration: msg.duration,\n });\n },\n\n /**\n * Handle indexing error\n */\n \"duplicates:indexing:error\": (\n ctx: PluginContext<SemanticState>,\n message: unknown\n ) => {\n const msg = message as DuplicatesIndexingErrorMessage;\n ctx.dispatch(\"handle-indexing-error\", { error: msg.error });\n },\n};\n"],"mappings":"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","names":[]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
//# sourceMappingURL=chunk-QZKD24XZ.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
export { a as Chunk, C as ChunkKind, b as ConfidenceLevel, c as DuplicateGroup, D as DuplicateMatch, h as DuplicatesIndexingCompleteMessage, i as DuplicatesIndexingErrorMessage, g as DuplicatesIndexingProgressMessage, f as DuplicatesIndexingStartMessage, j as DuplicatesMessage, F as FindDuplicatesOptions, d as IndexProgress, e as IndexStats, I as IndexStatus, k as SemanticState, S as SimilaritySearchOptions, default as semanticPlugin } from './plugin/index.js';
|
|
2
|
+
import 'uilint-core';
|
package/dist/index.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}
|
package/dist/node.d.ts
ADDED
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
export { a as Chunk, C as ChunkKind, b as ConfidenceLevel, c as DuplicateGroup, D as DuplicateMatch, h as DuplicatesIndexingCompleteMessage, i as DuplicatesIndexingErrorMessage, g as DuplicatesIndexingProgressMessage, f as DuplicatesIndexingStartMessage, j as DuplicatesMessage, F as FindDuplicatesOptions, d as IndexProgress, e as IndexStats, I as IndexStatus, k as SemanticState, S as SimilaritySearchOptions, default as semanticPlugin } from './plugin/index.js';
|
|
2
|
+
import 'uilint-core';
|
package/dist/node.js
ADDED
package/dist/node.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}
|
|
@@ -0,0 +1,207 @@
|
|
|
1
|
+
import { PluginWithHandlers } from 'uilint-core';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Semantic Plugin Types
|
|
5
|
+
*
|
|
6
|
+
* All types for semantic code analysis and duplicate detection.
|
|
7
|
+
*/
|
|
8
|
+
/**
|
|
9
|
+
* Kind of code chunk
|
|
10
|
+
*/
|
|
11
|
+
type ChunkKind = "component" | "hook" | "function";
|
|
12
|
+
/**
|
|
13
|
+
* Code chunk extracted for analysis
|
|
14
|
+
*/
|
|
15
|
+
interface Chunk {
|
|
16
|
+
/** Unique identifier */
|
|
17
|
+
id: string;
|
|
18
|
+
/** File path */
|
|
19
|
+
filePath: string;
|
|
20
|
+
/** Start line (1-indexed) */
|
|
21
|
+
startLine: number;
|
|
22
|
+
/** End line (1-indexed) */
|
|
23
|
+
endLine: number;
|
|
24
|
+
/** Chunk name (function/component name) */
|
|
25
|
+
name: string | null;
|
|
26
|
+
/** Kind of chunk */
|
|
27
|
+
kind: ChunkKind;
|
|
28
|
+
/** Source code content */
|
|
29
|
+
content: string;
|
|
30
|
+
/** File content hash (for incremental updates) */
|
|
31
|
+
fileHash: string;
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Confidence level for duplicate detection
|
|
35
|
+
*/
|
|
36
|
+
type ConfidenceLevel = "high" | "medium" | "low";
|
|
37
|
+
/**
|
|
38
|
+
* Duplicate match result
|
|
39
|
+
*/
|
|
40
|
+
interface DuplicateMatch {
|
|
41
|
+
/** Unique match ID */
|
|
42
|
+
id: string;
|
|
43
|
+
/** Similarity score (0-1) */
|
|
44
|
+
score: number;
|
|
45
|
+
/** Combined score (semantic + structural) */
|
|
46
|
+
combinedScore?: number;
|
|
47
|
+
/** File path of the duplicate */
|
|
48
|
+
filePath: string;
|
|
49
|
+
/** Start line of the duplicate */
|
|
50
|
+
startLine: number;
|
|
51
|
+
/** End line of the duplicate */
|
|
52
|
+
endLine: number;
|
|
53
|
+
/** Name of the duplicate chunk */
|
|
54
|
+
name: string | null;
|
|
55
|
+
/** Kind of the duplicate chunk */
|
|
56
|
+
kind: ChunkKind;
|
|
57
|
+
/** Confidence level */
|
|
58
|
+
confidence: ConfidenceLevel;
|
|
59
|
+
/** Source code of the match (for display) */
|
|
60
|
+
sourceCode?: string;
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* Duplicate group (set of similar chunks)
|
|
64
|
+
*/
|
|
65
|
+
interface DuplicateGroup {
|
|
66
|
+
/** Group ID */
|
|
67
|
+
id: string;
|
|
68
|
+
/** Representative chunk */
|
|
69
|
+
representative: Chunk;
|
|
70
|
+
/** All similar chunks in this group */
|
|
71
|
+
members: DuplicateMatch[];
|
|
72
|
+
/** Average similarity within group */
|
|
73
|
+
averageSimilarity: number;
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* Indexing status
|
|
77
|
+
*/
|
|
78
|
+
type IndexStatus = "idle" | "indexing" | "ready" | "error";
|
|
79
|
+
/**
|
|
80
|
+
* Indexing progress
|
|
81
|
+
*/
|
|
82
|
+
interface IndexProgress {
|
|
83
|
+
current: number;
|
|
84
|
+
total: number;
|
|
85
|
+
message?: string;
|
|
86
|
+
}
|
|
87
|
+
/**
|
|
88
|
+
* Index statistics
|
|
89
|
+
*/
|
|
90
|
+
interface IndexStats {
|
|
91
|
+
totalChunks: number;
|
|
92
|
+
added: number;
|
|
93
|
+
modified: number;
|
|
94
|
+
deleted: number;
|
|
95
|
+
duration: number;
|
|
96
|
+
}
|
|
97
|
+
/**
|
|
98
|
+
* Options for finding duplicates
|
|
99
|
+
*/
|
|
100
|
+
interface FindDuplicatesOptions {
|
|
101
|
+
/** Minimum similarity threshold (0-1) */
|
|
102
|
+
threshold?: number;
|
|
103
|
+
/** Minimum confidence level */
|
|
104
|
+
confidenceLevel?: ConfidenceLevel;
|
|
105
|
+
/** Filter by chunk kind */
|
|
106
|
+
kind?: ChunkKind | "all";
|
|
107
|
+
/** Include structural similarity boost */
|
|
108
|
+
useStructuralBoost?: boolean;
|
|
109
|
+
/** Include same-file duplicates */
|
|
110
|
+
includeSameFile?: boolean;
|
|
111
|
+
/** Minimum lines for a chunk */
|
|
112
|
+
minLines?: number;
|
|
113
|
+
/** Maximum results */
|
|
114
|
+
limit?: number;
|
|
115
|
+
}
|
|
116
|
+
/**
|
|
117
|
+
* Options for similarity search
|
|
118
|
+
*/
|
|
119
|
+
interface SimilaritySearchOptions {
|
|
120
|
+
/** Minimum similarity threshold (0-1) */
|
|
121
|
+
threshold?: number;
|
|
122
|
+
/** Maximum results */
|
|
123
|
+
limit?: number;
|
|
124
|
+
/** Filter by chunk kind */
|
|
125
|
+
kind?: ChunkKind | "all";
|
|
126
|
+
}
|
|
127
|
+
/**
|
|
128
|
+
* Server -> Client: Indexing started
|
|
129
|
+
*/
|
|
130
|
+
interface DuplicatesIndexingStartMessage {
|
|
131
|
+
type: "duplicates:indexing:start";
|
|
132
|
+
}
|
|
133
|
+
/**
|
|
134
|
+
* Server -> Client: Indexing progress
|
|
135
|
+
*/
|
|
136
|
+
interface DuplicatesIndexingProgressMessage {
|
|
137
|
+
type: "duplicates:indexing:progress";
|
|
138
|
+
message: string;
|
|
139
|
+
current?: number;
|
|
140
|
+
total?: number;
|
|
141
|
+
}
|
|
142
|
+
/**
|
|
143
|
+
* Server -> Client: Indexing complete
|
|
144
|
+
*/
|
|
145
|
+
interface DuplicatesIndexingCompleteMessage {
|
|
146
|
+
type: "duplicates:indexing:complete";
|
|
147
|
+
added: number;
|
|
148
|
+
modified: number;
|
|
149
|
+
deleted: number;
|
|
150
|
+
totalChunks: number;
|
|
151
|
+
duration: number;
|
|
152
|
+
}
|
|
153
|
+
/**
|
|
154
|
+
* Server -> Client: Indexing error
|
|
155
|
+
*/
|
|
156
|
+
interface DuplicatesIndexingErrorMessage {
|
|
157
|
+
type: "duplicates:indexing:error";
|
|
158
|
+
error: string;
|
|
159
|
+
}
|
|
160
|
+
/**
|
|
161
|
+
* Union of all duplicates WebSocket messages
|
|
162
|
+
*/
|
|
163
|
+
type DuplicatesMessage = DuplicatesIndexingStartMessage | DuplicatesIndexingProgressMessage | DuplicatesIndexingCompleteMessage | DuplicatesIndexingErrorMessage;
|
|
164
|
+
|
|
165
|
+
/**
|
|
166
|
+
* Semantic Plugin State
|
|
167
|
+
*
|
|
168
|
+
* State shape and initial state for the semantic plugin.
|
|
169
|
+
* No React - pure TypeScript.
|
|
170
|
+
*/
|
|
171
|
+
|
|
172
|
+
/**
|
|
173
|
+
* Semantic plugin state shape
|
|
174
|
+
*/
|
|
175
|
+
interface SemanticState {
|
|
176
|
+
/** Current index status */
|
|
177
|
+
indexStatus: IndexStatus;
|
|
178
|
+
/** Indexing progress */
|
|
179
|
+
indexProgress: IndexProgress | null;
|
|
180
|
+
/** Index statistics from last build */
|
|
181
|
+
indexStats: IndexStats | null;
|
|
182
|
+
/** Last indexing error */
|
|
183
|
+
lastIndexError: string | null;
|
|
184
|
+
/** Currently selected duplicate for inspector */
|
|
185
|
+
selectedDuplicate: {
|
|
186
|
+
sourceDataLoc: string;
|
|
187
|
+
targetDataLoc: string;
|
|
188
|
+
similarity: number;
|
|
189
|
+
} | null;
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
/**
|
|
193
|
+
* Semantic Plugin Definition
|
|
194
|
+
*
|
|
195
|
+
* Complete plugin export - NO REACT.
|
|
196
|
+
* This is the main plugin definition that gets registered with pluginRegistry.
|
|
197
|
+
*/
|
|
198
|
+
|
|
199
|
+
/**
|
|
200
|
+
* Semantic plugin definition
|
|
201
|
+
*
|
|
202
|
+
* Contains everything needed for semantic analysis EXCEPT React components.
|
|
203
|
+
* The host (uilint-react) imports this and renders the appropriate UI.
|
|
204
|
+
*/
|
|
205
|
+
declare const semanticPlugin: PluginWithHandlers<SemanticState>;
|
|
206
|
+
|
|
207
|
+
export { type ChunkKind as C, type DuplicateMatch as D, type FindDuplicatesOptions as F, type IndexStatus as I, type SimilaritySearchOptions as S, type Chunk as a, type ConfidenceLevel as b, type DuplicateGroup as c, type IndexProgress as d, semanticPlugin as default, type IndexStats as e, type DuplicatesIndexingStartMessage as f, type DuplicatesIndexingProgressMessage as g, type DuplicatesIndexingCompleteMessage as h, type DuplicatesIndexingErrorMessage as i, type DuplicatesMessage as j, type SemanticState as k, semanticPlugin };
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}
|
package/package.json
ADDED
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "uilint-semantic",
|
|
3
|
+
"version": "0.2.136",
|
|
4
|
+
"description": "Semantic code analysis and duplicate detection for UILint",
|
|
5
|
+
"author": "Peter Suggate",
|
|
6
|
+
"repository": {
|
|
7
|
+
"type": "git",
|
|
8
|
+
"url": "https://github.com/peter-suggate/uilint.git",
|
|
9
|
+
"directory": "packages/uilint-semantic"
|
|
10
|
+
},
|
|
11
|
+
"type": "module",
|
|
12
|
+
"main": "./dist/index.js",
|
|
13
|
+
"module": "./dist/index.js",
|
|
14
|
+
"types": "./dist/index.d.ts",
|
|
15
|
+
"exports": {
|
|
16
|
+
".": {
|
|
17
|
+
"types": "./dist/index.d.ts",
|
|
18
|
+
"import": "./dist/index.js"
|
|
19
|
+
},
|
|
20
|
+
"./node": {
|
|
21
|
+
"types": "./dist/node.d.ts",
|
|
22
|
+
"import": "./dist/node.js"
|
|
23
|
+
},
|
|
24
|
+
"./plugin": {
|
|
25
|
+
"types": "./dist/plugin/index.d.ts",
|
|
26
|
+
"import": "./dist/plugin/index.js"
|
|
27
|
+
}
|
|
28
|
+
},
|
|
29
|
+
"files": [
|
|
30
|
+
"dist"
|
|
31
|
+
],
|
|
32
|
+
"peerDependencies": {
|
|
33
|
+
"uilint-core": "0.2.136"
|
|
34
|
+
},
|
|
35
|
+
"dependencies": {
|
|
36
|
+
"@typescript-eslint/typescript-estree": "^8.35.1",
|
|
37
|
+
"glob": "^11.0.0",
|
|
38
|
+
"xxhash-wasm": "^1.1.0"
|
|
39
|
+
},
|
|
40
|
+
"optionalDependencies": {
|
|
41
|
+
"ollama": "^0.6.3"
|
|
42
|
+
},
|
|
43
|
+
"devDependencies": {
|
|
44
|
+
"typescript": "^5.7.0",
|
|
45
|
+
"tsup": "^8.0.0",
|
|
46
|
+
"vitest": "^2.0.0"
|
|
47
|
+
},
|
|
48
|
+
"scripts": {
|
|
49
|
+
"build": "tsup",
|
|
50
|
+
"dev": "tsup --watch",
|
|
51
|
+
"test": "vitest",
|
|
52
|
+
"typecheck": "tsc --noEmit"
|
|
53
|
+
}
|
|
54
|
+
}
|