(exploreTexts, answerText)
| 563 | * the common shape where call 1 is on target and call 3 is pure noise. |
| 564 | */ |
| 565 | export function computeAllocation(exploreTexts, answerText) { |
| 566 | const calls = exploreTexts.map(parseExploreCall).filter((c) => c.files.length); |
| 567 | const extensions = new Set(); |
| 568 | for (const c of calls) { |
| 569 | for (const f of c.files) { |
| 570 | const dot = f.path.lastIndexOf('.'); |
| 571 | if (dot > 0) extensions.add(f.path.slice(dot + 1).toLowerCase()); |
| 572 | } |
| 573 | } |
| 574 | const cited = answerCitations(answerText, extensions); |
| 575 | |
| 576 | // symbol -> the returned files whose header lists it as DEFINED. A `variable` |
| 577 | // is usually an import binding (`var compileETag = require('./utils')…`), so |
| 578 | // when the same name is also a real definition somewhere in the envelope, the |
| 579 | // definition wins and the aliasing file is not credited. |
| 580 | const symbolFiles = new Map(); |
| 581 | for (const c of calls) { |
| 582 | for (const f of c.files) { |
| 583 | for (const s of f.symbols) { |
| 584 | if (!symbolFiles.has(s.name)) symbolFiles.set(s.name, { strong: new Set(), weak: new Set() }); |
| 585 | const owners = symbolFiles.get(s.name); |
| 586 | (s.kind === 'variable' || s.kind === 'constant' ? owners.weak : owners.strong).add(f.path); |
| 587 | } |
| 588 | } |
| 589 | } |
| 590 | const ownersOf = (name) => { |
| 591 | const o = symbolFiles.get(name); |
| 592 | return o ? (o.strong.size ? o.strong : o.weak) : null; |
| 593 | }; |
| 594 | |
| 595 | // `lib/response.js` is cited by `lib/response.js`, by `response.js`, and by an |
| 596 | // absolute path ending in it — samePath already encodes exactly that. |
| 597 | const viaPath = (path) => cited.paths.some((p) => samePath(path, p)); |
| 598 | const viaSymbol = (path) => { |
| 599 | for (const s of cited.symbols) { |
| 600 | const owners = ownersOf(s); |
| 601 | if (owners && owners.size < SYMBOL_AMBIGUITY_LIMIT && owners.has(path)) return s; |
| 602 | } |
| 603 | return null; |
| 604 | }; |
| 605 | |
| 606 | const verdict = new Map(); // path -> { via, symbol } |
| 607 | const judge = (path) => { |
| 608 | if (!verdict.has(path)) { |
| 609 | if (viaPath(path)) verdict.set(path, { via: 'path' }); |
| 610 | else { |
| 611 | const s = viaSymbol(path); |
| 612 | verdict.set(path, s ? { via: 'symbol', symbol: s } : { via: null }); |
| 613 | } |
| 614 | } |
| 615 | return verdict.get(path); |
| 616 | }; |
| 617 | |
| 618 | const perCall = calls.map((c, i) => { |
| 619 | const files = c.files.map((f) => ({ ...f, ...judge(f.path) })); |
| 620 | const used = files.filter((f) => f.via).reduce((s, f) => s + f.chars, 0); |
| 621 | const usedPath = files.filter((f) => f.via === 'path').reduce((s, f) => s + f.chars, 0); |
| 622 | return { |
no test coverage detected