Skip to content

Commit a8649f1

Browse files
authored
fix: arcanes, release dates, categories (#959)
1 parent c063da3 commit a8649f1

9 files changed

Lines changed: 495 additions & 39 deletions

File tree

build/build.ts

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import fs from 'node:fs/promises';
22
import path from 'node:path';
33
import { fileURLToPath } from 'node:url';
44

5-
import minify from 'imagemin';
5+
import minify, { type Plugin } from 'imagemin';
66
import minifyPng from 'imagemin-pngquant';
77
import minifyJpeg from 'imagemin-jpegtran';
88
import fetch from 'node-fetch';
@@ -264,7 +264,7 @@ class Build {
264264
minifyPng({
265265
quality: [0.2, 0.4],
266266
}),
267-
],
267+
] as Plugin[],
268268
});
269269

270270
this.updateCache(item, cached, hash, isComponent);
@@ -374,7 +374,7 @@ class Build {
374374
const processedItems: Item[] = [];
375375
for (const imageName of Object.keys(itemsByImageName)) {
376376
// Items are grouped by imageName, there are non in this loop that will be undefined
377-
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
377+
378378
const group = itemsByImageName[imageName]!;
379379
if (group.length === 1) {
380380
processedItems.push(...group);
@@ -407,7 +407,7 @@ class Build {
407407
return (priorityMap[a.category] ?? Infinity) - (priorityMap[b.category] ?? Infinity);
408408
});
409409

410-
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
410+
411411
const mainItem = group[0]!;
412412
processedItems.push(mainItem);
413413

@@ -432,12 +432,12 @@ class Build {
432432
for (const item of [...processedItems, ...noImage]) {
433433
if (item.productCategory && allowedCustomCategories.includes(item.productCategory)) {
434434
// Since we're following the same logic as applying custom categories all keys should have an empty array and if they don't we should consider it an error
435-
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
435+
436436
data[item.productCategory]!.push(item);
437437
continue;
438438
}
439439

440-
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
440+
441441
data[item.category]!.push(item);
442442
}
443443
}

build/parser.ts

Lines changed: 91 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,8 @@ const warnings: Warnings = {
7979
missingType: [],
8080
failedImage: [],
8181
missingWikiThumb: [],
82+
missingReleaseDates: [],
83+
ambiguousWikiMatch: [],
8284
};
8385

8486
const filterBps = (blueprint: Partial<ItemComplete>): boolean => !bpConflicts.includes(blueprint.uniqueName ?? '');
@@ -223,13 +225,20 @@ class Parser {
223225
this.addDucats(result, data.wikia.ducats);
224226
this.addDropRate(result, data.drops);
225227
this.addPatchlogs(result, data.patchlogs);
226-
this.addAdditionalWikiaData(result, category, data.wikia);
228+
this.addAdditionalWikiaData(result, data.wikia);
227229
this.addIsPrime(result);
228230
this.addVaultData(result, category, data.wikia);
229231
this.addResistanceData(result, category);
230232
this.addRelics(result, data.relics, data.drops);
231233
this.applyMasterable(result);
232234
this.applyOverrides(result);
235+
if (!result.releaseDate) {
236+
if (result.masterable) {
237+
warnings.missingReleaseDates.push(result.name);
238+
} else if (result.category === 'Arcanes' && result.wikiAvailable) {
239+
warnings.missingReleaseDates.push(result.name);
240+
}
241+
}
233242
return result;
234243
}
235244

@@ -517,7 +526,7 @@ class Parser {
517526
}
518527

519528
if (item.productCategory && productCategoryTypeMap[item.productCategory]) {
520-
item.type = productCategoryTypeMap[item.productCategory];
529+
item.type = productCategoryTypeMap[item.productCategory]!;
521530
}
522531
}
523532

@@ -880,14 +889,44 @@ class Parser {
880889
return;
881890
}
882891

892+
/**
893+
* Map finalized output categories to wiki data buckets and merge handlers.
894+
* @param itemCategory category assigned by addCategory
895+
* @returns wiki bucket and handler key, if wiki merge applies
896+
*/
897+
resolveWikiaConfig(itemCategory?: string): { wikiCategory: string; handler: 'warframe' | 'weapon' | 'mod' | 'arcane' } | undefined {
898+
switch (itemCategory) {
899+
case 'Arcanes':
900+
return { wikiCategory: 'arcanes', handler: 'arcane' };
901+
case 'Warframes':
902+
return { wikiCategory: 'warframes', handler: 'warframe' };
903+
case 'Archwing':
904+
return { wikiCategory: 'archwings', handler: 'warframe' };
905+
case 'Primary':
906+
case 'Secondary':
907+
case 'Melee':
908+
case 'Arch-Gun':
909+
case 'Arch-Melee':
910+
return { wikiCategory: 'weapons', handler: 'weapon' };
911+
case 'Mods':
912+
return { wikiCategory: 'mods', handler: 'mod' };
913+
case 'Sentinels':
914+
return { wikiCategory: 'companions', handler: 'warframe' };
915+
default:
916+
return undefined;
917+
}
918+
}
919+
883920
/**
884921
* Adds data scraped from the wiki to a particular item
885922
* @param item to have wikia data added to
886-
* @param category of the data
887923
* @param wikiaData from wikia to apply
888924
*/
889-
addAdditionalWikiaData(item: ItemComplete, category: string, wikiaData: WikiaData): void {
890-
if (!['weapons', 'warframes', 'mods', 'upgrades', 'sentinels'].includes(category.toLowerCase())) return;
925+
addAdditionalWikiaData(item: ItemComplete, wikiaData: WikiaData): void {
926+
const config = this.resolveWikiaConfig(item.category);
927+
if (!config) return;
928+
929+
const { wikiCategory, handler } = config;
891930

892931
const slots: string[][] = [
893932
['Secondary'], // 0
@@ -907,35 +946,22 @@ class Parser {
907946
['Railjack Turret'], // 14
908947
];
909948

910-
let wikiCategory = category.toLowerCase();
911-
if (category === 'Upgrades') wikiCategory = 'mods';
912-
if (item.category === 'Archwing') wikiCategory = 'archwings';
913-
if (category === 'Sentinels') wikiCategory = 'companions';
914-
915-
const wikiaItem = (wikiaData[wikiCategory as keyof WikiaData] as WikiaWeapon[]).find((i) => {
916-
const uMatch = i.uniqueName === item.uniqueName;
917-
let nMatch = true;
918-
if (category.toLowerCase() === 'weapons' && typeof item.slot !== 'undefined') {
919-
nMatch = slots[item.slot]?.includes(i.slot?.toString() ?? '') ?? false;
920-
}
921-
return uMatch && nMatch;
922-
});
949+
const wikiaItem = this.findWikiaItem(item, wikiCategory, wikiaData, slots);
923950
if (!wikiaItem) return;
924951
item.wikiAvailable = true;
925952

926-
switch (category.toLowerCase()) {
927-
case 'sentinels':
928-
case 'warframes':
929-
this.addWarframeWikiaData(item, wikiaItem);
953+
switch (handler) {
954+
case 'warframe':
955+
this.addWarframeWikiaData(item, wikiaItem as WikiaWarframe);
930956
break;
931-
case 'weapons':
932-
this.addWeaponWikiaData(item, wikiaItem);
957+
case 'weapon':
958+
this.addWeaponWikiaData(item, wikiaItem as WikiaWeapon);
933959
break;
934-
case 'upgrades':
935-
this.addModWikiaData(item, wikiaItem);
960+
case 'mod':
961+
this.addModWikiaData(item, wikiaItem as WikiaMod);
936962
break;
937-
case 'arcanes':
938-
this.addArcaneWikiaData(item, wikiaItem);
963+
case 'arcane':
964+
this.addArcaneWikiaData(item, wikiaItem as WikiaArcane);
939965
break;
940966
default:
941967
break;
@@ -947,6 +973,40 @@ class Parser {
947973
if (item.introduced) item.releaseDate = item.introduced.date;
948974
}
949975

976+
/**
977+
* Find a wiki entry by uniqueName, falling back to an exact name match.
978+
* @param item item to match against wiki data
979+
* @param wikiCategory wiki data bucket key
980+
* @param wikiaData scraped wiki data
981+
* @param slots weapon slot compatibility map
982+
* @returns matched wiki entry, if unambiguous
983+
*/
984+
findWikiaItem(
985+
item: ItemComplete,
986+
wikiCategory: string,
987+
wikiaData: WikiaData,
988+
slots: string[][]
989+
): { uniqueName?: string; name: string; slot?: unknown; introduced?: string } | undefined {
990+
const wikiItems = (wikiaData[wikiCategory as keyof WikiaData] as
991+
| { uniqueName?: string; name: string; slot?: unknown; introduced?: string }[]
992+
| undefined) ?? [];
993+
994+
const uniqueMatch = wikiItems.find((i) => {
995+
const uMatch = i.uniqueName === item.uniqueName;
996+
let nMatch = true;
997+
if (wikiCategory === 'weapons' && typeof item.slot !== 'undefined') {
998+
nMatch = slots[item.slot]?.includes(String(i.slot ?? '')) ?? false;
999+
}
1000+
return uMatch && nMatch;
1001+
});
1002+
if (uniqueMatch) return uniqueMatch;
1003+
1004+
const nameMatches = wikiItems.filter((i) => i.name === item.name);
1005+
if (nameMatches.length === 1) return nameMatches[0];
1006+
if (nameMatches.length > 1) warnings.ambiguousWikiMatch.push(item.name);
1007+
return undefined;
1008+
}
1009+
9501010
addWarframeWikiaData(item: ItemComplete, wikiaItem: WikiaWarframe): void {
9511011
item.aura = wikiaItem.auraPolarity;
9521012
item.conclave = wikiaItem.conclave;
@@ -1018,7 +1078,7 @@ class Parser {
10181078
item.wikiaThumbnail = wikiaItem.thumbnail;
10191079
item.wikiaUrl = wikiaItem.url;
10201080
item.transmutable = wikiaItem.transmutable;
1021-
item.type = wikiaItem.type ?? item.type;
1081+
item.rarity = wikiaItem.rarity ?? item.rarity;
10221082
if (!wikiaItem.thumbnail) warnings.missingWikiThumb.push(item.name);
10231083
}
10241084

@@ -1077,8 +1137,8 @@ class Parser {
10771137
* @param drops drop rate data for refinement-specific chances
10781138
*/
10791139
addRelics(item: ItemComplete, relics: TitaniaRelic[], drops: RawDrop[]): void {
1080-
const hasRelicDrop = (item.components)?.some((c): boolean =>
1081-
c.drops?.some((d) => d.location.includes('Relic'))
1140+
const hasRelicDrop = item.components?.some((c) =>
1141+
c.drops?.some((d) => d.location.includes('Relic')) ?? false
10821142
);
10831143
if (item.type !== 'Relic' && !hasRelicDrop) return;
10841144

build/types/shared.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,8 @@ export interface Warnings {
8282
missingType: string[];
8383
failedImage: string[];
8484
missingWikiThumb: string[];
85+
missingReleaseDates: string[];
86+
ambiguousWikiMatch: string[];
8587
}
8688

8789
export type Locales = Record<string, Record<string, unknown>>;
@@ -158,6 +160,7 @@ export interface WikiaArcane {
158160
transmutable?: boolean;
159161
introduced?: string;
160162
type?: string;
163+
rarity?: string;
161164
thumbnail?: string;
162165
[key: string]: unknown;
163166
}

build/wikia/transformers/transformArcanes.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ interface OldArcane {
66
Transmutable?: boolean;
77
Introduced?: string;
88
Type?: string;
9+
Rarity?: string;
910
InternalName?: string;
1011
[key: string]: unknown;
1112
}
@@ -17,7 +18,7 @@ export default (oldArcane: OldArcane, imageUrls: Record<string, string>): WikiaA
1718
}
1819

1920
try {
20-
const { Image, Name, Transmutable, Introduced, Type, InternalName } = oldArcane;
21+
const { Image, Name, Transmutable, Introduced, Type, Rarity, InternalName } = oldArcane;
2122

2223
newArcane = {
2324
name: Name,
@@ -26,6 +27,7 @@ export default (oldArcane: OldArcane, imageUrls: Record<string, string>): WikiaA
2627
transmutable: Transmutable,
2728
introduced: Introduced,
2829
type: Type,
30+
rarity: Rarity ? Rarity.charAt(0).toUpperCase() + Rarity.slice(1).toLowerCase() : undefined,
2931
thumbnail: imageUrls[Image ?? ''],
3032
};
3133
} catch (error) {

eslint.config.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ export default defineConfig(
3434
],
3535
'@typescript-eslint/no-base-to-string': 'off',
3636
'@typescript-eslint/no-unnecessary-condition': 'off',
37+
'@typescript-eslint/no-non-null-assertion': 'off',
3738
},
3839
},
3940
// Disable type checking for all JS files

index.d.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -249,14 +249,16 @@ declare module '@wfcd/items' {
249249
attacks?: Attack[];
250250
maxLevelCap?: number;
251251
}
252-
interface Arcane extends Named, Buildable, Droppable {
252+
interface Arcane extends Named, Buildable, Droppable, WikiaItem {
253253
category: 'Arcanes';
254254
excludeFromCodex?: true;
255255
imageName: string;
256256
levelStats?: LevelStat[];
257257
masterable: false;
258258
patchlogs?: PatchLog[];
259+
releaseDate?: DateString;
259260
tradable: true;
261+
transmutable?: boolean;
260262
type: 'Arcane' | `${ArcaneType} Arcane`;
261263
}
262264
interface StanceMod extends Omit<Mod, 'levelStats'> {

0 commit comments

Comments
 (0)