style: format tracked files and ignore generated data exports

`prettier --check .` failed on 22 files, so gating CI on `npm run lint` was
never going to pass. These changes are whitespace only.

Adds the two remaining generated data exports to .prettierignore so this class
of churn cannot recur, along with machine-local settings files.
This commit is contained in:
Josh Creek
2026-09-13 17:38:35 +01:00
parent 676490b800
commit 08c5e3271c
12 changed files with 219 additions and 211 deletions
+8
View File
@@ -2,3 +2,11 @@
pnpm-lock.yaml
package-lock.json
yarn.lock
# Generated data exports. Nobody hand-edits these, and reformatting them buries real data
# changes under tens of thousands of lines of churn.
src/lib/helpers/pokeapi-pokemon.json
static/sprites/pokemon.json
# Machine-local editor and tool settings.
**/*.local.json
+5 -5
View File
@@ -1,6 +1,6 @@
export default {
plugins: {
tailwindcss: {},
autoprefixer: {},
},
}
plugins: {
tailwindcss: {},
autoprefixer: {}
}
};
+2 -2
View File
@@ -1,3 +1,3 @@
import process from 'node:process'
import process from 'node:process';
export const generateSW = process.env.GENERATE_SW === 'true'
export const generateSW = process.env.GENERATE_SW === 'true';
+133 -119
View File
@@ -17,167 +17,181 @@ const __dirname = path.dirname(__filename);
* Parse CSV file into array of objects
*/
function parseCSV(filePath) {
const content = fs.readFileSync(filePath, 'utf-8');
const lines = content.split('\n').filter(line => line.trim());
const headers = lines[0].split(',');
const content = fs.readFileSync(filePath, 'utf-8');
const lines = content.split('\n').filter((line) => line.trim());
const headers = lines[0].split(',');
return lines.slice(1).map(line => {
const values = parseCSVLine(line);
const obj = {};
headers.forEach((header, i) => {
obj[header] = values[i] || null;
});
return obj;
});
return lines.slice(1).map((line) => {
const values = parseCSVLine(line);
const obj = {};
headers.forEach((header, i) => {
obj[header] = values[i] || null;
});
return obj;
});
}
/**
* Parse a single CSV line, handling quoted values
*/
function parseCSVLine(line) {
const values = [];
let current = '';
let inQuotes = false;
const values = [];
let current = '';
let inQuotes = false;
for (let i = 0; i < line.length; i++) {
const char = line[i];
for (let i = 0; i < line.length; i++) {
const char = line[i];
if (char === '"') {
inQuotes = !inQuotes;
} else if (char === ',' && !inQuotes) {
values.push(current);
current = '';
} else {
current += char;
}
}
values.push(current);
if (char === '"') {
inQuotes = !inQuotes;
} else if (char === ',' && !inQuotes) {
values.push(current);
current = '';
} else {
current += char;
}
}
values.push(current);
return values;
return values;
}
/**
* Generate migration SQL from CSV data
*/
function generateMigration(region) {
console.log(`\nGenerating migration for ${region}...`);
console.log(`\nGenerating migration for ${region}...`);
// Determine generation number from region
const regionToGen = {
'Kanto': 1, 'Johto': 2, 'Hoenn': 3, 'Sinnoh': 4,
'Unova': 5, 'Kalos': 6, 'Alola': 7, 'Galar': 8,
'Hisui': 8, 'Paldea': 9
};
const gen = regionToGen[region] || 1;
// Determine generation number from region
const regionToGen = {
Kanto: 1,
Johto: 2,
Hoenn: 3,
Sinnoh: 4,
Unova: 5,
Kalos: 6,
Alola: 7,
Galar: 8,
Hisui: 8,
Paldea: 9
};
const gen = regionToGen[region] || 1;
// 1. Load CSV files
const pokemonPath = path.join(__dirname, '..', 'data', 'pokemon', `gen${gen}-${region.toLowerCase()}.csv`);
const gamesPath = path.join(__dirname, '..', 'data', 'pokemon', 'games.csv');
// 1. Load CSV files
const pokemonPath = path.join(
__dirname,
'..',
'data',
'pokemon',
`gen${gen}-${region.toLowerCase()}.csv`
);
const gamesPath = path.join(__dirname, '..', 'data', 'pokemon', 'games.csv');
if (!fs.existsSync(pokemonPath)) {
console.error(`Error: Pokemon CSV not found at ${pokemonPath}`);
process.exit(1);
}
if (!fs.existsSync(pokemonPath)) {
console.error(`Error: Pokemon CSV not found at ${pokemonPath}`);
process.exit(1);
}
const pokemon = parseCSV(pokemonPath);
const games = parseCSV(gamesPath);
const pokemon = parseCSV(pokemonPath);
const games = parseCSV(gamesPath);
// 2. Filter for this region
const regionGames = games.filter(g => g.region === region);
// 2. Filter for this region
const regionGames = games.filter((g) => g.region === region);
if (regionGames.length === 0) {
console.error(`Error: No games found for region ${region} in games.csv`);
process.exit(1);
}
if (regionGames.length === 0) {
console.error(`Error: No games found for region ${region} in games.csv`);
process.exit(1);
}
// 3. Generate SQL
let sql = `-- Seed ${region} region Pokémon (Gen ${gen})\n`;
sql += `-- Auto-generated from CSV files\n\n`;
// 3. Generate SQL
let sql = `-- Seed ${region} region Pokémon (Gen ${gen})\n`;
sql += `-- Auto-generated from CSV files\n\n`;
// Region-game mappings
sql += `-- Insert ${region} region-game mappings\n`;
sql += `INSERT INTO region_game_mappings (region, game) VALUES\n`;
sql += regionGames.map(g => ` ('${region}', '${g.displayName}')`).join(',\n');
sql += `\nON CONFLICT (region, game) DO NOTHING;\n\n`;
// Region-game mappings
sql += `-- Insert ${region} region-game mappings\n`;
sql += `INSERT INTO region_game_mappings (region, game) VALUES\n`;
sql += regionGames.map((g) => ` ('${region}', '${g.displayName}')`).join(',\n');
sql += `\nON CONFLICT (region, game) DO NOTHING;\n\n`;
// Pokemon entries (without regional dex number)
sql += `-- Insert ${region} Pokémon entries\n`;
sql += `INSERT INTO pokedex_entries (\n`;
sql += ` "pokedexNumber",\n`;
sql += ` pokemon,\n`;
sql += ` form,\n`;
sql += ` "canGigantamax",\n`;
sql += ` "regionToCatchIn",\n`;
sql += ` "gamesToCatchIn"\n`;
sql += `) VALUES\n`;
// Pokemon entries (without regional dex number)
sql += `-- Insert ${region} Pokémon entries\n`;
sql += `INSERT INTO pokedex_entries (\n`;
sql += ` "pokedexNumber",\n`;
sql += ` pokemon,\n`;
sql += ` form,\n`;
sql += ` "canGigantamax",\n`;
sql += ` "regionToCatchIn",\n`;
sql += ` "gamesToCatchIn"\n`;
sql += `) VALUES\n`;
const rows = pokemon.map(p => {
const form = p.form ? `'${p.form}'` : 'NULL';
// Use regionalDexGames for the database (regional dex availability)
// originGames column is for future origin dex feature
const gamesField = p.regionalDexGames || p.games; // Fallback to old 'games' column for compatibility
const gamesList = gamesField.split('|');
const gamesArray = `ARRAY[${gamesList.map(g => `'${g}'`).join(', ')}]`;
const rows = pokemon.map((p) => {
const form = p.form ? `'${p.form}'` : 'NULL';
// Use regionalDexGames for the database (regional dex availability)
// originGames column is for future origin dex feature
const gamesField = p.regionalDexGames || p.games; // Fallback to old 'games' column for compatibility
const gamesList = gamesField.split('|');
const gamesArray = `ARRAY[${gamesList.map((g) => `'${g}'`).join(', ')}]`;
return `(${p.pokedexNumber}, '${p.name}', ${form}, false, '${region}', ${gamesArray})`;
});
return `(${p.pokedexNumber}, '${p.name}', ${form}, false, '${region}', ${gamesArray})`;
});
sql += rows.join(',\n');
sql += '\nON CONFLICT ON CONSTRAINT unique_pokemon_form DO NOTHING;\n\n';
sql += rows.join(',\n');
sql += '\nON CONFLICT ON CONSTRAINT unique_pokemon_form DO NOTHING;\n\n';
// Regional dex numbers (separate table)
sql += `-- Insert ${region} regional dex numbers\n`;
// Regional dex numbers (separate table)
sql += `-- Insert ${region} regional dex numbers\n`;
const dexRows = pokemon
.filter(p => p.regionalNumber) // Only entries with regional dex numbers
.map(p => {
const formCondition = p.form
? `form = '${p.form}'`
: `form IS NULL`;
const dexRows = pokemon
.filter((p) => p.regionalNumber) // Only entries with regional dex numbers
.map((p) => {
const formCondition = p.form ? `form = '${p.form}'` : `form IS NULL`;
return ` ((SELECT id FROM pokedex_entries WHERE "pokedexNumber" = ${p.pokedexNumber} AND ${formCondition}), '${region}', ${p.regionalNumber})`;
});
return ` ((SELECT id FROM pokedex_entries WHERE "pokedexNumber" = ${p.pokedexNumber} AND ${formCondition}), '${region}', ${p.regionalNumber})`;
});
if (dexRows.length > 0) {
sql += `INSERT INTO regional_dex_numbers (\n`;
sql += ` pokedex_entry_id,\n`;
sql += ` region,\n`;
sql += ` dex_number\n`;
sql += `) VALUES\n`;
sql += dexRows.join(',\n');
sql += ';\n\n';
} else {
sql += '-- No regional dex numbers for this region\n\n';
}
if (dexRows.length > 0) {
sql += `INSERT INTO regional_dex_numbers (\n`;
sql += ` pokedex_entry_id,\n`;
sql += ` region,\n`;
sql += ` dex_number\n`;
sql += `) VALUES\n`;
sql += dexRows.join(',\n');
sql += ';\n\n';
} else {
sql += '-- No regional dex numbers for this region\n\n';
}
// Add metadata
sql += `-- Add metadata\n`;
sql += `INSERT INTO metadata (key, value) VALUES\n`;
sql += ` ('${region.toLowerCase()}_seeded', 'true'),\n`;
sql += ` ('${region.toLowerCase()}_seed_date', NOW()::TEXT),\n`;
sql += ` ('${region.toLowerCase()}_pokemon_count', '${pokemon.filter(p => !p.form).length}');\n`;
// Add metadata
sql += `-- Add metadata\n`;
sql += `INSERT INTO metadata (key, value) VALUES\n`;
sql += ` ('${region.toLowerCase()}_seeded', 'true'),\n`;
sql += ` ('${region.toLowerCase()}_seed_date', NOW()::TEXT),\n`;
sql += ` ('${region.toLowerCase()}_pokemon_count', '${pokemon.filter((p) => !p.form).length}');\n`;
// 4. Write file
const timestamp = new Date().toISOString().replace(/[-:T.]/g, '').slice(0, 14);
const filename = `${timestamp}_seed_${region.toLowerCase()}.sql`;
const outputPath = path.join(__dirname, '..', 'supabase', 'migrations', filename);
// 4. Write file
const timestamp = new Date()
.toISOString()
.replace(/[-:T.]/g, '')
.slice(0, 14);
const filename = `${timestamp}_seed_${region.toLowerCase()}.sql`;
const outputPath = path.join(__dirname, '..', 'supabase', 'migrations', filename);
fs.writeFileSync(outputPath, sql);
fs.writeFileSync(outputPath, sql);
console.log(`✓ Generated ${filename}`);
console.log(` - ${pokemon.length} Pokemon entries`);
console.log(` - ${dexRows.length} regional dex numbers`);
console.log(` - ${regionGames.length} games\n`);
console.log(`✓ Generated ${filename}`);
console.log(` - ${pokemon.length} Pokemon entries`);
console.log(` - ${dexRows.length} regional dex numbers`);
console.log(` - ${regionGames.length} games\n`);
return filename;
return filename;
}
// Run
const region = process.argv[2];
if (!region) {
console.error('Usage: node csv-to-migration.js <Region>');
console.error('Example: node csv-to-migration.js Kanto');
process.exit(1);
console.error('Usage: node csv-to-migration.js <Region>');
console.error('Example: node csv-to-migration.js Kanto');
process.exit(1);
}
generateMigration(region);
@@ -56,7 +56,6 @@
}
updateCatchRecord('toggle');
}
</script>
<div
+12 -14
View File
@@ -59,10 +59,8 @@
// Validation
$: hasAtLeastOneType =
pokedex.isLivingDex || pokedex.isShinyDex || pokedex.isOriginDex || pokedex.isFormDex;
$: hasDexScope =
!pokedex.gameScope || (pokedex.dexScopes && pokedex.dexScopes.length > 0);
$: canSubmit =
pokedex.name && pokedex.name.trim() !== '' && hasAtLeastOneType && hasDexScope;
$: hasDexScope = !pokedex.gameScope || (pokedex.dexScopes && pokedex.dexScopes.length > 0);
$: canSubmit = pokedex.name && pokedex.name.trim() !== '' && hasAtLeastOneType && hasDexScope;
$: if (pokedex.gameScope !== lastGameScope) {
const shouldResetDexes = mode === 'create' || hasSeenGameScope;
@@ -162,16 +160,14 @@
<option value={null}>All Games</option>
{#if loadingDexes}
<option disabled>Loading games...</option>
{:else if gameList.length > 0}
{#each gameList as game}
<option value={game.displayName}>{game.displayName}</option>
{/each}
{:else}
{#if gameList.length > 0}
{#each gameList as game}
<option value={game.displayName}>{game.displayName}</option>
{/each}
{:else}
{#each gameOrder.length > 0 ? gameOrder : Object.keys(gameDexes) as game}
<option value={game}>{game}</option>
{/each}
{/if}
{#each gameOrder.length > 0 ? gameOrder : Object.keys(gameDexes) as game}
<option value={game}>{game}</option>
{/each}
{/if}
</select>
</div>
@@ -181,7 +177,9 @@
<fieldset class="w-full">
<legend class="label">
<span class="label-text">Dex Scope</span>
<span class="label-text-alt text-error">{!hasDexScope ? 'Select at least one dex' : ''}</span>
<span class="label-text-alt text-error"
>{!hasDexScope ? 'Select at least one dex' : ''}</span
>
</legend>
{#if availableDexes.length === 0}
<p class="text-sm text-error">No dexes found for this game.</p>
@@ -8,9 +8,7 @@ import type { SupabaseClient } from '@supabase/supabase-js';
class PokedexEntryRepository {
constructor(private supabase: SupabaseClient) {}
private parseCatchInformation(
values: string[] | null
): Array<string | CatchInformationItem> {
private parseCatchInformation(values: string[] | null): Array<string | CatchInformationItem> {
if (!values) return [];
return values.map((value) => {
const trimmed = value.trim();
+1 -3
View File
@@ -84,9 +84,7 @@ export async function setPokedexDexScopes(
}
}
export async function listGameDexes(
supabase: SupabaseClient
): Promise<{
export async function listGameDexes(supabase: SupabaseClient): Promise<{
gameDexes: Record<string, GameDexRow[]>;
gameOrder: string[];
games: { displayName: string; releaseYear: number }[];
+51 -51
View File
@@ -24,62 +24,62 @@ type RegionalDexKey =
const gameToRegionalDexMap: Record<string, RegionalDexKey> = {
// Kanto region
'Red': 'kanto',
'Blue': 'kanto',
'Yellow': 'kanto',
'FireRed': 'kanto',
'LeafGreen': 'kanto',
Red: 'kanto',
Blue: 'kanto',
Yellow: 'kanto',
FireRed: 'kanto',
LeafGreen: 'kanto',
'LG: Pikachu': 'kanto',
'LG: Eevee': 'kanto',
// Johto region
'Gold': 'johto',
'Silver': 'johto',
'Crystal': 'johto',
'HeartGold': 'johto',
'SoulSilver': 'johto',
Gold: 'johto',
Silver: 'johto',
Crystal: 'johto',
HeartGold: 'johto',
SoulSilver: 'johto',
// Hoenn region
'Ruby': 'hoenn',
'Sapphire': 'hoenn',
'Emerald': 'hoenn',
'OmegaRuby': 'hoenn',
'AlphaSapphire': 'hoenn',
Ruby: 'hoenn',
Sapphire: 'hoenn',
Emerald: 'hoenn',
OmegaRuby: 'hoenn',
AlphaSapphire: 'hoenn',
// Sinnoh region
'Diamond': 'sinnoh',
'Pearl': 'sinnoh',
'Platinum': 'sinnoh',
'BrilliantDiamond': 'sinnoh',
'ShiningPearl': 'sinnoh',
Diamond: 'sinnoh',
Pearl: 'sinnoh',
Platinum: 'sinnoh',
BrilliantDiamond: 'sinnoh',
ShiningPearl: 'sinnoh',
// Unova region
'Black': 'unova_bw',
'White': 'unova_bw',
'Black2': 'unova_b2w2',
'White2': 'unova_b2w2',
Black: 'unova_bw',
White: 'unova_bw',
Black2: 'unova_b2w2',
White2: 'unova_b2w2',
// Kalos region - Note: All XY use all three sub-dexes
// We default to Central for simplicity
'X': 'kalos_central',
'Y': 'kalos_central',
X: 'kalos_central',
Y: 'kalos_central',
// Alola region
'Sun': 'alola_sm',
'Moon': 'alola_sm',
'UltraSun': 'alola_usum',
'UltraMoon': 'alola_usum',
Sun: 'alola_sm',
Moon: 'alola_sm',
UltraSun: 'alola_usum',
UltraMoon: 'alola_usum',
// Galar region
'Sword': 'galar',
'Shield': 'galar',
Sword: 'galar',
Shield: 'galar',
// Hisui region
'LegendsArceus': 'hisui',
LegendsArceus: 'hisui',
// Paldea region
'Scarlet': 'paldea',
'Violet': 'paldea'
Scarlet: 'paldea',
Violet: 'paldea'
};
/**
@@ -119,22 +119,22 @@ export function getRegionalDexFieldName(gameName: string): string | undefined {
// Map regional key to actual PokedexEntry field name (camelCase)
const fieldMap: Record<string, string> = {
'kanto': 'kantoDexNumber',
'johto': 'johtoDexNumber',
'hoenn': 'hoennDexNumber',
'sinnoh': 'sinnohDexNumber',
'unova_bw': 'unovaBwDexNumber',
'unova_b2w2': 'unovaB2w2DexNumber',
'kalos_central': 'kalosCentralDexNumber',
'kalos_coastal': 'kalosCoastalDexNumber',
'kalos_mountain': 'kalosMountainDexNumber',
'alola_sm': 'alolaSmDexNumber',
'alola_usum': 'alolaUsumDexNumber',
'galar': 'galarDexNumber',
'galar_isle_of_armor': 'galarIsleOfArmorDexNumber',
'galar_crown_tundra': 'galarCrownTundraDexNumber',
'hisui': 'hisuiDexNumber',
'paldea': 'paldeaDexNumber'
kanto: 'kantoDexNumber',
johto: 'johtoDexNumber',
hoenn: 'hoennDexNumber',
sinnoh: 'sinnohDexNumber',
unova_bw: 'unovaBwDexNumber',
unova_b2w2: 'unovaB2w2DexNumber',
kalos_central: 'kalosCentralDexNumber',
kalos_coastal: 'kalosCoastalDexNumber',
kalos_mountain: 'kalosMountainDexNumber',
alola_sm: 'alolaSmDexNumber',
alola_usum: 'alolaUsumDexNumber',
galar: 'galarDexNumber',
galar_isle_of_armor: 'galarIsleOfArmorDexNumber',
galar_crown_tundra: 'galarCrownTundraDexNumber',
hisui: 'hisuiDexNumber',
paldea: 'paldeaDexNumber'
};
return fieldMap[regionalKey];
@@ -47,7 +47,9 @@ export const PUT = async (event: RequestEvent) => {
.eq('userId', userId)
.is('pokedexId', null)
.eq('provider', provider)
.select('id, provider, enabled, fileName, folderId, path, metadata, lastExportedAt, lastError')
.select(
'id, provider, enabled, fileName, folderId, path, metadata, lastExportedAt, lastError'
)
.maybeSingle();
if (error) {
@@ -31,9 +31,7 @@ export const GET = async (event: RequestEvent) => {
const repo = new CatchRecordRepository(event.locals.supabase, userId, pokedexId);
const catchData = await repo.findAll();
const sortedData = catchData.sort(
(a, b) => Number(a.pokemonId) - Number(b.pokemonId)
);
const sortedData = catchData.sort((a, b) => Number(a.pokemonId) - Number(b.pokemonId));
return json(sortedData);
} catch (err) {
console.error(err);
@@ -159,10 +157,7 @@ export const POST = async (event: RequestEvent) => {
try {
await exportPokedexIfConfigured(event.locals.supabase, userId, pokedexId);
} catch (exportError) {
console.error(
'Failed to export pokedex after per-record catch updates:',
exportError
);
console.error('Failed to export pokedex after per-record catch updates:', exportError);
}
return json(insertedRecords);
}
+1 -5
View File
@@ -204,11 +204,7 @@
</h3>
<PokedexForm bind:pokedex={formData} {mode} onSubmit={handleSubmit} onCancel={closeModal} />
</div>
<button
type="button"
class="modal-backdrop"
aria-label="Close modal"
on:click={closeModal}
<button type="button" class="modal-backdrop" aria-label="Close modal" on:click={closeModal}
></button>
</div>
{/if}