From 48c1590b6b7cec50fd9c86eefc3a7dee78e8d142 Mon Sep 17 00:00:00 2001 From: EdJoPaTo Date: Fri, 17 Mar 2023 15:23:53 +0100 Subject: [PATCH 1/2] refactor: use typesave utils --- src/helpers/rank.ts | 26 ++++++++++++------------- src/helpers/simplify_text_attributes.ts | 5 +++-- src/helpers/sitelinks.ts | 20 +++++++++++-------- src/queries/cirrus_search.ts | 7 ++++--- src/utils/utils.ts | 11 +++++++++++ 5 files changed, 43 insertions(+), 26 deletions(-) diff --git a/src/helpers/rank.ts b/src/helpers/rank.ts index 0e1e6e3c..91799352 100644 --- a/src/helpers/rank.ts +++ b/src/helpers/rank.ts @@ -1,7 +1,14 @@ -import type { Claims, PropertyClaims } from '../types/claim.js' +import { typedEntries } from '../utils/utils.js' +import type { Claim, Claims, PropertyClaims, Rank } from '../types/claim.js' export function truthyPropertyClaims (propertyClaims: PropertyClaims): PropertyClaims { - const aggregate = propertyClaims.reduce(aggregatePerRank, {}) + const aggregate: Partial> = {} + for (const claim of propertyClaims) { + const { rank } = claim + aggregate[rank] ??= [] + aggregate[rank].push(claim) + } + // on truthyness: https://www.mediawiki.org/wiki/Wikibase/Indexing/RDF_Dump_Format#Truthy_statements return aggregate.preferred || aggregate.normal || [] } @@ -10,17 +17,10 @@ export function nonDeprecatedPropertyClaims (propertyClaims: PropertyClaims): Pr return propertyClaims.filter(claim => claim.rank !== 'deprecated') } -const aggregatePerRank = (aggregate, claim) => { - const { rank } = claim - aggregate[rank] || (aggregate[rank] = []) - aggregate[rank].push(claim) - return aggregate -} - export function truthyClaims (claims: Claims): Claims { - const truthClaimsOnly = {} - Object.keys(claims).forEach(property => { - truthClaimsOnly[property] = truthyPropertyClaims(claims[property]) - }) + const truthClaimsOnly: Claims = {} + for (const [ property, value ] of typedEntries(claims)) { + truthClaimsOnly[property] = truthyPropertyClaims(value) + } return truthClaimsOnly } diff --git a/src/helpers/simplify_text_attributes.ts b/src/helpers/simplify_text_attributes.ts index 8f208ea3..bf670664 100644 --- a/src/helpers/simplify_text_attributes.ts +++ b/src/helpers/simplify_text_attributes.ts @@ -1,3 +1,4 @@ +import { typedEntries } from '../utils/utils.js' import type { WmLanguageCode } from '../types/options.js' import type { Aliases, Descriptions, Glosses, Labels, Lemmas, Representations, SimplifiedAliases, SimplifiedDescriptions, SimplifiedGlosses, SimplifiedLabels, SimplifiedLemmas, SimplifiedRepresentations } from '../types/terms.js' @@ -5,7 +6,7 @@ type InValue = { readonly value: T } function singleValue (data: Partial>>>) { const simplified: Partial> = {} - for (const [ lang, obj ] of Object.entries(data)) { + for (const [ lang, obj ] of typedEntries(data)) { simplified[lang] = obj != null ? obj.value : null } return simplified @@ -13,7 +14,7 @@ function singleValue (data: Partial (data: Partial>>>>) { const simplified: Partial> = {} - for (const [ lang, obj ] of Object.entries(data)) { + for (const [ lang, obj ] of typedEntries(data)) { simplified[lang] = obj != null ? obj.map(o => o.value) : [] } return simplified diff --git a/src/helpers/sitelinks.ts b/src/helpers/sitelinks.ts index fcebfe0f..8c892710 100644 --- a/src/helpers/sitelinks.ts +++ b/src/helpers/sitelinks.ts @@ -1,7 +1,6 @@ -import { fixedEncodeURIComponent, isOfType, rejectObsoleteInterface, replaceSpaceByUnderscores } from '../utils/utils.js' +import { fixedEncodeURIComponent, isAKey, isOfType, rejectObsoleteInterface, replaceSpaceByUnderscores } from '../utils/utils.js' import { languages } from './sitelinks_languages.js' import { specialSites } from './special_sites.js' -import type { EntityId } from '../types/entity.js' import type { Url, WmLanguageCode } from '../types/options.js' import type { Site } from '../types/sitelinks.js' @@ -18,9 +17,14 @@ export function getSitelinkUrl ({ site, title }: GetSitelinkUrlOptions): Url { if (!site) throw new Error('missing a site') if (!title) throw new Error('missing a title') + if (isAKey(siteUrlBuilders, site)) { + return siteUrlBuilders[site](title) + } + const shortSiteKey = site.replace(/wiki$/, '') - const specialUrlBuilder = siteUrlBuilders[shortSiteKey] || siteUrlBuilders[site] - if (specialUrlBuilder) return specialUrlBuilder(title) + if (isAKey(siteUrlBuilders, shortSiteKey)) { + return siteUrlBuilders[shortSiteKey](title) + } const { lang, project } = getSitelinkData(site) title = fixedEncodeURIComponent(replaceSpaceByUnderscores(title)) @@ -34,7 +38,7 @@ const siteUrlBuilders = { mediawiki: (title: string) => `https://www.mediawiki.org/wiki/${title}`, meta: wikimediaSite('meta'), species: wikimediaSite('species'), - wikidata: (entityId: EntityId) => { + wikidata: (entityId: string) => { const prefix = prefixByEntityLetter[entityId[0]] let title = prefix ? `${prefix}:${entityId}` : entityId // Required for forms and senses @@ -84,9 +88,9 @@ export function getSitelinkData (site: Site | Url): SitelinkData { return { lang, project, key, title, url } } else { const key = site - const specialProjectName = specialSites[key] - if (specialProjectName) { - return { lang: 'en', project: specialProjectName, key } + if (isAKey(specialSites, site)) { + const project = specialSites[site] + return { lang: 'en', project, key } } let [ lang, projectSuffix, rest ] = key.split('wik') diff --git a/src/queries/cirrus_search.ts b/src/queries/cirrus_search.ts index b54c5f24..eb869899 100644 --- a/src/queries/cirrus_search.ts +++ b/src/queries/cirrus_search.ts @@ -1,6 +1,6 @@ // See https://www.wikidata.org/w/api.php?action=help&modules=query%2Bsearch -import { rejectObsoleteInterface } from '../utils/utils.js' +import { isAKey, rejectObsoleteInterface } from '../utils/utils.js' import type { Url, UrlResultFormat } from '../types/options.js' import type { BuildUrlFunction } from '../utils/build_url.js' @@ -23,11 +23,12 @@ export function cirrusSearchPagesFactory (buildUrl: BuildUrlFunction) { rejectObsoleteInterface(arguments) // Accept sr parameters with or without prefix - for (const key in options) { + for (const [ key, value ] of Object.entries(options)) { if (key.startsWith('sr')) { const shortKey = key.replace(/^sr/, '') + if (!isAKey(options, shortKey)) throw new Error(`${key} is not a valid option`) if (options[shortKey] != null) throw new Error(`${shortKey} and ${key} are the same`) - options[shortKey] = options[key] + options[shortKey] = value } } diff --git a/src/utils/utils.ts b/src/utils/utils.ts index ad30c692..093d5feb 100644 --- a/src/utils/utils.ts +++ b/src/utils/utils.ts @@ -56,3 +56,14 @@ export function rejectObsoleteInterface (args: IArguments): void { export function isOfType (all: readonly T[], element: unknown): element is T { return typeof element === 'string' && (all as readonly string[]).includes(element) } + +/** key is a key on the object */ +export function isAKey (obj: Readonly>>, key: PropertyKey): key is T { + return Object.prototype.hasOwnProperty.call(obj, key) +} + +/** like Object.entries() but with typed key */ +export function typedEntries (input: Readonly>>): Array<[K, V]> { + // @ts-expect-error string is not assignable to K as K is more specific + return Object.entries(input) +} From f949b6d40847380f1b042f6de5b60a29dedd8d5f Mon Sep 17 00:00:00 2001 From: EdJoPaTo Date: Mon, 27 Mar 2023 16:34:11 +0200 Subject: [PATCH 2/2] fix: use older syntax the new syntax works but is transpiled into usable code which is not so easy to read in JS Co-authored-by: maxlath --- src/helpers/rank.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/helpers/rank.ts b/src/helpers/rank.ts index 91799352..a94201dd 100644 --- a/src/helpers/rank.ts +++ b/src/helpers/rank.ts @@ -5,7 +5,7 @@ export function truthyPropertyClaims (propertyClaims: PropertyClaims): PropertyC const aggregate: Partial> = {} for (const claim of propertyClaims) { const { rank } = claim - aggregate[rank] ??= [] + aggregate[rank] = aggregate[rank] || [] aggregate[rank].push(claim) }