scip-query 0.2.0 → 0.3.1
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 +16 -43
- package/dist/chunk-26DOJ63W.js +161 -0
- package/dist/chunk-2UELLEBI.js +1 -0
- package/dist/{chunk-4PDAL6IL.js → chunk-4JCSOF2O.js} +3 -3
- package/dist/{chunk-6SXADWLW.js → chunk-5OMVSV6E.js} +13 -5
- package/dist/{chunk-KPPHZCZJ.js → chunk-7HK5ZLOE.js} +28 -46
- package/dist/{chunk-7RLE5EWE.js → chunk-7KIMF5PV.js} +34 -13
- package/dist/chunk-AXQKUYKF.js +1442 -0
- package/dist/chunk-C7H5WBTJ.js +46 -0
- package/dist/{chunk-NHBZIL2J.js → chunk-CHDJXYBG.js} +3 -28
- package/dist/{chunk-KCBMVQL5.js → chunk-CPVAQJEC.js} +13 -5
- package/dist/{chunk-ZQIIPFD7.js → chunk-DH7G3DDV.js} +2 -2
- package/dist/{chunk-BOVXCR46.js → chunk-EOROMIFO.js} +14 -6
- package/dist/{chunk-2CKGIR6G.js → chunk-EPWLXXBL.js} +3 -3
- package/dist/{chunk-5RMYT5WH.js → chunk-F7XU27LU.js} +2 -2
- package/dist/{chunk-63G7IQTD.js → chunk-FYYOWQXK.js} +20 -40
- package/dist/chunk-GEXE2T6I.js +87 -0
- package/dist/{chunk-DGUPQSOR.js → chunk-GJDHTTR2.js} +11 -4
- package/dist/chunk-GSH2FPKV.js +87 -0
- package/dist/{chunk-NUZ4OMU3.js → chunk-GU2H5QRN.js} +2 -2
- package/dist/{chunk-Z6YZJ36C.js → chunk-HJZUSUPU.js} +8 -4
- package/dist/{chunk-LAWMH22O.js → chunk-HLKAFWWJ.js} +82 -3
- package/dist/{chunk-HPFZLISB.js → chunk-HLUS2HEB.js} +2 -2
- package/dist/{chunk-H2MDONBU.js → chunk-J3JSOSUO.js} +9 -6
- package/dist/{chunk-7PBOG4YE.js → chunk-KBOQX573.js} +2 -2
- package/dist/{chunk-HDSRORNV.js → chunk-KKCHYLVI.js} +17 -11
- package/dist/{chunk-HMLMH7VZ.js → chunk-LFJQVJYJ.js} +2 -2
- package/dist/{chunk-4EXL2CUA.js → chunk-LQJUPXQY.js} +16 -8
- package/dist/{chunk-ITZ3DDOG.js → chunk-MPGIHELS.js} +18 -3
- package/dist/chunk-NFS5W3PP.js +37 -0
- package/dist/chunk-NG5F43OU.js +200 -0
- package/dist/{chunk-DCKMSTJ4.js → chunk-O7Q7FDUJ.js} +22 -14
- package/dist/{chunk-UJQN5N3I.js → chunk-OIDHN6GD.js} +6 -3
- package/dist/{chunk-Z4GHE2HD.js → chunk-P3E6L7KW.js} +6 -2
- package/dist/{chunk-BNN2RKD2.js → chunk-P4WO3BBW.js} +3 -3
- package/dist/{chunk-QOV2R2WT.js → chunk-QIXNAB5K.js} +42 -2
- package/dist/{chunk-NWCE4CIC.js → chunk-SMDCNPMK.js} +11 -28
- package/dist/{chunk-SEFSL2GF.js → chunk-TOIEB3LG.js} +2 -2
- package/dist/{chunk-4XHWPRAX.js → chunk-UGQKAVCD.js} +3 -3
- package/dist/{chunk-OVPLOMPY.js → chunk-UQEQ6AHX.js} +7 -4
- package/dist/{chunk-ZK6GXM3J.js → chunk-VIYSWZCO.js} +3 -3
- package/dist/chunk-VJJKSGIX.js +121 -0
- package/dist/{chunk-N5KEREIA.js → chunk-VT4JBH6L.js} +19 -7
- package/dist/{chunk-7LLPRPR5.js → chunk-WGAD3GNR.js} +2 -2
- package/dist/chunk-YDBXNPYU.js +69 -0
- package/dist/chunk-YY4QGUQ5.js +84 -0
- package/dist/{chunk-FGXRVW7G.js → chunk-YZ6L7GFO.js} +2 -2
- package/dist/{chunk-W4ALF422.js → chunk-ZEUCXQBN.js} +3 -3
- package/dist/cli.js +2697 -1054
- package/dist/{db-BNVVZSfP.d.ts → db-ShvwGDKf.d.ts} +12 -19
- package/dist/index.d.ts +15 -15
- package/dist/index.js +263 -234
- package/dist/postinstall.js +5 -76
- package/dist/queries/affected.d.ts +1 -1
- package/dist/queries/affected.js +3 -3
- package/dist/queries/bottlenecks.d.ts +1 -1
- package/dist/queries/bottlenecks.js +2 -2
- package/dist/queries/by-kind.d.ts +1 -1
- package/dist/queries/by-kind.js +2 -2
- package/dist/queries/call-graph.d.ts +1 -1
- package/dist/queries/call-graph.js +3 -3
- package/dist/queries/change-surface.d.ts +2 -2
- package/dist/queries/change-surface.js +3 -3
- package/dist/queries/code.d.ts +1 -1
- package/dist/queries/code.js +3 -3
- package/dist/queries/complexity-hotspots.d.ts +1 -1
- package/dist/queries/complexity-hotspots.js +3 -3
- package/dist/queries/complexity.d.ts +1 -1
- package/dist/queries/complexity.js +3 -3
- package/dist/queries/convergence.d.ts +1 -1
- package/dist/queries/convergence.js +3 -3
- package/dist/queries/coupling.d.ts +1 -1
- package/dist/queries/coupling.js +3 -1
- package/dist/queries/cycles.d.ts +1 -1
- package/dist/queries/cycles.js +3 -2
- package/dist/queries/dataflow.d.ts +1 -1
- package/dist/queries/dataflow.js +3 -3
- package/dist/queries/dead.d.ts +1 -1
- package/dist/queries/dead.js +4 -3
- package/dist/queries/deep-chains.d.ts +1 -1
- package/dist/queries/deep-chains.js +3 -2
- package/dist/queries/deps.d.ts +1 -1
- package/dist/queries/deps.js +3 -1
- package/dist/queries/diff-impact.d.ts +2 -2
- package/dist/queries/diff-impact.js +2 -3
- package/dist/queries/doc-coverage.d.ts +1 -1
- package/dist/queries/doc-coverage.js +2 -2
- package/dist/queries/drift.d.ts +1 -1
- package/dist/queries/drift.js +3 -2
- package/dist/queries/extract-candidates.d.ts +1 -1
- package/dist/queries/extract-candidates.js +3 -3
- package/dist/queries/fan.d.ts +1 -1
- package/dist/queries/fan.js +3 -2
- package/dist/queries/files.d.ts +1 -1
- package/dist/queries/health.d.ts +1 -1
- package/dist/queries/health.js +14 -14
- package/dist/queries/hierarchy.d.ts +1 -1
- package/dist/queries/hierarchy.js +3 -2
- package/dist/queries/hotspots.d.ts +1 -1
- package/dist/queries/hotspots.js +2 -2
- package/dist/queries/imports.d.ts +1 -1
- package/dist/queries/imports.js +3 -2
- package/dist/queries/index.d.ts +1 -2
- package/dist/queries/index.js +46 -51
- package/dist/queries/isolated.d.ts +1 -1
- package/dist/queries/isolated.js +4 -3
- package/dist/queries/members.d.ts +2 -2
- package/dist/queries/members.js +3 -2
- package/dist/queries/methods.d.ts +1 -1
- package/dist/queries/methods.js +2 -2
- package/dist/queries/outline.d.ts +1 -1
- package/dist/queries/outline.js +3 -2
- package/dist/queries/passthrough-candidates.d.ts +1 -1
- package/dist/queries/passthrough-candidates.js +3 -3
- package/dist/queries/redundant-reexports.d.ts +1 -1
- package/dist/queries/redundant-reexports.js +4 -2
- package/dist/queries/refs.d.ts +1 -1
- package/dist/queries/refs.js +3 -1
- package/dist/queries/similar-chains.d.ts +1 -1
- package/dist/queries/similar-chains.js +3 -2
- package/dist/queries/similar-files.d.ts +1 -1
- package/dist/queries/similar-files.js +3 -2
- package/dist/queries/similar-signatures.d.ts +1 -1
- package/dist/queries/similar-signatures.js +2 -2
- package/dist/queries/similar.d.ts +1 -1
- package/dist/queries/similar.js +3 -3
- package/dist/queries/slice.d.ts +2 -2
- package/dist/queries/slice.js +3 -3
- package/dist/queries/stale-abstractions.d.ts +1 -1
- package/dist/queries/stale-abstractions.js +3 -3
- package/dist/queries/stats.d.ts +1 -1
- package/dist/queries/surface.d.ts +1 -1
- package/dist/queries/surface.js +3 -2
- package/dist/queries/symbols.d.ts +1 -1
- package/dist/queries/symbols.js +3 -2
- package/dist/queries/system.d.ts +1 -1
- package/dist/queries/system.js +3 -2
- package/dist/queries/trace.d.ts +1 -1
- package/dist/queries/trace.js +3 -1
- package/dist/queries/wrapper-candidates.d.ts +1 -1
- package/dist/queries/wrapper-candidates.js +3 -3
- package/dist/reindex-worker.js +24 -12
- package/package.json +6 -1
- package/IMPROVEMENTS.md +0 -143
- package/PLAN.md +0 -320
- package/dist/chunk-2CKGIR6G.js.map +0 -1
- package/dist/chunk-3UOUTZQT.js +0 -45
- package/dist/chunk-3UOUTZQT.js.map +0 -1
- package/dist/chunk-4EXL2CUA.js.map +0 -1
- package/dist/chunk-4PDAL6IL.js.map +0 -1
- package/dist/chunk-4TYLS5XX.js.map +0 -1
- package/dist/chunk-4XHWPRAX.js.map +0 -1
- package/dist/chunk-5RMYT5WH.js.map +0 -1
- package/dist/chunk-63G7IQTD.js.map +0 -1
- package/dist/chunk-6SXADWLW.js.map +0 -1
- package/dist/chunk-74RFWB5T.js.map +0 -1
- package/dist/chunk-7LLPRPR5.js.map +0 -1
- package/dist/chunk-7PBOG4YE.js.map +0 -1
- package/dist/chunk-7RLE5EWE.js.map +0 -1
- package/dist/chunk-7UCKSQRS.js +0 -55
- package/dist/chunk-7UCKSQRS.js.map +0 -1
- package/dist/chunk-BNN2RKD2.js.map +0 -1
- package/dist/chunk-BOVXCR46.js.map +0 -1
- package/dist/chunk-D567NFIF.js +0 -65
- package/dist/chunk-D567NFIF.js.map +0 -1
- package/dist/chunk-DCKMSTJ4.js.map +0 -1
- package/dist/chunk-DEZKCZXD.js +0 -40
- package/dist/chunk-DEZKCZXD.js.map +0 -1
- package/dist/chunk-DGUPQSOR.js.map +0 -1
- package/dist/chunk-DVWGWHFW.js +0 -99
- package/dist/chunk-DVWGWHFW.js.map +0 -1
- package/dist/chunk-EQYLEQCW.js +0 -46
- package/dist/chunk-EQYLEQCW.js.map +0 -1
- package/dist/chunk-FGXRVW7G.js.map +0 -1
- package/dist/chunk-H2MDONBU.js.map +0 -1
- package/dist/chunk-HB7MRLLL.js +0 -76
- package/dist/chunk-HB7MRLLL.js.map +0 -1
- package/dist/chunk-HDSRORNV.js.map +0 -1
- package/dist/chunk-HMLMH7VZ.js.map +0 -1
- package/dist/chunk-HPFZLISB.js.map +0 -1
- package/dist/chunk-HZBC7PPD.js +0 -88
- package/dist/chunk-HZBC7PPD.js.map +0 -1
- package/dist/chunk-ITZ3DDOG.js.map +0 -1
- package/dist/chunk-JJP7KQND.js +0 -1
- package/dist/chunk-JJP7KQND.js.map +0 -1
- package/dist/chunk-KCBMVQL5.js.map +0 -1
- package/dist/chunk-KPPHZCZJ.js.map +0 -1
- package/dist/chunk-LAWMH22O.js.map +0 -1
- package/dist/chunk-MCUX5LA7.js +0 -103
- package/dist/chunk-MCUX5LA7.js.map +0 -1
- package/dist/chunk-MGNMHKX3.js.map +0 -1
- package/dist/chunk-N5KEREIA.js.map +0 -1
- package/dist/chunk-NHBZIL2J.js.map +0 -1
- package/dist/chunk-NUZ4OMU3.js.map +0 -1
- package/dist/chunk-NWCE4CIC.js.map +0 -1
- package/dist/chunk-OVPLOMPY.js.map +0 -1
- package/dist/chunk-QOV2R2WT.js.map +0 -1
- package/dist/chunk-SEFSL2GF.js.map +0 -1
- package/dist/chunk-UJQN5N3I.js.map +0 -1
- package/dist/chunk-W4ALF422.js.map +0 -1
- package/dist/chunk-Z4GHE2HD.js.map +0 -1
- package/dist/chunk-Z6YZJ36C.js.map +0 -1
- package/dist/chunk-ZK6GXM3J.js.map +0 -1
- package/dist/chunk-ZOGY2V3N.js +0 -158
- package/dist/chunk-ZOGY2V3N.js.map +0 -1
- package/dist/chunk-ZQIIPFD7.js.map +0 -1
- package/dist/cli.js.map +0 -1
- package/dist/index.js.map +0 -1
- package/dist/postinstall.js.map +0 -1
- package/dist/queries/affected.js.map +0 -1
- package/dist/queries/bottlenecks.js.map +0 -1
- package/dist/queries/by-kind.js.map +0 -1
- package/dist/queries/call-graph.js.map +0 -1
- package/dist/queries/change-surface.js.map +0 -1
- package/dist/queries/clean-signature.js.map +0 -1
- package/dist/queries/code.js.map +0 -1
- package/dist/queries/complexity-hotspots.js.map +0 -1
- package/dist/queries/complexity.js.map +0 -1
- package/dist/queries/convergence.js.map +0 -1
- package/dist/queries/coupling.js.map +0 -1
- package/dist/queries/cycles.js.map +0 -1
- package/dist/queries/dataflow.js.map +0 -1
- package/dist/queries/dead.js.map +0 -1
- package/dist/queries/deep-chains.js.map +0 -1
- package/dist/queries/deps.js.map +0 -1
- package/dist/queries/diff-impact.js.map +0 -1
- package/dist/queries/doc-coverage.js.map +0 -1
- package/dist/queries/drift.js.map +0 -1
- package/dist/queries/extract-candidates.js.map +0 -1
- package/dist/queries/fan.js.map +0 -1
- package/dist/queries/files.js.map +0 -1
- package/dist/queries/health.js.map +0 -1
- package/dist/queries/hierarchy.js.map +0 -1
- package/dist/queries/hotspots.js.map +0 -1
- package/dist/queries/imports.js.map +0 -1
- package/dist/queries/index.js.map +0 -1
- package/dist/queries/isolated.js.map +0 -1
- package/dist/queries/members.js.map +0 -1
- package/dist/queries/methods.js.map +0 -1
- package/dist/queries/outline.js.map +0 -1
- package/dist/queries/passthrough-candidates.js.map +0 -1
- package/dist/queries/redundant-reexports.js.map +0 -1
- package/dist/queries/refs.js.map +0 -1
- package/dist/queries/similar-chains.js.map +0 -1
- package/dist/queries/similar-files.js.map +0 -1
- package/dist/queries/similar-signatures.js.map +0 -1
- package/dist/queries/similar.js.map +0 -1
- package/dist/queries/slice.js.map +0 -1
- package/dist/queries/stale-abstractions.js.map +0 -1
- package/dist/queries/stats.js.map +0 -1
- package/dist/queries/surface.js.map +0 -1
- package/dist/queries/symbols.js.map +0 -1
- package/dist/queries/system.js.map +0 -1
- package/dist/queries/test-coverage.d.ts +0 -22
- package/dist/queries/test-coverage.js +0 -11
- package/dist/queries/test-coverage.js.map +0 -1
- package/dist/queries/trace.js.map +0 -1
- package/dist/queries/wrapper-candidates.js.map +0 -1
- package/dist/reindex-worker.js.map +0 -1
- package/docs/AGENT_GUIDE.md +0 -359
- package/reports/debloat/2026-04-10-scip-query-self-audit.md +0 -161
- package/src/cli.ts +0 -1480
- package/src/config.ts +0 -117
- package/src/db.ts +0 -127
- package/src/gitignore-filter.ts +0 -143
- package/src/index.ts +0 -11
- package/src/postinstall.ts +0 -8
- package/src/queries/affected.ts +0 -86
- package/src/queries/bottlenecks.ts +0 -67
- package/src/queries/by-kind.ts +0 -204
- package/src/queries/call-graph.ts +0 -66
- package/src/queries/change-surface.ts +0 -110
- package/src/queries/clean-signature.ts +0 -22
- package/src/queries/code.ts +0 -101
- package/src/queries/complexity-hotspots.ts +0 -119
- package/src/queries/complexity.ts +0 -152
- package/src/queries/convergence.ts +0 -82
- package/src/queries/coupling.ts +0 -99
- package/src/queries/cycles.ts +0 -78
- package/src/queries/dataflow.ts +0 -128
- package/src/queries/dead.ts +0 -122
- package/src/queries/deep-chains.ts +0 -59
- package/src/queries/deps.ts +0 -46
- package/src/queries/diff-impact.ts +0 -204
- package/src/queries/doc-coverage.ts +0 -86
- package/src/queries/drift.ts +0 -224
- package/src/queries/extract-candidates.ts +0 -167
- package/src/queries/fan.ts +0 -148
- package/src/queries/files.ts +0 -16
- package/src/queries/health.ts +0 -324
- package/src/queries/hierarchy.ts +0 -49
- package/src/queries/hotspots.ts +0 -53
- package/src/queries/imports.ts +0 -95
- package/src/queries/index.ts +0 -45
- package/src/queries/isolated.ts +0 -67
- package/src/queries/members.ts +0 -54
- package/src/queries/methods.ts +0 -27
- package/src/queries/outline.ts +0 -52
- package/src/queries/passthrough-candidates.ts +0 -94
- package/src/queries/redundant-reexports.ts +0 -170
- package/src/queries/refs.ts +0 -27
- package/src/queries/similar-chains.ts +0 -314
- package/src/queries/similar-files.ts +0 -140
- package/src/queries/similar-signatures.ts +0 -151
- package/src/queries/similar.ts +0 -305
- package/src/queries/slice.ts +0 -154
- package/src/queries/stale-abstractions.ts +0 -82
- package/src/queries/stats.ts +0 -22
- package/src/queries/surface.ts +0 -34
- package/src/queries/symbols.ts +0 -39
- package/src/queries/system.ts +0 -86
- package/src/queries/test-coverage.ts +0 -106
- package/src/queries/trace.ts +0 -55
- package/src/queries/wrapper-candidates.ts +0 -112
- package/src/query-support.ts +0 -226
- package/src/reindex/detect.ts +0 -58
- package/src/reindex/index.ts +0 -153
- package/src/reindex/indexers.ts +0 -220
- package/src/reindex/install.ts +0 -125
- package/src/reindex-worker.ts +0 -35
- package/src/setup.ts +0 -202
- package/src/symbol-parser.ts +0 -278
- package/src/types.ts +0 -654
- package/src/watch.ts +0 -274
- package/tests/gitignore-filter.test.ts +0 -48
- package/tests/queries.test.ts +0 -300
- package/tests/symbol-parser.test.ts +0 -157
- package/tsconfig.json +0 -20
- package/tsup.config.ts +0 -40
- package/vitest.config.ts +0 -7
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/queries/redundant-reexports.ts"],"sourcesContent":["import type { ScipDatabase } from '../db.js';\nimport type { RedundantReexport } from '../types.js';\nimport { shortenSymbol } from '../symbol-parser.js';\n\n/**\n * Find barrel re-exports that no consumer actually imports through.\n *\n * If `queries/index.ts` re-exports `byKind` from `by-kind.ts`, but every\n * consumer of `byKind` imports it directly from `by-kind.ts` (not through\n * `index.ts`), the re-export in the barrel is dead weight.\n *\n * Algorithm:\n * 1. Find all barrel files (index.ts / index.js)\n * 2. For each barrel, find symbols it re-exports (defined elsewhere, referenced in barrel with role=0)\n * 3. For each re-exported symbol, count consumers through the barrel vs direct from the source\n * 4. If zero consumers go through the barrel, the re-export is redundant\n */\nexport function redundantReexports(\n db: ScipDatabase,\n opts: { scope?: string; limit?: number } = {},\n): RedundantReexport[] {\n const { scope, limit } = opts;\n\n const scopeFilter = scope ? `AND barrel_d.relative_path LIKE '%${scope}%'` : '';\n\n // Step 1 + 2: Find all barrel files and symbols they re-export.\n // A re-export is a symbol that:\n // - is mentioned in a barrel file with role=0 (reference/import)\n // - has its definition (defn_enclosing_ranges) in a DIFFERENT file\n const reexportRows = db.all<{\n barrel_doc_id: number;\n barrel_path: string;\n symbol_id: number;\n symbol: string;\n original_doc_id: number;\n original_path: string;\n }>(\n `SELECT DISTINCT\n barrel_d.id AS barrel_doc_id,\n barrel_d.relative_path AS barrel_path,\n gs.id AS symbol_id,\n gs.symbol AS symbol,\n orig_d.id AS original_doc_id,\n orig_d.relative_path AS original_path\n FROM mentions m\n JOIN chunks c ON m.chunk_id = c.id\n JOIN documents barrel_d ON c.document_id = barrel_d.id\n JOIN global_symbols gs ON m.symbol_id = gs.id\n JOIN defn_enclosing_ranges der ON gs.id = der.symbol_id\n JOIN documents orig_d ON der.document_id = orig_d.id\n WHERE m.role != 1\n AND (barrel_d.relative_path LIKE '%/index.ts'\n OR barrel_d.relative_path LIKE '%/index.js'\n OR barrel_d.relative_path = 'index.ts'\n OR barrel_d.relative_path = 'index.js')\n AND orig_d.id != barrel_d.id\n ${db.pathExclusionsFor('barrel_d', 'orig_d')}\n ${db.symbolNoiseFor('gs')}\n -- Only function-level symbols (ending with ().), not module-level\n AND gs.symbol LIKE '%().'\n ${scopeFilter}\n ORDER BY barrel_d.relative_path, gs.symbol`,\n );\n\n const results: RedundantReexport[] = [];\n\n for (const row of reexportRows) {\n if (db.isIgnored(row.barrel_path) || db.isIgnored(row.original_path)) continue;\n\n // Step 3: Count consumers that reference this symbol through the barrel\n // A \"barrel consumer\" is a file (other than the barrel itself and the original file)\n // that mentions this symbol AND also mentions something from the barrel document.\n // More precisely: count distinct files that reference this symbol AND whose\n // chunk is in a document that also has a role=0 mention pointing to the barrel file's symbols.\n //\n // Simpler approach: count distinct documents that reference this symbol (role=0)\n // grouped by whether the reference chunk is in a file that imports from the barrel\n // or from the original.\n //\n // Actually, the most reliable approach with SCIP data: count how many distinct\n // consumer documents reference this symbol_id with role=0, excluding the barrel\n // and the original file themselves. Then check if those consumers also reference\n // ANY symbol through a mention in the barrel doc vs the original doc.\n //\n // Simplest correct approach: In SCIP, when file A does `import { foo } from './bar/index'`,\n // the mention of `foo` in file A points to the same global symbol regardless of import path.\n // SCIP doesn't track import provenance. BUT the barrel file itself contains mentions\n // (role=0 references) of the re-exported symbols. So we can check:\n // - barrelConsumers: files that mention both this symbol AND any symbol whose definition\n // is in the barrel (i.e., they import the barrel)\n // - directConsumers: files that mention this symbol but don't import the barrel\n //\n // Even simpler: check if the barrel document is in the deps of the consumer.\n // A consumer \"goes through the barrel\" if it has ANY role=0 mention pointing to a\n // chunk in the barrel file. Otherwise it goes direct.\n\n const consumerCounts = db.get<{\n barrel_consumers: number;\n direct_consumers: number;\n }>(\n `SELECT\n SUM(CASE WHEN uses_barrel = 1 THEN 1 ELSE 0 END) AS barrel_consumers,\n SUM(CASE WHEN uses_barrel = 0 THEN 1 ELSE 0 END) AS direct_consumers\n FROM (\n SELECT\n consumer_d.id AS consumer_doc_id,\n MAX(CASE WHEN EXISTS (\n SELECT 1\n FROM mentions barrel_m\n JOIN chunks barrel_c ON barrel_m.chunk_id = barrel_c.id\n WHERE barrel_c.document_id = consumer_d.id\n AND barrel_m.role != 1\n AND barrel_m.symbol_id IN (\n SELECT m2.symbol_id\n FROM mentions m2\n JOIN chunks c2 ON m2.chunk_id = c2.id\n WHERE c2.document_id = ?\n AND m2.role != 1\n )\n ) THEN 1 ELSE 0 END) AS uses_barrel\n FROM mentions ref_m\n JOIN chunks ref_c ON ref_m.chunk_id = ref_c.id\n JOIN documents consumer_d ON ref_c.document_id = consumer_d.id\n WHERE ref_m.symbol_id = ?\n AND ref_m.role != 1\n AND consumer_d.id != ?\n AND consumer_d.id != ?\n ${db.pathExclusionsFor('consumer_d')}\n GROUP BY consumer_d.id\n )`,\n row.barrel_doc_id, // for the inner subquery checking barrel mentions\n row.symbol_id, // the re-exported symbol\n row.barrel_doc_id, // exclude the barrel itself\n row.original_doc_id, // exclude the original file\n );\n\n const barrelConsumers = consumerCounts?.barrel_consumers ?? 0;\n const directConsumers = consumerCounts?.direct_consumers ?? 0;\n\n // In TypeScript, `import * as X from './barrel'` resolves all references\n // directly to the source file — the barrel is transparent to SCIP.\n // This means barrelConsumers is always 0 for namespace imports.\n //\n // We can only confidently report symbols with 0 consumers EVERYWHERE\n // (both barrel and direct). These are truly dead re-exports.\n //\n // Symbols with directConsumers > 0 but barrelConsumers === 0 might be\n // consumed through a namespace import — we can't tell, so we skip them.\n if (barrelConsumers === 0 && directConsumers === 0) {\n results.push({\n barrelFile: row.barrel_path,\n symbol: row.symbol,\n shortName: shortenSymbol(row.symbol),\n originalFile: row.original_path,\n barrelConsumers,\n directConsumers,\n });\n }\n }\n\n // Sort: symbols with the most direct consumers first (biggest cleanup wins),\n // then by barrel file path for stable output\n results.sort((a, b) =>\n b.directConsumers - a.directConsumers\n || a.barrelFile.localeCompare(b.barrelFile)\n || a.shortName.localeCompare(b.shortName),\n );\n\n return limit ? results.slice(0, limit) : results;\n}\n"],"mappings":";;;;;AAiBO,SAAS,mBACd,IACA,OAA2C,CAAC,GACvB;AACrB,QAAM,EAAE,OAAO,MAAM,IAAI;AAEzB,QAAM,cAAc,QAAQ,qCAAqC,KAAK,OAAO;AAM7E,QAAM,eAAe,GAAG;AAAA,IAQtB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,QAmBI,GAAG,kBAAkB,YAAY,QAAQ,CAAC;AAAA,QAC1C,GAAG,eAAe,IAAI,CAAC;AAAA;AAAA;AAAA,QAGvB,WAAW;AAAA;AAAA,EAEjB;AAEA,QAAM,UAA+B,CAAC;AAEtC,aAAW,OAAO,cAAc;AAC9B,QAAI,GAAG,UAAU,IAAI,WAAW,KAAK,GAAG,UAAU,IAAI,aAAa,EAAG;AA6BtE,UAAM,iBAAiB,GAAG;AAAA,MAIxB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,YA2BM,GAAG,kBAAkB,YAAY,CAAC;AAAA;AAAA;AAAA,MAGxC,IAAI;AAAA;AAAA,MACJ,IAAI;AAAA;AAAA,MACJ,IAAI;AAAA;AAAA,MACJ,IAAI;AAAA;AAAA,IACN;AAEA,UAAM,kBAAkB,gBAAgB,oBAAoB;AAC5D,UAAM,kBAAkB,gBAAgB,oBAAoB;AAW5D,QAAI,oBAAoB,KAAK,oBAAoB,GAAG;AAClD,cAAQ,KAAK;AAAA,QACX,YAAY,IAAI;AAAA,QAChB,QAAQ,IAAI;AAAA,QACZ,WAAW,cAAc,IAAI,MAAM;AAAA,QACnC,cAAc,IAAI;AAAA,QAClB;AAAA,QACA;AAAA,MACF,CAAC;AAAA,IACH;AAAA,EACF;AAIA,UAAQ;AAAA,IAAK,CAAC,GAAG,MACf,EAAE,kBAAkB,EAAE,mBACnB,EAAE,WAAW,cAAc,EAAE,UAAU,KACvC,EAAE,UAAU,cAAc,EAAE,SAAS;AAAA,EAC1C;AAEA,SAAO,QAAQ,QAAQ,MAAM,GAAG,KAAK,IAAI;AAC3C;","names":[]}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/queries/convergence.ts"],"sourcesContent":["import type { ScipDatabase } from '../db.js';\nimport { findFirstSymbolMatch, getCalleeRowsForSymbol } from '../query-support.js';\nimport type { ConvergenceResult } from '../types.js';\nimport { shortenSymbol } from '../symbol-parser.js';\n\n/**\n * Given two similar symbols, show what a consolidated version would look like.\n * The shared callee set becomes the common body. The unique callees become\n * the parameterization points.\n */\nexport function convergence(\n db: ScipDatabase,\n symbolPatternA: string,\n symbolPatternB: string,\n): ConvergenceResult | null {\n const matchA = findFirstSymbolMatch(db, symbolPatternA);\n const matchB = findFirstSymbolMatch(db, symbolPatternB);\n\n if (!matchA || !matchB) return null;\n\n const calleesA = new Set(\n getCalleeRowsForSymbol(db, matchA).map((r) => r.symbol),\n );\n const calleesB = new Set(\n getCalleeRowsForSymbol(db, matchB).map((r) => r.symbol),\n );\n\n const shared: string[] = [];\n for (const c of calleesA) {\n if (calleesB.has(c)) shared.push(c);\n }\n\n const uniqueA: string[] = [];\n for (const c of calleesA) {\n if (!calleesB.has(c)) uniqueA.push(c);\n }\n\n const uniqueB: string[] = [];\n for (const c of calleesB) {\n if (!calleesA.has(c)) uniqueB.push(c);\n }\n\n const union = new Set([...calleesA, ...calleesB]);\n const similarity = union.size > 0 ? shared.length / union.size : 0;\n\n // Generate a consolidation strategy description\n let strategy: string;\n if (uniqueA.length === 0 && uniqueB.length === 0) {\n strategy = 'These functions have identical callee sets. One can replace the other directly.';\n } else if (uniqueA.length === 0) {\n strategy = `A is a subset of B. A can be replaced by calling B (B does everything A does plus more).`;\n } else if (uniqueB.length === 0) {\n strategy = `B is a subset of A. B can be replaced by calling A (A does everything B does plus more).`;\n } else if (uniqueA.length <= 2 && uniqueB.length <= 2) {\n strategy = `Create a shared function with the ${shared.length} common callees. Pass the ${uniqueA.length + uniqueB.length} divergent callees as parameters or strategy callbacks.`;\n } else {\n strategy = `Extract the ${shared.length} shared callees into a common helper. Each function calls the helper plus its own unique logic (${uniqueA.length} callees in A, ${uniqueB.length} in B).`;\n }\n\n const locA = matchA.endLine - matchA.startLine + 1;\n const locB = matchB.endLine - matchB.startLine + 1;\n\n return {\n symbolA: {\n symbol: matchA.symbol,\n shortName: shortenSymbol(matchA.symbol),\n file: matchA.relativePath,\n loc: locA,\n },\n symbolB: {\n symbol: matchB.symbol,\n shortName: shortenSymbol(matchB.symbol),\n file: matchB.relativePath,\n loc: locB,\n },\n similarity,\n sharedCallees: shared.map(shortenSymbol),\n uniqueToA: uniqueA.map(shortenSymbol),\n uniqueToB: uniqueB.map(shortenSymbol),\n consolidationStrategy: strategy,\n };\n}\n"],"mappings":";;;;;;;;;AAUO,SAAS,YACd,IACA,gBACA,gBAC0B;AAC1B,QAAM,SAAS,qBAAqB,IAAI,cAAc;AACtD,QAAM,SAAS,qBAAqB,IAAI,cAAc;AAEtD,MAAI,CAAC,UAAU,CAAC,OAAQ,QAAO;AAE/B,QAAM,WAAW,IAAI;AAAA,IACnB,uBAAuB,IAAI,MAAM,EAAE,IAAI,CAAC,MAAM,EAAE,MAAM;AAAA,EACxD;AACA,QAAM,WAAW,IAAI;AAAA,IACnB,uBAAuB,IAAI,MAAM,EAAE,IAAI,CAAC,MAAM,EAAE,MAAM;AAAA,EACxD;AAEA,QAAM,SAAmB,CAAC;AAC1B,aAAW,KAAK,UAAU;AACxB,QAAI,SAAS,IAAI,CAAC,EAAG,QAAO,KAAK,CAAC;AAAA,EACpC;AAEA,QAAM,UAAoB,CAAC;AAC3B,aAAW,KAAK,UAAU;AACxB,QAAI,CAAC,SAAS,IAAI,CAAC,EAAG,SAAQ,KAAK,CAAC;AAAA,EACtC;AAEA,QAAM,UAAoB,CAAC;AAC3B,aAAW,KAAK,UAAU;AACxB,QAAI,CAAC,SAAS,IAAI,CAAC,EAAG,SAAQ,KAAK,CAAC;AAAA,EACtC;AAEA,QAAM,QAAQ,oBAAI,IAAI,CAAC,GAAG,UAAU,GAAG,QAAQ,CAAC;AAChD,QAAM,aAAa,MAAM,OAAO,IAAI,OAAO,SAAS,MAAM,OAAO;AAGjE,MAAI;AACJ,MAAI,QAAQ,WAAW,KAAK,QAAQ,WAAW,GAAG;AAChD,eAAW;AAAA,EACb,WAAW,QAAQ,WAAW,GAAG;AAC/B,eAAW;AAAA,EACb,WAAW,QAAQ,WAAW,GAAG;AAC/B,eAAW;AAAA,EACb,WAAW,QAAQ,UAAU,KAAK,QAAQ,UAAU,GAAG;AACrD,eAAW,qCAAqC,OAAO,MAAM,6BAA6B,QAAQ,SAAS,QAAQ,MAAM;AAAA,EAC3H,OAAO;AACL,eAAW,eAAe,OAAO,MAAM,mGAAmG,QAAQ,MAAM,kBAAkB,QAAQ,MAAM;AAAA,EAC1L;AAEA,QAAM,OAAO,OAAO,UAAU,OAAO,YAAY;AACjD,QAAM,OAAO,OAAO,UAAU,OAAO,YAAY;AAEjD,SAAO;AAAA,IACL,SAAS;AAAA,MACP,QAAQ,OAAO;AAAA,MACf,WAAW,cAAc,OAAO,MAAM;AAAA,MACtC,MAAM,OAAO;AAAA,MACb,KAAK;AAAA,IACP;AAAA,IACA,SAAS;AAAA,MACP,QAAQ,OAAO;AAAA,MACf,WAAW,cAAc,OAAO,MAAM;AAAA,MACtC,MAAM,OAAO;AAAA,MACb,KAAK;AAAA,IACP;AAAA,IACA;AAAA,IACA,eAAe,OAAO,IAAI,aAAa;AAAA,IACvC,WAAW,QAAQ,IAAI,aAAa;AAAA,IACpC,WAAW,QAAQ,IAAI,aAAa;AAAA,IACpC,uBAAuB;AAAA,EACzB;AACF;","names":[]}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/queries/complexity-hotspots.ts"],"sourcesContent":["import type { ScipDatabase } from '../db.js';\nimport { testFileExclusionSql } from '../query-support.js';\nimport type { ComplexityHotspot } from '../types.js';\nimport { shortenSymbol } from '../symbol-parser.js';\n\n/**\n * Find complexity hotspots: symbols with a composite score based on\n * LOC, fan-in, fan-out, and callee count.\n *\n * Score = (loc / 50) * (fanIn / 5) * max(fanOut / 5, 1)\n *\n * High scores indicate symbols that are large, widely depended upon,\n * AND reach out to many other modules — the riskiest code to change.\n */\nexport function complexityHotspots(\n db: ScipDatabase,\n opts?: { scope?: string; minLoc?: number; limit?: number },\n): ComplexityHotspot[] {\n const { scope, minLoc = 10, limit = 30 } = opts ?? {};\n const scopeFilter = scope ? `AND d.relative_path LIKE '%${scope}%'` : '';\n\n const rows = db.all<{\n symbol: string;\n file: string;\n start_line: number;\n end_line: number;\n loc: number;\n fan_in: number;\n fan_out: number;\n callee_count: number;\n }>(\n `SELECT\n gs.symbol,\n d.relative_path AS file,\n der.start_line,\n der.end_line,\n (der.end_line - der.start_line + 1) AS loc,\n -- fanIn: distinct files that reference this symbol\n (SELECT COUNT(DISTINCT ref_c.document_id)\n FROM mentions ref_m\n JOIN chunks ref_c ON ref_m.chunk_id = ref_c.id\n WHERE ref_m.symbol_id = gs.id AND ref_m.role != 1\n ) AS fan_in,\n -- fanOut: distinct symbols referenced within this definition range\n -- that are defined in different files\n (SELECT COUNT(DISTINCT out_gs.id)\n FROM mentions out_m\n JOIN chunks out_c ON out_m.chunk_id = out_c.id\n JOIN global_symbols out_gs ON out_m.symbol_id = out_gs.id\n JOIN defn_enclosing_ranges out_der ON out_gs.id = out_der.symbol_id\n WHERE out_c.document_id = der.document_id\n AND out_c.start_line >= der.start_line\n AND out_c.end_line <= der.end_line\n AND out_m.role != 1\n AND out_gs.id != gs.id\n AND out_der.document_id != der.document_id\n ) AS fan_out,\n -- calleeCount: total distinct callees within definition range\n (SELECT COUNT(DISTINCT callee_gs.id)\n FROM mentions callee_m\n JOIN chunks callee_c ON callee_m.chunk_id = callee_c.id\n JOIN global_symbols callee_gs ON callee_m.symbol_id = callee_gs.id\n WHERE callee_c.document_id = der.document_id\n AND callee_c.start_line >= der.start_line\n AND callee_c.end_line <= der.end_line\n AND callee_m.role != 1\n AND callee_gs.id != gs.id\n ) AS callee_count\n FROM global_symbols gs\n JOIN defn_enclosing_ranges der ON gs.id = der.symbol_id\n JOIN documents d ON der.document_id = d.id\n WHERE 1 = 1\n ${db.pathExclusionsFor('d')}\n AND ${testFileExclusionSql('d')}\n ${db.symbolNoiseFor('gs')}\n AND (der.end_line - der.start_line + 1) >= ?\n ${scopeFilter}\n ORDER BY (\n CAST((der.end_line - der.start_line + 1) AS REAL) / 50.0\n * CAST((SELECT COUNT(DISTINCT ref_c2.document_id)\n FROM mentions ref_m2\n JOIN chunks ref_c2 ON ref_m2.chunk_id = ref_c2.id\n WHERE ref_m2.symbol_id = gs.id AND ref_m2.role != 1\n ) AS REAL) / 5.0\n * MAX(CAST((SELECT COUNT(DISTINCT out_gs2.id)\n FROM mentions out_m2\n JOIN chunks out_c2 ON out_m2.chunk_id = out_c2.id\n JOIN global_symbols out_gs2 ON out_m2.symbol_id = out_gs2.id\n JOIN defn_enclosing_ranges out_der2 ON out_gs2.id = out_der2.symbol_id\n WHERE out_c2.document_id = der.document_id\n AND out_c2.start_line >= der.start_line\n AND out_c2.end_line <= der.end_line\n AND out_m2.role != 1\n AND out_gs2.id != gs.id\n AND out_der2.document_id != der.document_id\n ) AS REAL) / 5.0, 1.0)\n ) DESC\n LIMIT ?`,\n minLoc, limit,\n );\n\n return rows\n .filter((r) => !db.isIgnored(r.file))\n .map((r) => ({\n symbol: r.symbol,\n shortName: shortenSymbol(r.symbol),\n file: r.file,\n startLine: r.start_line,\n endLine: r.end_line,\n loc: r.loc,\n fanIn: r.fan_in,\n fanOut: r.fan_out,\n calleeCount: r.callee_count,\n score:\n Math.round(\n (r.loc / 50) * (r.fan_in / 5) * Math.max(r.fan_out / 5, 1) * 100,\n ) / 100,\n }));\n}\n"],"mappings":";;;;;;;;AAcO,SAAS,mBACd,IACA,MACqB;AACrB,QAAM,EAAE,OAAO,SAAS,IAAI,QAAQ,GAAG,IAAI,QAAQ,CAAC;AACpD,QAAM,cAAc,QAAQ,8BAA8B,KAAK,OAAO;AAEtE,QAAM,OAAO,GAAG;AAAA,IAUd;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,QAyCI,GAAG,kBAAkB,GAAG,CAAC;AAAA,YACrB,qBAAqB,GAAG,CAAC;AAAA,QAC7B,GAAG,eAAe,IAAI,CAAC;AAAA;AAAA,QAEvB,WAAW;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,IAsBf;AAAA,IAAQ;AAAA,EACV;AAEA,SAAO,KACJ,OAAO,CAAC,MAAM,CAAC,GAAG,UAAU,EAAE,IAAI,CAAC,EACnC,IAAI,CAAC,OAAO;AAAA,IACX,QAAQ,EAAE;AAAA,IACV,WAAW,cAAc,EAAE,MAAM;AAAA,IACjC,MAAM,EAAE;AAAA,IACR,WAAW,EAAE;AAAA,IACb,SAAS,EAAE;AAAA,IACX,KAAK,EAAE;AAAA,IACP,OAAO,EAAE;AAAA,IACT,QAAQ,EAAE;AAAA,IACV,aAAa,EAAE;AAAA,IACf,OACE,KAAK;AAAA,MACF,EAAE,MAAM,MAAO,EAAE,SAAS,KAAK,KAAK,IAAI,EAAE,UAAU,GAAG,CAAC,IAAI;AAAA,IAC/D,IAAI;AAAA,EACR,EAAE;AACN;","names":[]}
|
package/dist/chunk-ZOGY2V3N.js
DELETED
|
@@ -1,158 +0,0 @@
|
|
|
1
|
-
// src/query-support.ts
|
|
2
|
-
var TEST_FILE_PATTERNS = [
|
|
3
|
-
"%/__tests__/%",
|
|
4
|
-
"%.test.%",
|
|
5
|
-
"%.spec.%",
|
|
6
|
-
"%/test/%",
|
|
7
|
-
"%/tests/%",
|
|
8
|
-
"%_test.%",
|
|
9
|
-
"%_spec.%",
|
|
10
|
-
"%/test_%.%",
|
|
11
|
-
"%/spec_%.%"
|
|
12
|
-
];
|
|
13
|
-
var TEST_SUPPORT_PATH_PATTERNS = [
|
|
14
|
-
"%/test-utils/%"
|
|
15
|
-
];
|
|
16
|
-
function testFileMatchSql(alias, patterns = TEST_FILE_PATTERNS) {
|
|
17
|
-
return `(${patterns.map((pattern) => `${alias}.relative_path LIKE '${pattern}'`).join(" OR ")})`;
|
|
18
|
-
}
|
|
19
|
-
function testFileExclusionSql(alias, extraPatterns = []) {
|
|
20
|
-
const patterns = uniquePatterns([...TEST_FILE_PATTERNS, ...extraPatterns]);
|
|
21
|
-
return patterns.map((pattern) => `${alias}.relative_path NOT LIKE '${pattern}'`).join("\n AND ");
|
|
22
|
-
}
|
|
23
|
-
function buildFileDepGraph(db, scope) {
|
|
24
|
-
const scopeFilter = scope ? `AND d1.relative_path LIKE '%${scope}%'` : "";
|
|
25
|
-
const edges = db.all(
|
|
26
|
-
`SELECT DISTINCT
|
|
27
|
-
d1.relative_path AS from_file,
|
|
28
|
-
d2.relative_path AS to_file
|
|
29
|
-
FROM mentions m
|
|
30
|
-
JOIN chunks c ON m.chunk_id = c.id
|
|
31
|
-
JOIN documents d1 ON c.document_id = d1.id
|
|
32
|
-
JOIN global_symbols gs ON m.symbol_id = gs.id
|
|
33
|
-
JOIN defn_enclosing_ranges der ON gs.id = der.symbol_id
|
|
34
|
-
JOIN documents d2 ON der.document_id = d2.id
|
|
35
|
-
WHERE d1.id != d2.id
|
|
36
|
-
AND m.role != 1
|
|
37
|
-
${db.pathExclusionsFor("d1", "d2")}
|
|
38
|
-
${scopeFilter}`
|
|
39
|
-
);
|
|
40
|
-
const graph = /* @__PURE__ */ new Map();
|
|
41
|
-
for (const edge of edges) {
|
|
42
|
-
if (db.isIgnored(edge.from_file) || db.isIgnored(edge.to_file)) continue;
|
|
43
|
-
if (!graph.has(edge.from_file)) graph.set(edge.from_file, /* @__PURE__ */ new Set());
|
|
44
|
-
graph.get(edge.from_file).add(edge.to_file);
|
|
45
|
-
}
|
|
46
|
-
return graph;
|
|
47
|
-
}
|
|
48
|
-
function findFirstSymbolMatch(db, symbolPattern) {
|
|
49
|
-
const fileLineMatch = symbolPattern.match(/^(.+):(\d+)-(\d+)$/);
|
|
50
|
-
if (fileLineMatch) {
|
|
51
|
-
const [, filePath, startStr, endStr] = fileLineMatch;
|
|
52
|
-
const row = db.get(
|
|
53
|
-
`SELECT gs.id, gs.symbol, der.document_id, der.start_line, der.end_line, d.relative_path
|
|
54
|
-
FROM global_symbols gs
|
|
55
|
-
JOIN defn_enclosing_ranges der ON gs.id = der.symbol_id
|
|
56
|
-
JOIN documents d ON der.document_id = d.id
|
|
57
|
-
WHERE d.relative_path LIKE ?
|
|
58
|
-
AND der.start_line <= ? AND der.end_line >= ?
|
|
59
|
-
${db.pathExclusionsFor("d")}
|
|
60
|
-
ORDER BY (der.end_line - der.start_line) ASC
|
|
61
|
-
LIMIT 1`,
|
|
62
|
-
`%${filePath}%`,
|
|
63
|
-
parseInt(startStr, 10),
|
|
64
|
-
parseInt(endStr, 10)
|
|
65
|
-
);
|
|
66
|
-
if (row && !db.isIgnored(row.relative_path)) {
|
|
67
|
-
return {
|
|
68
|
-
symbolId: row.id,
|
|
69
|
-
symbol: row.symbol,
|
|
70
|
-
documentId: row.document_id,
|
|
71
|
-
startLine: row.start_line,
|
|
72
|
-
endLine: row.end_line,
|
|
73
|
-
relativePath: row.relative_path
|
|
74
|
-
};
|
|
75
|
-
}
|
|
76
|
-
}
|
|
77
|
-
const cleaned = symbolPattern.replace(/\(\)$/, "").replace(/\(.*$/, "");
|
|
78
|
-
for (const useNoiseFilter of [true, false]) {
|
|
79
|
-
const noiseClause = useNoiseFilter ? db.symbolNoiseFor("gs") : "";
|
|
80
|
-
const row = db.get(
|
|
81
|
-
`SELECT gs.id, gs.symbol, der.document_id, der.start_line, der.end_line, d.relative_path
|
|
82
|
-
FROM global_symbols gs
|
|
83
|
-
JOIN defn_enclosing_ranges der ON gs.id = der.symbol_id
|
|
84
|
-
JOIN documents d ON der.document_id = d.id
|
|
85
|
-
WHERE gs.symbol LIKE ?
|
|
86
|
-
${db.pathExclusionsFor("d")}
|
|
87
|
-
${noiseClause}
|
|
88
|
-
ORDER BY (der.end_line - der.start_line) DESC
|
|
89
|
-
LIMIT 1`,
|
|
90
|
-
`%${cleaned}%`
|
|
91
|
-
);
|
|
92
|
-
if (row && !db.isIgnored(row.relative_path)) {
|
|
93
|
-
return {
|
|
94
|
-
symbolId: row.id,
|
|
95
|
-
symbol: row.symbol,
|
|
96
|
-
documentId: row.document_id,
|
|
97
|
-
startLine: row.start_line,
|
|
98
|
-
endLine: row.end_line,
|
|
99
|
-
relativePath: row.relative_path
|
|
100
|
-
};
|
|
101
|
-
}
|
|
102
|
-
}
|
|
103
|
-
return null;
|
|
104
|
-
}
|
|
105
|
-
function getCalleeRowsForSymbol(db, symbol, opts = {}) {
|
|
106
|
-
const rows = db.all(
|
|
107
|
-
`SELECT DISTINCT
|
|
108
|
-
callee_gs.symbol AS symbol,
|
|
109
|
-
callee_d.relative_path AS file,
|
|
110
|
-
c.id AS chunk_id
|
|
111
|
-
FROM mentions m
|
|
112
|
-
JOIN chunks c ON m.chunk_id = c.id
|
|
113
|
-
JOIN global_symbols callee_gs ON m.symbol_id = callee_gs.id
|
|
114
|
-
JOIN defn_enclosing_ranges callee_der ON callee_gs.id = callee_der.symbol_id
|
|
115
|
-
JOIN documents callee_d ON callee_der.document_id = callee_d.id
|
|
116
|
-
WHERE c.document_id = ?
|
|
117
|
-
AND c.start_line >= ?
|
|
118
|
-
AND c.end_line <= ?
|
|
119
|
-
AND m.role != 1
|
|
120
|
-
AND callee_gs.id != ?
|
|
121
|
-
${db.symbolNoiseFor("callee_gs")}
|
|
122
|
-
${db.pathExclusionsFor("callee_d")}
|
|
123
|
-
ORDER BY callee_d.relative_path
|
|
124
|
-
${opts.limit ? "LIMIT ?" : ""}`,
|
|
125
|
-
...calleeQueryParams(symbol, opts.limit)
|
|
126
|
-
);
|
|
127
|
-
return rows.filter((row) => !db.isIgnored(row.file)).map((row) => ({
|
|
128
|
-
symbol: row.symbol,
|
|
129
|
-
file: row.file,
|
|
130
|
-
chunkId: row.chunk_id
|
|
131
|
-
}));
|
|
132
|
-
}
|
|
133
|
-
function calleeQueryParams(symbol, limit) {
|
|
134
|
-
const params = [
|
|
135
|
-
symbol.documentId,
|
|
136
|
-
symbol.startLine,
|
|
137
|
-
symbol.endLine,
|
|
138
|
-
symbol.symbolId
|
|
139
|
-
];
|
|
140
|
-
if (typeof limit === "number") {
|
|
141
|
-
params.push(limit);
|
|
142
|
-
}
|
|
143
|
-
return params;
|
|
144
|
-
}
|
|
145
|
-
function uniquePatterns(patterns) {
|
|
146
|
-
return [...new Set(patterns)];
|
|
147
|
-
}
|
|
148
|
-
|
|
149
|
-
export {
|
|
150
|
-
TEST_FILE_PATTERNS,
|
|
151
|
-
TEST_SUPPORT_PATH_PATTERNS,
|
|
152
|
-
testFileMatchSql,
|
|
153
|
-
testFileExclusionSql,
|
|
154
|
-
buildFileDepGraph,
|
|
155
|
-
findFirstSymbolMatch,
|
|
156
|
-
getCalleeRowsForSymbol
|
|
157
|
-
};
|
|
158
|
-
//# sourceMappingURL=chunk-ZOGY2V3N.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/query-support.ts"],"sourcesContent":["import type { ScipDatabase } from './db.js';\n\nexport interface SymbolLocation {\n documentId: number;\n startLine: number;\n endLine: number;\n symbolId: number;\n}\n\nexport interface SymbolMatch extends SymbolLocation {\n symbol: string;\n relativePath: string;\n}\n\nexport interface CalleeRow {\n symbol: string;\n file: string;\n chunkId: number;\n}\n\nexport const TEST_FILE_PATTERNS = [\n '%/__tests__/%',\n '%.test.%',\n '%.spec.%',\n '%/test/%',\n '%/tests/%',\n '%_test.%',\n '%_spec.%',\n '%/test_%.%',\n '%/spec_%.%',\n] as const;\n\nexport const TEST_SUPPORT_PATH_PATTERNS = [\n '%/test-utils/%',\n] as const;\n\nexport function testFileMatchSql(\n alias: string,\n patterns: readonly string[] = TEST_FILE_PATTERNS,\n): string {\n return `(${patterns.map((pattern) => `${alias}.relative_path LIKE '${pattern}'`).join(' OR ')})`;\n}\n\nexport function testFileExclusionSql(\n alias: string,\n extraPatterns: readonly string[] = [],\n): string {\n const patterns = uniquePatterns([...TEST_FILE_PATTERNS, ...extraPatterns]);\n return patterns\n .map((pattern) => `${alias}.relative_path NOT LIKE '${pattern}'`)\n .join('\\n AND ');\n}\n\nexport function buildFileDepGraph(\n db: ScipDatabase,\n scope?: string,\n): Map<string, Set<string>> {\n const scopeFilter = scope ? `AND d1.relative_path LIKE '%${scope}%'` : '';\n\n const edges = db.all<{ from_file: string; to_file: string }>(\n `SELECT DISTINCT\n d1.relative_path AS from_file,\n d2.relative_path AS to_file\n FROM mentions m\n JOIN chunks c ON m.chunk_id = c.id\n JOIN documents d1 ON c.document_id = d1.id\n JOIN global_symbols gs ON m.symbol_id = gs.id\n JOIN defn_enclosing_ranges der ON gs.id = der.symbol_id\n JOIN documents d2 ON der.document_id = d2.id\n WHERE d1.id != d2.id\n AND m.role != 1\n ${db.pathExclusionsFor('d1', 'd2')}\n ${scopeFilter}`,\n );\n\n const graph = new Map<string, Set<string>>();\n for (const edge of edges) {\n if (db.isIgnored(edge.from_file) || db.isIgnored(edge.to_file)) continue;\n if (!graph.has(edge.from_file)) graph.set(edge.from_file, new Set());\n graph.get(edge.from_file)!.add(edge.to_file);\n }\n\n return graph;\n}\n\nexport function findFirstSymbolMatch(\n db: ScipDatabase,\n symbolPattern: string,\n): SymbolMatch | null {\n // Handle file:line-line syntax (e.g., \"src/foo.ts:10-50\")\n const fileLineMatch = symbolPattern.match(/^(.+):(\\d+)-(\\d+)$/);\n if (fileLineMatch) {\n const [, filePath, startStr, endStr] = fileLineMatch;\n const row = db.get<{\n id: number;\n symbol: string;\n document_id: number;\n start_line: number;\n end_line: number;\n relative_path: string;\n }>(\n `SELECT gs.id, gs.symbol, der.document_id, der.start_line, der.end_line, d.relative_path\n FROM global_symbols gs\n JOIN defn_enclosing_ranges der ON gs.id = der.symbol_id\n JOIN documents d ON der.document_id = d.id\n WHERE d.relative_path LIKE ?\n AND der.start_line <= ? AND der.end_line >= ?\n ${db.pathExclusionsFor('d')}\n ORDER BY (der.end_line - der.start_line) ASC\n LIMIT 1`,\n `%${filePath}%`, parseInt(startStr!, 10), parseInt(endStr!, 10),\n );\n if (row && !db.isIgnored(row.relative_path)) {\n return {\n symbolId: row.id,\n symbol: row.symbol,\n documentId: row.document_id,\n startLine: row.start_line,\n endLine: row.end_line,\n relativePath: row.relative_path,\n };\n }\n }\n\n // Strip parentheses from the pattern to avoid shell escaping issues.\n // Agents often pass \"functionName()\" — strip the () for matching.\n const cleaned = symbolPattern.replace(/\\(\\)$/, '').replace(/\\(.*$/, '');\n\n // Try exact-ish match first (with noise filter), then fallback without noise filter.\n // The noise filter excludes %().(% which can incorrectly match some symbols.\n for (const useNoiseFilter of [true, false]) {\n const noiseClause = useNoiseFilter ? db.symbolNoiseFor('gs') : '';\n const row = db.get<{\n id: number;\n symbol: string;\n document_id: number;\n start_line: number;\n end_line: number;\n relative_path: string;\n }>(\n `SELECT gs.id, gs.symbol, der.document_id, der.start_line, der.end_line, d.relative_path\n FROM global_symbols gs\n JOIN defn_enclosing_ranges der ON gs.id = der.symbol_id\n JOIN documents d ON der.document_id = d.id\n WHERE gs.symbol LIKE ?\n ${db.pathExclusionsFor('d')}\n ${noiseClause}\n ORDER BY (der.end_line - der.start_line) DESC\n LIMIT 1`,\n `%${cleaned}%`,\n );\n\n if (row && !db.isIgnored(row.relative_path)) {\n return {\n symbolId: row.id,\n symbol: row.symbol,\n documentId: row.document_id,\n startLine: row.start_line,\n endLine: row.end_line,\n relativePath: row.relative_path,\n };\n }\n }\n\n return null;\n}\n\nexport function getCalleeRowsForSymbol(\n db: ScipDatabase,\n symbol: SymbolLocation,\n opts: { limit?: number } = {},\n): CalleeRow[] {\n const rows = db.all<{\n symbol: string;\n file: string;\n chunk_id: number;\n }>(\n `SELECT DISTINCT\n callee_gs.symbol AS symbol,\n callee_d.relative_path AS file,\n c.id AS chunk_id\n FROM mentions m\n JOIN chunks c ON m.chunk_id = c.id\n JOIN global_symbols callee_gs ON m.symbol_id = callee_gs.id\n JOIN defn_enclosing_ranges callee_der ON callee_gs.id = callee_der.symbol_id\n JOIN documents callee_d ON callee_der.document_id = callee_d.id\n WHERE c.document_id = ?\n AND c.start_line >= ?\n AND c.end_line <= ?\n AND m.role != 1\n AND callee_gs.id != ?\n ${db.symbolNoiseFor('callee_gs')}\n ${db.pathExclusionsFor('callee_d')}\n ORDER BY callee_d.relative_path\n ${opts.limit ? 'LIMIT ?' : ''}`,\n ...calleeQueryParams(symbol, opts.limit),\n );\n\n return rows.filter((row) => !db.isIgnored(row.file)).map((row) => ({\n symbol: row.symbol,\n file: row.file,\n chunkId: row.chunk_id,\n }));\n}\n\nfunction calleeQueryParams(\n symbol: SymbolLocation,\n limit?: number,\n): number[] {\n const params = [\n symbol.documentId,\n symbol.startLine,\n symbol.endLine,\n symbol.symbolId,\n ];\n\n if (typeof limit === 'number') {\n params.push(limit);\n }\n\n return params;\n}\n\nfunction uniquePatterns(patterns: readonly string[]): string[] {\n return [...new Set(patterns)];\n}\n"],"mappings":"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","names":[]}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/queries/deep-chains.ts"],"sourcesContent":["import type { ScipDatabase } from '../db.js';\nimport { buildFileDepGraph } from '../query-support.js';\nimport type { DeepChainResult } from '../types.js';\n\n/**\n * Find the longest transitive dependency chains between files.\n * A chain A → B → C → D means A depends on B, B on C, C on D.\n *\n * Long chains = high coupling depth = changes at the end ripple through many layers.\n */\nexport function deepChains(\n db: ScipDatabase,\n opts: { limit?: number; scope?: string; minDepth?: number } = {},\n): DeepChainResult[] {\n const { limit = 10, scope, minDepth = 3 } = opts;\n const graph = buildFileDepGraph(db, scope);\n\n // DFS to find longest paths (with cycle detection)\n const results: DeepChainResult[] = [];\n\n function dfs(node: string, path: string[], visited: Set<string>): void {\n const neighbors = graph.get(node);\n if (!neighbors || neighbors.size === 0) {\n if (path.length >= minDepth) {\n results.push({ chain: [...path], depth: path.length });\n }\n return;\n }\n\n let extended = false;\n for (const next of neighbors) {\n if (visited.has(next)) continue; // skip cycles\n visited.add(next);\n path.push(next);\n dfs(next, path, visited);\n path.pop();\n visited.delete(next);\n extended = true;\n }\n\n // If no unvisited neighbors, this is a leaf in this path\n if (!extended && path.length >= minDepth) {\n results.push({ chain: [...path], depth: path.length });\n }\n }\n\n // Start DFS from each node\n for (const startNode of graph.keys()) {\n const visited = new Set<string>([startNode]);\n dfs(startNode, [startNode], visited);\n\n // Early termination if we have enough results\n if (results.length > limit * 10) break;\n }\n\n // Sort by depth descending, take top N\n results.sort((a, b) => b.depth - a.depth);\n return results.slice(0, limit);\n}\n"],"mappings":";;;;;AAUO,SAAS,WACd,IACA,OAA8D,CAAC,GAC5C;AACnB,QAAM,EAAE,QAAQ,IAAI,OAAO,WAAW,EAAE,IAAI;AAC5C,QAAM,QAAQ,kBAAkB,IAAI,KAAK;AAGzC,QAAM,UAA6B,CAAC;AAEpC,WAAS,IAAI,MAAc,MAAgB,SAA4B;AACrE,UAAM,YAAY,MAAM,IAAI,IAAI;AAChC,QAAI,CAAC,aAAa,UAAU,SAAS,GAAG;AACtC,UAAI,KAAK,UAAU,UAAU;AAC3B,gBAAQ,KAAK,EAAE,OAAO,CAAC,GAAG,IAAI,GAAG,OAAO,KAAK,OAAO,CAAC;AAAA,MACvD;AACA;AAAA,IACF;AAEA,QAAI,WAAW;AACf,eAAW,QAAQ,WAAW;AAC5B,UAAI,QAAQ,IAAI,IAAI,EAAG;AACvB,cAAQ,IAAI,IAAI;AAChB,WAAK,KAAK,IAAI;AACd,UAAI,MAAM,MAAM,OAAO;AACvB,WAAK,IAAI;AACT,cAAQ,OAAO,IAAI;AACnB,iBAAW;AAAA,IACb;AAGA,QAAI,CAAC,YAAY,KAAK,UAAU,UAAU;AACxC,cAAQ,KAAK,EAAE,OAAO,CAAC,GAAG,IAAI,GAAG,OAAO,KAAK,OAAO,CAAC;AAAA,IACvD;AAAA,EACF;AAGA,aAAW,aAAa,MAAM,KAAK,GAAG;AACpC,UAAM,UAAU,oBAAI,IAAY,CAAC,SAAS,CAAC;AAC3C,QAAI,WAAW,CAAC,SAAS,GAAG,OAAO;AAGnC,QAAI,QAAQ,SAAS,QAAQ,GAAI;AAAA,EACnC;AAGA,UAAQ,KAAK,CAAC,GAAG,MAAM,EAAE,QAAQ,EAAE,KAAK;AACxC,SAAO,QAAQ,MAAM,GAAG,KAAK;AAC/B;","names":[]}
|