pokemon-showdown-client/build-tools/build-learnsets
Guangcong Luo a10821ab8b
Some checks are pending
Node.js CI / build (22.x) (push) Waiting to run
Update to ESLint 9 (#2326)
This finally removes the tslint dependency and switches to eslint.

There are a lot of other changes here, too, to bring the codebase up to
server standards. TSLint never had much in the way of indentation
enforcement.

Not very happy about eslint splitting itself up over 6 dependencies,
or its documentation over three websites, nor how poorly documented the
new flat config is, but I mean, eslint's gonna eslint. Customizing
would be even harder if we tried to use Biome or something. They mostly
seem to go full Prettier.

Also here are some changes to our style rules. In particular:

- Curly brackets (for objects etc) now have spaces inside them. Sorry
  for the huge change. ESLint doesn't support our old style, and most
  projects use Prettier style, so we might as well match them in this way.
  See https://github.com/eslint-stylistic/eslint-stylistic/issues/415

- String + number concatenation is no longer allowed (except in ES3
  code). We otherwise now consistently use template strings for this.
2025-02-25 20:05:32 -08:00

143 lines
4.6 KiB
JavaScript
Executable File

#!/usr/bin/env node
/**
* This script parses index.html and sets the version query string of each
* resource to be the MD5 hash of that resource.
* It also updates news and the learnsets-g6.js file.
*
* On the live web server, this script is set as the following git hooks:
* post-commit, post-checkout, post-merge, post-rewrite
*/
"use strict";
const path = require('path');
const fs = require('fs');
const thisFile = __filename;
const thisDir = __dirname;
const rootDir = path.resolve(thisDir, '../play.pokemonshowdown.com');
const Dex = require('../caches/pokemon-showdown/dist/sim/dex').Dex;
const toID = Dex.toID;
function updateLearnsets(callback) {
const numberRegExp = /^[0-9]*/;
const reservedRegExp = /^(return|\d.*)$/g; // `return` is the only ES3+ reserved keyword that (currently) raises conflicts
const alphabetize = (a, b) => a.localeCompare(b);
const formatLset = function (lset) {
const secondChar = lset.charAt(1);
if (secondChar !== 'L') return lset;
const firstFragment = lset.substr(0, 2);
const levelFragment = lset.substr(2).match(numberRegExp)[0];
const sortFragment = lset.substr(2 + levelFragment.length);
return firstFragment + levelFragment.padStart(3, '0') + sortFragment;
};
const Pokedex = Dex.data.Pokedex;
const Learnsets = Dex.data.Learnsets;
const newLearnsetsG6 = {};
for (const speciesid in Learnsets) {
const newLearnset = {};
const fullLearnset = Learnsets[speciesid].learnset;
for (const moveid in fullLearnset) {
newLearnset[moveid] = fullLearnset[moveid].filter(
learned => "87".includes(learned.charAt(0))
).map(
formatLset
).sort(
alphabetize
);
}
newLearnsetsG6[speciesid] = { learnset: newLearnset };
}
const buf = [];
const pokemonList = Object.keys(Pokedex).map(speciesId => Pokedex[speciesId]).sort((a, b) => {
// Missingno. goes first (zeroth); afterwards, CAP in descending dex order (increasingly negative)
// Finally, standard Pokémon in ascending dex order
if (a.num <= 0 && b.num > 0) return -1;
if (b.num <= 0 && a.num > 0) return 1;
if (a.num <= 0 && b.num <= 0) return b.num - a.num;
return a.num - b.num;
}).map(species => toID(species.name));
for (const speciesid of pokemonList) {
const entry = newLearnsetsG6[speciesid];
if (!entry || !entry.learnset) continue;
const lsetSerialized = '{' + Object.keys(entry.learnset).sort(alphabetize).map(
moveid => moveid.replace(reservedRegExp, '"$1"') + ':' + JSON.stringify(entry.learnset[moveid])
).join(',') + '}';
buf.push(speciesid + ':{learnset:' + lsetSerialized + '}');
}
const writeStream = fs.createWriteStream(path.join(rootDir, 'data', 'learnsets-g6.js')).on('error', callback);
writeStream.write('exports.BattleLearnsets = {\n\t' + buf.join(',\n\t') + '\n};\n');
writeStream.end(callback);
}
let indexStats, updateStats, indexMTime, updateMTime;
try {
indexStats = fs.statSync(path.join(rootDir, 'index.html'));
indexMTime = indexStats.mtime.getTime();
} catch (err) {
if (err.code !== 'ENOENT') throw err;
// It doesn't exist currently, but it will by the end of the script execution.
// Any other error is unacceptable and will throw.
}
{
updateStats = fs.statSync(thisFile);
updateMTime = updateStats.mtime.getTime();
}
// update learnsets-g6
process.stdout.write("Updating file `data/learnsets-g6`... ");
let learnsetsStats;
let learnsetsG6Stats;
let learnsetsG6ToUpdate = true;
try {
learnsetsStats = fs.statSync(path.join(rootDir, 'data', 'learnsets.js'));
} catch {
// Couldn't find learnsets.js, but that's not the end of the world: skip to next task.
console.error("Couldn't find `data/learnsets.js`. Task aborted.");
learnsetsG6ToUpdate = false;
}
if (learnsetsG6ToUpdate) {
try {
learnsetsG6Stats = fs.statSync(path.join(rootDir, 'data', 'learnsets-g6.js'));
} catch (err) {
if (err.code === 'ENOENT') {
// It doesn't exist currently, but it will by the end of the script execution.
} else {
// Any other error is unacceptable for learnsets-g6.js update: skip to next task.
console.error("Failed to read `data/learnsets-g6.js`. Task aborted.");
learnsetsG6ToUpdate = false;
}
}
}
if (learnsetsG6ToUpdate && (
!indexStats || !learnsetsG6Stats || indexMTime < updateMTime || indexMTime < learnsetsStats.mtime.getTime() ||
indexMTime < learnsetsG6Stats.mtime.getTime()
)) {
// Only merge learnsets.js with learnsets-g6.js if any of those files, or this one, have been modified recently (or if we don't know what "recently" means)
updateLearnsets(err => {
if (err) {
let stack = err.stack || '';
stack = "File `data/learnsets-g6` failed to update.\n" + stack;
console.error(stack);
} else {
console.log("DONE");
}
});
} else {
console.log("CACHED");
}