The second consumer drops its copy of the highlighter
people-groups-grid is the file whose duplicate earned useTextHighlight its place in library/ under RULE 2, and studio agreed to treat it as unowned and asked for it to be cut. Its hand-rolled range walk is replaced by highlightRanges(value, match.indices), and its local escapeHtml goes with it: once the loop moved, that function had ZERO callers left. ★ ONE CORRECTION TO THE STATED REASON. studio approved this with "removing the null-throw is reason enough". Measured before accepting it: that escaper has exactly three callers, all inside `if (value && typeof value === 'string')`, so the throw was NOT REACHABLE here. The library escaper's null guard is still an improvement in principle, but it fixes nothing that could fire in this file, and I would rather carry the smaller true reason than the larger convenient one. The reason that does hold is the original one: the same construction existed twice, character for character apart from variable names. One fact, one copy. The gate flips with it — check:text-highlight now asserts this file routes through the helper, that its escapeHtml is gone, and that the hand-rolled range walk is really removed rather than merely shadowed by the new import.
This commit is contained in:
parent
29bbab3967
commit
02b2e9d59a
2 changed files with 11 additions and 21 deletions
|
|
@ -42,6 +42,7 @@
|
|||
import { ref, computed, watch, onMounted, getCurrentInstance } from 'vue'
|
||||
// SHARED search primitives (one source of truth for BOTH cap-2 grid + cap-1 dashboard).
|
||||
import Fuse from '@map/components/search/fuzzy.js'
|
||||
import { highlightRanges } from '@map/composables/useTextHighlight.js'
|
||||
import SearchBar from '@map/components/search/SearchBar.vue'
|
||||
import FilterDropdown from '@map/components/search/FilterDropdown.vue'
|
||||
import { resolveAssetsBase } from '@map/utils/embedBase.js'
|
||||
|
|
@ -474,10 +475,6 @@ const visibleFilterCount = computed(() => Object.keys(activeFilters.value).lengt
|
|||
// ref is always empty there and only the prop is real.
|
||||
const isNarrowed = computed(() => activeFilterCount.value > 0 || !!(effSearchTerm.value || '').trim())
|
||||
|
||||
function escapeHtml(s) {
|
||||
return s.replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>')
|
||||
.replace(/"/g, '"').replace(/'/g, ''')
|
||||
}
|
||||
|
||||
function debounce(fn, ms = 500) {
|
||||
let timeout
|
||||
|
|
@ -731,18 +728,9 @@ function filterUUPGs() {
|
|||
value = newItem[String(matchKey)]
|
||||
}
|
||||
if (value && typeof value === 'string') {
|
||||
let currentIndex = 0
|
||||
let highlightedValue = ''
|
||||
match.indices.forEach((idx, i) => {
|
||||
const start = idx[0]
|
||||
const end = idx[1]
|
||||
const isLast = match.indices.length - 1 === i
|
||||
highlightedValue
|
||||
+= escapeHtml(value.slice(currentIndex, start))
|
||||
+ `<span class="search-highlight">${escapeHtml(value.slice(start, end + 1))}</span>`
|
||||
+ (isLast ? escapeHtml(value.slice(end + 1)) : '')
|
||||
currentIndex = end + 1
|
||||
})
|
||||
// The same construction regions-dashboard's UupgsList had, character for character apart from
|
||||
// variable names, which is what earned it a place in library/ under RULE 2. One fact, one copy.
|
||||
const highlightedValue = highlightRanges(value, match.indices)
|
||||
if (String(matchKey).includes('imb_alternate_name')) {
|
||||
newItem.matches.push({ key: t.value.alternate_name ?? 'Also known as', label: highlightedValue })
|
||||
} else if (String(matchKey).includes('.')) {
|
||||
|
|
|
|||
|
|
@ -80,12 +80,14 @@ ok(highlightRanges('Cameroon', [[2, 4]]) !== highlightRanges('Cameroon', [[0, 2]
|
|||
const upg = codeOnly(readFileSync(resolve(ROOT, 'plugin-designs/regions-dashboard/src/UupgsList.vue'), 'utf8'))
|
||||
ok(/highlightRanges/.test(upg), 'UupgsList routes through highlightRanges()')
|
||||
ok(!/function escapeHtml\(/.test(upg), 'and no longer hand-rolls escapeHtml (its only callers were inside highlight())')
|
||||
// people-groups-grid is the SECOND consumer that justified the extraction under RULE 2, and it is NOT
|
||||
// migrated yet: it is another owner's file, so it is handed over as source rather than self-landed.
|
||||
// Asserted as not-yet-migrated so this gate states the truth instead of implying the job is finished.
|
||||
// people-groups-grid is the SECOND consumer that justified the extraction under RULE 2. studio agreed
|
||||
// to treat it as unowned and asked for it to be cut, so it is migrated now and asserted here.
|
||||
const pgg = codeOnly(readFileSync(resolve(ROOT, 'plugin-designs/people-groups-grid/people-groups-grid.vue'), 'utf8'))
|
||||
ok(/search-highlight/.test(pgg),
|
||||
'people-groups-grid still builds its own highlight (the named second consumer, handed over, not yet migrated)')
|
||||
ok(/highlightRanges/.test(pgg), 'people-groups-grid routes through highlightRanges() too -- the duplicate is gone')
|
||||
ok(!/function escapeHtml\(/.test(pgg),
|
||||
'and its own escapeHtml is gone: after the loop moved, it had ZERO callers left')
|
||||
ok(!/currentIndex = end \+ 1/.test(pgg),
|
||||
'the hand-rolled range walk is really removed, not merely shadowed by the import')
|
||||
|
||||
console.log(fails ? `\nTEXT-HIGHLIGHT: ${fails} FAIL(s)` : '\nTEXT-HIGHLIGHT: the shared highlighter matches what it replaced, and both escaping deltas are pinned')
|
||||
process.exit(fails ? 1 : 0)
|
||||
|
|
|
|||
Loading…
Reference in a new issue