uilint-semantic 0.2.138 → 0.2.139

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.
@@ -0,0 +1 @@
1
+ //# sourceMappingURL=chunk-PXUZIL7H.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}
@@ -0,0 +1,605 @@
1
+ // src/eslint-rules/no-semantic-duplicates.ts
2
+ import { createRule, defineRuleMeta } from "uilint-eslint";
3
+ import { existsSync, readFileSync, appendFileSync, writeFileSync } from "fs";
4
+ import { dirname, join, relative } from "path";
5
+ var logFile = null;
6
+ var logInitialized = false;
7
+ function initLog(projectRoot) {
8
+ if (logFile) return;
9
+ const uilintDir = join(projectRoot, ".uilint");
10
+ if (existsSync(uilintDir)) {
11
+ logFile = join(uilintDir, "no-semantic-duplicates.log");
12
+ }
13
+ }
14
+ function log(message) {
15
+ if (!logFile) return;
16
+ try {
17
+ const timestamp = (/* @__PURE__ */ new Date()).toISOString();
18
+ const line = `[${timestamp}] ${message}
19
+ `;
20
+ if (!logInitialized) {
21
+ writeFileSync(logFile, line);
22
+ logInitialized = true;
23
+ } else {
24
+ appendFileSync(logFile, line);
25
+ }
26
+ } catch {
27
+ }
28
+ }
29
+ var meta = defineRuleMeta({
30
+ id: "no-semantic-duplicates",
31
+ version: "1.0.0",
32
+ name: "No Semantic Duplicates",
33
+ description: "Warn when code is semantically similar to existing code",
34
+ defaultSeverity: "warn",
35
+ category: "semantic",
36
+ icon: "\u{1F50D}",
37
+ hint: "Finds similar code via embeddings",
38
+ defaultEnabled: false,
39
+ plugin: "semantic",
40
+ eslintImport: "uilint-semantic/eslint-rules/no-semantic-duplicates",
41
+ customInspector: "duplicates",
42
+ requirements: [
43
+ {
44
+ type: "semantic-index",
45
+ description: "Requires semantic index for duplicate detection",
46
+ setupHint: "Run: uilint duplicates index"
47
+ }
48
+ ],
49
+ postInstallInstructions: "Run 'uilint duplicates index' to build the semantic index before using this rule.",
50
+ defaultOptions: [{
51
+ threshold: 0.75,
52
+ indexPath: ".uilint/.duplicates-index",
53
+ minLines: 3,
54
+ confidenceLevel: "low",
55
+ useStructuralBoost: true,
56
+ includeSameFile: false,
57
+ kind: "all"
58
+ }],
59
+ optionSchema: {
60
+ fields: [
61
+ {
62
+ key: "threshold",
63
+ label: "Similarity threshold",
64
+ type: "number",
65
+ defaultValue: 0.75,
66
+ description: "Minimum similarity score (0-1) to report as duplicate. Lower values catch more potential duplicates. Recommended: 0.75 (default), 0.85 (strict), 0.65 (lenient)."
67
+ },
68
+ {
69
+ key: "confidenceLevel",
70
+ label: "Minimum confidence",
71
+ type: "select",
72
+ defaultValue: "low",
73
+ options: [
74
+ { value: "high", label: "High (\u226590%) - Likely copy-paste" },
75
+ { value: "medium", label: "Medium (\u226575%) - Semantically similar" },
76
+ { value: "low", label: "Low (\u226560%) - Possibly related" }
77
+ ],
78
+ description: "Only report duplicates at or above this confidence level. High = fewer but more certain matches."
79
+ },
80
+ {
81
+ key: "useStructuralBoost",
82
+ label: "Use structural similarity",
83
+ type: "boolean",
84
+ defaultValue: true,
85
+ description: "Boost similarity scores based on structural overlap (props, JSX elements, hooks). Helps catch duplicates with different variable names."
86
+ },
87
+ {
88
+ key: "kind",
89
+ label: "Code kind filter",
90
+ type: "select",
91
+ defaultValue: "all",
92
+ options: [
93
+ { value: "all", label: "All code" },
94
+ { value: "component", label: "Components only" },
95
+ { value: "hook", label: "Hooks only" },
96
+ { value: "function", label: "Functions only" }
97
+ ],
98
+ description: "Only detect duplicates of a specific code type."
99
+ },
100
+ {
101
+ key: "includeSameFile",
102
+ label: "Include same-file duplicates",
103
+ type: "boolean",
104
+ defaultValue: false,
105
+ description: "Report duplicates within the same file (e.g., Card and CardAlt in cards.tsx)."
106
+ },
107
+ {
108
+ key: "indexPath",
109
+ label: "Index path",
110
+ type: "text",
111
+ defaultValue: ".uilint/.duplicates-index",
112
+ description: "Path to the semantic duplicates index directory."
113
+ },
114
+ {
115
+ key: "minLines",
116
+ label: "Minimum lines",
117
+ type: "number",
118
+ defaultValue: 3,
119
+ description: "Minimum number of lines for a chunk to be reported as a potential duplicate."
120
+ }
121
+ ]
122
+ },
123
+ docs: `
124
+ ## What it does
125
+
126
+ Warns when code (components, hooks, functions) is semantically similar to other
127
+ code in the codebase. Unlike syntactic duplicate detection, this finds code that
128
+ implements similar functionality even if written differently.
129
+
130
+ ## Prerequisites
131
+
132
+ Before using this rule, you must build the semantic index:
133
+
134
+ \`\`\`bash
135
+ uilint duplicates index
136
+ \`\`\`
137
+
138
+ This creates an embedding-based index at \`.uilint/.duplicates-index/\`.
139
+
140
+ ## Why it's useful
141
+
142
+ - **Reduce Duplication**: Find components/hooks that could be consolidated
143
+ - **Discover Patterns**: Identify similar code that could be abstracted
144
+ - **Code Quality**: Encourage reuse over reimplementation
145
+ - **Fast**: Queries pre-built index, no LLM calls during linting
146
+
147
+ ## How it works
148
+
149
+ 1. The rule checks if the current file is in the semantic index
150
+ 2. For each indexed code chunk, it looks up similar chunks
151
+ 3. If similar chunks exist above the threshold, it reports a warning
152
+
153
+ ## Examples
154
+
155
+ ### Semantic duplicates detected:
156
+
157
+ \`\`\`tsx
158
+ // UserCard.tsx - Original component
159
+ export function UserCard({ user }) {
160
+ return (
161
+ <div className="card">
162
+ <img src={user.avatar} />
163
+ <h3>{user.name}</h3>
164
+ </div>
165
+ );
166
+ }
167
+
168
+ // ProfileCard.tsx - Semantically similar (warning!)
169
+ export function ProfileCard({ profile }) {
170
+ return (
171
+ <article className="profile">
172
+ <img src={profile.avatarUrl} />
173
+ <h2>{profile.displayName}</h2>
174
+ </article>
175
+ );
176
+ }
177
+ \`\`\`
178
+
179
+ ## Configuration
180
+
181
+ \`\`\`js
182
+ // eslint.config.js
183
+ "uilint/no-semantic-duplicates": ["warn", {
184
+ // Core detection settings
185
+ threshold: 0.75, // Similarity threshold (0-1). Lower = more matches.
186
+ confidenceLevel: "medium", // "high" (\u226590%), "medium" (\u226575%), "low" (\u226560%)
187
+
188
+ // Detection enhancements
189
+ useStructuralBoost: true, // Boost scores based on props/JSX/hooks overlap
190
+
191
+ // Filtering
192
+ kind: "all", // "all", "component", "hook", "function"
193
+ includeSameFile: false, // Include duplicates within the same file
194
+ minLines: 3, // Minimum lines to report
195
+
196
+ // Index location
197
+ indexPath: ".uilint/.duplicates-index"
198
+ }]
199
+ \`\`\`
200
+
201
+ ### Preset configurations
202
+
203
+ **Strict** - High-confidence duplicates only:
204
+ \`\`\`js
205
+ { threshold: 0.85, confidenceLevel: "high" }
206
+ \`\`\`
207
+
208
+ **Normal** (default) - Balanced detection:
209
+ \`\`\`js
210
+ { threshold: 0.75, confidenceLevel: "low" }
211
+ \`\`\`
212
+
213
+ **Lenient** - Catch more potential duplicates:
214
+ \`\`\`js
215
+ { threshold: 0.65, confidenceLevel: "low" }
216
+ \`\`\`
217
+
218
+ ## Confidence Levels
219
+
220
+ The rule assigns confidence levels based on similarity scores:
221
+
222
+ - \u{1F534} **High (\u226590%)** - Likely copy-paste or near-identical code. Strongly recommend consolidation.
223
+ - \u{1F7E1} **Medium (75-89%)** - Semantically similar. Review for potential abstraction.
224
+ - \u{1F7E2} **Low (60-74%)** - Possibly related patterns. Optional review.
225
+
226
+ ## Notes
227
+
228
+ - Run \`uilint duplicates index\` after significant code changes
229
+ - Use \`uilint duplicates find\` to explore all duplicate groups
230
+ - The rule only reports if the file is in the index
231
+ - Structural boost helps detect duplicates with different variable names (e.g., Badge vs Tag)
232
+ `
233
+ });
234
+ var indexCache = null;
235
+ function clearIndexCache() {
236
+ indexCache = null;
237
+ }
238
+ function findProjectRoot(startPath, indexPath) {
239
+ let current = startPath;
240
+ let lastPackageJson = null;
241
+ while (current !== dirname(current)) {
242
+ const uilintDir = join(current, indexPath);
243
+ if (existsSync(join(uilintDir, "manifest.json"))) {
244
+ return current;
245
+ }
246
+ if (existsSync(join(current, "package.json"))) {
247
+ lastPackageJson = current;
248
+ }
249
+ current = dirname(current);
250
+ }
251
+ return lastPackageJson || startPath;
252
+ }
253
+ function loadIndex(projectRoot, indexPath) {
254
+ const fullIndexPath = join(projectRoot, indexPath);
255
+ log(`loadIndex called: projectRoot=${projectRoot}, indexPath=${indexPath}`);
256
+ log(`fullIndexPath=${fullIndexPath}`);
257
+ if (indexCache && indexCache.projectRoot === projectRoot) {
258
+ log(`Using cached index (${indexCache.vectorStore.size} vectors, ${indexCache.fileToChunks.size} files)`);
259
+ return indexCache;
260
+ }
261
+ const manifestPath = join(fullIndexPath, "manifest.json");
262
+ if (!existsSync(manifestPath)) {
263
+ log(`Index not found: manifest.json missing at ${manifestPath}`);
264
+ return null;
265
+ }
266
+ try {
267
+ const metadataPath = join(fullIndexPath, "metadata.json");
268
+ if (!existsSync(metadataPath)) {
269
+ log(`Index not found: metadata.json missing at ${metadataPath}`);
270
+ return null;
271
+ }
272
+ const metadataContent = readFileSync(metadataPath, "utf-8");
273
+ const metadataJson = JSON.parse(metadataContent);
274
+ const entries = metadataJson.entries || metadataJson;
275
+ log(`Loaded metadata.json: ${Object.keys(entries).length} entries`);
276
+ const metadataStore = /* @__PURE__ */ new Map();
277
+ const fileToChunks = /* @__PURE__ */ new Map();
278
+ for (const [id, meta2] of Object.entries(entries)) {
279
+ const m = meta2;
280
+ metadataStore.set(id, {
281
+ filePath: m.filePath,
282
+ startLine: m.startLine,
283
+ endLine: m.endLine,
284
+ startColumn: m.startColumn ?? 0,
285
+ endColumn: m.endColumn ?? 0,
286
+ name: m.name,
287
+ kind: m.kind
288
+ });
289
+ const chunks = fileToChunks.get(m.filePath) || [];
290
+ chunks.push(id);
291
+ fileToChunks.set(m.filePath, chunks);
292
+ }
293
+ log(`File to chunks mapping:`);
294
+ for (const [filePath, chunks] of fileToChunks.entries()) {
295
+ log(` ${filePath}: ${chunks.length} chunks (${chunks.join(", ")})`);
296
+ }
297
+ const vectorsPath = join(fullIndexPath, "embeddings.bin");
298
+ const idsPath = join(fullIndexPath, "ids.json");
299
+ const vectorStore = /* @__PURE__ */ new Map();
300
+ if (existsSync(vectorsPath) && existsSync(idsPath)) {
301
+ const idsContent = readFileSync(idsPath, "utf-8");
302
+ const ids = JSON.parse(idsContent);
303
+ log(`Loaded ids.json: ${ids.length} IDs`);
304
+ const buffer = readFileSync(vectorsPath);
305
+ const view = new DataView(buffer.buffer, buffer.byteOffset, buffer.byteLength);
306
+ const dimension = view.getUint32(0, true);
307
+ const count = view.getUint32(4, true);
308
+ log(`Embeddings binary: dimension=${dimension}, count=${count}`);
309
+ let offset = 8;
310
+ for (let i = 0; i < count && i < ids.length; i++) {
311
+ const vector = [];
312
+ for (let j = 0; j < dimension; j++) {
313
+ vector.push(view.getFloat32(offset, true));
314
+ offset += 4;
315
+ }
316
+ vectorStore.set(ids[i], vector);
317
+ }
318
+ log(`Loaded ${vectorStore.size} vectors into store`);
319
+ } else {
320
+ log(`Missing vectors or ids files: vectorsPath=${existsSync(vectorsPath)}, idsPath=${existsSync(idsPath)}`);
321
+ }
322
+ indexCache = {
323
+ projectRoot,
324
+ vectorStore,
325
+ metadataStore,
326
+ fileToChunks
327
+ };
328
+ log(`Index loaded successfully: ${vectorStore.size} vectors, ${metadataStore.size} metadata entries, ${fileToChunks.size} files`);
329
+ return indexCache;
330
+ } catch (err) {
331
+ log(`Error loading index: ${err}`);
332
+ return null;
333
+ }
334
+ }
335
+ function cosineSimilarity(a, b) {
336
+ if (a.length !== b.length) return 0;
337
+ let dotProduct = 0;
338
+ let normA = 0;
339
+ let normB = 0;
340
+ for (let i = 0; i < a.length; i++) {
341
+ dotProduct += a[i] * b[i];
342
+ normA += a[i] * a[i];
343
+ normB += b[i] * b[i];
344
+ }
345
+ const denominator = Math.sqrt(normA) * Math.sqrt(normB);
346
+ return denominator === 0 ? 0 : dotProduct / denominator;
347
+ }
348
+ function extractCodeFromFile(filePath, startLine, endLine) {
349
+ try {
350
+ if (!existsSync(filePath)) {
351
+ return null;
352
+ }
353
+ const content = readFileSync(filePath, "utf-8");
354
+ const lines = content.split("\n");
355
+ const start = Math.max(0, startLine - 1);
356
+ const end = Math.min(lines.length, endLine);
357
+ return lines.slice(start, end).join("\n");
358
+ } catch {
359
+ return null;
360
+ }
361
+ }
362
+ function findSimilarChunks(index, chunkId, threshold) {
363
+ log(`findSimilarChunks: chunkId=${chunkId}, threshold=${threshold}`);
364
+ const vector = index.vectorStore.get(chunkId);
365
+ if (!vector) {
366
+ log(` No vector found for chunk ${chunkId}`);
367
+ return [];
368
+ }
369
+ log(` Vector found: dimension=${vector.length}`);
370
+ const results = [];
371
+ const allScores = [];
372
+ for (const [id, vec] of index.vectorStore.entries()) {
373
+ if (id === chunkId) continue;
374
+ const score = cosineSimilarity(vector, vec);
375
+ allScores.push({ id, score });
376
+ if (score >= threshold) {
377
+ results.push({ id, score });
378
+ }
379
+ }
380
+ const sortedAll = allScores.sort((a, b) => b.score - a.score).slice(0, 10);
381
+ log(` Top 10 similarity scores (threshold=${threshold}):`);
382
+ for (const { id, score } of sortedAll) {
383
+ const meta2 = index.metadataStore.get(id);
384
+ const meetsThreshold = score >= threshold ? "\u2713" : "\u2717";
385
+ log(` ${meetsThreshold} ${(score * 100).toFixed(1)}% - ${id} (${meta2?.name || "anonymous"} in ${meta2?.filePath})`);
386
+ }
387
+ log(` Found ${results.length} chunks above threshold`);
388
+ return results.sort((a, b) => b.score - a.score);
389
+ }
390
+ var no_semantic_duplicates_default = createRule({
391
+ name: "no-semantic-duplicates",
392
+ meta: {
393
+ type: "suggestion",
394
+ docs: {
395
+ description: "Warn when code is semantically similar to existing code"
396
+ },
397
+ messages: {
398
+ semanticDuplicate: "This {{kind}} '{{name}}' is {{similarity}}% similar to '{{otherName}}' at {{otherLocation}}. Consider consolidating.",
399
+ noIndex: "Semantic duplicates index not found. Run 'uilint duplicates index' first."
400
+ },
401
+ schema: [
402
+ {
403
+ type: "object",
404
+ properties: {
405
+ threshold: {
406
+ type: "number",
407
+ minimum: 0,
408
+ maximum: 1,
409
+ description: "Similarity threshold (0-1)"
410
+ },
411
+ indexPath: {
412
+ type: "string",
413
+ description: "Path to the index directory"
414
+ },
415
+ minLines: {
416
+ type: "integer",
417
+ minimum: 1,
418
+ description: "Minimum number of lines for a chunk to be reported"
419
+ },
420
+ confidenceLevel: {
421
+ type: "string",
422
+ enum: ["high", "medium", "low"],
423
+ description: "Minimum confidence level to report"
424
+ },
425
+ useStructuralBoost: {
426
+ type: "boolean",
427
+ description: "Use structural similarity boost (props, JSX, hooks overlap)"
428
+ },
429
+ includeSameFile: {
430
+ type: "boolean",
431
+ description: "Include duplicates within the same file"
432
+ },
433
+ kind: {
434
+ type: "string",
435
+ enum: ["component", "hook", "function", "all"],
436
+ description: "Filter by code kind"
437
+ }
438
+ },
439
+ additionalProperties: false
440
+ }
441
+ ]
442
+ },
443
+ defaultOptions: [
444
+ {
445
+ threshold: 0.75,
446
+ indexPath: ".uilint/.duplicates-index",
447
+ minLines: 3,
448
+ confidenceLevel: "low",
449
+ useStructuralBoost: true,
450
+ includeSameFile: false,
451
+ kind: "all"
452
+ }
453
+ ],
454
+ create(context) {
455
+ const options = context.options[0] || {};
456
+ const threshold = options.threshold ?? 0.85;
457
+ const indexPath = options.indexPath ?? ".uilint/.duplicates-index";
458
+ const minLines = options.minLines ?? 3;
459
+ const filename = context.filename || context.getFilename();
460
+ const projectRoot = findProjectRoot(dirname(filename), indexPath);
461
+ const relativeFilename = relative(projectRoot, filename);
462
+ initLog(projectRoot);
463
+ log(`
464
+ ========== Rule create() ==========`);
465
+ log(`Filename: ${filename}`);
466
+ log(`Relative filename: ${relativeFilename}`);
467
+ log(`Threshold: ${threshold}`);
468
+ log(`Index path: ${indexPath}`);
469
+ log(`Min lines: ${minLines}`);
470
+ log(`Project root: ${projectRoot}`);
471
+ const index = loadIndex(projectRoot, indexPath);
472
+ const reportedChunks = /* @__PURE__ */ new Set();
473
+ function checkForDuplicates(node, name) {
474
+ log(`checkForDuplicates: name=${name}, file=${relativeFilename}`);
475
+ if (!index) {
476
+ log(` No index loaded`);
477
+ return;
478
+ }
479
+ const fileChunks = index.fileToChunks.get(relativeFilename);
480
+ log(` Looking for chunks for file: ${relativeFilename}`);
481
+ log(` Files in index: ${Array.from(index.fileToChunks.keys()).join(", ")}`);
482
+ if (!fileChunks || fileChunks.length === 0) {
483
+ log(` No chunks found for this file`);
484
+ return;
485
+ }
486
+ log(` Found ${fileChunks.length} chunks: ${fileChunks.join(", ")}`);
487
+ const nodeLine = node.loc?.start.line;
488
+ if (!nodeLine) {
489
+ log(` No node line number`);
490
+ return;
491
+ }
492
+ log(` Node starts at line ${nodeLine}`);
493
+ for (const chunkId of fileChunks) {
494
+ if (reportedChunks.has(chunkId)) {
495
+ log(` Chunk ${chunkId} already reported, skipping`);
496
+ continue;
497
+ }
498
+ const meta2 = index.metadataStore.get(chunkId);
499
+ if (!meta2) {
500
+ log(` No metadata for chunk ${chunkId}`);
501
+ continue;
502
+ }
503
+ log(` Checking chunk ${chunkId}: lines ${meta2.startLine}-${meta2.endLine} (node at line ${nodeLine})`);
504
+ if (nodeLine >= meta2.startLine && nodeLine <= meta2.endLine) {
505
+ log(` Node is within chunk range, searching for similar chunks...`);
506
+ const similar = findSimilarChunks(index, chunkId, threshold);
507
+ if (similar.length > 0) {
508
+ const best = similar[0];
509
+ const bestMeta = index.metadataStore.get(best.id);
510
+ if (bestMeta) {
511
+ const chunkLines = meta2.endLine - meta2.startLine + 1;
512
+ if (chunkLines < minLines) {
513
+ log(` Skipping: chunk has ${chunkLines} lines, below minLines=${minLines}`);
514
+ continue;
515
+ }
516
+ reportedChunks.add(chunkId);
517
+ const relPath = bestMeta.filePath;
518
+ const similarity = Math.round(best.score * 100);
519
+ const sourceCode = extractCodeFromFile(
520
+ filename,
521
+ meta2.startLine,
522
+ meta2.endLine
523
+ );
524
+ const targetAbsolutePath = join(projectRoot, bestMeta.filePath);
525
+ const targetCode = extractCodeFromFile(
526
+ targetAbsolutePath,
527
+ bestMeta.startLine,
528
+ bestMeta.endLine
529
+ );
530
+ log(` REPORTING: ${meta2.kind} '${name || meta2.name}' is ${similarity}% similar to '${bestMeta.name}' at ${relPath}:${bestMeta.startLine}`);
531
+ context.report({
532
+ node,
533
+ loc: {
534
+ start: { line: meta2.startLine, column: meta2.startColumn },
535
+ end: { line: meta2.endLine, column: meta2.endColumn }
536
+ },
537
+ messageId: "semanticDuplicate",
538
+ data: {
539
+ kind: meta2.kind,
540
+ name: name || meta2.name || "(anonymous)",
541
+ similarity: String(similarity),
542
+ otherName: bestMeta.name || "(anonymous)",
543
+ otherLocation: `${relPath}:${bestMeta.startLine}`,
544
+ // Extended data for inspector panel (use relative paths for portability)
545
+ sourceCode: sourceCode || "",
546
+ targetCode: targetCode || "",
547
+ sourceLocation: JSON.stringify({
548
+ filePath: relativeFilename,
549
+ startLine: meta2.startLine,
550
+ endLine: meta2.endLine,
551
+ startColumn: meta2.startColumn,
552
+ endColumn: meta2.endColumn
553
+ }),
554
+ targetLocation: JSON.stringify({
555
+ filePath: bestMeta.filePath,
556
+ // Already relative from index
557
+ startLine: bestMeta.startLine,
558
+ endLine: bestMeta.endLine,
559
+ startColumn: bestMeta.startColumn,
560
+ endColumn: bestMeta.endColumn
561
+ }),
562
+ sourceName: name || meta2.name || "(anonymous)",
563
+ targetName: bestMeta.name || "(anonymous)",
564
+ similarityScore: String(best.score)
565
+ }
566
+ });
567
+ }
568
+ } else {
569
+ log(` No similar chunks found above threshold`);
570
+ }
571
+ } else {
572
+ log(` Node line ${nodeLine} not in chunk range ${meta2.startLine}-${meta2.endLine}`);
573
+ }
574
+ }
575
+ }
576
+ return {
577
+ // Check function declarations
578
+ FunctionDeclaration(node) {
579
+ const name = node.id?.name || null;
580
+ checkForDuplicates(node, name);
581
+ },
582
+ // Check arrow functions assigned to variables
583
+ "VariableDeclarator[init.type='ArrowFunctionExpression']"(node) {
584
+ const name = node.id.type === "Identifier" ? node.id.name : null;
585
+ if (node.init) {
586
+ checkForDuplicates(node.init, name);
587
+ }
588
+ },
589
+ // Check function expressions
590
+ "VariableDeclarator[init.type='FunctionExpression']"(node) {
591
+ const name = node.id.type === "Identifier" ? node.id.name : null;
592
+ if (node.init) {
593
+ checkForDuplicates(node.init, name);
594
+ }
595
+ }
596
+ };
597
+ }
598
+ });
599
+
600
+ export {
601
+ meta,
602
+ clearIndexCache,
603
+ no_semantic_duplicates_default
604
+ };
605
+ //# sourceMappingURL=chunk-QDQHFV7C.js.map